OSDN Git Service

最初のコミット
[toppersjsp4bf/middleware-i2c.git] / i2c / i2c_subsystem.c
1 /**
2  * \file i2c_subsystem.c
3  * \brief i2cペリフェラルをマスターモードで使うためのサブシステム
4  * \details
5  * CMSISを使い、i2cペリフェラルをマスターモードで使用する
6  */
7 #include "i2c_subsystem.h"
8 #include "kernel_id.h"
9 #include "s_services.h"
10 #include <t_services.h>
11
12 #ifdef _COMMON_BF592
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>
20 #elif defined(_COMMON_BF548)
21 #include <cdefBF548.h>
22 #define MULTI_TWI
23 #else
24 #error "This processor is not supported"
25 #endif
26
27 #include <ccblkfn.h>
28
29
30 #define I2C_DEBUG
31
32 #ifdef I2C_DEBUG
33 #define I2C_SYSLOG(level,msg) syslog( level, msg )
34 #else
35 #define I2C_SYSLOG(level,msg)
36 #endif
37
38
39 /**
40  * \brief チップ上のI2Cペリフェラルの数
41  */
42 #define I2CNUM 1
43
44 /**
45  * \brief I2C ペリフェラルのタイムアウト時間(mSec)
46  */
47 #define I2C_TIMEOUT 100
48
49 /**
50  * \brief TWIペリフェラルの管理用ステート型。内部ステートマシンの状態値。
51  */
52 enum I2C_STATE {
53     I2C_XMT_ENTRY,
54     I2C_XMTRCV_ENTRY,
55     I2C_XMT_WAIT,
56     I2C_XMT_INT,
57     I2C_XMT_NEXT_BYTE,
58     I2C_RCV_ENTRY,
59     I2C_RCV_WAIT,
60     I2C_RCV_INT,
61     I2C_RCV_NEXT_BYTE,
62     I2C_EXIT
63 };
64
65
66 /**
67  * \brief I2C管理用構造体
68  */
69 struct I2C_MASTER_CONTROL_TYPE {
70     ID signal;                /**< データの送受信が全部終わったときに割り込みハンドラからタスクに知らせるためのセマフォ */
71     ID blocking;            /**< I2Cペリフェラルへの排他アクセスのためのセマフォ */
72     unsigned short intr_state;         /**< TWIペリフェラルの割り込みステータスのコピー **/
73     volatile unsigned short *   clkdiv;      /**< TWI_CLCKDIVレジスタのアドレス **/
74     volatile unsigned short *    control;     /**< TWI_CONTROLレジスタのアドレス **/
75     volatile unsigned short *    master_ctl;  /**< TWI_MASTER_CTLレジスタのアドレス **/
76     volatile unsigned short *    master_stat; /**< TWI_MASTER_STATレジスタのアドレス **/
77     volatile unsigned short *    master_addr; /**< TWI_MASTER_ADDRレジスタのアドレス **/
78     volatile unsigned short *    int_stat;    /**< TWI_MASTER_STATレジスタのアドレス **/
79     volatile unsigned short *    int_mask;    /**< TWI_INT_MASKレジスタのアドレス **/
80     volatile unsigned short *    fifo_ctl;    /**< TWI_FIFO_CTLレジスタのアドレス **/
81     volatile unsigned short *    fifo_stat;   /**< TWI_FIFO_STATレジスタのアドレス **/
82     volatile unsigned short *    xmt_data8;   /**< TWI_XMT_DATA8レジスタのアドレス **/
83     volatile unsigned short *    rcv_data8;   /**< TWI_RCV_DATA8レジスタのアドレス **/
84     volatile unsigned short *    slave_ctl;   /**< TWI_MASTER_CTLレジスタのアドレス **/
85
86 };
87
88 /**
89  * \brief i2cペリフェラルを管理するための構造体群。
90  */
91 static struct I2C_MASTER_CONTROL_TYPE i2c_control[I2CNUM];
92
93
94 int i2c_master_write( int peripheral, int slave, unsigned char write_data[], int write_count )
95 {
96         // read のための引数を0にしておくと、writeのみがおこなわれる
97     return i2c_master_write_read( peripheral, slave, write_data, write_count, 0, 0);
98 }
99
100 int i2c_master_read( int peripheral, int slave, unsigned char read_data[], int read_count )
101 {
102         // write のための引数を0にしておくと、readのみがおこなわれる
103     return i2c_master_write_read( peripheral, slave, 0, 0, read_data, read_count);
104 }
105
106
107 int i2c_master_write_read( int peripheral, int slave, unsigned char write_data[], int write_count, unsigned char read_data[], int read_count)
108 {
109     struct I2C_MASTER_CONTROL_TYPE *twi;
110     BOOL read, write, rstart, quit;
111     unsigned char *wptr, *rptr;
112     enum I2C_STATE state;      /**< I2Cペリフェラルハンドラの内部状態 */
113     PRI old_priority;         // タスクの優先順位を保存しておくための変数
114
115         // 可読性向上のため、TWI管理構造体のポインタを設定する。
116     twi = &i2c_control[peripheral];
117
118         // 送受信ポインタ.とかくこの世は生きにくい
119     wptr = write_data;
120     rptr = read_data;
121
122         // この代入は必要ないが、警告がうるさいので
123     rstart= FALSE;
124
125         // ループ終了条件の成立フラグ
126     quit = FALSE;
127
128         // 読み書きの有無の確認
129         // バッファがNULLだったり長さが0のパラメタは転送に使わない
130     write = ( write_data != NULL ) && ( write_count != 0 );
131     read =  ( read_data != NULL )  && ( read_count != 0 );
132
133         // パラメタの組み合わせによって初期状態が変わる
134     if ( read && write )
135         state = I2C_XMTRCV_ENTRY;
136     else if ( write && ! read )
137         state = I2C_XMT_ENTRY;
138     else if ( ! write && read )
139         state = I2C_RCV_ENTRY;
140     else
141         return I2C_ERR_WRONGPARAM;
142
143     if ( write_count > 254 || read_count > 254 )
144         return I2C_ERR_TOOLONGBUFFER;
145
146
147         // peripheral 引数で指定されたi2cペリフェラルを排他的に使うためのPV処理。
148         // これでスレッドセーフにできる
149     wai_sem(twi->blocking);
150
151         // タスクの優先順位を一旦引き上げる。これは、TWIのレスポンスを保証するためである
152     get_pri( TSK_SELF, &old_priority );
153     chg_pri( TSK_SELF, TMAX_TPRI );
154
155         // SPIスレーブデバイスのアドレスを設定(7bit)
156     *twi->master_addr = slave;
157
158         // FIFO フラッシュ
159     *twi->fifo_ctl = XMTFLUSH | RCVFLUSH;
160     ssync();
161
162         // FIFO 設定
163     *twi->fifo_ctl = XMTINTLEN;                        // 割り込みスレシホールドは送受とも1バイト、FLUSHはクリアする
164
165         // TWIステートマシン
166     do {
167         switch (state)
168         {
169         case I2C_XMT_ENTRY :
170             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_ENTRY" );
171             rstart = FALSE;
172             *twi->master_ctl = ( write_count<<6 ) | MEN;            // no rstart, transmit, no fast mode;
173             state = I2C_XMT_NEXT_BYTE;
174             break;
175         case I2C_XMTRCV_ENTRY :
176             I2C_SYSLOG(LOG_NOTICE, "I2C_XMTRCV_ENTRY" );
177             rstart = TRUE;
178             *twi->master_ctl = ( write_count<<6 ) | RSTART | MEN;    // rstart, transmit, no fast mode;
179             state = I2C_XMT_NEXT_BYTE;
180             break;
181         case I2C_RCV_ENTRY :
182             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_ENTRY" );
183             rstart = FALSE;
184             *twi->master_ctl = ( read_count<<6 ) | MDIR | MEN;      // no rstart, receive, no fast mode;
185             state = I2C_RCV_WAIT;
186             break;
187         case I2C_XMT_WAIT :
188             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_WAIT" );
189                 // 割り込みハンドラが送信割り込みを通知するまで待つ
190             if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
191             {
192                 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat;     // エラーなら割り込みステータスを併記
193                 state = I2C_EXIT;
194             }
195             else
196                 state = I2C_XMT_INT;
197             break;
198         case I2C_XMT_INT :
199             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_INT" );
200             if ( twi->intr_state & MERR )                        // エラーならすぐ終了
201                 state = I2C_EXIT;
202             else if ( ( twi->intr_state & MCOMP ) && rstart)    // MCOMP かつ RSTARTなら受信へ
203                 state = I2C_RCV_ENTRY;
204             else if ( twi->intr_state & MCOMP ){                // RSTARTがないMCOMPなら終了
205                 twi->intr_state &= ~MCOMP;
206                 state = I2C_EXIT;
207             }
208             else                                                // それ以外は送信割り込み
209                 state = I2C_XMT_NEXT_BYTE;
210             break;
211         case I2C_XMT_NEXT_BYTE :
212             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_NEXT_BYTE" );
213             *twi->xmt_data8 = *(wptr++);        // 1バイト送信
214             state = I2C_XMT_WAIT;          // 次の送信待
215             break;
216         case I2C_RCV_WAIT :
217             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_WAIT" );
218                 // 割り込みハンドラが受信割り込みを通知するまで待つ
219             if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
220             {
221                 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat;     // エラーなら割り込みステータスを併記
222                 state = I2C_EXIT;
223             }
224             else
225                 state = I2C_RCV_INT;
226             break;
227         case I2C_RCV_INT :
228             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_INT" );
229             if ( twi->intr_state & MERR)            // エラーならすぐ終了
230                 state = I2C_EXIT;
231             else if ( twi->intr_state & MCOMP ){   // 終了ならエラークリア
232                 twi->intr_state &= ~MCOMP;
233                 state = I2C_EXIT;
234             }
235             else                                   // それ以外は受信割り込み
236                 state = I2C_RCV_NEXT_BYTE;
237             break;
238         case I2C_RCV_NEXT_BYTE :
239             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_NEXT_BYTE" );
240             *(rptr++) = *twi->rcv_data8;        // 1バイト受信
241             state = I2C_RCV_WAIT;        // 次の受信待
242             break;
243         case I2C_EXIT :
244             I2C_SYSLOG(LOG_NOTICE, "I2C_EXIT" );
245             * twi->master_ctl = 0;            // マスターをディセーブルして終了
246             quit = TRUE;
247             break;
248         }
249     } while ( !quit );
250
251     // タスクの優先順位を元に戻す
252     chg_pri( TSK_SELF, old_priority);
253
254     // 排他区間の終了
255     sig_sem(twi->blocking);
256
257         // 割り込みステータスを返す。正常終了なら0
258     return twi->intr_state;
259 }
260
261 /**
262  * \brief i2c 割り込みサービスルーチン本体
263  * \param peripheral I2Cペリフェラル番号。0から始まる。
264  * \details
265  * 割り込みサービスルーチンから呼び出す。この関数が割り込みハンドラの実体である。
266  * チップに複数のTWIペリフェラルがある場合には、引数に当該TWIペリフェラル番号を与えて呼び出す。
267  *
268  * 関数内部では、タスクに割り込みが入ったことを知らせ、ペリフェラルの割り込みステータスを
269  * 変数に退避してから戻る。
270  */
271 static void i2c_master_handler( int peripheral )
272 {
273     struct I2C_MASTER_CONTROL_TYPE *twi;
274
275         // 可読性向上のため、TWI管理構造体のポインタを設定する。
276     twi = &i2c_control[peripheral];
277
278         // TWIペリフェラルの割り込みステータスを取得し、コピーを保存する。
279         // コピーを保存するのは、このあと割り込みクリアで消えるからである。
280     twi->intr_state = *twi->int_stat;
281
282         // 割り込みクリア。すべての割り込み要素をクリアしてしまう。ステータスはコピーしているのでタスクで処理する。
283     *twi->int_stat = 0xFF;
284
285         // 割り込みクリアが確定するまで待つ。
286     ssync();
287
288     /* 通知はセマフォを使う。タスクは i2c_master_write_read()の中で待っている。 */
289     isig_sem(twi->signal);
290 }
291
292
293 /**
294  * \details
295  * TOPPERS/JSPでは、DEF_INHから割り込みハンドラに引数を渡せない。そのため、この関数内で
296  * i2c_master_handler() を引数0決め打ちで呼ぶ。
297  */
298 void i2c0_master_handler(void)
299 {
300     i2c_master_handler( 0 );
301 }
302
303
304 /**
305  * \brief i2c イニシャライザ
306  * \param exinf ペリフェラル番号。TWI0ならば、0を渡す。
307  * \details
308  * I2C用のコントロールブロックのI2C相当部分を初期化する。
309  * 現段階ではTWI0にしか対応していない。
310  */
311 void i2c_master_initialize(VP_INT exinf)
312 {
313     struct I2C_MASTER_CONTROL_TYPE *twi;
314     unsigned int peripheral = (unsigned int)exinf;
315
316         // 可読性向上のため、TWI管理構造体のポインタを設定する。
317     twi = &i2c_control[peripheral];
318
319     twi->blocking = SEM_I2C0_BLOCK;    // ペリフェラルブロック用セマフォ
320     twi->signal = SEM_I2C0_SIGNAL;    // 割り込み・タスク通信用セマフォ
321         // レジスタアドレスを設定
322
323 #ifndef MULTI_TWI
324     twi->clkdiv         =    pTWI_CLKDIV    ;
325     twi->control        =    pTWI_CONTROL    ;
326     twi->master_ctl     =    pTWI_MASTER_CTL    ;
327     twi->master_stat    =    pTWI_MASTER_STAT    ;
328     twi->master_addr    =    pTWI_MASTER_ADDR    ;
329     twi->int_stat       =    pTWI_INT_STAT    ;
330     twi->int_mask       =    pTWI_INT_MASK    ;
331     twi->fifo_ctl       =    pTWI_FIFO_CTL    ;
332     twi->fifo_stat      =    pTWI_FIFO_STAT    ;
333     twi->xmt_data8      =    pTWI_XMT_DATA8    ;
334     twi->rcv_data8      =    pTWI_RCV_DATA8    ;
335     twi->slave_ctl      =    pTWI_SLAVE_CTL    ;
336 #else
337     twi->clkdiv         =    pTWI0_CLKDIV    ;
338     twi->control        =    pTWI0_CONTROL    ;
339     twi->master_ctl     =    pTWI0_MASTER_CTL    ;
340     twi->master_stat    =    pTWI0_MASTER_STAT    ;
341     twi->master_addr    =    pTWI0_MASTER_ADDR    ;
342     twi->int_stat       =    pTWI0_INT_STAT    ;
343     twi->int_mask       =    pTWI0_INT_MASK    ;
344     twi->fifo_ctl       =    pTWI0_FIFO_CTL    ;
345     twi->fifo_stat      =    pTWI0_FIFO_STAT    ;
346     twi->xmt_data8      =    pTWI0_XMT_DATA8    ;
347     twi->rcv_data8      =    pTWI0_RCV_DATA8    ;
348     twi->slave_ctl      =    pTWI0_SLAVE_CTL    ;
349 #endif
350
351     *twi->control = 0;        // TWI をディセーブル
352     *twi->master_ctl = 0;    // マスター機能をディセーブル
353     *twi->slave_ctl = 0;    // スレーブ機能をディセーブル
354
355     *twi->control = TWI_ENA | (SYSCLOCK/10000000);     // HWRによると、プリスケール値はSYSCLKを10MHzで割ったものでなければならない。
356     *twi->clkdiv = 30<<8 | 70;                         // TWI内部クロック10MHzに対して、100kHzのI2Cクロックは100分の1である
357
358         // 割り込みイネーブル設定。送受割り込み、完了割り込み及びエラー割り込み。
359     *twi->int_mask =     MCOMP | MERR | XMTSERV | RCVSERV;
360
361         // システム割り込みをイネーブルにする。
362 #ifndef MULTI_TWI
363     ena_int(INTNO_TWI);
364 #else
365     ena_int(INTNO_TWI0);
366 #endif
367 }
368
369