• 車種別
  • パーツ
  • 整備手帳
  • ブログ
  • みんカラ+
.
イイね!
2020年03月23日

PICとステッピングモーターを使った自作タコメーター ②プログラム編

②はプログラム編です。

ステッピングタコメータ-を作るにあたって、
rilassaruさんのブログ
http://rilassaru.blog.jp/archives/cat_46866.html
参考にさせて頂きました。
バイクのタコメーターやCDI、YPVSコントローラまで作ってしまう凄い人でした・・・!
プロの人かな?凄すぎる・・・


C言語という、人間にとってわかりやすい
(全然わかりやすくない・・・)
言語で記述したコードを、機械語に変換してPICに書き込みます。
alt

今回はPICを使うので、マイクロチップ社のMPLAB X IDEというソフトを利用し、
同じくマイクロチップ社が販売しているPICkit3を使用して書き込みました。
1つのソフト上でコードを書いてPICに書き込みまで完結するので、試行錯誤するときには手間が少なくていいですね。

alt

赤いフリスクみたいな大きさの奴です。


早速プログラム全文をどうぞ



/* ------------------------------------------------------------------
PIC12F1822 Pinout
+--------+
Vdd |1 8| GND
Captre-> RA5 |2 7| RA0 -> PLS
LED1 -> RA4 |3 6| RA1 -> DIR
MCLR -> RA3 |4 5| RA2 -> LED2
+--------+
-------------------------------------------------------------------*/


/* ------------------------------------------------------------------
ここから↓はヘッダーの記述やPICの設定、defineの記述、変数の初期化です。
例えば #define PLS LATA0と書かれている行は
PLSと書くとLATA0と置き換えられるので
以降はPLSと書くだけでパルス入力のLATA0として認識してくれるようになるワケです。
意味わかんないよね
-------------------------------------------------------------------*/


#include // include standard header file
#include
// set Config bits
#pragma config FOSC=INTOSC // Oscillator Selection bits
#pragma config PLLEN=OFF // PLL Enable bit
#pragma config WDTE=OFF // Watch dog timer enable bit
#pragma config MCLRE=ON // MCLEAR enable bit
#pragma config CLKOUTEN=OFF // Clock Out Enable bit
#pragma config IESO=OFF // Internal External Switchover bit
#pragma config FCMEN=OFF // Fail-Safe Clock Monitor Enable bit
#pragma config CP=OFF // Code Protection bit
#pragma config CPD=OFF // Data Code Protection bit
#pragma config BOREN=ON // Brown-out Reset Enable bits
#pragma config WRT=OFF // Flash Memory Self-Write Protection bits
#pragma config STVREN=ON // Stack Overflow/Underflow Reset Enable bit
#pragma config BORV=LO // Brown-out Reset Voltage Selection bit
#pragma config LVP=OFF // Low-Voltage Programming Enable bit

// Definitions
#define _XTAL_FREQ 8000000 // this is used by the __delay_ms(xx) and __delay_us(xx) functions

#define PLS LATA0
#define DIR LATA1
#define LED2 LATA2
#define MCLR RA3
#define LED1 LATA4
#define INPULSE RA5

#define LED00() {LED1=0;LED2=0;}
#define LED01() {LED1=0;LED2=1;}
#define LED10() {LED1=1;LED2=0;}
#define LED11() {LED1=1;LED2=1;}

#define TURN_L 0
#define TURN_R 1

#define AVE1 20 //ave RPM
#define AVE2 1 //raw RPM
#define diff_R 150 //Difference RPM_R
#define diff_L 150 //Difference RPM_L



//VariableSM_PULSE_INTERVAL
unsigned int DIR_RAW = 0;
unsigned int DIR_BEF = 0;
unsigned int STARTUP = 0;
static unsigned int PLS_CNT = 0;


//7000rpm 176degrees
//?RPM / 20 ? degrees
//1000rpm=50step
#define RPM600 30
#define SHIFTLAMP1 300 //300 = 6000rpm
#define SHIFTLAMP2 350 //350 = 7000rpm
#define SHIFTLAMP3 700 //not used
#define RPMMAX 475 //475 = 9500rpm


// Global
unsigned int gPos = 0;
unsigned int gSMTarget = 0; //raw
unsigned int gSMTarget2 = 0; //filter
static unsigned int gSMTargetDIF = 0;
unsigned int REFRESH_COUNT = 0;

//Average
unsigned int e0 = 0;
unsigned int e1 = 0;
unsigned int e2 = 0;
unsigned int e3 = 0;
unsigned int e4 = 0;
unsigned int e5 = 0;
unsigned int e6 = 0;
unsigned int e7 = 0;
unsigned int e8 = 0;
unsigned int e9 = 0;
unsigned int e10 = 0;
unsigned int e11 = 0;
unsigned int e12 = 0;
unsigned int AVERAGE = 0;


/* ------------------------------------------------------------------
PIC Interrupt
ここのvoid interrupt Interrupt()
はPICのCCPのキャプチャ機能を使った周波数の測定です。
点火パルスの間隔から周波数が得られるので、ハチロクのタコメーターのスケールと
ステッピングモーターの作動角に合わせた係数を掛けて、
gSMTargetを求める処理になります。
-------------------------------------------------------------------*/
void interrupt Interrupt()
{
if(CCP1IF)
{
unsigned int ccpr1;

// Reset Timer1
TMR1H=0;
TMR1L=0;

ccpr1 = ((CCPR1H * 0x100)|CCPR1L) + 1; // F(Hz)
gSMTarget = (unsigned int)(1522000UL/ccpr1);
// 1522000をかけると、ハチロクのメーターパネルと大体一致しました。目検です(笑)

if(gSMTarget<=RPM600)
//パルス間隔が広すぎるとメモリがオーバーフローして不安定になるので、
//600回転以下は強制的に0に変換します。

{
gSMTarget = 0;
LED11()
}
if(gSMTarget>RPM600)
{
LED00()
}
if(gSMTarget>=SHIFTLAMP1)
//上で定義したSHIFTLAMP1の回転を超えたらLEDの左側だけ1(ON)にします。

{
LED10()
}
if(gSMTarget>=SHIFTLAMP2)
//SHIFTLAMP2の回転を超えたら、右側のLEDも1(ON)にします。

{
LED11()
}
if(gSMTarget>RPMMAX)
//針が回りすぎるとメーターのケースに針が激突するので、RPMMAX以上はRPMMAXで固定します

{
gSMTarget = RPMMAX;
LED11()
}
CCP1IF = 0;
}

// Timer1: Timeout means engine stop 
if(TMR1IF) {
gSMTarget = 0;
if(0 == gPos) {
}
TMR1IF = 0;
LED00()

}
}

/* ------------------------------------------------------------------
Rotate stepping motor 0 R 1 L
ここがステッピングモータを回転させるコードです。
最初にDIRで回転方向を右か左か指定するIF文があり、
PLSを1(ON)→0(OFF)することでモータードライバが1ステップだけ動きます。
10ステップ動かしたいときは、void rotateが10回実行されます。
-------------------------------------------------------------------*/
void rotate(int dir)
{
//A4988 DIR 0or1
//メインの処理から右回転ならdirが0、左回転ならdirが1になって実行されます。
if(dir)
{
DIR = 0;
DIR_RAW = 0;
}
else
{
DIR = 1;
DIR_RAW = 1;
}





#define SM_PULSE_INTERVAL 1300
//1300マイクロ秒間隔を開けてステップさせます。速すぎると脱調して針がずれます。

#define V_INTERVAL 2000
//駆動間隔可変用の値です。

if(STARTUP == 2)__delay_us(950);
//STARTUP==2  0点出しの時だけ950usで固定

else
{
if(STARTUP == 1)__delay_us(950);
//STARTUP==1 オープニングは950us&可変間隔を利用

else __delay_us(SM_PULSE_INTERVAL);
//STARTUP==0 通常はSM_PULSE_INTERVALの1300と可変間隔を利用


//回転方向が前回の処理から変わったら、PLS_CNTを0にして可変間隔を再適用
if(DIR_RAW == DIR_BEF)
{}
else{
PLS_CNT = 0;
}

PLS_CNT++;
DIR_BEF = DIR_RAW;





switch (PLS_CNT) {
case 0:
__delay_us(V_INTERVAL);
break;
case 1:
__delay_us(V_INTERVAL);
// 1ステップ目は2000マイクロ秒待機
break;
case 2:
__delay_us(V_INTERVAL/5);
// 2ステップ目は2000/5=400マイクロ秒待機
break;
case 3:
__delay_us(V_INTERVAL/50);
// 3ステップ目は2000/50=40マイクロ秒待機
break;
case 4:
__delay_us(V_INTERVAL/500);
// 4ステップ目は2000/500=4マイクロ秒待機
break;
default:
break;
}
}

//A4988 rotate pulse  
//ここが実際にモーターを回すコード!!!!!!

PLS=1;
__delay_us(5);
PLS=0;

}




/* ------------------------------------------------------------------
initialize stepping motor and startup demo
0点出しとオープニングです。
位置センサのついてないステッピングモーターは、起動したときの現在地がわからない状態なので
左のストッパーに押し当てて0点出しをしています。
そのあとのLEDが光ったり振り切る動作は飾りです(笑)
一応シフトランプ1の回転数と、シフトランプ2の回転数を指示する動きにしたので
意味がなくは無いですね
-------------------------------------------------------------------*/
void startupdemo()
{
int ii;
int jj;
PLS_CNT = 100;
STARTUP = 2;
// delay 0.5sec. for battery less.
__delay_ms(500);

LED11()
//500ステップ左回転
for(jj=0;jj<5;jj++)
{
for(ii=0;ii<100;ii++)
rotate(TURN_L);
__delay_ms(20);
}

//1回目よりもゆっくり20回転させて、確実に0点出し
for(ii=0;ii<20;ii++)
{
rotate(TURN_L);
__delay_ms(10);
}

//0点出しが終わったらgPosに針の位置0を記憶させる
gPos == 0;
LED00()
PLS_CNT = 0;
STARTUP = 1;


// startup demo ここからオープニング

//0→MAX右回転 MAX→0左回転
for( ii = 0; ii < RPMMAX; ii++ )
{ rotate( TURN_R );}
for( ii = 0; ii < RPMMAX; ii++ )
{ rotate( TURN_L );}

//シフトランプ1まで右回転
for( ii = 0; ii < SHIFTLAMP1; ii++ )
{ rotate( TURN_R );}

//シフトランプ1のLEDを10回点滅
for(ii = 0; ii < 10 ; ii++)
{
LED10()
__delay_ms(40);
LED00()
__delay_ms(40);
}

//シフトランプ2まで右回転
for( ii = 0; ii < (SHIFTLAMP2 - SHIFTLAMP1); ii++ )
{ rotate( TURN_R );}

//シフトランプ2のLEDを10回点滅
for(ii = 0; ii < 10 ; ii++)
{
LED01()
__delay_ms(40);
LED00()
__delay_ms(40);
}

//gPosに現在の位置、シフトランプ2の位置を記憶させ、通常動作へ入る
gPos = SHIFTLAMP2;
STARTUP = 0;

}


/* ------------------------------------------------------------------
main
ここがメインになっていて、上から順番にPICの初期設定、メモリの初期化
0点出しとオープニングを実行したあと、while(1)の無限ループに入ります。
-------------------------------------------------------------------*/
void main ( )
{

//ここから下はPICの初期設定、メモリの初期化の処理です。

int ii;

OPTION_REG = 0b00000000; //
WPUA = 0b00000000; // pull-up pins

// set up oscillator control register
OSCCON= 0b01110010; // 8Mhz
//OSCCON= 0b00011010; // 16Mhz
//OSCCON= 0b11110000; // 32Mhz

// Set up I/O pins
// PORT A Assignments
APFCON= 0b00000001; // CCP1 function is on RA5
TRISA= 0b00101000; //
PORTA= 0b00000000; // Reset port
ANSELA= 0b00000000; // Disable analog input
LED11()

// Timer1 Clock Source Select bits
T1CONbits.TMR1CS=0x00; // 00 = Timer1 clock source is instruction clock (FOSC/4)
T1CONbits.T1CKPS=0x01; // Timer1 Input Clock Prescale Select bits 1:2 Prescale value
T1CONbits.T1OSCEN=0; // Dedicated Timer1 oscillator circuit disabled
T1CONbits.nT1SYNC=1; // 1 = Do not synchronize external clock input
T1CONbits.TMR1ON=0; // Timer1 On bit

// Set up Capture input
CCP1CON= 0b00000100; // Capture mode: every falling edge
//CCP1CON= 0b00000101; // Capture mode: every rizing edge

// Reset CCP1/Timer interrupt flag
CCP1IF=0;
TMR1IF=0;

startupdemo();

// Enable Interrupt
PEIE=1; // Enable peripheral interrupt
GIE=1; // enable global interrupt
CCP1IE=1; // enable the CCP1 interrupt
TMR1IE=1; // enable Timer1 intterupt on
TMR1ON=1; // Timer1 On




//main loopここのwhile(1)という文が無限ループになっていて、電源が切れるまで実行され続けます。

while(1) //L 1 R 0
{

//Average 平均化処理 e〇に前回の値を記憶させ、次の処理で平均化させることで、針が暴れるのを抑えます

e5 = e4;
e4 = e3;
e3 = e2;
e2 = e1;
e1 = e0;
e0 = gSMTarget;

//AVERAGE = (e0+e1+e2+e3+e4+e5+e6+e7+e8+e9+e10+e11+e12)/13;
AVERAGE = (e0+e1+e2+e3+e4+e5)/6; //12/27best?
//AVERAGE = (e0+e1+e2)/3;
//AVERAGE = (e0+e12)/2;
//diff <5 update
//if(abs(AVERAGE-gSMTarget)==0)
gSMTarget2 = AVERAGE;


#define REFRESH_RATE 100 //loop


//delay
REFRESH_COUNT++;
if(REFRESH_COUNT < REFRESH_RATE)continue;

//PICの処理速度そのままで針の駆動を実行すると速すぎるので
//上の平均化の処理を100回行ったら下の針を動かす処理に進みます。

REFRESH_COUNT = 0;

PLS_CNT = 0;




//RIGHT TURN 右に回したいのは 目標値>針の位置の時
if( gSMTarget2 > gPos )
{
gSMTargetDIF = gSMTarget2 - gPos;
//針の位置と目標値の差を求める

if(gSMTargetDIF>= (RPMMAX-gPos) )continue;
//最大値より大きい目標値はありえないので処理をやめてwhileの頭に戻る

for(ii=0;ii //針の位置と目標値の差だけ{}を繰り返す

{
rotate( TURN_R );
//上で書いてあるvoid rotate(int dir)のdirに右回転の値を指示して実行する

gPos ++;
//1ステップ右回転したので、現在の針の位置に+1する

if(gPos==gSMTarget)continue;
//現在の針の位置と目標値が同じ場合はwhileの頭に戻る(無くてもいい?)
}
continue; //whileの頭に戻る
}


//LEFT TURN 左に回したいのは 針の位置>目標値の時
else if( gPos > gSMTarget2 )
{
gSMTargetDIF = gPos - gSMTarget2;
//針の位置と目標値の差を求める

if(gSMTargetDIF> gPos )continue;
//0より小さい目標値はありえないので処理をやめてwhileの頭に戻る

for(ii=0;ii //針の位置と目標値の差だけ{}を繰り返す

{
rotate( TURN_L );
//上で書いてあるvoid rotate(int dir)のdirに左回転の値を指示して実行する

gPos --;
//1ステップ左回転したので、現在の針の位置から-1する

if(gPos==gSMTarget)continue;
//現在の針の位置と目標値が同じ場合はwhileの頭に戻る(無くてもいい?)
}
continue;
}
else {gSMTargetDIF = 0;}
//現在の針の位置と目標値が同じ場合はwhileの頭に戻る

}

}
/*
Copyright 2012-2014 Rilassaru All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the Rilassaru nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/



どうでしょうか?
1つ1つの処理を追いかけていけば何をやっているのかわかるんですけど、
1からこのコードを書くのは難しいですね。

まだブルブル針が動いてしまうんですけど、
defiのタコメーターのように滑らかかつ素早く正確に動かすには
根本的なプログラムの組み立て方が違うんでしょうか?


ブログ一覧 | 自作タコメータ作成記 | クルマ
Posted at 2020/03/24 12:24:51

イイね!0件



今、あなたにおすすめ

ブログ人気記事

今日は.パスタだよ.🍝
すっぱい塩さん

8.32
tompumpkinheadさん

実録「どぶろっく」187
桃乃木權士さん

ともさん行きつけの美容室。
とも ucf31さん

0828 🌅🍠🍦🍘💩💩 ...
どどまいやさん

隙間
ふじっこパパさん

この記事へのコメント

2020年7月23日 9:53
タコメーター自作で調べていたところ辿り着きました。
真似をして作成させていただいても良いですか?
ソースコードはコピー・ペーストで行うとビルドエラーが出てしまうのですが、なにか注意する点などありますでしょうか?

プロフィール

Tsukasaです。よろしくお願いします。
みんカラ新規会員登録

ユーザー内検索

<< 2025/9 >>

 123456
78910111213
14151617181920
21222324252627
282930    

リンク・クリップ

PICとステッピングモーターを使った自作タコメーター ②プログラム編 
カテゴリ:その他(カテゴリ未設定)
2024/06/11 09:01:34
トランク  フード ダンパー交換 
カテゴリ:その他(カテゴリ未設定)
2022/03/13 03:01:10
PICとステッピングモーターを使った自作タコメーター ①電子回路編 
カテゴリ:その他(カテゴリ未設定)
2021/05/30 09:12:40

愛車一覧

ホンダ CB400 SUPER BOL D'OR (スーパーボルドール) ホンダ CB400 SUPER BOL D'OR (スーパーボルドール)
初の新車です。 乗れば乗るほど乗りやすい、いいバイクです。 大型二輪取得しましたが、買 ...
トヨタ スプリンタートレノ 俺のトレノ (トヨタ スプリンタートレノ)
2016年2月23日 119540km~
トヨタ センチュリー(セダン) 俺のセンチュリー (トヨタ センチュリー(セダン))
おもしろい車が欲しかったので買いました! ラグジュアリーです
ホンダ Dio (ディオ) 0円ディオ (ホンダ Dio (ディオ))
2代目です。 1代目は転倒したらステムベアリングが砕けるほどフレームが曲がったので、 ...

過去のブログ

2021年
01月02月03月04月05月06月
07月08月09月10月11月12月
2020年
01月02月03月04月05月06月
07月08月09月10月11月12月
2018年
01月02月03月04月05月06月
07月08月09月10月11月12月
ヘルプ利用規約サイトマップ

あなたの愛車、今いくら?

複数社の査定額を比較して愛車の最高額を調べよう!

あなたの愛車、今いくら?
メーカー
モデル
年式
走行距離(km)
© LY Corporation