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