前回、Rabbit4000のGPIOポート合計40ビットのうち特定の目的に使用可能なポートは以外と少ないことが分かりました。その後Dynamic CのI2C用サンプルプログラムを調べたところ「手元のRCM4010+プロトタイプボード」では使用不可、という結論にいたりました。仕方が無いので自分で「サンプル」書きます。
※「うさちゃんと一緒」投稿順indexはこちら
前回、オープンドレインの入出力端子として「使えそうな」端子はポートCの2ビットとポートEの5ビットであることが判明しました。Rabbit4000にはハードウエアのI2Cの搭載はなく(Rabbit6000には搭載されているみたいです)、ソフトウエア制御なので端子さえアサインできればI2Cできそうではあります。
いつものようにDynamic C付属のSampleフォルダを漁ったところ、サンプルプログラムがあったのですがポートD用にハードコードされてる部分が含まれていました。手元のRabbit4000のRCM4010モジュール+プロトタイプボードではポートDが使えないことは前回調べたとおりです。また、サンプル接続のターゲットが11ビットアドレスのI2C接続EEPROMなのにレジスタアドレスが8ビット?と「怪しい」感じなので使用を断念、サンプルを横目に見つつも自力更生することにいたしました。
接続試験に用いるターゲットは当方における実験のルーチンといって良いでしょう、秋月通商製AQM1602のピッチ変換キット品です。16文字x2行のキャラクタディスプレイ。ぶちゃけI2C書き込みが成功していれば文字が表示されるので細かいことを調べなくてもOKという感じです。
実験用回路
実験用の回路が以下に。AQM1602のピッチ変換基板上にはオンボード上に「半田を盛れば」使えるプルアップ抵抗があるのですが今回使用のデバイスでは未接続、外付けのプルアップ使ってます。電源電圧は拡張コネクタ上の3.3Vを使っています。Arduino用に5V接続のつもりで書かれている秋月殿の説明書記載のコントラスト値だと若干色が薄いかもです。
接続実験用のRabbit4000用Dynamic Cのソース
Dynamic C付属のライブラリであるi2c_devices.libから呼ばれる低レベルの入出力関数は、SCL、SDAの端子名、ON/OFF/読み取り関数などをマクロで参照しています。それらをライブラリよりも先に定義してしまえば当方の定義が優先するようであったので、ポートCの端子4,5用にマクロを定義しています。
しかし、ライブラリ内の初期化関数などはポートD用にハードコードされていたので、自前のものを用意しました。また「高水準」のAPI関数もちょっと当方の目的からすると?なところがあったので、AQM1602用に自前のものを作成しています。また、自前の関数は大幅手抜きです。本来はACKの確認はするべきですが、AQM1602の場合、書き込み失敗は「見れば分かる」ので「とりあえず接続が確かめられればOKな」今回は、ACKは全て無視しています。乱暴な。
#class auto #define AQM1602 (0x7C) #define CMD (0x00) #define DATA (0x40) #define I2CSCLBit (4) #define I2CSDABit (5) #define i2c_SCL_H() BitWrPortI(PCDDR,&PCDDRShadow,0,I2CSCLBit) #define i2c_SCL_L() BitWrPortI(PCDDR,&PCDDRShadow,1,I2CSCLBit) #define i2c_SDA_H() BitWrPortI(PCDDR,&PCDDRShadow,0,I2CSDABit) #define i2c_SDA_L() BitWrPortI(PCDDR,&PCDDRShadow,1,I2CSDABit) #define i2c_SCL() BitRdPortI(PCDR,I2CSCLBit) #define i2c_SDA() BitRdPortI(PCDR,I2CSDABit) // 0123456789ABCDEF char test_pattern[] = "AQM1602_LCD_****"; #use "i2c_devices.lib" void waitMs(int ms) { unsigned long t; t = MS_TIMER; while((long)(MS_TIMER - t) < ms); } int writeAQM1602(unsigned char cmd, unsigned char dat) { int ack1, ack2, ack3; i2c_startw_tx(); ack1 = i2c_write_char(AQM1602); ack2 = i2c_write_char(cmd); ack3 = i2c_write_char(dat); i2c_stop_tx(); return ack3; } void init_AQM1602() { waitMs(100); writeAQM1602(CMD, 0x38); waitMs(20); writeAQM1602(CMD, 0x39); waitMs(20); writeAQM1602(CMD, 0x14); waitMs(20); writeAQM1602(CMD, 0x73); waitMs(20); writeAQM1602(CMD, 0x56); waitMs(20); writeAQM1602(CMD, 0x6C); waitMs(20); writeAQM1602(CMD, 0x38); waitMs(20); writeAQM1602(CMD, 0x01); waitMs(20); writeAQM1602(CMD, 0x0C); } void i2c_init_PC() { // No Peripheral clock control BitWrPortI(PCFR ,&PCFRShadow ,0,I2CSCLBit); // Set SCL, Normal BitWrPortI(PCFR ,&PCFRShadow ,0,I2CSDABit); // Set SDA, Normal BitWrPortI(PCDDR,&PCDDRShadow,0,I2CSCLBit); // Set SCL, Input BitWrPortI(PCDDR,&PCDDRShadow,0,I2CSDABit); // Set SDA, Input BitWrPortI(PCDCR,&PCDCRShadow,1,I2CSCLBit); // Set SCL, Open Drain BitWrPortI(PCDCR,&PCDCRShadow,1,I2CSDABit); // Set SDA, Open Drain BitWrPortI(PCDR ,&PCDRShadow ,0,I2CSCLBit); // Set SCL output register 0 BitWrPortI(PCDR ,&PCDRShadow ,0,I2CSDABit); // Set SDA output register 0 i2c_clocks_per_us = (int)(19200L*32*freq_divider/1000000L); if(i2c_clocks_per_us < 3) { i2c_clocks_per_us = 3; } } void main() { int ack; unsigned long t; int counter=4; int idx; i2c_init_PC(); init_AQM1602(); for(idx = 0; idx < 16; idx++) { writeAQM1602(DATA, test_pattern[idx]); } writeAQM1602(CMD, 0x40+0x80); for(idx = 0; idx < 16; idx++) { writeAQM1602(DATA, idx+0x30); } printf("End of AQM1602 I2CWrite test\n"); }
動作確認
いつものテストパターンの文字が表示されているのが見えます。
とりあえず、PortCの2端子でI2Cへの出力が出来ることは分かり申した。しかし、SCLを見ていると標準よりかなり遅い気がします。気のせいなのか。。。ソフト制御だし、そんなもんなのか。