OSDN Git Service

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