前回は書き変えに便利で安心EEPROMを使ってみました。今回はFlashメモリのCRCをCPU使わずに計算することもできるCRCモジュールを使用してみます。Flashに対して使用すれば「改ざん」なども検出可能かも。ただし、今回はデータメモリの中の配列のCRC8をCPU制御で計算してもらいます。設定が分かり難い?
※『PIC三昧』PIC関係の記事総目次はこちら
※作業はMicroChip社の統合開発環境 MPLAB X IDEと、その上で走るコンフィギュレーションツールMCC Classic を使用して行っています。ターゲット・マイコンはPIC16F18855です。
Cyclic Redundancy Check(CRC) Module
CRCは通信の途中でビットが化けてんじゃねえか、とかいうときにその検出に威力を発揮いたします。すこし前に別シリーズのArmのマシン語命令で「2を法とする世界の多項式」に向き合いビビリましたが、まさにCRC計算に使うのがその多項式であります。まあCRCは頻繁に使うのでその度にビビっているわけにもいかず。機械的に計算して済ませるのみっす。
実はCRCモジュールには、メモリをスキャンするスキャナーが内蔵されておるということを知り、流石PIC16と感心。データメモリの上の配列をスキャンするのにスキャナーを使おうと野望を燃やしたのです。しかし失敗。なぜならば、
-
- CRCスキャナーはFlashメモリに対してのみ働く
- データメモリ上のオブジェクトの計算をする場合はCRCモジュールにCPUでデータを順次書き込め
ということみたいです。データシートよく読んだら書いてありました。トホホ。。
今回計算してみるCRCは、CRC8です。ハードウエア的にはCRC16も計算できるのですが、ビット幅が狭い方が簡単だし。
例によってCRC8にもいろいろあり、なのであります。今回成り行きで設定したものは、「CRC業界」じゃCRC-8/DVB-S2というお名前で知られているものみたいです。MPLAB X IDEのMCCクラシックからCRC8を設定したときに「プリディファイン」かつ「デフォルト値のまま」が「たまたま」それだった、というだけのことです。
相当前にAHT21B温湿度センサのデータ検査に旧MAXIM社方式のCRC-8をMicroPythonで計算したことがありましたが、それとは多項式が違います。なおPICマイコンのCRCモジュールは汎用性が高いです。設定さえ間違わなければMAXIM社方式であろうとCCITTであろうとなんでも計算できるのではないかと思います。
MCC Classicでの設定
CRCを制御するためのAPIを自動生成してもらうためにCRCを追加。そして標準出力にPrintfしたいのでEUSARTも取り込んでおきます。
CRCの設定画面が以下に。Use Pre-defined Polynominalにチェックを入れ、プルダウンメニューからCRC-8を選択すると、「多項式」は0xD5になってました。各ビットがxのべき乗の項の係数を表しますが、そこには踏み込みませぬぞ。メンドイし。Seed(初期値ということでよいのかな)は0で良いみたい。この設定がCRC-8/DVB-S2ということかね(お名前からするとデジタルテレビ向けに昔開発されたもの?)
緑色のマーカ部分、CRC Calculationという機能があり、早とちりで「やったね」と思った私は馬鹿でした。ここに書き込んでCalculateボタンを押したら設定条件のCRCを自在に計算してくれる?と思ったら、設定例の1例くらいを表示してくれるだけのものみたいです。けっこうショボい機能。すみません。
なお赤のところにEnable Scannerというチェックボックスがあり、これにチェックを入れると、CPUの横でCRCモジュールが勝手にFlashメモリにアクセスしてCRCを計算してくれる機能が使えるようになります。一種DMA的な。CPUの邪魔にならないようになど、メモリアクセス方法などもいろいろ選択可能デス。ただし対象はFlashメモリだけなので、今回のようにデータメモリ上のデータをスキャンすることはできないみたいです。
自動生成された関数に一か所疑義あり
MCCクラシックは設定条件に合わせて一そろいのAPIを自動生成してくれるので、いつも助かってます。しかし、今回は1か所納得いかない部分あり。以下です。
CRCのInitialize用の関数で、ターゲットのビット長や多項式の次数を制御するためのDLEN、PLENの設定のところです。コメントアウトしてある
CRCCON1 = (0 << 4) | (7);
上記が自動生成されたコードです。しかし、これだと期待通りの結果が出ませぬ。そこで以下のようにベタに0x77を代入してます。なぜかは知らねど、こう変更すると結果オーライ。
手抜きなテストプログラム
上記の修正以外はあまり頭を使っていない実験コードが以下に。
uint8_t calcCRC8(uint8_t* buf, int siz) { CRC_Initialize(); CRC_Start(); for (int idx=0; idx < siz; idx++) { while(CRC_IsBusy()); CRC_8BitDataWrite(buf[idx]); } while(CRC_IsBusy()); return (uint8_t)CRC_CalculatedResultGet(NORMAL,0x00); } void main(void) { SYSTEM_Initialize(); INTERRUPT_GlobalInterruptEnable(); INTERRUPT_PeripheralInterruptEnable(); uint8_t test1[]={0x55, 0x66, 0x77, 0x88}; uint8_t test2[]={0x01, 0x23, 0x45, 0x67, 0x89}; while (1) { RA0 = 1; __delay_ms(1000); printf("TEST1 CRC=0x%02x\n", calcCRC8(test1, 4)); RA0 = 0; __delay_ms(1000); printf("TEST2 CRC=0x%02x\n", calcCRC8(test2, 5)); } }
calcCRC8()関数に、CRC計算対象へのポインタとデータ長さを渡すとCRC8を計算してくれます。今回はCRC-8/DVB-S2ですが、MCCで設定する多項式や初期値などでどうとでもなります。
CRC期待値
CRCの期待値は、以前にもお世話になった以下のサイトで計算させていただきました。このサイト、よくできてます。中間結果も出力されるのが何気に便利。
末尾をみれば0x56であると。
こんどは0x4Cでした。
実験結果
PIC16F18855にオブジェクトを書き込んで計算させた結果が以下に
期待値通りの結果がでてますな。
まさか自動生成のDLENの値が不適合とは思っていなかったので、ちょっと危なかったケド。