こんにちは、ENGかぴです。
PIC12F675のEEPROMは読み書きに数msの時間が必要です。ウェイトで待つ方法もありますが、プログラムの規模が大きくなるほどウェイトを持たせると不都合が発生します。ウェイトせずにモード遷移の方法で管理する方法を説明しています。
モード遷移の方法は対象の処理が待機中の場合でも他の処理が非同期に動作させることができるためウェイトを持たせたくない時は有効な方法です。
基本構成やEEPの使い方は下記記事と同様ですが、プログラムをDIフィルタとタイマー管理の方法を使って拡張した応用編です。
PIC12F675を使ってマイコンの動きを勉強するためにPIC12F675の機能でできることについてまとめています。
PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ
モード遷移の方法
モード遷移の方法を使わずにEEPROMを書き込みや読み込みを行う場合はEEPROMの書き込み完了の割り込みを使用したりすることで非同期の動作を作ることはできますが、書き込みデータとチェック用データの2か所を書き込むため管理が煩雑になります。そのため割り込みを使わない方法としてモード遷移の方法を考えていきます。
ウェイトを使った場合の問題点

EEPROMを書き込みは約5msかかります。データをチェックのため2か所に書いていることから1度の書き込みのシーケンスが終了するまでには約10msになります。
モードの切り替えの考え方

EEPROMの書き込みの手順はシーケンスに基づいて行う必要があります。この手順をモードで分けて管理します。
- アドレス指定
- 書き込みたいデータをセット
- 書き込み許可をセット
- シーケンススタート(0x55、0xAAを書き込み)
- 書き込みスタート(WR)をセット
- WRが0になるまで待つ
書き込むデータはチェック用も含めて2か所になるので書き込みがデータ用とチェック用のモードを準備します。準備するモードは以下の7つとします。
- 待機モード
- データ書き込みスタートモード
- 書き込み完了待ちモード
- チェック用データ書き込みスタートモード
- チェック用書き込み完了待ちモード
- 書き込み完了モード
- 書き込みエラーモード
PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール
PIC12F675の初期化
モード遷移の方法の他にボタンのチャタリング防止のためDIフィルタやタイマー管理の方法を追加しています。これらの方法と考え方については下記を参考にしてください。
PICマイコン(PIC12F675)のDIピンのフィルタの考え方
PICマイコン(PIC12F675)のタイマーの管理の仕方の一例
定義部分について
EEPROMを書き込むためのモードをenumの型としてEEPWR_MODEとして定義しています。enumの順番は上記の7つのモードに従って、定義しています。
typedef enum{
EEP_WR_IDLE = 0,
EEP_WR_START,
EEP_WR_START_WAIT,
EEP_WRCHK_START,
EEP_WRCHK_START_WAIT,
EEP_WR_END,
EEP_WR_ERR,
EEP_WR_MAX
}EEPWR_MODE;
//-----------------変数定義--------------------------
EEPWR_MODE wrmode;
short timWrStart = TIME_OFF;
short timWrWait = TIME_OFF;
変数としてEEPWR_MODEの型でwrmodeを宣言しています。モードの管理はwrmodeの値を変更することで行います。
タイマー管理についても変数を準備しています。timWrWaitはEEPROMの書き込みをスタートして書き終わるまでの時間の最大待ち時間としています。
timWrStartはボタンを長押し任意の時間が経過するとEEPROMの書き込みを実行するためのタイマーとしています。
PR:わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
各種レジスタの初期化とDIフィルタなどの初期化の関係
typedef struct{
char cnt;
char buf[ DI_NUM ];
}FILT_DATA;
//-----------------変数定義--------------------------
FILT_DATA DiData;
//-----------------メイン関数------------------------
void main(void) {
//各種レジスタ初期化
EepRead(); //EEPROMの読み込み
//タイマ起動
T1CON = 0;
TMR1H = TIME_START_H;//タイマ初期化
TMR1L = TIME_START_L;//タイマ初期化
TMR1ON = 1;//タイマ1ON
TMR1IF = 0;//割り込みクリア
//DIフィルタ初期起動
timWait = TIME_WAIT_MAX;
while( timWait > TIME_UP){ //タイムアップするまでにDIを確定
CLRWDT();
mainTimer();
DiFilter();
}
timWait = TIME_OFF;
while(1){
CLRWDT();
//各種処理
}
}
DIフィルタを実装するためFILT_DATAの型を定義して変数で宣言しています。TMR1は10msでオーバーフローするように初期値を設定しています。DIフィルタによるDI情報などメインのwhile(1)に入る前に確定したいものを初期化時に実施しています。
PR:エンジニア転職なら100%自社内開発求人に強い【クラウドリンク】
各種機能を実装する

SW1を押すと押した回数をカウントします。SW1を長押し(3秒)するとEEPROMの書き込みを行います。LED2はメイン関数に入ると点灯するようにしています。
初期化時にEEPROMの値を参照して保存されたカウント値だけのLED1を点灯/消灯させてカウント値を表現します。
EEPROMの書き込み部分を実装
//-----------eepライト処理---------------------------------
void EepWrite(void){
switch(wrmode){
case EEP_WR_IDLE:
//待機
break;
case EEP_WR_START:
GPIO2 = BIT_SET;
GIE =BIT_CLR; //シーケンス中に割り込みが入らないように割り込み禁止
EEADR = 0x00; //アドレス指定
EEDATA = eepWrData; //書き込みたいデータをセット
WREN = BIT_SET; //書き込み許可をセット
EECON2 = 0x55; //シーケンス
EECON2 = 0xAA; //シーケンス
WR = BIT_SET; //書き込みセット
GIE = BIT_SET;
//割り込み禁止を解除
wrmode = EEP_WR_START_WAIT;
timWrWait = TIME_WAIT_MAX;
break;
case EEP_WR_START_WAIT:
if(WR==BIT_CLR || timWrWait == TIME_UP){
timWrWait = TIME_OFF;
wrmode = EEP_WRCHK_START;
if(WR == BIT_SET){ //タイムアップなので異常とみなす
wrmode = EEP_WR_ERR;
}
}
break;
case EEP_WRCHK_START://書き込みデータ(チェック領域)
GIE =BIT_CLR;
EEADR = 0x40; //アドレス指定
EEDATA = eepWrData; //書き込みたいデータをセット
EECON2 = 0x55; //シーケンス
EECON2 = 0xAA; //シーケンス
WR = BIT_SET; //書き込みセット
GIE =BIT_SET;
wrmode = EEP_WRCHK_START_WAIT;
timWrWait = TIME_WAIT_MAX;
break;
case EEP_WRCHK_START_WAIT:
if(WR==BIT_CLR || timWrWait == TIME_UP){
timWrWait = TIME_OFF;
wrmode = EEP_WR_END;
if(WR == BIT_SET){ //タイムアップなので異常とみなす
wrmode = EEP_WR_ERR;
}
}
break;
case EEP_WR_END:
GPIO2 = BIT_CLR;
WREN = BIT_CLR; //書き込み許可をクリア
wrmode = EEP_WR_IDLE;
break;
case EEP_WR_ERR:
//必要に応じてエラーを通知する処理などを入れる
GPIO2 = BIT_SET;
wrmode = EEP_WR_IDLE;
break;
}
}
各モードによる処理が完了すると次のモードを指定して関数を抜けてメイン関数に戻ることができます。各モードではウェイトがないためモード内の処理が完了するとスムーズに次の処理に移ることができます。
PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!
読み込み関数
読み込み関数はメイン関数に入る前に実行するためモードでの管理はしていません。実装例はソースコード全体で確認してください。
タイマー管理とDIフィルタを実装
//----------タイマー管理----------------------------------
void mainTimer(void){
if( cnt10ms >= CNT10_MAX){
cnt10ms -=CNT10_MAX;
if( timWait > TIME_UP ){
--timWait;
}
if( timWrStart > TIME_UP ){
--timWrStart;
}
}
}
割り込み関数はTMR1が10ms毎に割り込みが発生するようにしています。割り込み関数内でcnt10msをカウントアップしています。タイマー管理個所においてCNT10_MAXは1と定義しているため10ms毎にソフトウェアタイマを更新するように管理しています。
//--------モード切替部のDIフィルタ----------------------------
void DiFilter(void){
if( filtflg ){
filtflg = BIT_CLR;
DiData.buf[ DiData.cnt ] = GPIO3;
if( DiData.buf[ 0 ] == DiData.buf[ 1 ] &&
DiData.buf[ 1 ] == DiData.buf[ 2 ] &&
DiData.buf[ 2 ] == DiData.buf[ 3 ] ){
//比較して一致したら値を採用
gpio3_buf = DiData.buf[ 0 ];
}
if( ++DiData.cnt >= DI_NUM) DiData.cnt = 0;
}
}
DIフィルタは10ms毎に動作するようにしているため割り込み関数の中でfiltflgをセットしDIフィルタの処理を行うようにしています。フラグ管理でなくタイマ管理にすると50ms毎にDIフィルタの処理したりと任意のタイミングで実行できます。
PR:(即戦力のスキルを身に着ける:DMM WEBCAMP 学習コース(はじめてのプログラミングコース))
ボタン長押しで書き込み処理を行う
//--------ボタン処理----------------------------
void BtnMain(void){
if( gpio3_buf == BIT_SET){
if( btnOn == BIT_CLR ){
btnOn = BIT_SET;
timWrStart = TIME_BUTTON_WAIT_MAX; //ボタンの長押しをタイマ管理する
if(++eepWrData >= LED_CNT_MAX){
eepWrData = 0;
}
}
}else{
btnOn = BIT_CLR;
timWrStart = TIME_OFF;
}
if(timWrStart == TIME_UP){ //タイムアップするとwrmodeを更新しEEPROMの書き込みがスタートする
timWrStart = TIME_OFF;
if(eepWrData != 0x00){
--eepWrData;
}
wrmode = EEP_WR_START;
}
}
ボタンを押したとき一度だけカウントアップするようにしたいのでbtnOnをフラグとして管理しています。ボタン長押しをやめるとtimWrStartをTIME_OFFとすることでタイマの更新を停止しています。
ボタンを長押してタイムアップするとwrmodeを更新するためEEPROMへの書き込みを処理がスタートします。長押しした際にカウントアップした分をカウントが増えているためカウントを戻す処理を入れています。(長押しして書き込むときはボタンを押した回数に含めたくなかったためそのようにしています。)
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。
リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。
main.cをコピーすると使用できます。
関連リンク
PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。
PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ
PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ
最後まで、読んでいただきありがとうございました。