訓練家的佈弱格-Patch1.2

The BLOG of trainer



編輯日期:2021-10-04 10:10

類型:NSR 150
作者:羽山
文章時間:2021-10-04 10:10:54
瀏覽人數:3895人
標題: NSR150 自製可程式 CDI 程式說明
網址:https://3wa.tw/blog/blog.php?id=1934
內容:
 

 

 這篇文章針對 Nodemcu V2 開發的 NSR-150 可程式 CDI 說明

 

Nodemcu 使用接腳(V0.4版):

D1:脈波 PWM 輸入

D4:RC 訊號輸出

D6:點火 輸出

 

可程式的 CDI 如果只是拿來點火讓引擎運轉,最重要的是 D1 接腳的脈波 PWM 輸入

 (此圖是追風的轉子)

當轉子在旋轉時,轉子外圍的凸起的鐵塊,就會被脈波線圈接收器接收(俗稱黑豆乾或黑豆仔)

 

 黑豆乾負責偵測凸台,每次凸台經過時線圈就會產生一個彈跳

 

 脈波的彈跳

 

 

 同學在驗證波型時,要考慮是先負再正,還是先正才負,延遲的時間會不一樣

所以首先先抓出原廠CDI每個轉速從偵測到凸台,到點火的這段時間是延遲多少

那不論是正、是負,照原本的時間作好Delay後點火,就能作到原廠CDI的騎乘感受^^

 

 手冊中的波型

 

 

 實際量測到的結果(X軸是轉速,Y軸是 65°-(點火時間-凸台時間)/360

 又不知不覺得寫的有點遠,感覺很重要~

 

接下來作程式說明(V0.4版):

程式碼位置:https://github.com/shadowjohn/NSR-150_CDI

/*
 * NSR-150 可程式 CDI
 * Version: V0.4
 * V0.04 版功能是將二期 RC 改成一期RC的騎乘體驗感
 * 接腳如下:
 *   D1 凸台 
 *   D4 RC 訊號輸出
 *   D6 點火 
 *   
 * Release Date: 2021-09-26
 * Author: 羽山秋人 (https://3wa.tw)
 * Author: @FB 田峻墉
 */
#include <Arduino.h>

/*
轉速   60 轉 =  每分鐘    60 轉,每秒  1    轉,1轉 = 1          秒 = 1000.000 ms = 1000000us
轉速   100 轉 = 每分鐘   100 轉,每秒  1.67 轉,1轉 = 0.598802   秒 =  598.802 ms =  598802us
轉速   200 轉 = 每分鐘   200 轉,每秒  3.3  轉,1轉 = 0.300003   秒 =  300.003 ms =  300003us
轉速   600 轉 = 每分鐘   600 轉,每秒  10   轉,1轉 = 0.1        秒 =  100.000 ms =  100000us
轉速  1500 轉 = 每分鐘  1500 轉,每秒  25   轉,1轉 = 0.04       秒 =   40.000 ms =   40000us
轉速  6000 轉 = 每分鐘  6000 轉,每秒  60   轉,1轉 = 0.01666... 秒 =   16.667 ms =   16667us
轉速 10000 轉 = 每分鐘 10000 轉,每秒 166.6 轉,1轉 = 0.00599... 秒 =    5.999 ms =    5999us
轉速 14000 轉 = 每分鐘 14000 轉,每秒 233.3 轉,1轉 = 0.0042863. 秒 =    4.286 ms =    4286us
轉速 14060 轉 = 每分鐘 14060 轉,每秒 240   轉,1轉 = 0.0041667. 秒 =    4.167 ms =    4167us
轉速 16000 轉 = 每分鐘 16000 轉,每秒 266.6 轉,1轉 = 0.0037500. 秒 =    3.750 ms =    3750us 
*/
// 宣告接腳
// 宣告成 const 代表變數值不會變動
const int ToPin = D1;  //凸台
const int RCPin = D4;  //RC訊號
const int FirePin = D6;  //點火

// 宣告成 volatile 代表該變數變動會很頻繁
// now_degree 是指目前的角度,初始值先給個 12,引擎一轉就會一直變動
volatile float now_degree = 12;
// NSR維修手冊裡的圖
// volatile const float degree[16] = {12, 12, 12, 12, 17, 29, 29, 25, 23, 20, 17, 13, 10, 8, 8, 8};

// 這個是羽山原本CDI量到的
volatile const float degree[16] = {10, 12, 12, 25, 26, 23, 20, 16, 13, 9, 9, 9, 8, 8, 8, 8};

// CDI_DELAY = (1000000.0 / (float(rpm) / 60.0)) * ((fullAdv - r)/ 360.0);
// 凸台->點火這段等待時間(CDI_DELAY) = 每個轉速時間 * ((65-該轉速的角度)/360)
// 這樣算起來的數值就等於羽山量到的delay時間
volatile const float fullAdv = 65;

// 是否點火,有經過凸台後,isFiring 才會變  true,點完後改回 false
volatile bool isFiring = false;

// Nodemcu由於不適合作多工開發,所以一般都使用外部中斷 (ISR) 來處理其他接腳觸發的事件
// 同時在 ISR 的過程,也不適合在裡面寫 delay、delayMicroseconds,會影響 ISR 取值的正確性
// 所以 ISR 的過程,都會拿來更新 Global 總體變數
// 最後在 loop() 裡,才會處理延遲、點火等工作
volatile unsigned long C = micros(); //紀錄碰到凸台的時間
volatile unsigned long C_old = 0; //紀錄上次碰到凸台的時間
volatile unsigned long rpm = 0; //紀錄當前 RPM
volatile unsigned long RPM_DELAY = 0; //凸台跟上一次凸台相距時間,用來推算當前RPM多少
volatile unsigned int isShowCount = 0; //在 loop 中每轉 100次才回報一次到 Serial 觀看
bool isFirstRC_Flag = true; //當 kick start 時,RC 全開一次的旗標
// 電開機時,isFirstRC_Flag 預設 true,作完就 false,只作一次
// (此變數其實有點多寫了,因為放在 setup 裡,本身也只會執行一次...)

volatile double CDI_DELAY = -1; //計算每一圈碰到凸台後,要延遲多久時間才點火 us

// 這裡就是 ISR 的事件,當凸台發生一個彈跳,且為 RISING,就會觸發這個function (countup)
// 新版的Nodemcu程式,在宣告 ISR 時,要多寫「ICACHE_RAM_ATTR」
// 不然編譯會無法過關
void ICACHE_RAM_ATTR countup() {  //For newest version
  //收到CDI點火,扣掉偵測到凸台RISING時間
  //只要是Rising就是Fire

  C = micros();
  // 不可能有超過 17000rpm 的狀況
  // (1/(17000/60) *1000 * 1000 = 3529
  // 當 17000轉,每一圈轉速只有 3529us,相當快,比這個數值更小,轉速就更高
  RPM_DELAY = C - C_old;
  if(RPM_DELAY < 3500) {
    //超過 17000rpm 了
    return;
  }
  //其實還是要限轉,NSR也不可能拉轉超過17000rpm,超過就當作凸台偵測異常
  //保守的同學,其實超過 13000rpm 就可以跳過了,甚至 12000rpm,自己再換算一下

  if(RPM_DELAY > 598802) {
    //低於 100rpm
    C_old = C;
    rpm = 0;
    return;
  }      
  // 過慢的轉速,也跳過,不能永無止盡的等下去,低於 100rpm 跳過當作熄火
  rpm = 60000000UL / RPM_DELAY; //計算當前轉速,如1300就 1300rpm
  C_old = C; //上一次的凸台時間,改成現在的時間
  if(rpm>17000)
  {
    //不可能的
    //rpm = 0;
    return;
  }
  //再保護一次,如果超過 17000rpm,就跳過不點火
  if(rpm<80)
  {
    //太低了,熄火吧
    //rpm = 0;
    return;
  }
// 再保護一次,如果低於 80rpm,就跳過不點火
if (rpm != 0)   {
    //以下備註用 2500rpm 當說明
    int index = (int)rpm / 1000;    //將轉速除1000,轉整數,就可以得到現在是對應哪個角度,如 2500rpm,index : 2
    int index_n = index + 1;         //抓下一個角度,如 2500rpm,index=2+1 = 3
    index = (index >= 16) ? 15 : index; //我只有定 16組,0~15
    if (index_n >= 16) index_n = 15; //最後一組也不能超過 16,0~15
    long s = index * 1000;   // 2000
    long e = index_n * 1000;  // 3000
    long ss = degree[index] * 1000;   // 12*1000 = 12000
    long ee = degree[index_n] * 1000; // 25*1000 = 25000
    double r = map(int(rpm), s, e, ss, ee) / 1000.0;    // map(2500,2000,3000,12000,25000) / 1000.0 = 18.5
    now_degree = r;   //所以在 2500rpm ,得 18.5°

    //新的 CDI 計算方法
    //delay = adv/360*轉速一圈時間
    //rpm 一圈時間 * r * 360)
    //計算每一圈碰到凸台後,要延遲多久時間才點火 us
    CDI_DELAY = (1000000.0 / (float(rpm) / 60.0)) * ((fullAdv - r)/ 360.0);
    isFiring = true;
    //算出了點火的延遲時間 CDI_DELAY,把 isFiring 設成允許點火
    //改成在 loop 裡執行
    //delayMicroseconds(long(CDI_DELAY));
    //digitalWrite(FirePin, HIGH);
    //點火持續時間
    //固定點 200us    
    //delayMicroseconds(200);
    //digitalWrite(FirePin, LOW);
  }
}



void setup() {
  Serial.begin(250000);  //胞率設大一些,用電腦看好像比較流暢
  Serial.println("Counting...");

  // 宣告凸台角(D1),為 INPUT_PULLUP
  pinMode(ToPin, INPUT_PULLUP);

  // 偽造給 RC 輸出腳的訊號,為 OUTPUT
  pinMode(RCPin, OUTPUT);

  // 點火腳 D6,也是 OUTPUT,要去觸發 2SC1815 再觸發 SCR
  pinMode(FirePin, OUTPUT);  

  // 註冊凸台中斷
  // 使用 digitalPinToInterrupt 才能自動幫你把接角換成 attachInterrupt 的腳位
  // 如果使用 Arduino UNO,要注意可用的ISR腳很少…
  attachInterrupt(digitalPinToInterrupt(ToPin), countup, RISING); //RISING

  // 剛開機,不準點火
  digitalWrite(FirePin, LOW);  

  // 開機後,觸發讓 RC 全開一次
  if(isFirstRC_Flag == true)
  {
    isFirstRC_Flag=false;
    // 開機後,RC全開,再關上一次
    for(int i=0;i<250;i++)
    {
      // 50 % duty , 10000rpm , 250 次,就可以全開
      // 250次約耗時 1.5 秒
      // 要注意不能讓程式在  setup() 階段作太久作太多事,不然 nodemcu 會以為當機就停了)
      // 1個 pwm 用了  6ms (約等於 10000rpm)
      digitalWrite(RCPin, HIGH);
      delayMicroseconds(3000); //3ms
      digitalWrite(RCPin, LOW);
      delayMicroseconds(3000); //3ms    
    }
  }
  digitalWrite(RCPin, LOW);
  // 只要不繼續給  pwm 訊號,RC 電腦就會自己關閥門了
}

void loop() {

  // put your main code here, to run repeatedly:

  // 這個 isShowCount 是用來給電腦看了,插 USB 在 Serial 每 loop 100 次,就會回饋一筆電腦上看
  // 每次加1,超過100歸零
  if (isShowCount > 100)
  {
    //display_rpm();
    isShowCount = 0;    

    // 觀察 Global 總體變數, rpm、now_degree、CDI_DELAY
    // 這樣作好的 CDI,插上訊號產生器,每個轉速看一看,是不是跟在車上量到的 delay 時間
    // 時間一致就是正確,就不用一直拿到車上拉轉量延遲時間...
    Serial.print(rpm);
    Serial.print(" , ");
    Serial.print(now_degree);
    Serial.print(" , ");
    Serial.print(CDI_DELAY);
    Serial.println("");
  }
  isShowCount++;  
  
  //如果點火旗標變 true,就代表凸台抓到了
  if(isFiring == true)
  {
    isFiring = false;
    // 由於要讓2期的RC有1期 RC 的驗乘體驗
    // 低於5000完全不需要送出 RC 訊號
    // 5000以上,收多少訊號,就給多少到RC    
    /*
轉速  6000 轉 = 每分鐘  6000 轉,每秒  60   轉,1轉 = 0.01666... 秒 =   16.667 ms =   16667us
轉速 10000 轉 = 每分鐘 10000 轉,每秒 166.6 轉,1轉 = 0.00599... 秒 =    5.999 ms =    5999us
     */

    if(rpm>=5000)
    {
      // 超過5000轉,RC 訊號給 HIGH
      // 如果你希望還是跟二期 RC 一樣,那把 rpm>=5000 改成  rpm>=0 即可
      // 但如果你本來就是一期的 RC 電腦,那就只有一期電腦的特性,只有二期才能模擬一期的功能
      digitalWrite(RCPin, HIGH); 
    }    
    // 開始延遲,凸台->點火的延遲時間
    delayMicroseconds(long(CDI_DELAY));
    digitalWrite(FirePin, HIGH);  //點火
    // 點火持續時間
    // 固定點 200us    
    delayMicroseconds(200); //點200us
    digitalWrite(FirePin, LOW);   //停止點火
    digitalWrite(RCPin, LOW);    //RC 訊號給 Low
  }   
}

 

寫完了,扣掉囉哩八嗦的註解後,程式不長,也很好理解

回頭再看程式,感覺還有很多小細節可以改進呢^^"

特別是在換算角度的部分,寫的稍醜了點

不過用訊號產生器抽樣挑了幾個轉速,換算後 CDI_DELAY 時間,跟實際量到的數值都一致

 

羽山也順順騎上下班一個禮拜,體驗相當好~

冷車超級好發,踩一下馬上咚咚咚就轉起來,完全不熄火^~^

 

未來也許可以改改角度,看看會不會更有樂趣

 

首頁  上十頁  上一頁  1 下一頁    最末頁 (總共有...1頁)

第 1 頁

有話要說  看留言 【6】
其他分類
當月訓練
(2021-10-27)
【電腦應用】羅技 M585 滑鼠按鍵維修

(2021-10-25)
【網頁設計心得】live2d 空洞臉問題修正

(2021-10-25)
【NSR 150】NSR150 發電線圈、脈波線圈、激磁線圈

(2021-10-25)
【NSR 150】NSR150 碼表溫度計、油表電阻

(2021-10-25)
【機車綜合相關】SJ4000 4K Air 行車紀錄器

(2021-10-17)
【酷龍 150】酷龍 150 換機油 34015km

(2021-10-04)
【電腦應用】Netflix 字幕加大心得

(2021-10-04)
【NSR 150】NSR150 自製可程式 CDI 程式說明

(2021-10-02)
【KTM 390】KTM RC390 更換打檔桿油封

最新訓練
(2024-03-09)
【HONDA CBR1000RR】CBR1000RR 原廠管

(2024-02-24)
【NSR 150】NSR150 整理後卡鉗

(2024-02-14)
【機車綜合相關】買了牧田的打氣機與心得

(2024-02-10)
【NSR 150】NSR150 整流器接頭重作

(2024-02-09)
【機車綜合相關】鋰電、鋰鐵電池、電源充電器

(2024-01-07)
【NSR 150】NSR150 貨架耳朵斷裂維修

(2024-01-06)
【KTM 390】KTM Duke390 維修發電問題 電盤內仁

(2023-12-29)
【NSR 150】NSR150 改可調拉桿

(2023-11-20)
【遊戲 / 生活】防盜洗手罐

(2023-11-17)
【KTM 390】KTM Duke390 製作鋰鐵電池