2 * \file i2c_subsystem.c
3 * \brief i2cペリフェラルをマスターモードで使うためのサブシステム
5 * CMSISを使い、i2cペリフェラルをマスターモードで使用する
7 #include "i2c_subsystem.h"
9 #include <s_services.h>
10 #include <t_services.h>
13 #include <cdefBF592-A.h>
14 #elif defined(_COMMON_BF506)
15 #include <cdefBF506F.h>
16 #elif defined(_COMMON_BF518)
17 #include <cdefBF518.h>
18 #elif defined(_COMMON_BF537)
19 #include <cdefBF537.h>
21 #error "This processor is not supported"
30 #define I2C_SYSLOG(level,msg) syslog( level, msg )
32 #define I2C_SYSLOG(level,msg)
37 * \brief チップ上のI2Cペリフェラルの数
42 * \brief I2C ペリフェラルのタイムアウト時間(mSec)
44 #define I2C_TIMEOUT 100
47 * \brief TWIペリフェラルの管理用ステート型。内部ステートマシンの状態値。
66 struct I2C_MASTER_CONTROL_TYPE {
67 ID signal; /**< データの送受信が全部終わったときに割り込みハンドラからタスクに知らせるためのセマフォ */
68 ID blocking; /**< I2Cペリフェラルへの排他アクセスのためのセマフォ */
69 unsigned short intr_state; /**< TWIペリフェラルの割り込みステータスのコピー **/
70 volatile unsigned short * clkdiv; /**< TWI_CLCKDIVレジスタのアドレス **/
71 volatile unsigned short * control; /**< TWI_CONTROLレジスタのアドレス **/
72 volatile unsigned short * master_ctl; /**< TWI_MASTER_CTLレジスタのアドレス **/
73 volatile unsigned short * master_stat; /**< TWI_MASTER_STATレジスタのアドレス **/
74 volatile unsigned short * master_addr; /**< TWI_MASTER_ADDRレジスタのアドレス **/
75 volatile unsigned short * int_stat; /**< TWI_MASTER_STATレジスタのアドレス **/
76 volatile unsigned short * int_mask; /**< TWI_INT_MASKレジスタのアドレス **/
77 volatile unsigned short * fifo_ctl; /**< TWI_FIFO_CTLレジスタのアドレス **/
78 volatile unsigned short * fifo_stat; /**< TWI_FIFO_STATレジスタのアドレス **/
79 volatile unsigned short * xmt_data8; /**< TWI_XMT_DATA8レジスタのアドレス **/
80 volatile unsigned short * rcv_data8; /**< TWI_RCV_DATA8レジスタのアドレス **/
81 volatile unsigned short * slave_ctl; /**< TWI_MASTER_CTLレジスタのアドレス **/
86 * \brief i2cペリフェラルを管理するための構造体群。
88 static struct I2C_MASTER_CONTROL_TYPE i2c_control[I2CNUM];
90 static char * i2c_strerror( ER ercd )
95 case I2C_ERR_WRONGPARAM :
96 return("I2C_ERR_WRONGPARAM");
97 case I2C_ERR_TOOLONGBUFFER :
98 return("I2C_ERR_TOOLONGBUFFER");
99 case I2C_ERR_TIMEOUT :
100 return ("I2C_ERR_TIMEOUT");
102 return ("I2C_ERR_UNKNOWN");
107 i2c_perror(UINT prio, const char *file, int line, const char *expr, ER ercd)
109 syslog_5(prio, "%s reported by `%s' in line %d of `%s'. Interrupt Status = %04x",
110 i2c_strerror(ercd), expr, line, file, ercd & 0xFFFF);
114 int i2c_master_write( int peripheral, int slave, unsigned char write_data[], int write_count )
116 // read のための引数を0にしておくと、writeのみがおこなわれる
117 return i2c_master_write_read( peripheral, slave, write_data, write_count, 0, 0);
120 int i2c_master_read( int peripheral, int slave, unsigned char read_data[], int read_count )
122 // write のための引数を0にしておくと、readのみがおこなわれる
123 return i2c_master_write_read( peripheral, slave, 0, 0, read_data, read_count);
127 int i2c_master_write_read( int peripheral, int slave, unsigned char write_data[], int write_count, unsigned char read_data[], int read_count)
129 struct I2C_MASTER_CONTROL_TYPE *twi;
130 BOOL read, write, rstart, quit;
131 unsigned char *wptr, *rptr;
132 enum I2C_STATE state; /**< I2Cペリフェラルハンドラの内部状態 */
133 PRI old_priority; // タスクの優先順位を保存しておくための変数
135 // 可読性向上のため、TWI管理構造体のポインタを設定する。
136 twi = &i2c_control[peripheral];
138 // 送受信ポインタ.とかくこの世は生きにくい
142 // この代入は必要ないが、警告がうるさいので
149 // バッファがNULLだったり長さが0のパラメタは転送に使わない
150 write = ( write_data != NULL ) && ( write_count != 0 );
151 read = ( read_data != NULL ) && ( read_count != 0 );
153 // パラメタの組み合わせによって初期状態が変わる
155 state = I2C_XMTRCV_ENTRY;
156 else if ( write && ! read )
157 state = I2C_XMT_ENTRY;
158 else if ( ! write && read )
159 state = I2C_RCV_ENTRY;
161 return I2C_ERR_WRONGPARAM;
163 if ( write_count > 254 || read_count > 254 )
164 return I2C_ERR_TOOLONGBUFFER;
167 // peripheral 引数で指定されたi2cペリフェラルを排他的に使うためのPV処理。
169 wai_sem(twi->blocking);
171 // タスクの優先順位を一旦引き上げる。これは、TWIのレスポンスを保証するためである
172 get_pri( TSK_SELF, &old_priority );
173 chg_pri( TSK_SELF, TMAX_TPRI );
175 // SPIスレーブデバイスのアドレスを設定(7bit)
176 *twi->master_addr = slave;
179 *twi->fifo_ctl = XMTFLUSH | RCVFLUSH;
183 *twi->fifo_ctl = XMTINTLEN; // 割り込みスレシホールドは送受とも1バイト、FLUSHはクリアする
190 I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_ENTRY" );
192 *twi->master_ctl = ( write_count<<6 ) | MEN; // no rstart, transmit, no fast mode;
193 state = I2C_XMT_NEXT_BYTE;
195 case I2C_XMTRCV_ENTRY :
196 I2C_SYSLOG(LOG_NOTICE, "I2C_XMTRCV_ENTRY" );
198 *twi->master_ctl = ( write_count<<6 ) | RSTART | MEN; // rstart, transmit, no fast mode;
199 state = I2C_XMT_NEXT_BYTE;
202 I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_ENTRY" );
204 *twi->master_ctl = ( read_count<<6 ) | MDIR | MEN; // no rstart, receive, no fast mode;
205 state = I2C_RCV_WAIT;
208 I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_WAIT" );
209 // 割り込みハンドラが送信割り込みを通知するまで待つ
210 if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
212 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat; // エラーなら割り込みステータスを併記
219 I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_INT" );
220 if ( twi->intr_state & MERR ) // エラーならすぐ終了
222 else if ( ( twi->intr_state & MCOMP ) && rstart) // MCOMP かつ RSTARTなら受信へ
223 state = I2C_RCV_ENTRY;
224 else if ( twi->intr_state & MCOMP ){ // RSTARTがないMCOMPなら終了
225 twi->intr_state &= ~MCOMP;
229 state = I2C_XMT_NEXT_BYTE;
231 case I2C_XMT_NEXT_BYTE :
232 I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_NEXT_BYTE" );
233 *twi->xmt_data8 = *(wptr++); // 1バイト送信
234 state = I2C_XMT_WAIT; // 次の送信待
237 I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_WAIT" );
238 // 割り込みハンドラが受信割り込みを通知するまで待つ
239 if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
241 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat; // エラーなら割り込みステータスを併記
248 I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_INT" );
249 if ( twi->intr_state & MERR) // エラーならすぐ終了
251 else if ( twi->intr_state & MCOMP ){ // 終了ならエラークリア
252 twi->intr_state &= ~MCOMP;
256 state = I2C_RCV_NEXT_BYTE;
258 case I2C_RCV_NEXT_BYTE :
259 I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_NEXT_BYTE" );
260 *(rptr++) = *twi->rcv_data8; // 1バイト受信
261 state = I2C_RCV_WAIT; // 次の受信待
264 I2C_SYSLOG(LOG_NOTICE, "I2C_EXIT" );
265 * twi->master_ctl = 0; // マスターをディセーブルして終了
272 chg_pri( TSK_SELF, old_priority);
275 sig_sem(twi->blocking);
277 // 割り込みステータスを返す。正常終了なら0
278 return twi->intr_state;
282 * \brief i2c 割り込みサービスルーチン本体
283 * \param peripheral I2Cペリフェラル番号。0から始まる。
285 * 割り込みサービスルーチンから呼び出す。この関数が割り込みハンドラの実体である。
286 * チップに複数のTWIペリフェラルがある場合には、引数に当該TWIペリフェラル番号を与えて呼び出す。
288 * 関数内部では、タスクに割り込みが入ったことを知らせ、ペリフェラルの割り込みステータスを
291 static void i2c_master_handler( int peripheral )
293 struct I2C_MASTER_CONTROL_TYPE *twi;
295 // 可読性向上のため、TWI管理構造体のポインタを設定する。
296 twi = &i2c_control[peripheral];
298 // TWIペリフェラルの割り込みステータスを取得し、コピーを保存する。
299 // コピーを保存するのは、このあと割り込みクリアで消えるからである。
300 twi->intr_state = *twi->int_stat;
302 // 割り込みクリア。すべての割り込み要素をクリアしてしまう。ステータスはコピーしているのでタスクで処理する。
303 *twi->int_stat = 0xFF;
308 /* 通知はセマフォを使う。タスクは i2c_master_write_read()の中で待っている。 */
309 isig_sem(twi->signal);
315 * TOPPERS/JSPでは、DEF_INHから割り込みハンドラに引数を渡せない。そのため、この関数内で
316 * i2c_master_handler() を引数0決め打ちで呼ぶ。
318 void i2c0_master_handler(void)
320 i2c_master_handler( 0 );
326 * \param exinf ペリフェラル番号。TWI0ならば、0を渡す。
328 * I2C用のコントロールブロックのI2C相当部分を初期化する。
330 void i2c_master_initialize(VP_INT exinf)
332 struct I2C_MASTER_CONTROL_TYPE *twi;
333 unsigned int peripheral = (unsigned int)exinf;
335 // 可読性向上のため、TWI管理構造体のポインタを設定する。
336 twi = &i2c_control[peripheral];
338 twi->blocking = SEM_I2C0_BLOCK; // ペリフェラルブロック用セマフォ
339 twi->signal = SEM_I2C0_SIGNAL; // 割り込み・タスク通信用セマフォ
341 twi->clkdiv = pTWI_CLKDIV ;
342 twi->control = pTWI_CONTROL ;
343 twi->master_ctl = pTWI_MASTER_CTL ;
344 twi->master_stat = pTWI_MASTER_STAT ;
345 twi->master_addr = pTWI_MASTER_ADDR ;
346 twi->int_stat = pTWI_INT_STAT ;
347 twi->int_mask = pTWI_INT_MASK ;
348 twi->fifo_ctl = pTWI_FIFO_CTL ;
349 twi->fifo_stat = pTWI_FIFO_STAT ;
350 twi->xmt_data8 = pTWI_XMT_DATA8 ;
351 twi->rcv_data8 = pTWI_RCV_DATA8 ;
352 twi->slave_ctl = pTWI_SLAVE_CTL ;
355 *twi->control = 0; // TWI をディセーブル
356 *twi->master_ctl = 0; // マスター機能をディセーブル
357 *twi->slave_ctl = 0; // スレーブ機能をディセーブル
358 *twi->master_addr = 0;
361 *twi->int_stat = 0x1F; // すべての割り込みステータスをクリア
362 *twi->master_stat =0x1F; // すべてのステータスをクリア
364 *twi->control = TWI_ENA | (SYSCLOCK/10000000); // HWRによると、プリスケール値はSYSCLKを10MHzで割ったものでなければならない。
365 *twi->clkdiv = 30<<8 | 70; // TWI内部クロック10MHzに対して、100kHzのI2Cクロックは100分の1である
367 // 割り込みイネーブル設定。送受割り込み、完了割り込み及びエラー割り込み。
368 *twi->int_mask = MCOMP | MERR | XMTSERV | RCVSERV;
370 // システム割り込みをイネーブルにする。