PICマイコンの覚書
Print文の出力
XC8の場合、PICでprint文を表示するには、printf関数を書くだけではなくputch関数を記載する必要がある。 XC16では、 printf関数は通常使用が可能。
環境
型番 | PIC18F25K80 |
コンパイラ | XC8 |
実装
なにはともあれ#include <stdio.h>
を記述。
main関数内にpirntf("%d",hensu);
と記述すると問題なくコンパイル通るもののprint文は記述されない。
print文を表示させるには、以下のputch関数を記載する。
putch関数の記載場所によってはredefineエラーが出る。
その場合は記載場所を変える(とりあえずmain.cに記載すればOK)。
レジスタ名称はPICのシリーズや型番によって若干異なるので注意。
* 以下の例はUSART1に表示する場合。
* USART2に表示する場合は1
を2
に変えれば良い。
void putch(char Data){ while(!TXSTA1bits.TRMT); TXREG1 = Data; }
CAN通信
PICでCAN通信を開通するために、MCCでIDを設定したが、設定した結果はMASKとFilterに反映される。
CANのIOピンの設定がどこに反映されているか不明だったため、念のためPRAGMAを設定した。
環境
:--: | :--: |
---|---|
型番 | PIC18F25K80 |
コンパイラ | XC8 |
MaskとFilterについて
CAN通信モジュールは、MCCで使用するIDを設定できる。
全てのIDを受信可能にもできるが、ノイズを考慮し、必要な分だけ受信できるように設定したほうが良い。
設定結果はFilterとMaskに反映される。
Maskで1となっている全bitについて、Filterに記載された値と受信データの値が等しい場合のみ、データは受信できる。
例えばMask=0x3E、Filter=0xA7の場合、受信データは0bxx10011xとなる(xは0/1どちらでも良い)。
Hex | BIN | |
---|---|---|
Mask | 0x3E | 0b0011_1110 |
Filter | 0xA7 | 0b1010_0111 |
Valid Data | 0bxx10_011x |
CANモジュールのPRAGMA設定
なにかしらのヘッダーに以下を追加。
#pragma config CANMX = PORTB
コメントアウトしてもうまくいったので、多分必要ないんだろうけど、
今後のことを考えてブログに残しておく。
ADC
dsPICでADCの設定をMCCに任せっきりにした場合、
単chのADCはうまくいったが、複数chをスキャンする場合にAD変換できなかった。
調査した結果、ADCを行えるようになったのでメモ。
環境
シリーズ | dsPIC33E |
コンパイラ | XC16 |
実装1:単chのAD変換
CH0にAN0を1っ回だけAD変換する場合は以下。 下記以外の設定はMCCに任せてよい。
AD1CON2bits.CHPS = 0;//CH0をセット AD1CHS0bits.CH0SA = 0;//AN0をセット AD1CON1bits.SAMP = 1;//サンプリングを開始する int i = 0; for( i=0;i <1000;i++){}//サンプリングするため一定時間設ける AD1CON1bits.SAMP = 0;//サンプリングを止める while(!AD1CON1bits.DONE){}//サンプリングデータのAD変換完了を待つ printf("%x",ADC1BUF0);//データはADC1BUF0で取り出せる。
実装2:複数chのAD変換(スキャンモード)
CH0にAN0~AN5をAD変換(スキャン)する場合は以下。
AD1CON1bits.ASAM = 1;//自動変換モードをセット AD1CON2bits.CHPS = 0;//Ch0を使用 AD1CON2bits.BUFM = 0;/バッファのモードを設定 AD1CON2bits.ALTS = 0;//MUXAのみ使用する AD1CON4bits.ADDMAEN = 0;//DMAは使用しない AD1CON2bits.CSCNA = 1;//CH0をスキャンモードに使用する AD1CON2bits.SMPI = 0x5;//AD変換6回実施後に変換完了したことを割り込みで伝える AD1CSSL = 0x3F;//スキャンするCHをセット(ここでは0~5)) AD1CON1bits.SSRC = 0x7;//サンプリングを自動開始するようにセット int main(void){ while(!AD1CON1bits.DONE){}//AD変換完了を待つ //各信号のAD変換結果はADC1BUFxから取得できる。 printf("BUF0:0x%x\r\n",ADC1BUF0); printf("BUF1:0x%x\r\n",ADC1BUF1); printf("BUF2:0x%x\r\n",ADC1BUF2); printf("BUF3:0x%x\r\n",ADC1BUF3); printf("BUF4:0x%x\r\n",ADC1BUF4); printf("BUF5:0x%x\r\n",ADC1BUF5); }
PWM制御
モーター駆動用にPWM制御を実装した。PWM周期の基準クロックはFosc/2でなくFoscな点に注意。
勘違いしてハマりました。
環境
シリーズ | dsPIC33E |
コンパイラ | XC16 |
実装
PWMモジュールには、ハイサイドとローサイドのスイッチを制御するモードが用意されている。
PWMを一定周期で動作させるには、タイムベースをプライマリに設定する。
PWM周期は、PTPERレジスタで設定する。
例えばFoscが30MHzかつクロック分周が1:1、PTPERが100だった場合、PWM周期は
1秒 / 30MHz x 100 = 3.3マイクロ秒
となる。
PWMのDutyは、ハイサイドはPDCxレジスタ、ローサイドはSDCxレジスタで設定する。
PDCx/SDCxレジスタの値とPTPERの値の比がDuty比となる。
例えばFoscが30MHzかつクロック分周が1:1、PDC1を40とした場合、PWM1Hピンが'H'となる時間は
1秒 / 30MHz x 40 = 1.32マイクロ秒
となる。
Timer割り込み
PWM周期毎にDuty比変えるためにタイマー2の割り込みを使用。
環境
シリーズ | dsPIC33E |
コンパイラ | XC16 |
実装
Timer2の割り込みをEnableにする。
割り込みのタイミングはTMR2レジスタで決まる。基準となるクロックはFosc/2。
PWM周期毎にDuty比変えるには、TMR2レジスタ値はPTPERレジスタの半分にする必要がある。
またはPTPERレジスタ値を2倍にするか、PWMのクロック分周を1:2にすれば良い。
割り込み時に実行する処理は、mccで生成されたtmr2.c内にあるCallBack関数内に記述する。
delay関数
delay関数でマイクロ秒以下の制御を行うには、_delay();
を使う。
環境
型番 | PIC18F25K80 |
コンパイラ | XC8 |
実装
例えばシステムクロックが16MHzの場合、以下の記述で250ns遅延させられる。
_delay(1);
括弧内の1
は実行する命令数。
1命令あたり4クロック必要なので、
1秒/16MHz X 4CLK = 250ns の遅延となる。
余談
if文やfor文を使用すると、それだけで命令が実行される。
その遅れを避けるためには、マイクロで関数のようなものを記述すればよい。
for(int i =0;i<4;i++){ function(); }
この場合は、
#define FUNC function(); #define FUNC4 FUNC FUNC FUNC FUNC void main(){ FUNC4 }
もっといい方法ありそうなもんですが。