1 /* Grandstream BT20x driver as an extension to the tic55x driver
3 * Ideally, all this would be is wiring to the generic functions of
4 * chips connected to the DSP. And of course a lot of register setup
5 * code. In practice, it is not as sharply divided, sometimes for
6 * reasons of efficiency, sometimes for other reasons. The ideal is
7 * the best judgement however, and any debate on where code should go
8 * should be based on this ideal plus the realism that too much
9 * indirection will slow down a program that is going to deal with
10 * static hardware anyway.
12 * This file is part of 0cpm Firmerware.
14 * 0cpm Firmerware is Copyright (c)2011 Rick van Rein, OpenFortress.
16 * 0cpm Firmerware is free software: you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License as
18 * published by the Free Software Foundation, version 3.
20 * 0cpm Firmerware is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with 0cpm Firmerware. If not, see <http://www.gnu.org/licenses/>.
39 #include <0cpm/timer.h>
43 #include <0cpm/show.h>
44 #include <0cpm/flash.h>
46 #include <0cpm/cons.h>
48 #include <bottom/ht162x.h>
49 #include <bottom/ksz8842.h>
53 /******** EXTERNAL INTERRUPTS SERVICE ROUTINES ********/
56 interrupt void tic55x_int0_isr (void) {
57 tic55x_top_has_been_interrupted = true;
58 ksz8842_interrupt_handler ();
62 interrupt void tic55x_int1 (void) {
63 tic55x_top_has_been_interrupted = true;
67 interrupt void tic55x_int2 (void) {
68 tic55x_top_has_been_interrupted = true;
72 interrupt void tic55x_int3 (void) {
73 tic55x_top_has_been_interrupted = true;
80 /******** FLASH PARTITION ACCESS ********/
83 /* An external definition (usually in phone-specific code)
84 * contains an array of at least one entry of flashpart
85 * structures. Only the last will have the FLASHPART_FLAG_LAST
88 * There will usually be one partition with name ALLFLASH.BIN
89 * that covers the entire flash memory, including things that
90 * may not actually be in any partition. Usually, this is the
91 * last entry in the flash partition table.
93 struct flashpart bottom_flash_partition_table [] = {
94 { FLASHPART_FLAG_LAST, "ALLFLASH.BIN", 0, 4096 }
98 /* Read a 512-byte block from flash.
99 * The return value indicates success.
101 bool bottom_flash_read (uint16_t blocknr, uint8_t data [512]) {
104 if (blocknr >= 4096) {
107 flashidx = ((uint32_t) blocknr) * 256;
109 uint16_t sample = flash_16 [flashidx];
111 // data [ctr++] = (sample >> 24) & 0xff;
112 // data [ctr++] = (sample >> 16) & 0xff;
113 data [ctr++] = (sample >> 8) & 0xff;
114 data [ctr++] = sample & 0xff;
120 /* Write a 512-byte block to flash. It is assumed that this
121 * is done sequentially; any special treatment for a header
122 * page will be done by the bottom layer, not the top.
123 * The return value indicates success.
125 bool boot_flash_write (uint16_t blocknr, uint8_t data [512]) {
130 /* Retrieve the current phone's MAC address from Flash.
132 void bottom_flash_get_mac (uint8_t mac [6]) {
133 uint32_t flashidx = flash_offset_mymac;
136 uint16_t sample = flash_16 [flashidx];
138 mac [ctr++] = (sample >> 8) & 0xff;
139 mac [ctr++] = sample & 0xff;
145 /******** TLV320AIC20K PROGRAMMING ACCESS OVER I2C ********/
148 /* The codec can be programmed over I2C, so the low-level routines
149 * for driving the codec end up passing bytes over I2C. The
150 * procedure of cycling through subregisters is not performed here.
153 void tlv320aic2x_setreg (uint8_t channel, uint8_t reg, uint8_t val) {
154 // Wait as long as the bus is busy
155 bottom_led_set (LED_IDX_MESSAGE, 1);
156 while (I2CSTR & REGVAL_I2CSTR_BB) {
159 // bottom_printf ("I2C.pre = 0x%04x,0x%04x\n", (intptr_t) I2CSTR, (intptr_t) I2CMDR);
160 // Set transmission mode for 2 bytes to "channel"
161 I2CSAR = 0x40 | channel;
162 //TODO// I2CSAR = 0x00; // Broadcast
165 // Send the register index
166 // Initiate the transfer by setting STT and STP flags
167 I2CMDR = REGVAL_I2CMDR_TRX | REGVAL_I2CMDR_MST | REGVAL_I2CMDR_STT | REGVAL_I2CMDR_STP | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
168 // bottom_printf ("I2C.set = 0x%04x,0x%04x\n", (intptr_t) I2CSTR, (intptr_t) I2CMDR);
169 // Wait for the START condition to occur
170 // bottom_led_set (LED_IDX_HANDSET, 1);
171 while (I2CMDR & REGVAL_I2CMDR_STT) {
174 // bottom_printf ("I2C.stt = 0x%04x,0x%04x\n", (intptr_t) I2CSTR, (intptr_t) I2CMDR);
175 // bottom_led_set (LED_IDX_HANDSET, 0);
176 // Wait until the I2C bus is ready, then send the value
177 // bottom_led_set (LED_IDX_SPEAKERPHONE, 1);
178 while (!(I2CSTR & REGVAL_I2CSTR_XRDY)) {
179 if (I2CSTR & REGVAL_I2CSTR_NACK) {
180 bottom_printf ("I2C received NACK\n");
181 I2CSTR = REGVAL_I2CSTR_NACK;
182 I2CMDR = REGVAL_I2CMDR_MST | REGVAL_I2CMDR_STP | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
186 // bottom_printf ("I2C.xrdy = 0x%04x,0x%04x\n", (intptr_t) I2CSTR, (intptr_t) I2CMDR);
187 // bottom_led_set (LED_IDX_SPEAKERPHONE, 0);
189 // Wait for the STOP condition to occur
190 while (I2CMDR & REGVAL_I2CMDR_STP) {
193 // bottom_printf ("I2C.post = 0x%04x,0x%04x\n", (intptr_t) I2CSTR, (intptr_t) I2CMDR);
194 bottom_led_set (LED_IDX_MESSAGE, 0);
197 uint8_t tlv320aic2x_getreg (uint8_t channel, uint8_t reg) {
200 bottom_led_set (LED_IDX_MESSAGE, 1);
201 // bottom_led_set (LED_IDX_HANDSET, 0);
202 // Wait as long as the bus is busy
203 while (I2CSTR & REGVAL_I2CSTR_BB) {
206 // Set transmission mode for 1 byte to "channel"
207 I2CSAR = 0x40 | channel;
208 //TODO// I2CSAR = 0x00; // Broadcast
211 // Send the register index
212 // Initiate the transfer by setting STT flag, but withhold STP
213 I2CMDR = REGVAL_I2CMDR_TRX | REGVAL_I2CMDR_MST | REGVAL_I2CMDR_STT | /* REGVAL_I2CMDR_STP | */ REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
214 // Wait until the START condition has occurred
215 while (I2CMDR & REGVAL_I2CMDR_STT) {
218 // ...send address and write mode bit...
219 // bottom_led_set (LED_IDX_HANDSET, 1);
220 // Wait until ready to setup for receiving
221 // while (I2CMDR & REGVAL_I2CMDR_STP) {
222 while (I2CSTR & (REGVAL_I2CSTR_XRDY | REGVAL_I2CSTR_XSMT) != (REGVAL_I2CSTR_XRDY | REGVAL_I2CSTR_XSMT)) {
223 if (I2CSTR & (REGVAL_I2CSTR_NACK | REGVAL_I2CSTR_AL)) {
224 I2CSTR = REGVAL_I2CSTR_NACK | REGVAL_I2CSTR_AL;
225 I2CMDR = REGVAL_I2CMDR_MST | REGVAL_I2CMDR_STP | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
229 // bottom_led_set (LED_IDX_HANDSET, 0);
231 // Restart with STT flag, also permit stop with STP; do not set TRX
232 I2CMDR = REGVAL_I2CMDR_MST | /* REGVAL_I2CMDR_STT | */ REGVAL_I2CMDR_STP | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
234 while (!(I2CSTR & REGVAL_I2CSTR_RRDY)) {
236 if (I2CSTR & REGVAL_I2CSTR_NACK) {
237 I2CSTR = REGVAL_I2CSTR_NACK;
238 // I2CMDR = REGVAL_I2CMDR_MST | REGVAL_I2CMDR_STP | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
245 bottom_led_set (LED_IDX_MESSAGE, 0);
250 /******** TLV320AIC20K DATA ACCESS OVER MCBSP1 ********/
253 #define BUFSZ (64*25)
255 extern volatile uint16_t samplebuf_play [BUFSZ];
256 extern volatile uint16_t samplebuf_record [BUFSZ];
258 extern volatile uint16_t available_play;
259 extern volatile uint16_t available_record;
261 extern volatile uint16_t threshold_play;
262 extern volatile uint16_t threshold_record;
265 /* Copy encoded samples to plain samples */
266 //TODO// Better to switch once and then loop in a separate routine which may even be .asm -- but that could require a codec-specific state storage structure
267 int16_t codec_decode (codec_t codec, uint8_t *in, uint16_t inlen, uint16_t *out, uint16_t outlen) {
268 while ((inlen > 0) && (outlen > 0)) {
269 register uint16_t outval;
270 register uint8_t inval = *in++;
273 //TODO// *out++ = (inval ^ 0x80) << 8;
274 *out++ = 16384 + 1 + (outlen & 0x01)? 0: 32768;
280 outval = (inval & 0x0f);
284 outval <<= ((inval >> 4) & 0x07);
291 outval = (inval & 0x0f);
293 outval <<= ((inval >> 4) & 0x07);
307 return inlen - outlen;
310 /* Copy plain samples to encoded samples */
311 //TODO// Better to switch once and then loop in a separate routine which may even be .asm -- but that could require a codec-specific state storage structure
312 int16_t codec_encode (codec_t codec, uint16_t *in, uint16_t inlen, uint8_t *out, uint16_t outlen) {
313 while ((inlen > 0) && (outlen > 0)) {
314 register uint16_t inval = *in++;
319 *out++ = (inval >> 8) ^ 0x80;
326 signbit = inval >> 15;
330 // Find exponent part of sample
331 for (exp = 7; exp >= 0; exp--) {
332 if (inval >= (1 << (8 + exp))) {
336 // Encode is sign/exp/mant
337 *out++ = 0x55 ^ ( (signbit << 7) | (exp << 4) | ((inval >> (exp + 3)) & 0x0f) );
341 signbit = inval >> 15;
345 // Shift range for uLaw
347 // Find exponent part of sample
348 for (exp = 7; exp >= 0; exp--) {
349 if (inval >= (1 << (8 + exp))) {
353 // Encode in sign/exp/mant
354 *out++ = 0xff ^ ( (signbit << 7) | (exp << 4) | ((inval >> (exp + 3)) & 0x0f) );
364 return outlen - inlen;
367 /* If the chip has not been brought up yet, do it now */
368 void tlv320aic2x_setup_chip (void) {
369 // Now, if not done yet, unreset and setup the TLV320AIC20K codec
370 if ((IODATA & (1 << 7)) == 0) {
371 volatile uint16_t ctr;
372 for (ctr=0; ctr < 7 * (600 / 12); ctr++) /* Wait 7x MCLK */ ;
373 for (ctr=0; ctr < 7 * (600 / 12); ctr++) /* Wait 7x MCLK */ ;
374 for (ctr=0; ctr < 7 * (600 / 12); ctr++) /* Wait 7x MCLK */ ;
375 for (ctr=0; ctr < 7 * (600 / 12); ctr++) /* Wait 7x MCLK */ ;
376 for (ctr=0; ctr < 7 * (600 / 12); ctr++) /* Wait 7x MCLK */ ;
378 for (ctr=0; ctr < 132 * (600 / 12); ctr++) /* Wait at least 132 MCLK cycles */ ;
379 for (ctr=0; ctr < 132 * (600 / 12); ctr++) /* Wait at least 132 MCLK cycles */ ;
380 for (ctr=0; ctr < 132 * (600 / 12); ctr++) /* Wait at least 132 MCLK cycles */ ;
381 for (ctr=0; ctr < 132 * (600 / 12); ctr++) /* Wait at least 132 MCLK cycles */ ;
382 for (ctr=0; ctr < 132 * (600 / 12); ctr++) /* Wait at least 132 MCLK cycles */ ;
386 static int TODO_setratectr = 0;
388 /* Set a frequency divisor for the intended sample rate */
389 //TODO// Not all this code is properly split between generic TLV and specific BT200
390 void tlv320aic2x_set_samplerate (uint32_t samplerate) {
393 SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
394 { uint32_t ctr = 100; while (ctr-- > 0) ; }
395 SPCR1_1 |= REGVAL_SPCR1_RRST_NOTRESET;
396 SPCR2_1 |= REGVAL_SPCR2_XRST_NOTRESET;
397 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
398 DXR1_1 = DXR1_1; // Flag down XEMPTY
399 tlv320aic2x_setreg (chan, 3, 0x31); // Channel offline
400 { uint32_t ctr = 1000; while (ctr-- > 0) ; }
401 (void) DRR1_1; // Flag down RFULL
403 // Determine the dividors m, n and p
406 m = ( 30720000 / 16 ) / ( n * p * samplerate );
407 // #ifdef TODO_OPTIMISE_PLL_AWAY
409 // Save PLL energy without compromising accuracy
410 p = 8; // Factor 2 -> 8 so multiplied by 8
411 m >>= 2; // Divide by 4
418 { uint8_t ip4 [4]; ip4 [0] = m; ip4 [1] = n; ip4 [2] = p; ip4 [3] = ++TODO_setratectr; bottom_show_ip4 (APP_LEVEL_CONNECTING, ip4); }
419 bottom_printf ("TLV320AIC20K setting: M=%d, N=%d, P=%d\n", (intptr_t) m, (intptr_t) n, (intptr_t) p);
421 n &= 0x0f; // Ignore range problems?
423 // With the codec up and running, configure the sample rate
424 tlv320aic2x_setreg (chan, 4, 0x00 | (n << 3) | p);
425 tlv320aic2x_setreg (chan, 4, 0x80 | m);
426 { uint32_t ctr = 1000; while (ctr-- > 0) ; }
427 // #ifndef TODO_FS_ONLY_DURING_SOUND_IO
428 // SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
429 // SPCR1_1 |= REGVAL_SPCR1_RRST_NOTRESET;
430 // SPCR2_1 |= REGVAL_SPCR2_XRST_NOTRESET;
431 SPCR1_1 &= ~REGVAL_SPCR1_RRST_NOTRESET;
432 SPCR2_1 &= ~REGVAL_SPCR2_XRST_NOTRESET;
433 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
434 SPCR2_1 &= ~ ( REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET );
435 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
436 // SPCR2_1 &= ~ ( REGVAL_SPCR2_FRST_NOTRESET );
437 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
439 samplerate = 12288000 / samplerate;
440 if (samplerate >= 4096) {
442 } else if (samplerate == 0) {
445 SRGR2_1 = REGVAL_SRGR2_CLKSM | REGVAL_SRGR2_FSGM | (samplerate - 1);
446 // #ifndef TODO_FS_ONLY_DURING_SOUND_IO
447 // SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
448 SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET;
449 tlv320aic2x_setreg (chan, 3, 0x01); // Channel online
450 { uint32_t ctr = 1000; while (ctr-- > 0) ; }
451 // tlv320aic2x_setreg (chan, 4, 0x00 | (n << 3) | p);
452 // tlv320aic2x_setreg (chan, 4, 0x80 | m);
453 // { uint32_t ctr = 10000; while (ctr-- > 0) ; }
454 SPCR1_1 |= REGVAL_SPCR1_RRST_NOTRESET;
455 SPCR2_1 |= REGVAL_SPCR2_XRST_NOTRESET;
456 { uint32_t ctr = 1000; while (ctr-- > 0) ; }
457 SPCR2_1 |= REGVAL_SPCR2_FRST_NOTRESET;
458 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
459 DXR1_1 = DXR1_1; // Flag down XEMPTY
460 (void) DRR1_1; // Flag down RFULL
462 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
466 /* A full frame of 64 samples has been recorded. See if space exists for
467 * another, otherwise disable DMA until a dmahint_play() restarts it.
469 interrupt void tic55x_dmac0_isr (void) {
470 uint16_t irq = DMACSR_0; // Note causes and clear
471 tic55x_top_has_been_interrupted = true;
472 if ((available_record += 64) > (BUFSZ - 64)) {
473 SPCR2_1 &= ~REGVAL_SPCR2_XRST_NOTRESET;
474 SPCR2_1 |= REGVAL_SPCR2_XRST_NOTRESET;
475 DMACCR_0 &= ~REGVAL_DMACCR_EN;
476 // #ifdef TODO_FS_ONLY_DURING_SOUND_IO
477 // SPCR1_1 &= ~REGVAL_SPCR1_RRST_NOTRESET;
478 // if ((SPCR2_1 & REGVAL_SPCR2_XRST_NOTRESET) == 0) {
479 // SPCR2_1 &= ~ (REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET);
483 if (available_record >= threshold_record) {
484 top_can_record (available_record);
488 /* A full frame of 64 samples has been played. See if another is availabe,
489 * otherwise disable DMA until a dmahint_record() restarts it.
491 interrupt void tic55x_dmac1_isr (void) {
492 uint16_t irq = DMACSR_1; // Note causes and clear
494 tic55x_top_has_been_interrupted = true;
495 if ((available_play -= 64) < 64) {
496 // #ifdef TODO_FS_ONLY_DURING_SOUND_IO
497 // SPCR2_1 &= ~REGVAL_SPCR2_XRST_NOTRESET;
498 // if ((SPCR1_1 & REGVAL_SPCR1_RRST_NOTRESET) == 0) {
499 // SPCR2_1 &= ~ (REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET);
502 DMACCR_1 &= ~REGVAL_DMACCR_EN;
504 toplay = BUFSZ - available_play;
505 if (BUFSZ - available_play >= threshold_play) {
506 top_can_play (available_play);
510 /* Data has been removed from what was recorded. As a result,
511 * it may be possible to restart DMA channel 1 if it was disabled.
513 void dmahint_record (void) {
514 return; //TODO// TMP-DISABLE DMAHINTS
515 if (! (DMACCR_0 & REGVAL_DMACCR_EN)) {
516 if (available_record <= (BUFSZ - 64)) {
517 if (!(DMACCR_0 & REGVAL_DMACCR_EN)) {
518 (void) DRR1_1; // Flag down RFULL
520 DMACCR_0 |= REGVAL_DMACCR_EN;
521 // #ifdef TODO_FS_ONLY_DURING_SOUND_IO
522 // SPCR1_1 |= REGVAL_SPCR1_RRST_NOTRESET;
523 // SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
526 // bottom_printf ("dmahint_record() enabled DMA from %d bytes out of %d\n", (intptr_t) available_record, (intptr_t) BUFSZ);
531 /* New data has been written for playback. As a result, it may
532 * be possible to restart DMA channel 0 if it was disabled.
534 void dmahint_play (void) {
535 return; //TODO// TMP-DISABLE DMAHINTS
536 if ((available_play >= 64) && ! (DMACCR_1 & REGVAL_DMACCR_EN)) {
537 DXR1_1 = DXR1_1; // Flag down XEMPTY
538 DMACCR_1 |= REGVAL_DMACCR_EN;
539 bottom_printf ("dmahint_play() started playing DMA\n");
540 // #ifdef TODO_FS_ONLY_DURING_SOUND_IO
541 // SPCR2_1 |= REGVAL_SPCR2_XRST_NOTRESET | REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
544 //TODO:DEBUG// else bottom_printf ("dmahint_play() did not start playing -- available_play = %d\n", (intptr_t) available_play);
549 /******** HT162x LCD DRIVER LOW-LEVEL FUNCTIONS ********/
552 /* The program defines an array of LCDCMD_xxx to
553 * initialise the HT162x chip. The array should be
554 * terminated with HT162x_LCDCMD_DONE.
556 extern uint16_t ht162x_setup_cmdseq [] = {
557 HT162x_LCDPREFIX_CMD,
558 HT162x_LCDCMD_LCDOFF,
559 HT162x_LCDCMD_NORMAL,
560 HT162x_LCDCMD_RC256K,
563 HT162x_LCDCMD_TIMERDIS,
564 HT162x_LCDCMD_WDTDIS,
565 HT162x_LCDCMD_TONEOFF,
566 HT162x_LCDCMD_BIAS3_4,
567 HT162x_LCDCMD_TONE2K,
568 // Terminate the sequence:
573 /* Chip-enable or -disable the LCD driver */
574 void ht162x_chipselect (bool selected) {
576 IODATA &= ~ (1 << 1);
583 /* Send out a databit for the LCD driver, but do not clock it in */
584 static uint16_t kbdisp_outbuf = 0x0000;
585 void ht162x_databit_prepare (bool databit) {
587 kbdisp_outbuf &= ~0x4040;
588 kbdisp_outbuf |= 0x8080;
590 kbdisp_outbuf &= ~0xc0c0;
592 kbdisp = kbdisp_outbuf;
595 /* Clock in the database that was prepared for the LCD driver */
596 void ht162x_databit_commit (void) {
597 kbdisp_outbuf |= 0x4040;
598 kbdisp = kbdisp_outbuf;
602 /******** LCD-SPECIFIC FORM PAINTING ROUTINES ********/
605 /* Digit notations on the LCD's 7 segment displays */
606 uint8_t lcd7seg_digits [10] = {
607 0xaf, 0xa0, 0xcb, 0xe9, 0xe4, // 01234
608 0x6d, 0x6f, 0xa8, 0xef, 0xed // 56789
611 /* Alphabetic notations on the LCD's 7 segment displays */
612 uint8_t lcd7seg_alpha [26] = {
613 0xee, 0x67, 0x0f, 0xe3, 0x4f, // AbCdE
614 0x4e, 0x2f, 0xe6, 0x06, 0xa1, // FGHIJ
615 0x6e, 0x07, 0x6a, 0x62, 0x63, // kLMno
616 0xce, 0xec, 0x42, 0x6d, 0x47, // PQRST
617 0xa7, 0x23, 0x2b, // Uvw
621 /* Put a character into display byte idx, with ASCII code ch */
622 void ht162x_putchar (uint8_t idx, uint8_t ch, bool notify) {
623 uint8_t chup = ch & 0xdf;
625 if ((ch >= '0') && (ch <= '9')) {
626 lcd7seg = lcd7seg_digits [ch - '0'];
627 } else if ((chup >= 'A') && (chup <= 'Z')) {
628 lcd7seg = lcd7seg_alpha [chup - 'A'];
629 } else if (chup == 0x00) {
630 lcd7seg = 0x00; // space for chup one of 0x00 and 0x20
632 lcd7seg = 0x40; // dash
634 ht162x_dispdata [idx] = (ht162x_dispdata [idx] & 0x10) | lcd7seg;
636 ht162x_dispdata_notify (idx, idx);
640 uint8_t lcd_bars [8] = {
641 0x00, 0x20, 0x60, 0xe0, 0xe8, 0xec, 0xee, 0xef
644 /* Level indication for volume, showing the upper 3 bits of "level" */
645 void ht162x_audiolevel (uint8_t level) {
646 uint8_t bars = lcd_bars [level >> 5];
647 ht162x_dispdata [15] = (ht162x_dispdata [15] & 0x10) | bars;
648 ht162x_dispdata_notify (15, 15);
651 #define LCDSYM_NETWORK 14
652 #define LCDSYM_HANDSET 13
653 #define LCDSYM_SPEAKER 11
654 #define LCDSYM_FORWARD 10
655 #define LCDSYM_CLOCK 8
656 #define LCDSYM_LOCK 7
657 #define LCDSYM_BINARY 15
660 #define LCDSYM_HOUR10 0
661 #define LCDSYM_HMSEP 1
664 #define LCDSYM_DOT12 12
665 #define LCDSYM_DOT9 9
666 #define LCDSYM_DOT6 6
668 /* Special flag setting on the LCD, to flag conditions */
669 void ht162x_led_set (uint8_t lcdidx, led_colour_t col, bool notify) {
671 ht162x_dispdata [lcdidx] |= 0x10;
673 ht162x_dispdata [lcdidx] &= ~0x10;
676 ht162x_dispdata_notify (lcdidx, lcdidx);
681 /******** BOTTOM FUNCTIONS FOR LEDS AND BUTTONS ********/
684 /* Bottom-half operations to manipulate LED states */
685 void bottom_led_set (led_idx_t ledidx, led_colour_t col) {
687 case LED_IDX_HANDSET:
688 ht162x_led_set (13, col, true);
690 case LED_IDX_SPEAKERPHONE:
691 ht162x_led_set (11, col, true);
693 case LED_IDX_MESSAGE:
700 case LED_IDX_BACKLIGHT:
701 // Set bit DXSTAT=5 in PCR=0x2812 to 1/0
703 PCR0 |= 1 << REGBIT_PCR_DXSTAT ;
705 PCR0 &= ~ ( 1 << REGBIT_PCR_DXSTAT );
714 /* See if the phone (actually, the horn) is offhook */
715 bool bottom_phone_is_offhook (void) {
716 // The hook switch is attached to GPIO pin 5
717 return (IODATA & 0x20) != 0;
721 /* Scan to see if the top_hook_update() function must be called */
722 #if !defined NEED_HOOK_SCANNER_WHEN_ONHOOK || !defined NEED_HOOK_SCANNER_WHEN_OFFHOOK
723 # error "The BT200 does not generate an interrupt on hook contact changes"
725 static bool bt200_offhook = false;
726 void bottom_hook_scan (void) {
727 if (bt200_offhook != bottom_phone_is_offhook ()) {
728 bt200_offhook = !bt200_offhook;
729 top_hook_update (bt200_offhook);
733 /* Scan to see if the top_button_press() or top_button_release()
734 * functions must be called.
736 * The following bits are written to activate one or more keyboard rows:
737 * D0..D4 are sent to the variable "kbdisp" which has the proper address.
738 * The intermediate variable "kbdisp_outbuf" stores a copy of the bits,
739 * and is shared with the LCD routines so unused bits must be retained.
741 * The following bits of McBSP0 are read from PCR0 as keyboard columns:
742 * C0=CLKRP, C1=CLKXP, C2=FSRP, C3=FSXP, C4=DRSTAT
744 * The following routine is designed from the assumption that the keyboard
745 * is operated a single key at a time; if not, then any response could be
746 * valid. Note that we do not suppress multiple keys by sending an error
747 * code, or behaving like with key release.
749 #if !defined NEED_KBD_SCANNER_BETWEEN_KEYS || !defined NEED_KBD_SCANNER_DURING_KEYPRESS
750 # error "The BT200 does not generate an interrupt on key changes"
752 #define KBD_COLUMNS_MASK ( REGVAL_PCR_CLKRP | REGVAL_PCR_CLKXP | REGVAL_PCR_FSRP | REGVAL_PCR_FSXP | REGVAL_PCR_DRSTAT )
753 static bool bt200_kbd_pressed = false;
754 static const buttoncode_t keynum2code [25] = {
755 '1', '2', '3', HAVE_BUTTON_MESSAGE, HAVE_BUTTON_HOLD,
756 '4', '5', '6', HAVE_BUTTON_TRANSFER, HAVE_BUTTON_CONFERENCE,
757 '7', '8', '9', HAVE_BUTTON_FLASH, HAVE_BUTTON_MUTE,
758 '*', '0', '#', HAVE_BUTTON_SEND, HAVE_BUTTON_SPEAKER,
759 HAVE_BUTTON_DOWN, HAVE_BUTTON_UP, HAVE_BUTTON_CALLERS, HAVE_BUTTON_CALLED, HAVE_BUTTON_MENU
761 void bottom_keyboard_scan (void) {
763 scan = PCR0 & KBD_COLUMNS_MASK;
764 if (bt200_kbd_pressed) {
765 // Respond if the key is released
766 if (scan == KBD_COLUMNS_MASK) {
767 bt200_kbd_pressed = false;
768 kbdisp_outbuf &= 0xe0e0; // Make D0..D4 low
769 kbdisp = kbdisp_outbuf;
770 top_button_release ();
773 // Respond if a key is being pressed
774 if (scan != KBD_COLUMNS_MASK) {
776 for (row = 0; row < 5; row++) {
777 kbdisp_outbuf |= 0x1f1f; // Make D0..D4 high
778 kbdisp_outbuf &= ~(0x0101 << row); // Set D$row low
779 kbdisp = kbdisp_outbuf;
780 { volatile int pause = 5; while (pause > 0) pause--; }
781 scan = PCR0 & KBD_COLUMNS_MASK;
782 if (scan != KBD_COLUMNS_MASK) {
784 for (col = 0; col < 5; col++) {
785 if (! (scan & (0x01 << col))) {
786 uint16_t keynum = row * 5 + col;
787 buttonclass_t bcl = ((row <= 3) && (col <= 2))? BUTCLS_DTMF: BUTCLS_FIXED_FUNCTION;
788 buttoncode_t cde = keynum2code [keynum];
789 bt200_kbd_pressed = true;
790 top_button_press (bcl, cde);
796 // Failed, usually due to an I/O glitch
797 kbdisp_outbuf &= 0xe0e0; // Make D0..D4 low
798 kbdisp = kbdisp_outbuf;
804 /******** BOTTOM FUNCTIONS FOR FORMATTING / PRINTING INFORMATION ********/
807 /* Send an elapsed period (not a wallclock time) to the display.
808 * For BT200, this is shown in the top digits as MM:SS or as HH:MM.
809 * Note that there are 2 positions, and the first digit can only show a 1.
811 static app_level_t bt200_period_level = APP_LEVEL_ZERO;
812 void bottom_show_period (app_level_t level, uint8_t h, uint8_t m, uint8_t s) {
813 if (bt200_period_level > level) {
814 // Ignore those lower values, as this is highly time-dependent
817 bt200_period_level = level;
818 if ((h == 0) && (m <= 19)) {
822 ht162x_led_set (2, false, false); // "AM" off
823 ht162x_led_set (3, false, false); // "PM" off
824 ht162x_led_set (1, true, false); // ":" in "xx:xx"
825 ht162x_led_set (0, h >= 10, false); // "1" in "1x:xx"
826 ht162x_putchar (0, (h % 10) + '0', false);
827 ht162x_putchar (1, (m / 10) + '0', false);
828 ht162x_putchar (2, (m % 10) + '0', false);
829 ht162x_dispdata_notify (0, 3);
832 /* The level of the main portion of the display, along with an array
833 * with the contents at each level
835 static bt200_displine_level = APP_LEVEL_ZERO;
836 static bool bt200_level_active [APP_LEVEL_COUNT];
837 static uint8_t bt200_level_maintext [APP_LEVEL_COUNT] [12];
838 static uint8_t bt200_level_dotbits [APP_LEVEL_COUNT];
840 /* Internal routine to move a display level to the display.
841 * Note that no "led" style bits other than the dots in the main
842 * line are influenced.
844 static void bt200_displine_showlevel (app_level_t level) {
846 for (i = 0; i < 12; i++) {
847 ht162x_putchar (14 - i, bt200_level_maintext [level] [i], false);
849 ht162x_led_set (12, (bt200_level_dotbits [level] & 0x01), false);
850 ht162x_led_set ( 9, (bt200_level_dotbits [level] & 0x02), false);
851 ht162x_led_set ( 6, (bt200_level_dotbits [level] & 0x04), false);
852 ht162x_dispdata_notify (3, 14);
855 /* Internal routine to send a string to the display, either with or
856 * without setting the various dots. The dots are coded in bits
857 * 0, 1 and 2 of the dotbits parameter, from left to right.
859 static void bt200_display_showtxt (app_level_t level, char *txt, uint8_t dotbits) {
860 // 1. Print text to the proper level
862 while ((idx < 12) && *txt) {
863 bt200_level_maintext [level] [idx++] = *txt++;
866 bt200_level_maintext [level] [idx++] = ' ';
868 bt200_level_dotbits [level] = dotbits;
869 bt200_level_active [level] = true;
870 // 2. If the level is not exceeded by the current, reveal it
871 if (bt200_displine_level <= level) {
872 bt200_displine_showlevel (level);
873 bt200_displine_level = level;
878 /* Stop displaying content at the specified level. In some cases, older
879 * content may now pop up, in others the display could get cleared.
881 void bottom_show_close_level (app_level_t level) {
882 if (bt200_period_level == level) {
883 bt200_period_level = APP_LEVEL_ZERO;
884 ht162x_led_set (0, false, false);
885 ht162x_led_set (1, false, false);
886 ht162x_dispdata [0] = 0x00;
887 ht162x_dispdata [1] = 0x00;
888 ht162x_dispdata [2] = 0x00;
889 ht162x_dispdata_notify (0, 2);
891 if (bt200_displine_level == level) {
892 bt200_level_active [level] = false;
893 memset (bt200_level_maintext [level], ' ', 12);
894 while ((bt200_displine_level > APP_LEVEL_ZERO)
895 && !bt200_level_active [bt200_displine_level]) {
896 bt200_displine_level--;
898 bt200_displine_showlevel (bt200_displine_level);
902 /* Print an IPv4 address on the display */
903 void bottom_show_ip4 (app_level_t level, uint8_t bytes [4]) {
907 for (idx = 0; idx < 4; idx++) {
908 *ipptr++ = '0' + (bytes [idx] / 100);
909 *ipptr++ = '0' + ((bytes [idx] % 100) / 10);
910 *ipptr++ = '0' + (bytes [idx] % 10);
912 bt200_display_showtxt (level, ip, 0x07);
915 /* Print an IPv6 address, as far as this is possible, on the display
916 * - Remove the prefix /64 as it is widely known
917 * -TODO- If middle word is 0xfffe, remove it, display the rest, set dot #2
918 * -TODO- If first word is 0x0000, remove it, display the rest, set dot #1
919 * -TODO- If last word is 0x0001, remove it, display the rest, set dot #3
920 * - If nothing else is possible, use 6 bits per digit, dots and last digit blanc
921 void bottom_show_ip6 (app_level_t level, uint16_t words [8]) {
922 // TODO: Extra cases; for now, just dump 6 bits per 7-segment display
927 ht162x_led_set (12, 0, false);
928 ht162x_led_set ( 9, 0, false);
929 ht162x_led_set ( 6, 0, false);
930 for (idxo = 14; idxo >= 3; idxo--) {
931 ht162x_dispdata [idxo] &= 0x10;
938 uint8_t twobits = (words [idxi] >> shfi) & 0x03;
940 if (twobits & 0x10) {
941 twobits += 0x80 - 0x10; // bit 7 replaces bit 4
943 ht162x_dispdata [idxo] |= (twobits << shfo);
957 ht162x_dispdata_notify (14, 3);
960 /* A list of fixed messages, matching the fixed_msg_t values */
962 static char *bt200_fixed_messages [FIXMSG_COUNT] = {
972 /* Print a fixed message on the main line of the display */
973 void bottom_show_fixed_msg (app_level_t level, fixed_msg_t msg) {
974 if (msg < FIXMSG_COUNT) {
975 bt200_display_showtxt (level, bt200_fixed_messages [msg], 0x00);
979 /* Print a notification of the number of new / old voicemails */
980 void bottom_show_voicemail (app_level_t level, uint16_t new, uint16_t old) {
985 memcpy (msg, "xxx messages", 12);
987 msg [1] = (new % 100) / 10;
989 bt200_display_showtxt (level, msg, 0x00);
992 /******** BOTTOM LEVEL MAIN PROGRAM ********/
994 /* Setup the connectivity of the TIC55x as used on Grandstream BT20x */
997 led_colour_t led = LED_STABLE_ON;
999 // PLL setup: Crystal is 16.384 MHz, increase that 15x
1000 // 1. Switch to bypass mode by setting the PLLEN bit to 0.
1001 PLLCSR &= ~REGVAL_PLLCSR_PLLEN;
1002 // 2. Set the PLL to its reset state by setting the PLLRST bit to 1.
1003 PLLCSR |= REGVAL_PLLCSR_PLLRST;
1004 // 3. Change the PLL setting through the PLLM and PLLDIV0 bits.
1005 PLLM = REGVAL_PLLM_TIMES_15;
1006 PLLDIV0 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_1;
1007 // 4. Wait for 1 µs.
1008 { int ctr = 1000; while (ctr--) ; }
1009 // 5. Release the PLL from its reset state by setting PLLRST to 0.
1010 PLLCSR &= ~REGVAL_PLLCSR_PLLRST;
1011 // 6. Wait for the PLL to relock by polling the LOCK bit or by setting up a LOCK interrupt.
1012 while ((PLLCSR & REGVAL_PLLCSR_LOCK) == 0) {
1015 // 7. Switch back to PLL mode by setting the PLLEN bit to 1.
1016 PLLCSR |= REGVAL_PLLCSR_PLLEN;
1017 PLLDIV1 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_2;
1018 PLLDIV2 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_4;
1019 PLLDIV3 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_4;
1020 //TODO// PLLDIV3 = REGVAL_PLLDIVx_DxEN | REGVAL_PLLDIVx_PLLDIVx_2;
1021 //TODO// PLLCSR |= REGVAL_PLLCSR_PLLEN;
1023 // CPU clock is 245.76 MHz
1024 // SYSCLK1 is 122.88 MHz
1025 // SYSCLK2 is 61.44 MHz
1027 // EMIF settings, see SPRU621F, section 2.12 on "EMIF Registers"
1030 // EGCR2 = 0x0009; // ECLKOUT2-DIV-4
1031 // EGCR2 = 0x0001; //ECLKOUT2-DIV-1//
1032 EGCR2 = 0x0005; //ECLKOUT2-DIV-2//
1033 // EGCR1 = ...; // (defaults)
1034 // EGCR2 = ...; // (defaults)
1035 // CESCR1 = ...; // (defaults)
1036 // CESCR2 = ...; // (defaults)
1038 // CE0 selects the network interface
1039 // CE0_1 = 0xff03; // DEFAULT 8-bit async (and defaults)
1040 // Fail: CE0_1 = 0xc112; // 16-bit async, rd setup 2, rd strobe 1, rd hold 2
1041 // Fail: CE0_2 = 0x20a2; // wr setup 2, wr strobe 2, wr hold 2, rd setup 2
1044 // CE0_2 = ...; // (defaults)
1045 // CE0_SC1 = ...; // (defaults)
1046 // CE0_SC2 = ...; // (defaults)
1048 // CE1 selects the flash chip
1049 //WORKED?// CE1_1 = 0xff13; // 16-bit async (and defaults)
1052 // CE1_2 = ...; // (defaults)
1053 // CE1_SC1 = ...; // (defaults)
1054 // CE1_SC2 = ...; // (defaults)
1056 // CE2 selects the SDRAM chips
1057 //WORKS?// CE2_1 = 0xff33; // 32-bit SDRAM (and defaults)
1059 //TEST// CE2_1 = 0xff13; // 16-bit async (and defaults)
1060 // CE2_2 = ...; // (defaults)
1061 // CE2_SC1 = ...; // (defaults)
1062 // CE2_SC2 = ...; // (defaults)
1063 // Possible: SDC1, SDC2, SDRC1, SDRC2, SDX1, SDX2
1071 // CE3 selects the D-flipflops for keyboard and LCD
1072 CE3_1 = 0xff13; // 16-bit async (and defaults)
1073 //BT200ORIG// CE3_1 = 0x0220;
1074 //BT200ORIG// CE3_2 = 0x0270;
1075 // CE3_2 = ...; // (defaults)
1076 // CE3_SC1 = ...; // (defaults)
1077 // CE3_SC2 = ...; // (defaults)
1079 // Setup McBSP1 for linking to TLV320AIC20K
1080 // Generate a CLKG at 12.288 MHz, and FS at 8 kHz
1081 // following the procedure of spru592e section 3.5
1084 SPCR1_1 = 0x0000; // Disable/reset receiver, required in spru592e
1085 SPCR2_1 = 0x0000; // Disable/reset sample rate generator
1086 SRGR1_1 = REGVAL_SRGR1_FWID_1 | REGVAL_SRGR1_CLKGDIV_4;
1087 SRGR2_1 = REGVAL_SRGR2_CLKSM | REGVAL_SRGR2_FSGM | REGVAL_SRGR2_FPER_1535;
1088 //COPIED_BELOW// PCR1 = /*TODO: (1 << REGBIT_PCR_IDLEEN) | */ (1 << REGBIT_PCR_FSXM) /* | (1 << REGBIT_PCR_FSRM) */ | (1 << REGBIT_PCR_CLKXM) /* | (1 << REGBIT_PCR_CLKRM) */ /* receive on falling, xmit on rising edge -- | (1 << REGBIT_PCR_CLKXP) | (1 << REGBIT_PCR_CLKRP) */ ;
1089 PCR1 = (1 << REGBIT_PCR_FSXM) | (1 << REGBIT_PCR_CLKXM);
1090 { uint32_t ctr = 10000; while (ctr-- > 0) ; }
1091 // SPCR2_1 |= REGVAL_SPCR2_GRST_NOTRESET | REGVAL_SPCR2_FRST_NOTRESET;
1092 RCR1_1 = (0 << 8) | (2 << 5); // Read 1 frame of 16 bits per FS
1093 XCR1_1 = (0 << 8) | (2 << 5); // Write 1 frame of 16 bits per FS
1094 RCR2_1 = 0x0001; // Read with 1 clockcycle delay
1095 XCR2_1 = 0x0001; // Write with 1 clockcycle delay
1096 //TODO:NOT-SPI-BUT-CONTINUOUS-CLOCK// SPCR1_1 |= REGVAL_SPCR1_CLKSTP_NODELAY;
1098 // Setup I2C for communication with the TLV320AIC20K codec
1099 // Prescale SYSCLK2 down from 61.44 MHz to 10.24 MHz so it falls
1100 // in the required 7 Mhz to 12 MHz range; support a 100 kHz I2C bus
1101 // by setting low/high period to 51 such periods.
1102 // Note: The only peripheral TLV320AIC20K could go up to 900 kHz
1103 I2CMDR = REGVAL_I2CMDR_MST | /* reset to set PSC */ REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
1105 I2CCLKH = 51 - 5; /* TODO: 900 kHz is possible on TLV320AIC20K */
1107 I2COAR = REGVAL_I2COAR;
1108 I2CMDR = REGVAL_I2CMDR_MST | REGVAL_I2CMDR_NORESET | REGVAL_I2CMDR_FREE | REGVAL_I2CMDR_BC_8;
1110 // Setup DMA channel 0 for playing from DSP to TLV320AIC2x
1111 // Setup DMA channel 1 for recording from TLV320AIC2x to DSP
1113 // Both channels have a block (their entire RAM buffer) comprising
1114 // of 25 frames, which each are 64 samples of 16 bits in size.
1115 // At 8 kHz sample rate, frames cause 125 interrupts per second;
1116 // at 48 kHz this rises to 750 (per channel), still comfortable.
1117 // The total buffer is 1600 samples of 16 bits long. Each time a
1118 // frame send finishes, an interrupt checks if there is another
1119 // frame of 64 samples ready to go; if not, it will disable the
1120 // DMA channel. Hint routines serve to restart DMA after that.
1121 // Conversely, the DMA interrupt handlers can make top-calls to
1122 // indicate that data is ready for reading or that space is
1123 // available for writing.
1124 // The settings below prepare DMA for continuous playing and
1125 // recording, but with the setup disabled until hinted.
1127 DMAGCR = REGVAL_DMAGCR_FREE;
1128 DMAGTCR = 0x00; // No timeout support
1129 #ifdef TODO_DMA_CONFIGUREREN_BIJ_OPSTART
1130 DMACCR_0 = REGVAL_DMACCR_SRCAMODE_CONST | REGVAL_DMACCR_DSTAMODE_POSTINC | REGVAL_DMACCR_PRIO | REGVAL_DMACCR_SYNC_MCBSP1_REV | REGVAL_DMACCR_REPEAT | REGVAL_DMACCR_AUTOINIT;
1131 DMACCR_1 = REGVAL_DMACCR_SRCAMODE_POSTINC | REGVAL_DMACCR_DSTAMODE_CONST | REGVAL_DMACCR_PRIO | REGVAL_DMACCR_SYNC_MCBSP1_TEV | REGVAL_DMACCR_REPEAT | REGVAL_DMACCR_AUTOINIT;
1132 DMACICR_0 = REGVAL_DMACICR_FRAMEIE;
1133 DMACICR_1 = REGVAL_DMACICR_FRAMEIE;
1134 //TODO// DMACSDP_0 = REGVAL_DMACSDP_SRC_PERIPH | REGVAL_DMACSDP_DST_DARAM1 | REGVAL_DMACSDP_DATATYPE_16BIT;
1135 //TODO// DMACSDP_1 = REGVAL_DMACSDP_SRC_DARAM0 | REGVAL_DMACSDP_DST_PERIPH | REGVAL_DMACSDP_DATATYPE_16BIT;
1136 DMACSDP_0 = REGVAL_DMACSDP_SRC_PERIPH | REGVAL_DMACSDP_DST_EMIF | REGVAL_DMACSDP_DATATYPE_16BIT;
1137 DMACSDP_1 = REGVAL_DMACSDP_SRC_EMIF | REGVAL_DMACSDP_DST_PERIPH | REGVAL_DMACSDP_DATATYPE_16BIT;
1138 DMACSSAL_0 = ((intptr_t) &DRR1_1) << 1;
1139 DMACSSAU_0 = ((intptr_t) &DRR1_1) >> 15;
1140 DMACDSAL_0 = (uint16_t) (((intptr_t) samplebuf_record) << 1);
1141 DMACDSAU_0 = (uint16_t) (((intptr_t) samplebuf_record) >> 15);
1142 DMACSSAL_1 = (uint16_t) (((intptr_t) samplebuf_play) << 1);
1143 DMACSSAU_1 = (uint16_t) (((intptr_t) samplebuf_play) >> 15);
1144 DMACDSAL_1 = ((intptr_t) &DXR1_1) << 1;
1145 DMACDSAU_1 = ((intptr_t) &DXR1_1) >> 15;
1146 #if TRY_SOMETHING_ELSE_TO_GET_INTERRUPTS
1147 DMACEN_0 = 64; /* 64 elements (samples) per frame (continue-checks) */
1149 DMACFN_0 = (BUFSZ / 64); /* 25 frames (continue-checks) per block (buffer) */
1150 DMACFN_1 = (BUFSZ / 64);
1160 // Further initiation follows
1162 IODIR |= (1 << 7) | (1 << 1);
1163 IODATA = (1 << 1); // Updated below small delay
1164 for (idx = 0; idx < APP_LEVEL_COUNT; idx++) {
1165 bt200_level_active [idx] = false;
1167 IODATA |= (1 << 7) | (1 << 1); // See above small delay
1168 asm (" bclr xf"); // Switch off MESSAGE LED
1169 { uint16_t ctr = 250; while (ctr > 0) { ctr--; } }
1170 bottom_critical_region_begin (); // _disable_interrupts ();
1171 IER0 = IER1 = 0x0000;
1172 tic55x_setup_timers ();
1173 tic55x_setup_interrupts ();
1174 ht162x_setup_lcd ();
1175 // tlv320aic2x_set_samplerate (8000);
1176 tlv320aic2x_setup_sound ();
1177 ksz8842_setup_network ();
1178 // Enable INT0..INT3
1179 //TODO:TEST// IER0 |= 0x0a0c;
1180 //TODO:TEST// IER1 |= 0x0005;
1181 IER0 |= (1 << REGBIT_IER0_DMAC1) | (1 << REGBIT_IER0_INT0) | (1 << REGBIT_IER0_TINT0); // 0x0214;
1182 IER1 |= (1 << REGBIT_IER1_DMAC0); // 0x0004;
1183 PCR0 = (1 << REGBIT_PCR_XIOEN) | (1 << REGBIT_PCR_RIOEN);
1186 {uint8_t idx, dig, bar;
1187 idx=0; dig=0; bar=0;
1188 memset (ht162x_dispdata, 0x00, sizeof (ht162x_dispdata));
1191 ht162x_putchar (14 - idx, (dig < 10)? (dig + '0'): (dig + 'A' - 11));
1192 ht162x_audiolevel ((bar < 8)? (bar << 5): ((14-bar) << 5));
1193 // bottom_led_set (LED_IDX_MESSAGE, LED_STABLE_OFF);
1194 ht162x_dispdata_notify (14 - idx, 14 - idx);
1195 ht162x_dispdata_notify (15, 15);
1196 // bottom_led_set (LED_IDX_MESSAGE, LED_STABLE_ON );
1197 idx = (idx + 1) % 12;
1198 dig = (dig + 1) % 38;
1199 bar = (bar + 1) % 14;
1200 ctr = 650000; while (ctr>0) ctr--;
1205 //TODO// IER0 = 0xdefc;
1206 //TODO// IER1 = 0x00ff;
1207 /* TODO: Iterate over flash partitions that can boot, running each in turn: */