#include #include #include #include #include #include "mw_main.h" #include "mw_uart.h" #include "mw_bt.h" #include "bt_hci.h" #include "bt_l2cap.h" #include "bluetooth_init_cc256x.h" static char bt_rx_buf[BT_RX_MAX_SIZE]; static unsigned char bt_rx_buf_wpos = 0; static unsigned char bt_rx_buf_rpos = 0; static uint8_t mw_bt_enabled = 0; int mw_bt_get_rxbuf_len(void) { if (bt_rx_buf_rpos > bt_rx_buf_wpos) return (BT_RX_MAX_SIZE - bt_rx_buf_rpos) + bt_rx_buf_wpos; else return bt_rx_buf_wpos - bt_rx_buf_rpos; } const unsigned char *mw_bt_get_rx_buf(unsigned char **rpos, unsigned char *len) { *rpos = &bt_rx_buf_rpos; if (bt_rx_buf_rpos > bt_rx_buf_wpos) *len = (BT_RX_MAX_SIZE - bt_rx_buf_rpos) + bt_rx_buf_wpos; else *len = bt_rx_buf_wpos - bt_rx_buf_rpos; // if we reach high water mark raise RTS to stop more data if (*len > (BT_RX_MAX_SIZE-(BT_RX_MAX_SIZE/10))) { debug_uart_tx("BT UART RTS\n"); BT_IO_POUT |= BT_IO_RTS; // low == ready, high == !ready } else { BT_IO_POUT &= ~BT_IO_RTS; // low == ready, high == !ready } return (unsigned char *)bt_rx_buf; } #pragma vector=USCI_A1_VECTOR __interrupt void UCA1_ISR (void) { switch (UCA1IV) { case 2: // RXIFG /* clear IRQ flag */ //UCA1IFG &= ~UCRXIFG; /* wake up to handle the received char */ if (UCA1STAT & UCRXERR) { debug_uart_tx("BT UART RXERR: "); if (UCA1STAT & UCOE) debug_uart_tx("overrun "); if (UCA1STAT & UCPE) debug_uart_tx("parity err "); if (UCA1STAT & UCFE) debug_uart_tx("frm-err "); debug_uart_tx("\n"); } bt_rx_buf[bt_rx_buf_wpos++] = UCA1RXBUF; bt_rx_buf_wpos %= BT_RX_MAX_SIZE; // LPM3_EXIT; LPM3_EXIT_ISR(); _event_src |= BT_UART_RCV_EVENT; break; case 4: // TXIFG debug_uart_tx("BT UART TX IRQ - huh?\n"); break; default: break; }; } void mw_init_bt_uart(const bt_uart_baud_t baud) { UCA1CTL1 = UCSWRST; UCA1CTL1 |= UCSSEL__SMCLK; switch (baud) { case BT_UART_BD115200: default: UCA1BR0 = 138; UCA1MCTL = UCBRS_7 + UCBRF_0; break; }; UCA1STAT = 0; // UCA1CTL0 = UCMODE_0; // UART mode // UCA1CTL0 &= ~UC7BIT; // 8bit char //UCA1CTL0 |= UCRXEIE; UCA1CTL1 &= ~UCSWRST; /* clear interrup flags */ UCA1IFG = 0; UCA1IE = UCRXIE; } #if 0 // Does never finish, presumably trigger does not trigger, unknown :( void mw_bt_uart_tx(const void *buf, const unsigned int len) { UCA1IE &= UCTXIE; DMACTL0 = DMA0TSEL_21; DMA0DA = (unsigned int) &UCA1TXBUF; DMA0SA = (uint32_t) buf; DMA0SZ = len; //DMA0CTL = 0x03F0; DMA0CTL = DMADT_1 | DMASRCINCR_3 | DMASBDB | DMALEVEL | DMAIE; UCA1IFG &= ~UCTXIFG; DMA0CTL |= DMAEN; while ((DMA0CTL & DMAIFG) == 0 && (DMA0CTL & DMAABORT) == 0) nop(); } #else int mw_bt_uart_tx(const void *buf, const unsigned int len) { unsigned int pos, i; // char txstr[8]; pos = 0; // debug_uart_tx("BT tx: "); while (pos < len) { // watch for CTS to be low i = 0; while ((BT_IO_PIN & BT_IO_CTS) && (i < 1000)) { __delay_cycles(16000); i++; if (i >= 1000) return -1; // nop(); } // do not start a transfer if UART is busy, e.g. rx-ing while (UCA1STAT & UCBUSY) nop(); UCA1TXBUF = *(unsigned char *) (buf+pos); // debug_uart_tx_char(*(unsigned char *) (buf+pos)); // snprintf(txstr, 8, "0x%02x ", *(unsigned char *) (buf+pos)); // debug_uart_tx(txstr); pos++; while ((UCA1IFG & UCTXIFG) == 0) nop(); } while (UCA1STAT & UCBUSY) nop(); return len; } #endif static int load_cc256x_init_script(void) { uint32_t pos; unsigned char *tptr; int tlen; pos = 0; while (pos < cc256x_init_script_size) { if (_event_src != 0) handle_event(); tptr = (unsigned char *)(cc256x_init_script + pos); tlen = mw_bt_uart_tx(tptr, 4 + tptr[3]); if (tlen < 0) return -1; pos += tlen /*4 + tptr[3]*/; // each init script part is one HCI command so wait for reply if (_event_src != 0) handle_event(); } return 0; } void mw_enable_bt(void) { int i; /* make sure it resets */ BT_SHUTDOWN(); __delay_cycles(16000); /* enable 32kHz ACLK output to BT module */ P11DIR |= BIT0; P11SEL |= BIT0; // wait for clock to stabilize __delay_cycles(16000); // disable the IRQ on CTS, later used to get a wakeup IRQ from eHCILL // will be enabled when going to sleep P1IE &= ~BT_IO_CTS; P1IES &= ~BT_IO_CTS; BT_IO_PDIR &= ~(BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ); BT_IO_PDIR |= BT_IO_RTS; BT_IO_POUT &= ~(BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ); BT_IO_POUT &= ~BT_IO_RTS; // low == ready, high == !ready BT_IO_REN |= BT_IO_CTS; // enable pull-down on CTS, POUT-CTS is 0 already /* setup UART pins */ BT_UART_PSEL |= BT_UART_TX_PIN | BT_UART_RX_PIN; // P5OUT |= BT_UART_TX_PIN | BT_UART_RX_PIN; // P5REN |= BT_UART_TX_PIN | BT_UART_RX_PIN; mw_init_bt_uart(BT_UART_BD115200); bt_rx_buf_wpos = 0; bt_rx_buf_rpos = 0; /* release BT reset pin */ BT_ENABLE(); for (i=0; i<1000; i++) { __delay_cycles(16000); if ((BT_IO_PIN & BT_IO_CTS) == 0) // when CTS goes low module is ready break; } if (i>=1000) { debug_uart_tx("Timeout waiting for CC256x to lower CTS\n"); mw_bt_enabled = 0; } else { debug_uart_tx("CC256x CTS low - uploading init\n"); // the init script consists of HCI cmds so HCI must be setup before bt_hci_init(); // give it some more time before anyone sends data for (i=0; i<10; i++) { __delay_cycles(16000); } if (load_cc256x_init_script() < 0) { debug_uart_tx("init upload failed!\n"); return; } __delay_cycles(32000); debug_uart_tx("init uploaded\n"); init_l2cap(); if (_event_src != 0) handle_event(); mw_bt_enabled = 1; } } void mw_disable_bt(void) { mw_bt_enabled = 0; // disable the IRQ on CTS P1IE &= ~BT_IO_CTS; P1IES &= ~BT_IO_CTS; // BT_IO_REN &= ~BT_IO_CTS; // disable pull-down on CTS P1IFG &= ~BT_IO_CTS; /* disable UART RX interrupt */ UCA1IE &= ~UCRXIE; /* disable UART pins */ BT_UART_PSEL &= ~(BT_UART_TX_PIN | BT_UART_RX_PIN); /* set BT reset pin */ BT_SHUTDOWN(); /* disable 32kHz ACLK output to BT module */ P11DIR &= ~BIT0; P11SEL &= ~BIT0; /* make all I/O Pins inputs so we do not drive against a "deaf" module */ BT_IO_PDIR &= ~(BT_IO_RTS | BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ); BT_IO_POUT &= ~(BT_IO_RTS | BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ); } uint8_t mw_bt_is_enabled(void) { return mw_bt_enabled; }