xref: /btstack/port/archive/msp430f5229lp-cc2564b/src/hal_uart_dma.c (revision 2fca4dad957cd7b88f4657ed51e89c12615dda72)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 /**
39  * @file  hal_bt.c
40  ***************************************************************************/
41 #include <stdint.h>
42 
43 #include <msp430.h>
44 #include "hal_compat.h"
45 
46 #include "hal_uart_dma.h"
47 
48 extern void hal_cpu_set_uart_needed_during_sleep(uint8_t enabled);
49 
50 // debugging only
51 // #include <stdio.h>
52 
53 
54 // RXD 3.4
55 // TXD 3.3
56 #define BT_PORT_OUT      P3OUT
57 #define BT_PORT_SEL      P3SEL
58 #define BT_PORT_DIR      P3DIR
59 #define BT_PORT_REN      P3REN
60 #define BT_PIN_TXD       BIT3
61 #define BT_PIN_RXD       BIT4
62 
63 // RTS P2.3
64 #define RTS_SEL P2SEL
65 #define RTS_OUT P2OUT
66 #define RTS_DIR P2DIR
67 #define RTS_PIN BIT3
68 
69 // CTS P8.1 rewired to P2.6 - only P1 & P2 have interrupts
70 #define CTS_SEL P2SEL
71 #define CTS_OUT P2OUT
72 #define CTS_DIR P2DIR
73 #define CTS_PIN BIT6
74 
75 // N_SHUTDOWN P4.1
76 #define N_SHUTDOWN_SEL P4SEL
77 #define N_SHUTDOWN_OUT P4OUT
78 #define N_SHUTDOWN_DIR P4DIR
79 #define N_SHUTDOWN_PIN BIT1
80 
dummy_handler(void)81 void dummy_handler(void){};
82 
83 // rx state
84 static uint16_t  bytes_to_read = 0;
85 static uint8_t * rx_buffer_ptr = 0;
86 
87 // tx state
88 static uint16_t  bytes_to_write = 0;
89 static uint8_t * tx_buffer_ptr = 0;
90 
91 // handlers
92 static void (*rx_done_handler)(void) = dummy_handler;
93 static void (*tx_done_handler)(void) = dummy_handler;
94 static void (*cts_irq_handler)(void) = dummy_handler;
95 
96 /**
97  * @brief  Initializes the serial communications peripheral and GPIO ports
98  *         to communicate with the PAN BT .. assuming 16 Mhz CPU
99  *
100  * @param  none
101  *
102  * @return none
103  */
hal_uart_dma_init(void)104 void hal_uart_dma_init(void)
105 {
106     BT_PORT_SEL |= BT_PIN_RXD + BT_PIN_TXD;
107     BT_PORT_DIR |= BT_PIN_TXD;
108     BT_PORT_DIR &= ~BT_PIN_RXD;
109 
110     // set BT RTS
111     RTS_SEL &= ~RTS_PIN;  // = 0 - I/O
112     RTS_DIR |=  RTS_PIN;  // = 1 - Output
113     RTS_OUT |=  RTS_PIN;  // = 1 - RTS high -> stop
114 
115     // set BT CTS
116     CTS_SEL &= ~CTS_PIN;  // = 0 - I/O
117     CTS_DIR &= ~CTS_PIN;  // = 0 - Input
118 
119     // set BT SHUTDOWN to 1 (active low)
120     N_SHUTDOWN_SEL &= ~N_SHUTDOWN_PIN;  // = 0 - I/O
121     N_SHUTDOWN_DIR |=  N_SHUTDOWN_PIN;  // = 1 - Output
122     N_SHUTDOWN_OUT |=  N_SHUTDOWN_PIN;  // = 1 - Active low -> ok
123 
124     // wait for Bluetooth to power up properly after providing 32khz clock
125     waitAboutOneSecond();
126 
127     UCA0CTL1 |= UCSWRST;              //Reset State
128     UCA0CTL0 = UCMODE_0;
129 
130     UCA0CTL0 &= ~UC7BIT;              // 8bit char
131     UCA0CTL1 |= UCSSEL_2;
132 
133     UCA0CTL1 &= ~UCSWRST;             // continue
134 
135     hal_uart_dma_set_baud(115200);
136 }
137 
138 /**
139 
140  UART used in low-frequency mode
141  In this mode, the maximum USCI baud rate is one-third the UART source clock frequency BRCLK.
142 
143  16000000 /  576000 = 277.77
144  16000000 /  115200 = 138.88
145  16000000 /  921600 =  17.36
146  16000000 / 1000000 =  16.00
147  16000000 / 2000000 =   8.00
148  16000000 / 2400000 =   6.66
149  16000000 / 3000000 =   3.33
150  16000000 / 4000000 =   2.00
151 
152  */
hal_uart_dma_set_baud(uint32_t baud)153 int hal_uart_dma_set_baud(uint32_t baud){
154 
155     int result = 0;
156 
157     UCA0CTL1 |= UCSWRST;              //Reset State
158 
159     switch (baud){
160 
161         case 4000000:
162             UCA0BR0 = 2;
163             UCA0BR1 = 0;
164             UCA0MCTL= 0 << 1;  // + 0.000
165             break;
166 
167         case 3000000:
168             UCA0BR0 = 3;
169             UCA0BR1 = 0;
170             UCA0MCTL= 3 << 1;  // + 0.375
171             break;
172 
173         case 2400000:
174             UCA0BR0 = 6;
175             UCA0BR1 = 0;
176             UCA0MCTL= 5 << 1;  // + 0.625
177             break;
178 
179         case 2000000:
180             UCA0BR0 = 8;
181             UCA0BR1 = 0;
182             UCA0MCTL= 0 << 1;  // + 0.000
183             break;
184 
185         case 1000000:
186             UCA0BR0 = 16;
187             UCA0BR1 = 0;
188             UCA0MCTL= 0 << 1;  // + 0.000
189             break;
190 
191         case 921600:
192             UCA0BR0 = 17;
193             UCA0BR1 = 0;
194             UCA0MCTL= 7 << 1;  // 3 << 1;  // + 0.375
195             break;
196 
197         case 115200:
198             UCA0BR0 = 138;  // from family user guide
199             UCA0BR1 = 0;
200             UCA0MCTL= 7 << 1;  // + 0.875
201             break;
202 
203         case 57600:
204             UCA0BR0 = 21;
205             UCA0BR1 = 1;
206             UCA0MCTL= 7 << 1;  // + 0.875
207             break;
208 
209         default:
210             result = -1;
211             break;
212     }
213 
214     UCA0CTL1 &= ~UCSWRST;             // continue
215 
216     return result;
217 }
218 
hal_uart_dma_set_block_received(void (* the_block_handler)(void))219 void hal_uart_dma_set_block_received( void (*the_block_handler)(void)){
220     rx_done_handler = the_block_handler;
221 }
222 
hal_uart_dma_set_block_sent(void (* the_block_handler)(void))223 void hal_uart_dma_set_block_sent( void (*the_block_handler)(void)){
224     tx_done_handler = the_block_handler;
225 }
226 
hal_uart_dma_set_csr_irq_handler(void (* the_irq_handler)(void))227 void hal_uart_dma_set_csr_irq_handler( void (*the_irq_handler)(void)){
228 #ifdef HAVE_CTS_IRQ
229     if (the_irq_handler){
230         P2IFG  =  0;     // no IRQ pending
231         P2IV   =  0;     // no IRQ pending
232         P2IES &= ~ CTS_PIN;  // IRQ on 0->1 transition
233         P2IE  |=   CTS_PIN;  // enable IRQ for P8.1
234         cts_irq_handler = the_irq_handler;
235         return;
236     }
237     P2IE  &= ~CTS_PIN;
238     cts_irq_handler = dummy_handler;
239 #endif
240 }
241 
242 /**********************************************************************/
243 /**
244  * @brief  Disables the serial communications peripheral and clears the GPIO
245  *         settings used to communicate with the BT.
246  *
247  * @param  none
248  *
249  * @return none
250  **************************************************************************/
hal_uart_dma_shutdown(void)251 void hal_uart_dma_shutdown(void) {
252 
253     UCA0IE &= ~(UCRXIE | UCTXIE);
254     UCA0CTL1 = UCSWRST;                          //Reset State
255     BT_PORT_SEL &= ~( BT_PIN_RXD + BT_PIN_TXD );
256     BT_PORT_DIR |= BT_PIN_TXD;
257     BT_PORT_DIR |= BT_PIN_RXD;
258     BT_PORT_OUT &= ~(BT_PIN_TXD + BT_PIN_RXD);
259 }
260 
hal_uart_dma_send_block(const uint8_t * data,uint16_t len)261 void hal_uart_dma_send_block(const uint8_t * data, uint16_t len){
262 
263     // printf("hal_uart_dma_send_block, size %u\n\r", len);
264 
265     UCA0IE &= ~UCTXIE ;  // disable TX interrupts
266 
267     tx_buffer_ptr = (uint8_t *) data;
268     bytes_to_write = len;
269 
270     UCA0IE |= UCTXIE;    // enable TX interrupts
271 }
272 
hal_uart_dma_enable_rx(void)273 static inline void hal_uart_dma_enable_rx(void){
274     RTS_OUT &= ~ RTS_PIN;  // = 0 - RTS low -> ok
275 }
276 
hal_uart_dma_disable_rx(void)277 static inline void hal_uart_dma_disable_rx(void){
278     RTS_OUT |= RTS_PIN;  // = 1 - RTS high -> stop
279 }
280 
hal_uart_dma_receive_block(uint8_t * buffer,uint16_t len)281 void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len){
282     // disable RX interrupts
283     UCA0IE &= ~UCRXIE;
284 
285     rx_buffer_ptr = buffer;
286     bytes_to_read = len;
287 
288     // check if byte already received
289     int pending = UCA0IFG & UCRXIFG;
290 
291     // enable RX interrupts - will trigger ISR below if byte pending
292     UCA0IE |= UCRXIE;    // enable RX interrupts
293 
294     // if byte was pending, ISR controls RTS
295     if (!pending) {
296         hal_uart_dma_enable_rx();
297     }
298 }
299 
hal_uart_dma_set_sleep(uint8_t sleep)300 void hal_uart_dma_set_sleep(uint8_t sleep){
301     hal_cpu_set_uart_needed_during_sleep(!sleep);
302 }
303 
304 // block-wise "DMA" RX/TX UART driver
305 #ifdef __GNUC__
306 __attribute__((interrupt(USCI_A0_VECTOR)))
307 #endif
308 #ifdef __IAR_SYSTEMS_ICC__
309 #pragma vector=USCI_A0_VECTOR
310 __interrupt
311 #endif
usbRxTxISR(void)312 void usbRxTxISR(void){
313 
314     // find reason
315     switch (UCA0IV){
316 
317         case 2: // RXIFG
318             if (bytes_to_read == 0) {
319                 hal_uart_dma_disable_rx();
320                 UCA0IE &= ~UCRXIE ;  // disable RX interrupts
321                 return;
322             }
323             *rx_buffer_ptr = UCA0RXBUF;
324             ++rx_buffer_ptr;
325             --bytes_to_read;
326             if (bytes_to_read > 0) {
327                 hal_uart_dma_enable_rx();
328                 return;
329             }
330             RTS_OUT |= RTS_PIN;      // = 1 - RTS high -> stop
331             UCA0IE &= ~UCRXIE ; // disable RX interrupts
332 
333             (*rx_done_handler)();
334 
335             // force exit low power mode
336             __bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU
337 
338             break;
339 
340         case 4: // TXIFG
341             if (bytes_to_write == 0){
342                 UCA0IE &= ~UCTXIE ;  // disable TX interrupts
343                 return;
344             }
345             UCA0TXBUF = *tx_buffer_ptr;
346             ++tx_buffer_ptr;
347             --bytes_to_write;
348 
349             if (bytes_to_write > 0) {
350                 return;
351             }
352 
353             UCA0IE &= ~UCTXIE ;  // disable TX interrupts
354 
355             (*tx_done_handler)();
356 
357             // force exit low power mode
358             __bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU
359 
360             break;
361 
362         default:
363             break;
364     }
365 }
366 
367 
368 // CTS ISR
369 #ifdef HAVE_CTS_IRQ
370 // TODO: there's no PORT8_VECTOR, but configuration seems possible
371 
372 extern void ehcill_handle(uint8_t action);
373 #define EHCILL_CTS_SIGNAL      0x034
374 
375 #ifdef __GNUC__
376 __attribute__((interrupt(PORT2_VECTOR)))
377 #elif defined( __IAR_SYSTEMS_ICC__)
378 #pragma vector=PORT2_VECTOR
379 __interrupt
380 #endif
ctsISR(void)381 void ctsISR(void){
382     P2IV = 0;
383     (*cts_irq_handler)();
384 }
385 #endif
386 
387