自分のブログなので,自分のまとめとしておく。
PIC, AT, STMといったいわゆる小型のマイコンを,趣味,興味から使い始めてみて2ヶ月ほど。その間に,ありがたいことで,職場でも使わせていただく機会があり,いろいろ勉強になった数ヶ月だった。
なんだかんだと,PICを一番使っている。特に,内蔵モジュールのPWMとコンパレータを使った案件は楽しかった! まさにPICを使いこなしている,という雰囲気だ。
ただ,XC8/XC16と,MPLAB IDEには手を焼く。
まず,XC8は整数の扱いが他のコンパイラーと少し違う? 計算が正しいか,入念なチェックが必要。
XC16+MCCは無愛想で,生成されたサンプルがさっぱり理解できない...
とか,細かいところはやはり不満。
そして,ICSPによるプログラム書き込みはいいけど,そのままそれをシリアルモニターできないのはツラい。別途シリアル回線を準備するか,OLED等で状況表示,スイッチ等で動作変更が必要となる。
使う用途は限定されるが,MPLAB IDEのsimulationは,それなりに使える。上記,「コンパイラーは正しく計算しているか?」の検証は,ある程度できる。
STM32 Nucleo + mbed では,上記のようなPICの不満は解消される。
ハードウエア設定はほぼ皆無でよく,サンプルを見てソースコードを埋め込めば,割とあっさりと動くようになる。USBで接続したまま,mbedのweb上のコンパイラーからダウンロードされた .bin を,ターゲットにコピーするようにすれば,即,実行される。さらにUSBを接続したままで,パソコン上でシリアルターミナルソフトで状況確認が容易である。
惜しいのは,大掛かりなmbedの影響か,CPUの能力を100%引き出せてはいないのではないか,ということ。STM32であれば,ST純正の開発環境を使えば少しは違うのかと思うが,まだ試せていない。
職場でも使ってみた。いい感じではあったが,残念ながら,私の接続ミスでターゲットを壊してしまった。反省...
古いSTM32も,mbedで.binをつくり,STLINKでターゲットに書いて実行させることはできるようになった。ただしこちらも壊してしまい,深くは検証できていない。また反省...
アトメルのCPU + Arduinoの環境,小規模な制御は,上記の流れから「かなりいい線」ではないかと思っている。シリアルを接続したまま,書き込みと,そのままArduino IDEのシリアルモニターが使える。Arduino言語はそれなりによくできていて,参考になるコードもいっぱい。コーディングはしやすい。
惜しいのは,クラウドベースの開発環境が無いこと。mbedの「場所を問わずに開発」はよくできている。
それはともかく,もう少し使ってみよう,ということで,職場でも使えそうなこれを発注してみた。
https://www.dfrobot.com/product-1075.html
職場では,ブレッドボードは使わない。実験基板に,あるいは直接はんだ付けしてテストする。小規模な方が,組み込むにせよ,外でテストするにしても,有利である。
USBからプログラム,シリアルモニターが使えそうなので,開発や,状況確認も簡単そうだ。
さて,届くまで楽しみに,別のボードで遊んでいよう。
土曜日, 12月 07, 2019
火曜日, 11月 12, 2019
働くPIC
そうそう,こういうの,やりたかったんだ。
micro:bitから基準電圧を出すために,PWMの波形を確認。
というコードで,1/100usec = 10kHzの矩形波をP1から出力している。これをPIC24Fの簡易オシロで確認した。
これなら,1k+10uFで,十分簡易DCにできそうだ。
micro:bitから基準電圧を出すために,PWMの波形を確認。
pins.analogSetPeriod(AnalogPin.P1, 100)
これなら,1k+10uFで,十分簡易DCにできそうだ。
土曜日, 11月 09, 2019
PIC24FV16KM202 EEPROMを使う
簡易オシロスコープの設定を記憶させるために,EEPROMを使うことを目論んだ。
いつも通りMCCで生成し,サンプルを見ながら仕込むが,どうも固まってしまう。
webで調査,以下のページの関数を頂きました。コメント送信欄がありませんでしたので,この場をお借りしてお礼申し上げます。
http://jr4pdp.blog.enjoy.jp/myblog/2015/06/pic24fv32keepro-1fc7.html
関数はコピペでOK,MCCの[MEMORY]も不要でした。
eeWrite(0,(uint16_t(書きたいデータ)); で書き込み。
(uint16_tの変数)= eeRead(0); で最初のアドレスの16ビット読み込み。
注意としては,MPLAB ideでビルドしてプログラムを焼き込むと,データはHで埋めつくされちゃうということ。これがわからず,しばらく悩んだ...
プログラムを焼き込んで動作したら,MPLAB ideを終了, チップ単品でON/OFFさせても,しっかり覚えていることを確認。よかった...
いつも通りMCCで生成し,サンプルを見ながら仕込むが,どうも固まってしまう。
webで調査,以下のページの関数を頂きました。コメント送信欄がありませんでしたので,この場をお借りしてお礼申し上げます。
http://jr4pdp.blog.enjoy.jp/myblog/2015/06/pic24fv32keepro-1fc7.html
関数はコピペでOK,MCCの[MEMORY]も不要でした。
eeWrite(0,(uint16_t(書きたいデータ)); で書き込み。
(uint16_tの変数)= eeRead(0); で最初のアドレスの16ビット読み込み。
注意としては,MPLAB ideでビルドしてプログラムを焼き込むと,データはHで埋めつくされちゃうということ。これがわからず,しばらく悩んだ...
プログラムを焼き込んで動作したら,MPLAB ideを終了, チップ単品でON/OFFさせても,しっかり覚えていることを確認。よかった...
PIC24FV16KM202 w/microstick 簡易オシロスコープ 途中だけど
趣味,勉強も兼ねて,作ってみた。プログラム組んでみた。自分自身で出す1kHz/50% PWMを直結して読んでみた。あまりテストしていないので,まだまだ誤りが多いと思うが...
C言語の,データ型の変換やら,グローバル変数の扱いやらに手を焼きながら,これだけでも数日かかってしまった。まあ,これが楽しいのだけど。
TMR1は5usecで割り込み。これを指定されたスキップ値で無視して,ADを取り込んでいる。
本当は,時間が長い時は,トリガーオフで「ロール」つまり逐一表示していくほうがいいのだけど,手を抜いて全てバッファで読み込むだけにしている。
- 実験値,100k sample/sec ぐらいが限度だった。
- 5V ADC を 1ch 読んでグラフ化。手を抜くため,分圧,増幅等しない。他の5V/3.3Vマイコンの波形が少し見られれば良いとする。
- 表示領域は128*16ドット。OLED用のバッファにデータを仮置き。つまりAD値を8ビットに圧縮。有効数字は約2桁。たぶんこれで十分。
- AD値は256回読む。簡易トリガー判定(下記)を行い,そのうち128回分のデータをグラフ化。
- 横時間軸はスイッチで加減できるようにする。手を抜くため,単純に2倍または1/2にする。
- 簡易トリガー
- 最初の立ち上がりのみ判定。
- 判定値はAD値の半分固定。
- 見つかったら,トリガーポイントを表示領域の中央にする。見つからなかったら最初の128個のデータを表示。
- 簡易周波数測定
- 最初の2回の立ち上がり時刻の差をとり,計算。表示するプログラムにまだバグがある。(kHz以上がうまく表示できない)
C言語の,データ型の変換やら,グローバル変数の扱いやらに手を焼きながら,これだけでも数日かかってしまった。まあ,これが楽しいのだけど。
/** Section: Included Files */ #include "mcc_generated_files/system.h" #include "mcc_generated_files/mcc.h" #include "oled.h" /* Main application */ #define FCY 16000000UL #include <libpic30.h> // #define address 0x3C unsigned int ADC_COUNT = 256; unsigned int ADC_SKIP_INIT = 0x08; unsigned int ADC_SKIP = 0x08; int PREV_SEC = 0; extern char prev_key_state; extern char current_key_state; extern char key_data; extern char pressed_key; extern char _KEY_STATE; extern unsigned char OLED_buffer[128 * 32 / 8]; char d1[30]=""; void My_TMR1_ISR(void) { // read AD value and store ADC_SKIP--; if (!ADC_SKIP) { if (ADC_COUNT) { ADC_COUNT--; OLED_buffer[255-ADC_COUNT]=(ADC1_ConversionResultGet())>>2; } /* sprintf(d1,"ADC_COUNT=%3d",ADC_COUNT); OLED_string(d1,0,0); OLED_write(); */ ADC_SKIP=ADC_SKIP_INIT; } } void show_status(void) { // sprintf(d1,"Last ADC %5d",ADC1_ConversionResultGet()); // OLED_string(d1,0,16); // TMR1 period 5us -> 200kHz sampling // switch (ADC_SKIP_INIT) { case 2: sprintf(d1,"100kHz/s "); break; case 4: sprintf(d1,"50kHz/s "); break; case 8: sprintf(d1,"25kHz/s "); break; case 16: sprintf(d1,"12.5kHz/s "); break; default: sprintf(d1,"SKIP %4d",ADC_SKIP_INIT); break; } OLED_string(d1,0,16); OLED_write(); } int main(void) { // initialize the device SYSTEM_Initialize(); TMR1_Stop(); __delay_ms(500); OLED_Init(0x3C); OLED_clear(); // 12345678901234567890 OLED_string(" Loading...", 0, 0); OLED_string(" PIC24FV16KM202", 0, 8); OLED_string(" with OLED 128*64.", 0, 24); OLED_write(); __delay_ms(500); OLED_clear(); OLED_write(); TMR1_SetInterruptHandler(My_TMR1_ISR) ; TMR1_Start(); show_status(); while(1) { // switch if (_KEY_STATE==3) { key_data=current_key_state; _KEY_STATE=0; // means clear if (key_data==3) { switch (pressed_key) { case 3: break; case 1: // 12345678901234567890 // OLED_string("RA1 pressed. ",0,24); // OLED_write(); pressed_key=3; // RA1 program TMR1_Stop(); if (ADC_SKIP_INIT<0x10000) {ADC_SKIP_INIT=ADC_SKIP_INIT<<1;} show_status(); ADC_SKIP=ADC_SKIP_INIT; ADC_COUNT=256; TMR1_Start(); break; case 2: // 12345678901234567890 // OLED_string("RB3 pressed. ",0,24); // OLED_write(); pressed_key=3; // RB3 program TMR1_Stop(); if (ADC_SKIP_INIT>2) {ADC_SKIP_INIT=ADC_SKIP_INIT>>1;} show_status(); ADC_SKIP=ADC_SKIP_INIT; ADC_COUNT=256; TMR1_Start(); break; case 0: // 12345678901234567890 // OLED_string("RA1 and RB3 pressed.",0,24); // OLED_write(); // RA1 and RB3 program pressed_key=3; TMR1_Stop(); show_status(); TMR1_Start(); break; } } else { pressed_key = pressed_key & key_data; } } /* sprintf(d1," AD = %5d",ADC1_ConversionResultGet()); OLED_string(d1,0,8); OLED_write(); */ if (!ADC_COUNT) { TMR1_Stop(); unsigned char i=0; // find trigger point // trigger result -> OFFSET. unsigned char OFFSET=0; unsigned char OFFSET_1=0; for(i=63; i<191; i++) { if ( ((unsigned char)(OLED_buffer[i])<127) & ((unsigned char)(OLED_buffer[i+1])>127) ) { if (OFFSET == 0) { OFFSET=i; } else if (OFFSET_1 == 0) { OFFSET_1=i; } } } // print measured freq uint32_t freq; uint8_t freq_unit; if ( (OFFSET>0) && (OFFSET_1>OFFSET) ) { // period = (OFFSET_1-OFFSET) * ADC_SKIP_INIT * 5us sprintf(d1,"%u",(OFFSET_1-OFFSET) * ADC_SKIP_INIT * 5); sprintf(d1,"%s[us]",d1); // OLED_string(d1,0,24); // freq to display STILL BUG!!! freq=100000000/(5*(OFFSET_1-OFFSET)*ADC_SKIP_INIT); // freq=(real freq)*100 if (freq>100000) { freq_unit='k'; freq=freq/1000; } else {freq_unit=' '; } if (freq>100000) { sprintf(d1,"%lu.%lu",freq/1000,freq-1000*freq/1000); } else if (freq>10000) { sprintf(d1,"%lu",freq/100); } else { sprintf(d1,"%lu",freq); } // sprintf(d1,"%lu",freq); sprintf(d1,"%s%c[Hz]",d1,freq_unit); OLED_string(d1,0,24); } // data shift using OFFSET. // At the same time, measure the AD value. if (OFFSET>0) { for(i=0; i<127; i++) { OLED_buffer[i]=OLED_buffer[i+OFFSET-63]; } } // convert to graph unsigned int bar,a,b,c,prev_a; for(i=0; i<127; i++) { a=(unsigned int)((unsigned char)OLED_buffer[i]); if (i>0) { b=prev_a; } else { b=a; } prev_a=a; // sprintf(d1,"a=%1x,b=%x,i=%x ",a,b,i); OLED_string(d1,0,16); OLED_write(); __delay_ms(100); if (b>a) { c=b; b=a; a=c; } a = (unsigned int)((0x8000)>>((unsigned char)(a>>4))); b = (unsigned int)((0x8000)>>((unsigned char)(b>>4))); // sprintf(d1,"a=%1x,b=%x,i=%x ",a,b,i); OLED_string(d1,0,24); OLED_write(); __delay_ms(100); a= ~(a-1); b=(b-1)|b; // sprintf(d1,"a=%1x,b=%x,i=%x ",a,b,i); OLED_string(d1,0,24); OLED_write(); __delay_ms(100); OLED_buffer[i]=((a&b) & 0xFF); OLED_buffer[i+128]=(a&b)>>8; } OLED_write(); ADC_COUNT=256; TMR1_Start(); /* sprintf(d1,"KEY_STATE = %d",_KEY_STATE); _KEY_STATE=0; OLED_string(d1,0,16); OLED_write(); */ } // __delay_ms(500); // switch test // _LATA0=_RA1; _LATA0=(_RA1)*(_RB3); // means LED=ON when RA1 and/or RB3 are pressed // _LATA0=_RB6; }; return 1; } /** End of File */ |
TMR1は5usecで割り込み。これを指定されたスキップ値で無視して,ADを取り込んでいる。
本当は,時間が長い時は,トリガーオフで「ロール」つまり逐一表示していくほうがいいのだけど,手を抜いて全てバッファで読み込むだけにしている。
水曜日, 11月 06, 2019
XC8 と XC16 で複数ソースファイルに使える「グローバル変数」の定義の仕方が違う?
前提として,
XC8
XC8の別の方法
XC16
うーむ,わけわからん。。。誰か教えてください!(切実)
- main.c
- 再利用できそうな関数を記述した my_lib.c とそのヘッダーファイル my_header.h
- main.c と my_lib.c で共通で使う変数 my_c
XC8
my_header.h
| |
my_lib.c
| |
main.c
|
XC8の別の方法
my_header.h
| |
my_lib.c
| |
main.c
|
XC16
my_header.h
| |
my_lib.c
| |
main.c
|
うーむ,わけわからん。。。誰か教えてください!(切実)
火曜日, 11月 05, 2019
PIC24FV16KM202 w/microstick SCCPモジュールでPWMテスト
これもsaharaさんのページをめちゃ参考にしております。
複雑そうに見えたPWM設定,できた。テスト用なので,2秒周期でLEDを光らせるありがちなやつを試した。
MCCで今回はSCCPを追加。
Compare/PWMモードを選ぶ。
Clock SourceはFOSCだと早すぎるので,LPRC=31kにする。
Modeは[Dual Edge Compare]にして,下3つは以下のように設定
Primary Compare Value = 0
Secondary Compare Value = (Time Period)*(Duty)=0xF0(要するに短め)
Time Period = 31k*2 = 0xF230
Saharaさんのページによれば,Primary Compare Valueの値を数えたらHにする,とのことなので,0でOK
Pin Managerで出力ポートを設定する。わりと固定的だった。最初はSCCP4を追加してみたけど,I2Cのポートと重なっていたので,SCCP5に変更した。
複雑そうに見えたPWM設定,できた。テスト用なので,2秒周期でLEDを光らせるありがちなやつを試した。
MCCで今回はSCCPを追加。
Compare/PWMモードを選ぶ。
Clock SourceはFOSCだと早すぎるので,LPRC=31kにする。
Modeは[Dual Edge Compare]にして,下3つは以下のように設定
Primary Compare Value = 0
Secondary Compare Value = (Time Period)*(Duty)=0xF0(要するに短め)
Time Period = 31k*2 = 0xF230
Saharaさんのページによれば,Primary Compare Valueの値を数えたらHにする,とのことなので,0でOK
Pin Managerで出力ポートを設定する。わりと固定的だった。最初はSCCP4を追加してみたけど,I2Cのポートと重なっていたので,SCCP5に変更した。
月曜日, 11月 04, 2019
PIC24FV16KM202 w/microstick MCCPモジュールで2つ目のタイマーを
I2Cを使っているからか,1個目のタイマーをADCにしたら,他のタイマーが見当たらない。いつものようにsaharaさんのページなどを参照すると,MCCPの機能と一つとして,「もう一つのタイマー」として使えるようだ。
MCCでMCCP1を追加。
あいかわらず無愛想だ... データシートも読む。16Bitだと,複雑なタイマーも仕掛けられるようだが,今回は10msecごとの単純なタイマー割り込みなので,設定が簡単そうな32bitを選択。
Primary Timer Period だけを示せばようだ。
その上,FOSC/2 -> 16MHz がもとのクロックなので,10msec / (1/16MHz) = 160000(dec) = 0x27100 が設定値。
そのタイマー処理だが,TMR1とは違って,main.cの中で定義できそうにない。やむを得ず,mccp1_tmr.c に記載する。
そして main.cの処理 共通で使う変数をexternで記載する。これがわからなくて悩んだ... C言語の基本が...
ふう。できた。
MCCでMCCP1を追加。
あいかわらず無愛想だ... データシートも読む。16Bitだと,複雑なタイマーも仕掛けられるようだが,今回は10msecごとの単純なタイマー割り込みなので,設定が簡単そうな32bitを選択。
Primary Timer Period だけを示せばようだ。
その上,FOSC/2 -> 16MHz がもとのクロックなので,10msec / (1/16MHz) = 160000(dec) = 0x27100 が設定値。
そのタイマー処理だが,TMR1とは違って,main.cの中で定義できそうにない。やむを得ず,mccp1_tmr.c に記載する。
char _KEY_STATE = 0; void __attribute__ ((weak)) MCCP1_TMR_Timer32CallBack(void) { // Add your custom callback code here _KEY_STATE++; } |
そして main.cの処理 共通で使う変数をexternで記載する。これがわからなくて悩んだ... C言語の基本が...
extern char _KEY_STATE; (中略 RTCCで1秒おきに以下を実行) sprintf(d1,"KEY_STATE = %d",_KEY_STATE); _KEY_STATE=0; OLED_string(d1,0,16); OLED_write(); |
ふう。できた。
PIC24FV16KM202 w/microstick ADCテスト
PIC16FのXC8と勝手が違っていたので,メモ。
参考リンク
テストとして,プルアップしたスイッチをADCとして読み,読み値をOLEDに表示。
MCCを使ったセットアップ。
saharaさんのページの通り,[Enable Auto Sampling]にチェック。TADが指示された値より大きくなるように,Conversion Clockを設定。Conversion Triggerは[Internal counter...]を選ぶ。
これだけかと思った。プログラムを走らせてみると,どうもRA0の値を読んでいるっぽい。読みたいのはRB3なのに...
というわけで,MCCのADC画面の下,RB3=AN5のところ,[Scan Enable]にチェックを入れる。これが正解だった。複数チャンネルの時はどうするんだろう... 後で考えよう。
main.cでは,ADC1_ConversionResultGet()を使えば良い。
無事,5Vっぽい値が読めている。スイッチを押すと,ゼロになる。
(追記)
タイマー割り込みを使い,どのぐらいたくさんのサンプリングが行えるか実験した。
結果,5usec間隔の206k サンプル/秒 ぐらいが限界点だと思われた。
ADCの設定では1usecで変換が終わることになっているが,無理は禁物...
PLL使用の32MHz動作。検証は以下のmain.c 生存確認のため,Lチカのスイッチテストと,稼働時間が表示されている。
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/rtcc.h"
#include "mcc_generated_files/mcc.h"
#define FCY 16000000UL
#include <libpic30.h>
#define address 0x3C
struct tm CURRENT_RTCC;
unsigned long int ADC_COUNT = 0;
unsigned long int ADC_COUNT_PREV = 0;
int PREV_SEC = 0;
void My_TMR1_ISR(void) {
ADC1_ConversionResultGet();
ADC_COUNT++;
}
int main(void)
{
// initialize the device
SYSTEM_Initialize();
__delay_ms(500);
OLED_Init(0x3C);
OLED_clear();
OLED_string("This is from my", 0, 0);
OLED_string(" PIC24FV16KM202", 0, 8);
OLED_string("with OLED 128*64.", 0, 24);
OLED_write();
__delay_ms(500);
OLED_clear();
OLED_write();
char d1[30]="";
TMR1_SetInterruptHandler(My_TMR1_ISR) ;
TMR1_Start();
while(1) {
RTCC_TimeGet(&CURRENT_RTCC);
if (CURRENT_RTCC.tm_sec != PREV_SEC) {
PREV_SEC=CURRENT_RTCC.tm_sec;
sprintf(d1,"%02d:%02d:%02d",CURRENT_RTCC.tm_hour,CURRENT_RTCC.tm_min,CURRENT_RTCC.tm_sec);
OLED_string(d1,0,0);
TMR1_Stop();
sprintf(d1,"ADC_COUNT = %8lu",ADC_COUNT);
OLED_string(d1,0,8);
OLED_write();
ADC_COUNT=0;
TMR1_Start();
}
_LATA0=_RA1; // for switch test
};
return 1;
}
参考リンク
テストとして,プルアップしたスイッチをADCとして読み,読み値をOLEDに表示。
MCCを使ったセットアップ。
saharaさんのページの通り,[Enable Auto Sampling]にチェック。TADが指示された値より大きくなるように,Conversion Clockを設定。Conversion Triggerは[Internal counter...]を選ぶ。
これだけかと思った。プログラムを走らせてみると,どうもRA0の値を読んでいるっぽい。読みたいのはRB3なのに...
というわけで,MCCのADC画面の下,RB3=AN5のところ,[Scan Enable]にチェックを入れる。これが正解だった。複数チャンネルの時はどうするんだろう... 後で考えよう。
main.cでは,ADC1_ConversionResultGet()を使えば良い。
無事,5Vっぽい値が読めている。スイッチを押すと,ゼロになる。
(追記)
タイマー割り込みを使い,どのぐらいたくさんのサンプリングが行えるか実験した。
結果,5usec間隔の206k サンプル/秒 ぐらいが限界点だと思われた。
ADCの設定では1usecで変換が終わることになっているが,無理は禁物...
PLL使用の32MHz動作。検証は以下のmain.c 生存確認のため,Lチカのスイッチテストと,稼働時間が表示されている。
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/rtcc.h"
#include "mcc_generated_files/mcc.h"
#define FCY 16000000UL
#include <libpic30.h>
#define address 0x3C
struct tm CURRENT_RTCC;
unsigned long int ADC_COUNT = 0;
unsigned long int ADC_COUNT_PREV = 0;
int PREV_SEC = 0;
void My_TMR1_ISR(void) {
ADC1_ConversionResultGet();
ADC_COUNT++;
}
int main(void)
{
// initialize the device
SYSTEM_Initialize();
__delay_ms(500);
OLED_Init(0x3C);
OLED_clear();
OLED_string("This is from my", 0, 0);
OLED_string(" PIC24FV16KM202", 0, 8);
OLED_string("with OLED 128*64.", 0, 24);
OLED_write();
__delay_ms(500);
OLED_clear();
OLED_write();
char d1[30]="";
TMR1_SetInterruptHandler(My_TMR1_ISR) ;
TMR1_Start();
while(1) {
RTCC_TimeGet(&CURRENT_RTCC);
if (CURRENT_RTCC.tm_sec != PREV_SEC) {
PREV_SEC=CURRENT_RTCC.tm_sec;
sprintf(d1,"%02d:%02d:%02d",CURRENT_RTCC.tm_hour,CURRENT_RTCC.tm_min,CURRENT_RTCC.tm_sec);
OLED_string(d1,0,0);
TMR1_Stop();
sprintf(d1,"ADC_COUNT = %8lu",ADC_COUNT);
OLED_string(d1,0,8);
OLED_write();
ADC_COUNT=0;
TMR1_Start();
}
_LATA0=_RA1; // for switch test
};
return 1;
}
火曜日, 10月 29, 2019
PIC24FV16KM202 w/microstick OLEDを使う
(追記始め)
以前の表示だが,かろうじてI2Cが接続できただけで,やっぱりどうもデータが化けている。いろいろデータの送り方を試行錯誤したが,最終的にはsaharaさんのページで紹介されていたマイクロチップのサポートページのoled.zipをダウンロード。自分の環境に移植することで,うまく動くようになった。
本体メモリーにバッファを持つタイプ。描画スピードとかは少しずつテストしていこう。
やれやれ。ともかく,目標はクリアした。きれい! うれしい!!
ちなみに移植手段としては,saharaさんのページの通り,I2C初期化はMCCのコードに任せ,送信関数部分のレジスターの定義名などをPIC24FV16KM202用に修正しました。
ああ,振り返ってみると,これだけで動いたんだな。でもいい勉強になった。
あ,横5ドットの文字で,5ドットに続けて次の文字が書かれていたので,1ドット余白を作るようにしました。
amazonで買った,SSD1306の128*32です。
MPLAB X IDE v5.25
XC16 (v1.41) でした。
(追記終わり)
当面の目標であったOLEDだが,やっとPIC24FV16KM202で動き始めた。まだめちゃくちゃな表示だけど,うれしい!
参考にさせて頂いたwebページはたくさんあれど,とりあえず以下3つ。
http://www.eonet.ne.jp/~jr3tgs/oled_test.htm
https://neo-sahara.com/wp/2017/11/16/pic24fj64gb002-ssd1306gm009605/
https://tool-lab.com/make/pic-practice-40/
普通にMCCでMSSPを追加,生成された例をよく読んで,必要なコマンドを書いていったつもりだが,どうにも動かない。I2Cに適切に送り出していない。タイムアウトせず,反応待ちで止まっている感じ。
micro:bitでは,あっけなく表示できたので,OLEDの故障ではない。
MSSP1ではなくてMSSP2を使わないとうまくいかないPICもある,ということで試すもダメ。
そこで,上のSaharaさんのページに,こんなことが書いてあった。
「基本的に、I2Cの初期化関連はMCCの吐いたコードに任せ、OLEDとのやり取りは参考にするコードの方をなるべく生かす。」
ということで,基本設定はMCCで行い,I2Cの基本はとっても理解しやすかったJR3TGSさんのページから移植することにした。
ビット定義等を修正しつつ,コンパイルエラーをとにかくなくす。動いた!
以下,MCCとソースの覚書
クロックは最速でOK
MSSP1の設定 High Speedで,Baud Rate Generatorの数字を調整して400kに合わせる。
以下をmain.cに記述。赤字は修正したところ。私の環境の都合で,日本語コメントは削除してコンパイルする必要があります。(冒頭の追記参照 結局これは使わず,別のものから移植しました)
void i2c_start() {
SSP1CON2bits.SEN = 1;
while(SSP1CON2bits.SEN);
}
void i2c_stop() {
SSP1CON2bits.PEN = 1;
while(SSP1CON2bits.PEN);
}
void i2c_write(unsigned char byte) {
IFS1bits.SSP1IF = 0; // レジスタ名をPIC24F用に修正
SSP1BUF = byte;
while(!IFS1bits.SSP1IF);
SSP1CON1bits.CKP = 1;
}
void oled_ini(void) {
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x8D); // Set charge pump
i2c_write(0x14); // Enable charge pump
i2c_write(0xAF); // Display ON
i2c_stop();
}
void oled_clr(void) {
unsigned int i;
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x20); // Set memory addressing mode
i2c_write(0x00); // Horizontal addressing mode
i2c_write(0x21); // Set column address
i2c_write(0x00); // Column start address 0
i2c_write(0x7F); // Column end address 127d
i2c_write(0x22); // Set page address
i2c_write(0x00); // Page start address 0
i2c_write(0x03); // Page end address 3d
i2c_stop();
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x40); // Control byte Co=0, D/C#=1 (The following data bytes are stored at the GDDRAM)
for(i=0; i<512; i++) // 128 column * 4page
i2c_write(0x00); // filled with 0 (OLED clear)
i2c_stop();
}
int main(void)
{
// initialize the device
SYSTEM_Initialize();
oled_ini();
oled_clr();
// sample print A
i2c_start();
i2c_write(0x78);
i2c_write(0x40);
i2c_write(0x7C);
i2c_write(0x12);
i2c_write(0x11);
i2c_write(0x12);
i2c_write(0x7C);
i2c_stop();
while (1) {
_LATA0=(_RA1); // これはテスト用(ボタンを押したらLED消える)
}
return 1;
}
以前の表示だが,かろうじてI2Cが接続できただけで,やっぱりどうもデータが化けている。いろいろデータの送り方を試行錯誤したが,最終的にはsaharaさんのページで紹介されていたマイクロチップのサポートページのoled.zipをダウンロード。自分の環境に移植することで,うまく動くようになった。
本体メモリーにバッファを持つタイプ。描画スピードとかは少しずつテストしていこう。
やれやれ。ともかく,目標はクリアした。きれい! うれしい!!
ちなみに移植手段としては,saharaさんのページの通り,I2C初期化はMCCのコードに任せ,送信関数部分のレジスターの定義名などをPIC24FV16KM202用に修正しました。
ああ,振り返ってみると,これだけで動いたんだな。でもいい勉強になった。
あ,横5ドットの文字で,5ドットに続けて次の文字が書かれていたので,1ドット余白を作るようにしました。
amazonで買った,SSD1306の128*32です。
MPLAB X IDE v5.25
XC16 (v1.41) でした。
(追記終わり)
当面の目標であったOLEDだが,やっとPIC24FV16KM202で動き始めた。まだめちゃくちゃな表示だけど,うれしい!
参考にさせて頂いたwebページはたくさんあれど,とりあえず以下3つ。
http://www.eonet.ne.jp/~jr3tgs/oled_test.htm
https://neo-sahara.com/wp/2017/11/16/pic24fj64gb002-ssd1306gm009605/
https://tool-lab.com/make/pic-practice-40/
普通にMCCでMSSPを追加,生成された例をよく読んで,必要なコマンドを書いていったつもりだが,どうにも動かない。I2Cに適切に送り出していない。タイムアウトせず,反応待ちで止まっている感じ。
micro:bitでは,あっけなく表示できたので,OLEDの故障ではない。
MSSP1ではなくてMSSP2を使わないとうまくいかないPICもある,ということで試すもダメ。
そこで,上のSaharaさんのページに,こんなことが書いてあった。
「基本的に、I2Cの初期化関連はMCCの吐いたコードに任せ、OLEDとのやり取りは参考にするコードの方をなるべく生かす。」
ということで,基本設定はMCCで行い,I2Cの基本はとっても理解しやすかったJR3TGSさんのページから移植することにした。
ビット定義等を修正しつつ,コンパイルエラーをとにかくなくす。動いた!
以下,MCCとソースの覚書
クロックは最速でOK
MSSP1の設定 High Speedで,Baud Rate Generatorの数字を調整して400kに合わせる。
以下をmain.cに記述。赤字は修正したところ。私の環境の都合で,日本語コメントは削除してコンパイルする必要があります。(冒頭の追記参照 結局これは使わず,別のものから移植しました)
void i2c_start() {
SSP1CON2bits.SEN = 1;
while(SSP1CON2bits.SEN);
}
void i2c_stop() {
SSP1CON2bits.PEN = 1;
while(SSP1CON2bits.PEN);
}
void i2c_write(unsigned char byte) {
IFS1bits.SSP1IF = 0; // レジスタ名をPIC24F用に修正
SSP1BUF = byte;
while(!IFS1bits.SSP1IF);
SSP1CON1bits.CKP = 1;
}
void oled_ini(void) {
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x8D); // Set charge pump
i2c_write(0x14); // Enable charge pump
i2c_write(0xAF); // Display ON
i2c_stop();
}
void oled_clr(void) {
unsigned int i;
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x00); // Control byte Co=0, D/C#=0
i2c_write(0x20); // Set memory addressing mode
i2c_write(0x00); // Horizontal addressing mode
i2c_write(0x21); // Set column address
i2c_write(0x00); // Column start address 0
i2c_write(0x7F); // Column end address 127d
i2c_write(0x22); // Set page address
i2c_write(0x00); // Page start address 0
i2c_write(0x03); // Page end address 3d
i2c_stop();
i2c_start();
i2c_write(0x78); // OLED slave address
i2c_write(0x40); // Control byte Co=0, D/C#=1 (The following data bytes are stored at the GDDRAM)
for(i=0; i<512; i++) // 128 column * 4page
i2c_write(0x00); // filled with 0 (OLED clear)
i2c_stop();
}
int main(void)
{
// initialize the device
SYSTEM_Initialize();
oled_ini();
oled_clr();
// sample print A
i2c_start();
i2c_write(0x78);
i2c_write(0x40);
i2c_write(0x7C);
i2c_write(0x12);
i2c_write(0x11);
i2c_write(0x12);
i2c_write(0x7C);
i2c_stop();
while (1) {
_LATA0=(_RA1); // これはテスト用(ボタンを押したらLED消える)
}
return 1;
}
土曜日, 10月 19, 2019
PIC24FV16KM202 スイッチ読み込み(基本的...)
勤務先のソフト屋さんに相談したら,あまりに基本的で怒られそうな案件だ...
だが,自分の大切な記録として残しておく。
設計仕様
だが,自分の大切な記録として残しておく。
設計仕様
- RA1とRB3に接続した押しボタンスイッチで,PICの動作を変えたりできるようにする。
- とりあえずテストのため,ボタンが押された状況をUART(printf)で送信。
- ボタンは「オフトリガー」すなわちある程度押され,離した時に処理を開始する。
- [RA1] 単独,[RB3] 単独,[RA1] と [RB3] の同時押し,以上3つを判定する。
オフトリガーで同時押しも実装しようとしたのは,micro:bitが良い動きだったから。
ボタンで割り込みがかかるとかっこいいのだが,難しそうなので諦め,mainループ内で判定することにした。
/**
Section: Included Files
*/
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/mcc.h"
#include <stdio.h>
/*
Main application
*/
unsigned long int GLOBAL_TIME = 0;
unsigned long int NEXT_TIME = 0;
char RECEIVED_CHAR = ' ';
char prev_key_state = 3;
char current_key_state = 3;
char key_data = 3;
char pressed_key = 3;
char KEY_STATE = 0;
void My_TMR1_ISR(void)
{
GLOBAL_TIME++;
current_key_state = ((_RA1)<<1)|(_RB3);
if (GLOBAL_TIME>NEXT_TIME) {
// printf("Interrupted at %lu.\n",NEXT_TIME);
NEXT_TIME=NEXT_TIME+100; // means 1 sec
// printf("RA1 state:%d\n",_RA1);
// printf("RB3 state:%d\n",_RB3);
// printf("current_key_state = %d.\n",current_key_state);
}
switch(KEY_STATE) {
case 0:
if (current_key_state != prev_key_state) {
KEY_STATE=1;
prev_key_state=current_key_state;
}
break;
case 1:
case 2:
if (current_key_state == prev_key_state) {
KEY_STATE++;
} else {
KEY_STATE=0;
}
break;
}
}
int main(void)
{
// initialize the device
SYSTEM_Initialize();
TMR1_SetInterruptHandler(My_TMR1_ISR);
NEXT_TIME=100;
// TMR1_Start();
while (1) {
// test code
_LATA0=(_RA1)*(_RB3); // means LED=ON when RA1 and/or RB3 are pressed
// UART RX
if (UART1_IsRxReady()) {
RECEIVED_CHAR=UART1_Read();
printf("%c was received at %lu.\n",RECEIVED_CHAR,GLOBAL_TIME);
// printf(" -->> current_key_state = %d.\n",current_key_state);
// printf(" -->> prev_key_state = %d.\n",prev_key_state);
// printf(" -->> SW_STATE = %d.\n",KEY_STATE);
}
// KEY routine
if (KEY_STATE==3) {
key_data=current_key_state;
KEY_STATE=0; // means clear
// printf("key_data = %d.\n",key_data);
if (key_data==3) {
switch (pressed_key) {
case 3:
break;
case 1:
printf("RA1 pressed.¥n");
// RA1 program
pressed_key=3;
break;
case 2:
printf("RB3 pressed.¥n");
// RB3 program
pressed_key=3;
break;
case 0:
printf("Both RA1 and RB3 pressed.¥n");
// RA1 and RB3 program
pressed_key=3;
break;
}
} else {
pressed_key = pressed_key & key_data;
}
}
}
return 1;
}
|
いわゆる「チャタリング処理」を施している。いろいろ試行した結果,20msecとした。タイマーを用いているが,のちのち間違えそうなので,タイマーそのものは10msecごと。3回読んで(つまり20msec間)スイッチの状態が同じなら,チャタリングではないと判定した。
それでも,「同時押し」では,例えば [RA1] 押した -> さらに[RB3] 押した と離散的なデータが取れてしまう。考えた末,やはりオフトリガー,すなわちスイッチが1個,または2個完全に離された状態を判定して,そこまで片方しか押されなかったのか,同時押しだったのかを覚えておくようにした。最後の方の pressed_key = pressed_key & key_data;
スイッチが2個だけなので,単純にビット操作を使うとうまくいった。
PIC24FV16KM202 w/microstick USBシリアルボードでテスト
以下二つを接続して使うテストを行う。
microstick 5V
秋月さんで購入したUSBシリアル変換ボード
まずは電源接続を確認する。microstick回路図では,CPUはVDDと名付けられたネットで接続されている。

これは,デバッガーブロックのFPF2102というICでON/OFFコントロールされる。

FPF2102はオンセミのロードスイッチ。データシートに,VOUTにはVINを超える電圧をかけてはダメ,と明記されている。
したがって,USBモジュールからmicrostickのPICに直接電源を接続しちゃダメ,ということになる。
なので,普通に,microstickにはmicrostickの電源を接続し,モジュールはUSBから電源をもらい,両者は信号ピンとグランドのみを接続する,ということになる。
microstickは5Vでうごき,モジュールは5V入力対応だが,電源OFFは少し心配だ。だが,PICを守るためにも,microstickの電源を先に入れ,モジュールをUSBで接続,という順番を守らなくてはならない。
結果,動いた。
プログラムは前回使ったもの。
接続イメージ

組み立てた様子
Mac側はmicro:bitの時と同様
microstick 5V
秋月さんで購入したUSBシリアル変換ボード
まずは電源接続を確認する。microstick回路図では,CPUはVDDと名付けられたネットで接続されている。
これは,デバッガーブロックのFPF2102というICでON/OFFコントロールされる。
FPF2102はオンセミのロードスイッチ。データシートに,VOUTにはVINを超える電圧をかけてはダメ,と明記されている。
したがって,USBモジュールからmicrostickのPICに直接電源を接続しちゃダメ,ということになる。
なので,普通に,microstickにはmicrostickの電源を接続し,モジュールはUSBから電源をもらい,両者は信号ピンとグランドのみを接続する,ということになる。
microstickは5Vでうごき,モジュールは5V入力対応だが,電源OFFは少し心配だ。だが,PICを守るためにも,microstickの電源を先に入れ,モジュールをUSBで接続,という順番を守らなくてはならない。
結果,動いた。
プログラムは前回使ったもの。
接続イメージ

組み立てた様子
Mac側はmicro:bitの時と同様
登録:
投稿 (Atom)