#include #include #include #include #include "serial.h" #include "a3kpacket.h" #include "gpio.h" #include "a3k.h" #include "tick.h" #define WAIT_TIMEOUT (100) /* 100ms */ #define RATE_PINS (6) struct fifo_tag { char *pnew; /* points to newest fifo element */ char *pold; /* points to oldest fifo element */ short nelem;/* number of bytes currently in fifo */ short blen; char *bend; char *b; }; typedef struct fifo_tag FIFO; struct a3k_tag { struct serial *uart; uint32_t rst; uint32_t rts; int pseudo; uint32_t rate[RATE_PINS]; uint32_t ec; uint32_t ns; uint32_t es; FIFO rx; uint8_t _buf[FORMAT3KBLEN]; }; static char *put_next_byte( char *p, short val ) { *p++ = val; return p; } static char *put_next_word( char *p, short val ) { *p++ = (val >> 8) & 0x00ff; *p++ = val & 0x00ff; return p; } static char *get_next_byte( char *p, short *val ) { *val = *p++ & 0x00ff; return p; } static char *get_next_word( char *p, short *val ) { *val = *p++; *val <<= 8; *val |= *p++; return p; } static void __send_all(a3k_t tag, const uint8_t *p, int len) { int n; while (len > 0) { n = serial_send_buffered(tag->uart, p, len); p += n; len -= n; } } static void fifo_putpacket(a3k_t tag, char *p) { int len; len = (((short)p[1])<<8)|p[2]; len += 4; __send_all(tag, (void *)p, len); } #if 0 static char __getc(a3k_t a3k) { char c; while (serial_recv_buffered(a3k->uart, (void *)&c, 1) != 1) ; return c; } #endif static char __fifo_getc(FIFO *f) { if (f->nelem) { char c; c = *f->pold++; if(f->pold == f->bend) f->pold = f->b; f->nelem -= 1; return c; } else return 0; } static int __getpacket(a3k_t tag, char *p) { short len; short i; uint8_t *p_save = (uint8_t *)p; FIFO *f = &tag->rx; p[0] = __fifo_getc(f); //get the header p[1] = __fifo_getc(f); //get the len (msb) p[2] = __fifo_getc(f); //get the len (lsb) p[3] = __fifo_getc(f); //get the type len = p[1]; len <<= 8; len |= (p[2] & 0x00ff); p += 4; for ( i = 0; i < len; i++ ) { *p++ = __fifo_getc(f); } printf("\r\n"); for (i = 0; i < (len + 4); ++i) { printf("%02x ", p_save[i]); } printf("\r\n"); return len + 4; } static void put_packet(a3k_t a3k, char *packet, char *end, char type, char parity) { char *p = packet; short len = end - packet - 4; if (parity) len += 2; /* * add the header, length, and type */ p = put_next_byte( p, PKT_HEADER ); p = put_next_word( p, len ); p = put_next_byte( p, type ); /* * from this point on, len is the total length of the * packet including header/len/type */ len += 4; /* * add the parity field if enabled. Note that the parity * byte does not cover the header byte. */ if (parity) { short i; char pbyte = 0; end = put_next_byte(end, PKT_PARITYBYTE ); for ( i = 1; i < len - 1; i++ ) { pbyte ^= packet[i]; } end = put_next_byte(end, pbyte ); } fifo_putpacket(a3k, packet); } /* * This function gets the next character from a fifo. It is assumed * that the fifo contains at leas one character. If it does not the * function returns a null character. */ static int __fifo_waitpacket(a3k_t tag) { int n, len; char *old; FIFO *f = &tag->rx; uint64_t end = millis() + WAIT_TIMEOUT; restart: if (millis() > end) { return -1; } /* try to receive from serial */ while (f->blen != f->nelem) { len = f->bend - f->pnew; n = serial_recv_buffered(tag->uart, (void *)f->pnew, len); if (n > 0) { f->pnew += n; if (f->pnew == f->bend) { f->pnew = f->b; } f->nelem += n; } else { break; } } while ((f->nelem > 0) && (*f->pold != PKT_HEADER)) { __fifo_getc(f); } if (*f->pold != PKT_HEADER) goto restart; if (f->nelem < 3) goto restart; old = f->pold + 1; if (old == f->bend) { old = f->b; } len = *old++; len <<= 8; if (old == f->bend) { old = f->b; } len |= *old++; if (f->nelem < (len + 4)) { goto restart; } return 0; } static int get_packet(a3k_t a3k, char *packet) { #if 0 return __getpacket(a3k, packet); #else if (__fifo_waitpacket(a3k)) { return 0; } return __getpacket(a3k, packet); #endif } static int wait_a3k_ready(a3k_t tag) { char packet[30]; memset(packet, 0, sizeof packet); get_packet(tag, packet); if (packet[0] != 0x61) return (1); if (packet[1] != 0x0) return (2); if (packet[2] != 0x03) return (3); if(packet[3] != 0x0) return (4); if(packet[4] != 0x39) return (5); if(packet[5] != 0x2f) return (6); if (packet[6] != (0x3^0x39^0x2f)) return (7); /* we should verify that it is a ready packet, but for now we do not */ return (0); } int get_cfg(a3k_t a3k, short * cfg0, short * cfg1, short * cfg2) { char packet[30]; char *p = packet+4; short plen; short chk_header, chk_len, chk_type, chk_field; p = put_next_byte(p,PKT_GETCFG); put_packet(a3k, packet, p, PKT_CONTROL, 1); plen = get_packet(a3k,packet); if(plen == 0) return 1; p = packet; p = get_next_byte(p, &chk_header); if(chk_header != PKT_HEADER) return -1; p = get_next_word(p, &chk_len); if(chk_len != 6) return -2; p = get_next_byte(p,&chk_type); if(chk_type != PKT_CONTROL) return -3; p = get_next_byte(p, &chk_field); if(chk_field != PKT_GETCFG) return -4; p = get_next_byte(p, cfg0); p = get_next_byte(p, cfg1); p = get_next_byte(p, cfg2); return 0; } static int packet_reset_a3k(a3k_t a3k) { uint64_t end = millis() + 5; gpio_clear(a3k->rst); while (millis() < end) ; serial_rx_buffer_clear(a3k->uart); serial_tx_buffer_clear(a3k->uart); gpio_set(a3k->rst); wait_a3k_ready(a3k); #if 1 char packet[30]; char *p = packet+4; short cfg = (1<<13); //noise suppressor on // short cfg0, cfg1, cfg2; p = put_next_byte(p, PKT_RESETSOFTCFG ); cfg |= (5<<8); /* UART */ p = put_next_byte( p, 0x05); //DTX_ENABLE = 0, IF_SELECT0 = 1, IF_SELECT1 = 0, IF_SELECT2 = 1; p = put_next_byte( p, 0); p = put_next_byte( p, 0); p = put_next_byte( p, 0x0D); //DTX_ENABLE = 0, IF_SELECT0 = 1, IF_SELECT1 = 0, IF_SELECT2 = 1; p = put_next_byte( p, 0x00); p = put_next_byte( p, 0x00); put_packet(a3k, packet, p, PKT_CONTROL, 1); return wait_a3k_ready(a3k); // get_cfg(a3k, &cfg0, &cfg1, &cfg2); #endif } static int config_a3k(a3k_t interface, short ratet, char input_gain, char output_gain) { /* * we can modify this to put characters into a fifo instead of * a buffer. We would then modify put_packet and get_packet * to work with a fifo instead of a buffer. This will save * stack space and save code. we would need to create * fifo_putword() which puts two characters. */ char packet[30]; char *p = packet+4; short chk_header, chk_len, chk_type, chk_field, chk_response_ok; short plen; short lowpower = 0; if (ratet >= 0) { p = put_next_byte( p, PKT_RATET ); p = put_next_byte( p, ratet ); } if (input_gain != 0 || output_gain != 0) { p = put_next_byte(p, PKT_GAIN); p = put_next_byte(p, input_gain); p = put_next_byte(p, output_gain); } p = put_next_byte( p, PKT_LOWPOWER ); p = put_next_byte( p, lowpower ); p = put_next_byte( p, PKT_INIT ); p = put_next_byte( p, PKT_INIT_ENCODER|PKT_INIT_DECODER ); put_packet(interface, packet, p, PKT_CONTROL, 1); //we expect a 9 byte response packet plen = get_packet(interface, packet); if (plen == 0) { return 1; } p = packet; p = get_next_byte( p, &chk_header ); if (chk_header != PKT_HEADER) return -1; p = get_next_word( p, &chk_len ); if (ratet >= 0) { if (input_gain != 0 || output_gain != 0) { if (chk_len != 6 + 4) return -2; } else { if (chk_len != 6+2) return -2; } } else { if (input_gain != 0 || output_gain != 0) { if (chk_len != (6 + 2)) return -2; } else { if (chk_len != 6) return -2; } } p = get_next_byte( p, &chk_type ); if (chk_type != PKT_CONTROL) return -3; if (ratet >= 0) { p = get_next_byte( p, &chk_field); if (chk_field != PKT_RATET) return -4; p = get_next_byte( p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -5; } if (input_gain != 0 || output_gain != 0) { p = get_next_byte(p, &chk_field); if (chk_field != PKT_GAIN) { return -6; } p = get_next_byte(p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -7; } p = get_next_byte( p, &chk_field); if (chk_field != PKT_LOWPOWER) return -8; p = get_next_byte( p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -9; p = get_next_byte( p, &chk_field); if (chk_field != PKT_INIT) return -10; p = get_next_byte( p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -11; return 0; } static int config_a3k_codec(a3k_t interface ) { char packet[128]; char *p = packet+4; short plen; short INPUT_GAIN; short INPUT_SEL; short ADCGAIN, DACGAIN; INPUT_GAIN = 0x3; // INPUT_GAIN = 0x3b; // mute,24db INPUT_SEL = 0x2; ADCGAIN = 0x3e; //20 dB // ADCGAIN = 0x2A; //0 dB DACGAIN = 0x7e; //20db // DACGAIN = 0x74; //0db #if 0 /* * there is two ways to set up the codec. This is the first * method. Use PKT_CODECCFG to specify what control registers * to change and the new values. Then later they will be * sent to the codec when the PKT_CODECSTART is sent. */ p = put_next_byte( p, PKT_CODECCFG ); p = put_next_byte( p, 6 ); p = put_next_word (p, (2<<8)| (1<<7)|(4<<3)); // set TURBO=1 (SCLK=MCLK/P), keep I2C addr=4 p = put_next_word (p, (1<<8)| 1|(1<<6) /*|(1<<5)*/ ); // set 16 bit DAC mode, set continuous data transfer mode p = put_next_word (p, (4<<8)| 0x80|3); // set M=3 p = put_next_word (p, (5<<8)| ADCGAIN ); p = put_next_word (p, (5<<8)| 0x80|(7<<3)| INPUT_GAIN); // sidetone=MUTE / INPUT GAIN = 24dB ==> 0x3 p = put_next_word (p, (6<<8)| (INPUT_SEL<<1)); // set input LINE IN = 0x6 MIC = 0x2 #else /* * For the second method, use PKT_CODECCFG to specify that 0 * registers are configured by PKT_CODECSTART. Then use * PKT_SETCODECRESET to take the codec out of reset. Use * PKT_DELAYNUS to insert delay, then use PKT_WRITEI2C * repeatedly to generate i2c writes. This sets up the AIC14, * later when PKT_CODECSTART is received, it will not try to * reconfigure the aic14. All it does is enable the codec * interface of the a3k without configuring the aic14 first. * Either method works. This method is more generic. */ p = put_next_byte( p, PKT_CODECCFG ); p = put_next_byte( p, 0 ); p = put_next_byte( p, PKT_SETCODECRESET ); p = put_next_byte( p, PKT_DELAYNUS ); p = put_next_word( p, 10 ); #define PKT_WRITEREG( p, reg, val ) p = put_next_byte( p, PKT_WRITEI2C ); p = put_next_byte( p, 3 ); p = put_next_byte( p, 0x80 ); p = put_next_byte( p, reg ); p = put_next_byte( p, val ) PKT_WRITEREG (p, 2, (1<<7)|(4<<3)); // set TURBO=1 (SCLK=MCLK/P), keep I2C addr=4 PKT_WRITEREG (p, 1, 1|(1<<6) /*|(1<<5)*/); // set 16 bit DAC mode, set continuous data transfer mode PKT_WRITEREG (p, 4, 0x8A); PKT_WRITEREG (p, 4, (0x10)); PKT_WRITEREG (p, 5, ADCGAIN); PKT_WRITEREG (p, 5, DACGAIN); PKT_WRITEREG (p, 5, 0x80|(7<<3)| INPUT_GAIN); // sidetone=MUTE / INPUT GAIN = 24dB ==> 0x3 PKT_WRITEREG (p, 6, (INPUT_SEL<<1)); // set input LINE IN = 0x6 MIC = 0x2 #endif p = put_next_byte( p, PKT_DISCARDNCODEC ); p = put_next_word( p, 128 ); //tell the a3k to discard the first 128 codec samples, output 0 //final delay will delay response packet which puts some time //between config_a3k_codec and start_a3k_codec. p = put_next_byte( p, PKT_DELAYNUS ); p = put_next_word( p, 10 ); put_packet(interface, packet, p, PKT_CONTROL, 1); //we expect a 9 byte response packet plen = get_packet(interface, packet); if (plen == 0) return 1; return 0; } static int start_a3k_codec(a3k_t interface, short skew, short passthru, short codecif) { char packet[30]; char *p = packet+4; short chk_header, chk_len, chk_type, chk_field, chk_response_ok; short plen; p = put_next_byte( p, PKT_STARTCODEC ); p = put_next_byte( p, (skew & 1) | ((passthru & 1)<<1) | ((codecif & 1)<<2) ); put_packet(interface, packet, p, PKT_CONTROL, 1); plen = get_packet(interface, packet); if (plen == 0) return 1; p = packet; p = get_next_byte( p, &chk_header ); if (chk_header != PKT_HEADER) return -1; p = get_next_word( p, &chk_len ); if (chk_len != 4) return -2; p = get_next_byte( p, &chk_type ); if (chk_type != PKT_CONTROL) return -3; p = get_next_byte( p, &chk_field); if (chk_field != PKT_STARTCODEC) return -12; p = get_next_byte( p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -13; return 0; } static int __attribute__((unused)) stop_a3k_codec(a3k_t interface) { char packet[30]; char *p = packet+4; short chk_header, chk_len, chk_type, chk_field, chk_response_ok; short plen; p = put_next_byte( p, PKT_STOPCODEC ); put_packet(interface, packet, p, PKT_CONTROL, 1); plen = get_packet(interface, packet); if (plen == 0) return 1; p = packet; p = get_next_byte( p, &chk_header ); if (chk_header != PKT_HEADER) return -1; p = get_next_word( p, &chk_len ); if (chk_len != 4) return -2; p = get_next_byte( p, &chk_type ); if (chk_type != PKT_CONTROL) return -3; p = get_next_byte( p, &chk_field); if (chk_field != PKT_STARTCODEC) return -12; p = get_next_byte( p, &chk_response_ok); if (chk_response_ok != PKT_RESPONSE_OK) return -13; return 0; } void pins_set(a3k_t tag, int rate, int ec, int ns, int es) { gpio_write(tag->rate[0], (0x01 & rate)); gpio_write(tag->rate[1], (0x02 & rate)); gpio_write(tag->rate[2], (0x04 & rate)); gpio_write(tag->rate[3], (0x08 & rate)); gpio_write(tag->rate[4], (0x10 & rate)); gpio_write(tag->rate[5], (0x20 & rate)); gpio_write(tag->ec, ec); gpio_write(tag->ns, ns); gpio_write(tag->es, es); } void read_cfg(a3k_t tag) { char packet[10]; char *p = packet + 4; p = put_next_byte(p, 0x37); put_packet(tag, packet, p, PKT_CONTROL, 1); get_packet(tag, packet); } static void __fifo_init(FIFO *f, short blen, char *b) { f->pnew = f->pold = b; f->blen = blen; f->bend = b + blen; f->b = b; f->nelem = 0; } /* * This function does not take anything out of the fifo. It assumes * that there is a complete packet in the fifo (or at least the * header/length/type). It just looks at the 4th byte which is the * type byte and returns it. In the event that the fifo does not * contain at least 4 bytes, it returns 0x0ff. */ int a3k_peekpackettype(a3k_t tag) { FIFO *f = &tag->rx; char i; char c; volatile char *old = f->pold; for ( i = 0; i < 4; i++ ) { c = *old++; if (old == f->bend) old = f->b; } /* * just return the forth byte. Note that nelem wasn't * decremented and the fifo pointers are unchanged. * This function peeked into the fifo without changing * it. */ return c; } /* * this function discards a packet from the fifo. */ void a3k_discardpacket(a3k_t tag) { FIFO *f = &tag->rx; /* * one we get len we can just subtract len+1 from elem and * then do a circular wrap around of the fifo pointer. * for now i discard characters one at a time. */ char c; short len; short i; __fifo_getc(f); c = __fifo_getc(f); len = c; len <<= 8; c = __fifo_getc( f ); len |= (c & 0x00ff); len++; //dont forget the type byte for ( i = 0; i < len; i++ ) { __fifo_getc( f ); } } int a3k_getpacket(a3k_t tag, uint8_t *buf, int size) { char c; int i = 0, x, len; FIFO *f = &tag->rx; buf[i++] = __fifo_getc(f); c = __fifo_getc(f); buf[i++] = c; len = (c << 8); c = __fifo_getc(f); buf[i++] = c; len |= (c & 0xff); ++len; // dont forget the type byte for (x = 0; x < len && x < (size - 4); ++x) { buf[i++] = __fifo_getc(f); } while (x < len) { /* drop */ __fifo_getc(f); ++x; } return (len + 3); } int a3k_get_rawpacket(a3k_t tag, uint8_t *buf, int size) { uint8_t c; int n = 0, len; FIFO *f = &tag->rx; __fifo_getc(f); /* drop 0x61 */ c = __fifo_getc(f); /* len (MSB) */ len = (c << 8) | (__fifo_getc(f) & 0xff); /* len (LSB) */ __fifo_getc(f); /* drop type */ c = __fifo_getc(f); /* drop CHAND field */ assert(c == 1); c = __fifo_getc(f); /* CHANAD bits */ c /= 8; /* to bytes */ for (n = 0; n < c && n < size; ++n) { buf[n] = __fifo_getc(f); } len -= n; while (len > 0) { /* drop 0x2f, parity, etc ... */ __fifo_getc(f); } return (n); } int a3k_putpacket(a3k_t tag, const void *_ptr, int len) { __send_all(tag, _ptr, len); return (len); } /* * This function just checks the fifo without modifying it, unless it * discovers that the header is missing. When the fifo is missing * the header all data is discarded until a header is found. */ int a3k_checkfullpacket(a3k_t tag) { FIFO *f = &tag->rx; int len, n; char *old; start: /* try to receive from serial */ while (f->blen != f->nelem) { len = f->bend - f->pnew; n = serial_recv_buffered(tag->uart, (void *)f->pnew, len); if (n > 0) { f->pnew += n; if (f->pnew == f->bend) { f->pnew = f->b; } f->nelem += n; } else { break; } } /* * if there is a character in the fifo and it is not * a header byte then discard it. Keep going as * long as there are still bytes in the fifo. if a packet * with bad parity is received it is discarded. */ while ((f->nelem > 0) && ( *f->pold != PKT_HEADER )) { __fifo_getc(f); } /* * now we are either out of characters in the fifo * or the first character is a header byte. If we * dont have at least the header byte and the two * len bytes yet, then just return. */ if (f->nelem < 3) return 0; /* * we now have the header and two len bytes in the * fifo. */ old = f->pold + 1; if (old == f->bend) old = f->b; len = *old++; len <<= 8; if (old == f->bend) old = f->b; len |= *old++; if (len > 340) { __fifo_getc(f); goto start; } if (f->nelem >= len+4) { /* * we now have received a full packet. We will * check its parity. if the parity is bad then * we will discard the packet, and return 0. * If the packet is good then we will return 1. */ short i; char parity = 0; parity ^= ((len >> 8) & 0x00ff); parity ^= (len & 0x00ff); if (old == f->bend) old = f->b; for(i=0;ibend) old = f->b; } if (parity == 0) return 1; /* we have a full packet with good parity check */ else { a3k_discardpacket(tag); return 0; } } else return 0; } int a3k_setup(a3k_t tag, int baudrate) { int err = 0; #ifdef STM32F429_439xx int i; #endif __fifo_init(&tag->rx, FORMAT3KBLEN, (void *)tag->_buf); if (baudrate < 0) { baudrate = A3K_UART_BAUDRATE; } serial_setup(tag->uart, baudrate, 8, SERIAL_STOPBITS_1); if (tag->pseudo) { return 0; } #ifdef STM32F429_439xx gpio_init(tag->rst, GPIO_OUTPUT | GPIO_FLAG_PP | GPIO_FLAG_PU); gpio_init(tag->rts, GPIO_INPUT | GPIO_FLAG_PP | GPIO_FLAG_PU | GPIO_SPEED_FAST); gpio_init(tag->ec, GPIO_OUTPUT); gpio_init(tag->ns, GPIO_OUTPUT); gpio_init(tag->es, GPIO_OUTPUT); for (i = 0; i < RATE_PINS; ++i) { gpio_init(tag->rate[i], GPIO_OUTPUT); } pins_set(tag, 0x2c, 0, 1, 0); //ec, ns, es #endif if (packet_reset_a3k(tag)) { printf("reset a3k failed.\r\n"); err = -1; goto tail; } /* if (stop_a3k_codec(tag)) { printf("stop codec failed.\r\n"); } */ if (config_a3k(tag, -1, 0, 0)) { /* ratet, input_gain, output_gain */ printf("config a3k failed.\r\n"); err = -2; goto tail; } if (config_a3k_codec(tag)) { printf("config a3k codec failed.\r\n"); err = -3; goto tail; } // read_cfg(tag); if (start_a3k_codec(tag, 0, 0, 0)) { printf("start a3k codec mode failed.\r\n"); err = -4; goto tail; } tail: return err; } void a3k_close(a3k_t tag) { if (!tag->pseudo) { packet_reset_a3k(tag); } serial_close(tag->uart); } #if (TARGET_HAS_A3K_0) struct a3k_tag a3k0 = { .uart = &serial1, .rst = PF10, .rts = PG7, .pseudo = 0, .rate = {PB0, PC4, PC5, PC3, PC0, PC1}, .ec = PC2, .ns = PJ15, .es = PG12, }; #endif #if TARGET_HAS_A3K_1 struct a3k_tag a3k1 = { .uart = &serial2, .rst = PI14, .rts = PK2, .pseudo = 0, .rate = {PJ4, PJ13, PJ14, PK3, PG10, PK4}, .ec = PI15, .ns = PJ12, .es = PH4, }; #endif #if TARGET_HAS_A3K_2 struct a3k_tag a3k2 = { .uart = &serial3, .pseudo = 1, }; #endif #if TARGET_HAS_A3K_3 struct a3k_tag a3k3 = { .uart = &serial5, .pseudo = 1, }; #endif