LPC810メモ:I2C-bus ROM API(テキストLCDを使う)
I2C-bus ROM API利用して(マスタによる送信のみだけど)、テキストLCDを使ってみる。
前提とする環境はこちら→LPC810メモ 共通の準備
利用したテキストLCDはAQM0802
- 秋月で600円
- でもはんだ付け苦戦したので、完成品(700円)の方を買えばよかった。
サンプルコード
#include "LPC8xx.h" // UM10601 24.4 API description // に相当すると思われるプロトタイプ宣言を、 // LPCOpenのヘッダから持ってきた。 extern "C" { typedef int ErrorCode_t; // 本当はenumだけど今回は値は見ない typedef int CHIP_I2C_MODE_T; // これも本当はenum typedef void *I2C_HANDLE_T; typedef void (*I2C_CALLBK_T)(uint32_t err_code, uint32_t n); typedef struct I2C_PARAM { uint32_t num_bytes_send; uint32_t num_bytes_rec; uint8_t *buffer_ptr_send; uint8_t *buffer_ptr_rec; I2C_CALLBK_T func_pt; uint8_t stop_flag; uint8_t dummy[3]; } I2C_PARAM_T; typedef struct I2C_RESULT { uint32_t n_bytes_sent; uint32_t n_bytes_recd; } I2C_RESULT_T; typedef struct I2CD_API { void (*i2c_isr_handler)(I2C_HANDLE_T *handle); ErrorCode_t (*i2c_master_transmit_poll)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_master_receive_poll)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_master_tx_rx_poll)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_master_transmit_intr)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_master_receive_intr)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_master_tx_rx_intr)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_slave_receive_poll)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_slave_transmit_poll)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_slave_receive_intr)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_slave_transmit_intr)(I2C_HANDLE_T *handle, I2C_PARAM_T *param, I2C_RESULT_T *result); ErrorCode_t (*i2c_set_slave_addr)(I2C_HANDLE_T *handle, uint32_t slave_addr_0_3, uint32_t slave_mask_0_3); uint32_t (*i2c_get_mem_size)(void); I2C_HANDLE_T * (*i2c_setup)( uint32_t i2c_base_addr, uint32_t * start_of_ram); ErrorCode_t (*i2c_set_bitrate)(I2C_HANDLE_T *handle, uint32_t p_clk_in_hz, uint32_t bitrate_in_bps); uint32_t (*i2c_get_firmware_version)(void); CHIP_I2C_MODE_T (*i2c_get_status)(I2C_HANDLE_T *handle); ErrorCode_t (*i2c_set_timeout)(I2C_HANDLE_T *handle, uint32_t timeout); } I2CD_API_T; // 「UM10601 21.5.2 ROM-based APIs」に相当するstruct // 本当はI2C以外も定義されているが今回は省略 typedef struct ROM_API { const uint32_t dummy1[5]; const I2CD_API_T *pI2CD; const uint32_t dummy2[4]; } LPC_ROM_API_T; // UM10601 24.3 General description // より、アドレスの設定 #define LPC_I2CD_API ((*(LPC_ROM_API_T * *) 0x1FFF1FF8UL)->pI2CD) } // 前のサンプルで書いたwait_msを流用 volatile bool waiting = false; void wait_ms(uint32_t ms) { waiting = true; LPC_MRT->Channel[0].INTVAL = (12000000 / 1000) * ms; LPC_MRT->Channel[0].INTVAL |= 0x1UL<<31; while(waiting) {}; } extern "C" { void MRT_IRQHandler(void) { if ( LPC_MRT->Channel[0].STAT & 0x1) { LPC_MRT->Channel[0].STAT = 0x1; waiting = false; } return; } } // APIを使う前提となる変数を用意 // (2つめが0x20である理由は知らない。LPCOpenのサンプルの真似) static I2C_HANDLE_T *i2cHandleMaster; static uint32_t i2cMasterHandleMEM[0x20]; // I2Cのマスタとして、'0x7c'のスレーブに2byte送信 // (今回使うAQM0802に合わせて'0x7c'と2byteを決め打ち。汎用性は無い。) static void send(uint8_t d1, uint8_t d2) { uint8_t data[3] = {(uint8_t)0x7c, d1, d2}; I2C_PARAM_T param; I2C_RESULT_T result; param.num_bytes_send = 3; // 3byte送信 param.buffer_ptr_send = &data[0]; param.num_bytes_rec = 0; // 受信はしない(byte) param.stop_flag = 1; // 送信終わったらstop // タイムアウト設定 LPC_I2CD_API->i2c_set_timeout(i2cHandleMaster, 100000); // 送信 LPC_I2CD_API->i2c_master_transmit_poll(i2cHandleMaster, ¶m, &result); // ここで戻り値が0(LP_OK)なら成功している。本例ではチェックしない。 } int main(void) { // MRTの設定(上のwait_msを使うため。I2Cとは直接関係なし) LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<10); LPC_SYSCON->PRESETCTRL &= ~(0x1<<7); LPC_SYSCON->PRESETCTRL |= (0x1<<7); NVIC->ISER[0] = (0x1 << 10); LPC_MRT->Channel[0].CTRL = (0x01 << 1) | (0x1); // I2Cを使うために、SWMでpinを割りあてる // SWMのclockを有効化。 // UM10601 4.6.13 System clock control register // → SWMは7bit目 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7); // SDAを4番pinに // UM10601 9.5.8 Pin assign register 7 LPC_SWM->PINASSIGN7 = 0x02ffffffUL; // SCLを3番pinに // UM10601 9.5.9 Pin assign register 8 LPC_SWM->PINASSIGN8 = 0xffffff03UL; // fixed pin functionはreset以外全部disable // UM10601 9.5.10 Pin enable register 0 LPC_SWM->PINENABLE0 = 0xffffffbfUL; // UM10601 16.3 Basic configuration // にしたがってI2Cの準備 // In the SYSAHBCLKCTRL register, set bit 5 (Table 30) to enable the clock to theregister interface. LPC_SYSCON->SYSAHBCLKCTRL |= (0x1<<5); // Clear the I2C peripheral reset using the PRESETCTRL register (Table 19). LPC_SYSCON->PRESETCTRL &= ~(0x1<<6); LPC_SYSCON->PRESETCTRL |= (0x1<<6); // Enable/disable the I2C interrupt in interrupt slots #8 in the NVIC. // -> defaultはdisableっぽいし、今回は割り込みいらないから略。 // Configure the I2C pin functions through the switch matrix. See Section 16.4. // -> 上の方ですでにやった。 // The peripheral clock for the I2C is the system clock (see Figure 32). // -> これは単なる説明だよね? // I2C-bus ROM APIを使って初期設定 i2cHandleMaster = LPC_I2CD_API->i2c_setup(LPC_I2C_BASE, i2cMasterHandleMEM); LPC_I2CD_API->i2c_set_bitrate(i2cHandleMaster, 12000000, 100000); // 速度100KHz // 以下、LCDにコマンドを送る // (送信するコマンドの内容は、mbedにあったAQM0802ライブラリを真似した) wait_ms(100); // Function set = 0x38 send(0x00, 0x38); wait_ms(1); // Function set = 0x39 send(0x00, 0x39); wait_ms(1); // Internal OSC frequency = 0x14 send(0x00, 0x14); wait_ms(1); // Contrast set = 0x78 send(0x00, 0x78); wait_ms(1); // Power/ICON/Contrast control = 0x55 send(0x00, 0x55); wait_ms(1); // Follower control = 0x6C send(0x00, 0x6C); wait_ms(200); // Function set = 0x38 send(0x00, 0x38); wait_ms(1); // Display ON/OFF control = 0x0C send(0x00, 0x0C); wait_ms(1); // Clear Display = 0x01 send(0x00, 0x01); wait_ms(2); // 'Hello'と表示 send(0x40, 'H'); send(0x40, 'e'); send(0x40, 'l'); send(0x40, 'l'); send(0x40, 'o'); send(0x40, '!'); return 0 ; }
補足/雑感
- SWMのクロックは、pinの設定が終わったら節電のために無効にして良いらしい。この例ではしてない。
- SWMの設定の時にリセットも無効にしてしまうと、次にFlashMagicで書き込もうとした時に失敗した。その場合、5番pinをlowにした状態で電源を入れると、強制的にISPモードになって書き込めるようになるらしい。
- それにしても長くなってしまった。ROM APIとかチップの機能なのだから、関数プロトタイプとかLPC8xx.hに入っててほしかった。そういうものじゃないのだろうか。
- ユーザマニュアル(UM10601)は全体的に、自分のような初心者にはキツすぎる。わかる人にはわかるんだろうか。
- 特にROM APIは、説明する気がないんじゃないかと思うくらい。
- これだけで1084byteか。Hello!って表示しただけなのに1kb超えるとは、この先が不安。
- 何にせよ、文字を出せるようになってよかった。もうLEDの点滅回数とかでデバッグしなくて良くなる。達成感あり。