OSDN Git Service

First commitment for the BlackTank LPC1769.
[blacktank/blacktank.git] / i2s_subsystem.c
1 /**
2  * \file i2s_subsystem.c
3  * \brief I2S制御関数群
4  * \date 2011/02/19
5  * \author takemasa
6  */
7
8 #include "i2s_subsystem.h"
9 #include "kernel_cfg.h"
10
11 #include <LPC17xx.h>
12 #include <kernel.h>
13 #include <t_syslog.h>
14
15 void i2s_init()
16 {
17     /* I2Sの電源をオン */
18     LPC_SC->PCONP |= 1 << 27;
19     /*  I2SのPCLKを 1/4 にセット (25MHz) */
20     LPC_SC->PCLKSEL0 &= ~(0x3 << 22);
21
22     /** P0.4 - P0.9を I2Sに */
23     LPC_PINCON->PINSEL0 &= ~(0xFFF << 8);
24 //      LPC_PINCON->PINSEL0 |= 0x555 << 8;
25     LPC_PINCON->PINSEL0 |= 0x550 << 8;  // RX ws/clk disconnect
26
27     /*
28      * I2Sのモード設定。
29      * I2S, 32bit, 4-wire transmitter slave mode sharing the
30      * receiver bit clock and WS Typical receiver slave mode
31      * stereo, stop & reset.
32      *
33      * TXのビット/ワードクロックはRXから供給を受け、RXの
34      * ビット/ワードクロックは外部から供給を受ける。
35      * この場合、TXはデバイダ関係の設定が不要のはずである。
36      * また、UM10360 rev 2 Fig111によるとI2SRXBITRATEは
37      * div by 1になるよう設定しなければならない。
38      */
39
40     /*
41      * Digital Audio Output & Input
42      */
43
44     /*
45      * UM10360には詳細がしるされていないが、NXPによるCMSISの例題を
46      * 読む限り、設定前にDAO/DAIをreset, stop状態に置き、
47      * 処理開始時にresetから解除し、stopから解除する動作になっている。
48      * reset状態でレジスタを操作する事には不安を感じるが、例題しか
49      * 情報源がないのでそれにしたがう。
50      */
51     LPC_I2S->I2SDAO = 0x0;      /* Disable */
52     LPC_I2S->I2SDAI = 0x0;      /* Disable */
53
54     LPC_I2S->I2SDAO = 3 << 0    // 32bit data
55         | 0 << 2                // mono : 1 is mono, 0 is stereo
56         | 1 << 3                // stop : 1 is stop
57         | 1 << 4                // reset : 1 is reset;
58         | 1 << 5                // ws_sel : 1 is slave, 0 is master
59         | 31 << 6               // ws_halfpeiod : half period value -1;
60         | 0 << 15;              // mute : 1 is mute, 0 is unmute
61
62     LPC_I2S->I2SDAI = 3 << 0    // 32bit data
63         | 0 << 2                // mono : 1 is mono, 0 is stereo
64         | 1 << 3                // stop : 1 is stop
65         | 1 << 4                // reset : 1 is reset;
66         | 1 << 5                // ws_sel : 1 is slave, 0 is master
67         | 31 << 6;              // ws_halfpeiod : half period value -1;
68
69     /* DMA Configuration. RX DMA is Enabled */
70     LPC_I2S->I2SDMA1 = 1 << 0   // RX enable
71         | 2 << 8;               // FIFO depth = 2
72     /* DMA Configuration. TX DMA is enabled */
73     LPC_I2S->I2SDMA2 = 1 << 1   // TX DMA Enable
74         | 2 << 16;              // FIFO depth = 2;
75
76     /*
77      * I2SRXBITRATE
78      */
79
80     /*
81      * 外部ビットクロックから内部ビットクロックを生成する分周器。1で割る。
82      */
83     LPC_I2S->I2SRXBITRATE = 0;  /* RX bit clock div by 1 */
84     LPC_I2S->I2STXBITRATE = 0;  /* RX bit clock div by 1 */
85
86     /* 送信モード */
87     LPC_I2S->I2STXMODE = 0;     /* Transmit slave typical mode  */
88     /* 受信モード */
89     LPC_I2S->I2SRXMODE = 4;     /* Receive slave typical mode */
90 //      LPC_I2S->I2SRXMODE = 0;                                 /* Receive slave 4-wire mode */
91
92 }
93
94 void i2s_start()
95 {
96     /* TX I2S をストップから開放 */
97     LPC_I2S->I2SDAO &= ~(1 << 3);
98     /* RX I2S をストップから開放 */
99     LPC_I2S->I2SDAI &= ~(1 << 3);
100     /* TX I2S をリセットから開放 */
101     LPC_I2S->I2SDAO &= ~(1 << 4);
102     /* RX I2S をリセットから開放 */
103     LPC_I2S->I2SDAI &= ~(1 << 4);
104 }
105
106 void i2s_dma_init(struct I2S_AUDIO_DATA *audio_data)
107 {
108     int i;
109
110     /* DMA の電源をオン */
111     LPC_SC->PCONP |= 1 << 29;
112     /* DMA Controler Enable */
113     LPC_GPDMA->DMACConfig = 0x01;       /* Little Endian, Enable */
114
115     /* 送信LLIの初期化 */
116     audio_data->txI2SLLI[0].SrcAddr = audio_data->txBuffer[0];  /* 送信データのバッファ */
117     audio_data->txI2SLLI[0].DstAddr = (int *) &LPC_I2S->I2STXFIFO;      /* I2SのFIFOに送信する */
118     audio_data->txI2SLLI[0].nextLLI = &(audio_data->txI2SLLI[1]);       /* 次のLLIへのリンク */
119     /* No interrupt, Source Incr, Src/Dest 32bit, Src/Dest Bust=1, Bufsize = AUDIOBUFSIZE */
120     audio_data->txI2SLLI[0].Control = 0 << 31   // I : Interrupt : Do not trigger interrupt
121         | 0 << 27               // DI : Destination Increment : Do not increment after transfer
122         | 1 << 26               // SI : Source increment : Increment after transfer
123         | 2 << 21               // Dwidth : Destination width : 32bit
124         | 2 << 18               // SWidth : Source width : 32bit
125         | 0 << 15               // DBSize : Destination Burst
126         | 0 << 12               // SBSize : Source burst
127         | AUDIOBUFSIZE;         // Transfer size [count]
128
129     /* 送信LLIの初期化 */
130     audio_data->txI2SLLI[1].SrcAddr = audio_data->txBuffer[1];  /* 送信データのバッファ */
131     audio_data->txI2SLLI[1].DstAddr = (int *) &LPC_I2S->I2STXFIFO;      /* I2SのFIFOに送信する */
132     audio_data->txI2SLLI[1].nextLLI = &(audio_data->txI2SLLI[0]);       /* 次のLLIへのリンク */
133     /* No interrupt, Source Incr, Src/Dest 32bit, Src/Dest Bust=1, Bufsize = AUDIOBUFSIZE */
134     audio_data->txI2SLLI[1].Control = 0 << 31   // I : Interrupt : Do not trigger interrupt
135         | 0 << 27               // DI : Destination Increment : Do not increment after transfer
136         | 1 << 26               // SI : Source increment : Increment after transfer
137         | 2 << 21               // Dwidth : Destination width : 32bit
138         | 2 << 18               // SWidth : Source width : 32bit
139         | 0 << 15               // DBSize : Destination Burst
140         | 0 << 12               // SBSize : Source burst
141         | AUDIOBUFSIZE;         // Transfer size [count]
142
143     /* 受信LLIの初期化 */
144     audio_data->rxI2SLLI[0].DstAddr = audio_data->rxBuffer[0];  /* 受信データのバッファ */
145     audio_data->rxI2SLLI[0].SrcAddr = (int *) &LPC_I2S->I2SRXFIFO;      /* I2SのFIFOから受信する */
146     audio_data->rxI2SLLI[0].nextLLI = &(audio_data->rxI2SLLI[1]);       /* 次のLLIへのリンク */
147     /* No interrupt, Source Incr, Src/Dest 32bit, Src/Dest Bust=1, Bufsize = AUDIOBUFSIZE */
148     audio_data->rxI2SLLI[0].Control = 1 << 31   // I : Interrupt : trigger interrupt
149         | 1 << 27               // DI : Destination Increment : Increment after transfer
150         | 0 << 26               // SI : Source increment : Do not increment after transfer
151         | 2 << 21               // Dwidth : Destination width : 32bit
152         | 2 << 18               // SWidth : Source width : 32bit
153         | 0 << 15               // DBSize : Destination Burst
154         | 0 << 12               // SBSize : Source burst
155         | AUDIOBUFSIZE;         // Transfer size [count]
156
157     /* 受信LLIの初期化 */
158     audio_data->rxI2SLLI[1].DstAddr = audio_data->rxBuffer[1];  /* 受信データのバッファ */
159     audio_data->rxI2SLLI[1].SrcAddr = (int *) &LPC_I2S->I2SRXFIFO;      /* I2SのFIFOから受信する */
160     audio_data->rxI2SLLI[1].nextLLI = &(audio_data->rxI2SLLI[0]);       /* 次のLLIへのリンク */
161     /* No interrupt, Source Incr, Src/Dest 32bit, Src/Dest Bust=1, Bufsize = AUDIOBUFSIZE */
162     audio_data->rxI2SLLI[1].Control = 1 << 31   // I : Interrupt : trigger interrupt
163         | 1 << 27               // DI : Destination Increment : Increment after transfer
164         | 0 << 26               // SI : Source increment : Do not increment after transfer
165         | 2 << 21               // Dwidth : Destination width : 32bit
166         | 2 << 18               // SWidth : Source width : 32bit
167         | 0 << 15               // DBSize : Destination Burst
168         | 0 << 12               // SBSize : Source burst
169         | AUDIOBUFSIZE;         // Transfer size [count]
170
171     /* 送信バッファのクリア */
172     for (i = 0; i < AUDIOBUFSIZE; i++) {
173         audio_data->txBuffer[0][i] = 0x00;      /* ゼロフィル */
174         audio_data->txBuffer[1][i] = 0x00;
175     }
176
177     /* 受信バッファのクリア */
178     for (i = 0; i < AUDIOBUFSIZE; i++) {
179         audio_data->rxBuffer[0][i] = 0x00;      /* ゼロフィル */
180         audio_data->rxBuffer[1][i] = 0x00;
181     }
182
183     /*
184      * DMAの最初の1回の動作はレジスタを直接初期化して起動する。
185      * LPC1768のDMA起動方法は洗練されているとは言い難い。
186      */
187
188     /* 送信DMAの起動準備 */
189     LPC_GPDMACH0->DMACCSrcAddr = (int) audio_data->txI2SLLI[0].SrcAddr;
190     LPC_GPDMACH0->DMACCDestAddr = (int) audio_data->txI2SLLI[0].DstAddr;
191     LPC_GPDMACH0->DMACCLLI = (int) &audio_data->txI2SLLI[1];
192     LPC_GPDMACH0->DMACCControl = audio_data->txI2SLLI[0].Control;
193
194     /* 受信DMAの起動準備 */
195     LPC_GPDMACH1->DMACCSrcAddr = (int) audio_data->rxI2SLLI[0].SrcAddr;
196     LPC_GPDMACH1->DMACCDestAddr = (int) audio_data->rxI2SLLI[0].DstAddr;
197     LPC_GPDMACH1->DMACCLLI = (int) &(audio_data->rxI2SLLI[1]);
198     LPC_GPDMACH1->DMACCControl = audio_data->rxI2SLLI[0].Control;
199
200     /*
201      * DMAチャンネルの設定を行う。
202      * この時点ではDMAはディセーブルである。
203      */
204
205     /* TX */
206     LPC_GPDMACH0->DMACCConfig = 0       // Enable : 0 is Disable
207         | 0 << 1                // Src Peripheral ( Ignored when M->P )
208         | 6 << 6                // Dst Peripheral, Dest is I2Sch1
209         | 1 << 11               // Transfer type : 1 is M->P
210         | 0 << 14               // IE : Interrupt Enable : 0 is Disable
211         | 0 << 15               // ITC : Terminal Count Intr Mask : 0 is masking out
212         | 0 << 18;              // H : Halt : 0 is active, 1 is Halt
213     /* RX */
214     LPC_GPDMACH1->DMACCConfig = 0       // Enable : 0 is Disable
215         | 5 << 1                // Src Peripheral, Src is I2Sch0
216         | 0 << 6                // Dst Peripheral, ( Ignored when P->M )
217         | 2 << 11               // Transfer type : 2 is P->M
218         | 1 << 14               // IE : Interrupt Enable : 0 is Disable
219         | 1 << 15               // ITC : Terminal Count Intr Mask : 0 is masking out
220         | 0 << 18;              // H : Halt : 0 is active, 1 is Halt
221
222     /* 最後の処理としてDMAチャンネルを有功にする */
223     LPC_GPDMACH0->DMACCConfig |= 1;
224     LPC_GPDMACH1->DMACCConfig |= 1;
225 }
226
227 /**
228  * \brief 現在ソフトウェアが使えるI2S TXバッファをかえす。
229  * \return プログラムが書き込んでいい送信バッファの先頭アドレス
230  * \details
231  * DMAが使用していないバッファをかえす。
232  * DMAと同期したプログラムを書く場合には必ずこのルーチンを使用して
233  * プログラムが使ってもよいバッファを決定する。
234  * \todo
235  * DMAチャンネルの番号は決め打ちである。
236  * 適切なDMA管理機構を使った方式への変更が必要である。
237  */
238 AUDIOSAMPLE *i2s_getTxBuf()
239 {
240     struct LLI *nextLLI;
241
242     // このプログラムではDMACH0をI2S TXに使っている。
243     // 使用していないバッファとは、現在使用中のLLIの、
244     // 次のLLIが指し示すバッファである。
245     nextLLI = (struct LLI *) LPC_GPDMACH0->DMACCLLI;
246     return nextLLI->SrcAddr;
247 }
248
249 /**
250  * \brief 現在ソフトウェアが使えるI2S RXバッファをかえす。
251  * \return プログラムが読み込んでいい受信バッファの先頭アドレス
252  * \details
253  * DMAが使用していないバッファをかえす。
254  * DMAと同期したプログラムを書く場合には必ずこのルーチンを使用して
255  * プログラムが使ってもよいバッファを決定する。
256  * \todo
257  * DMAチャンネルの番号は決め打ちである。
258  * 適切なDMA管理機構を使った方式への変更が必要である。
259  */
260 AUDIOSAMPLE *i2s_getRxBuf()
261 {
262     struct LLI *nextLLI;
263
264     // このプログラムではDMACH1をI2S RXに使っている。
265     // 使用していないバッファとは、現在使用中のLLIの、
266     // 次のLLIが指し示すバッファである。
267     nextLLI = (struct LLI *) LPC_GPDMACH1->DMACCLLI;
268     return nextLLI->DstAddr;
269 }
270
271 void i2s_dma_intr_handler()
272 {
273     if (LPC_GPDMA->DMACIntTCStat & (1 << 1))
274     {
275         /*
276          * I2S DMAバッファ終了割り込み
277          */
278         // CH1 (I2S DMA)のTC割り込みをクリア
279         LPC_GPDMA->DMACIntTCClear = (1 << 1);
280         // IS2 バッファ転送割り込みが起きたことをタスクに通知する。
281         isig_sem(SEM_I2SDMA);
282     }
283 }
284