xref: /btstack/port/samv71-xplained-atwilc3000/main.c (revision 7d0ceef15816750577c88837cf6ac3eede3224d2)
1 #include "asf.h"
2 #include "stdio_serial.h"
3 #include "conf_board.h"
4 #include "conf_clock.h"
5 
6 // BTstack
7 #include "btstack_chipset_atwilc3000.h"
8 #include "btstack_debug.h"
9 #include "btstack_memory.h"
10 #include "btstack_run_loop.h"
11 #include "btstack_run_loop_embedded.h"
12 #include "classic/btstack_link_key_db.h"
13 #include "hal_uart_dma.h"
14 #include "hal_cpu.h"
15 #include "hal_tick.h"
16 #include "hci.h"
17 #include "hci_dump.h"
18 #include "wilc3000_bt_firmware.h"
19 
20 // #define USE_XDMAC_FOR_USART
21 #define XDMA_CH_UART_TX  0
22 #define XDMA_CH_UART_RX  1
23 
24 /** All interrupt mask. */
25 #define ALL_INTERRUPT_MASK   0xffffffff
26 
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
31 extern int btstack_main(int argc, const char * argv[]);
32 
33 static void dummy_handler(void){}
34 static void (*tick_handler)(void) = &dummy_handler;
35 
36 static btstack_uart_config_t uart_config;
37 
38 static hci_transport_config_uart_t transport_config = {
39 	HCI_TRANSPORT_CONFIG_UART,
40 	2000000,  // directly use high baud rate after config
41 	0, 		  // use 0 to skip baud rate change from 115200 to X for debugging purposes
42 	1,        // flow control
43 	NULL,
44 };
45 
46 /**
47  *  \brief Handler for System Tick interrupt.
48  */
49 void SysTick_Handler(void)
50 {
51 	tick_handler();
52 }
53 
54 /**
55  *  Configure UART console.
56  */
57 // [main_console_configure]
58 static void configure_console(void)
59 {
60 	const usart_serial_options_t uart_serial_options = {
61 		.baudrate = CONF_UART_BAUDRATE,
62 #ifdef CONF_UART_CHAR_LENGTH
63 		.charlength = CONF_UART_CHAR_LENGTH,
64 #endif
65 		.paritytype = CONF_UART_PARITY,
66 #ifdef CONF_UART_STOP_BITS
67 		.stopbits = CONF_UART_STOP_BITS,
68 #endif
69 	};
70 
71 	/* Configure console UART. */
72 	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
73 	stdio_serial_init(CONF_UART, &uart_serial_options);
74 }
75 
76 // [main_console_configure]
77 
78 /**
79  * \brief Wait for the given number of milliseconds (ticks
80  * generated by the SAM's microcontrollers's system tick).
81  *
82  * \param ul_dly_ticks  Delay to wait for, in milliseconds.
83  */
84 // [main_ms_delay]
85 static void mdelay(uint32_t delay_in_ms)
86 {
87 	// delay_ms(delay_in_ms);
88 	uint32_t time_to_wait = btstack_run_loop_get_time_ms() + delay_in_ms;
89 	while (btstack_run_loop_get_time_ms() < time_to_wait);
90 }
91 // [main_ms_delay]
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 // hal_cpu.h implementation
95 ////////////////////////////////////////////////////////////////////////////////
96 // hal_led.h implementation
97 #include "hal_led.h"
98 void hal_led_off(void);
99 void hal_led_on(void);
100 
101 void hal_led_off(void){
102 	// gpio_set_pin_low(GPIOA, GPIO_LED2);
103 }
104 void hal_led_on(void){
105 	// gpio_set_pin_high(GPIOA, GPIO_LED2);
106 }
107 void hal_led_toggle(void){
108 	// gpio_toggle_pin(GPIOA, GPIO_LED2);
109 }
110 
111 // hal_cpu.h implementation
112 #include "hal_cpu.h"
113 
114 void hal_cpu_disable_irqs(void){
115 	//__disable_irq();
116 }
117 
118 void hal_cpu_enable_irqs(void){
119 	// __enable_irq();
120 }
121 
122 void hal_cpu_enable_irqs_and_sleep(void){
123 	hal_led_off();
124 	// __enable_irq();
125 	// __asm__("wfe");	// go to sleep if event flag isn't set. if set, just clear it. IRQs set event flag
126 
127 	// note: hal_uart_needed_during_sleep can be used to disable peripheral clock if it's not needed for a timer
128 	hal_led_on();
129 }
130 
131 
132 #ifndef USE_XDMAC_FOR_USART
133 // RX state
134 static volatile uint16_t  bytes_to_read = 0;
135 static volatile uint8_t * rx_buffer_ptr = 0;
136 
137 // TX state
138 static volatile uint16_t  bytes_to_write = 0;
139 static volatile uint8_t * tx_buffer_ptr = 0;
140 #endif
141 
142 static volatile int       rx_notify;
143 static volatile int       tx_notify;
144 
145 static int simulate_flowcontrol;
146 
147 // handlers
148 static void (*rx_done_handler)(void) = dummy_handler;
149 static void (*tx_done_handler)(void) = dummy_handler;
150 static void (*cts_irq_handler)(void) = dummy_handler;
151 
152 // @note While the Atmel SAM S7x data sheet states
153 // "The hardware handshaking feature enables an out-of-band flow control by automatic management
154 //  of the pins RTS and CTS.",
155 // I didn't see RTS going up automatically up, ever. So, at least for RTS, the automatic management
156 // is just a glorified GPIO pin control feature, which provides no benefit, but irritates a lot
157 
158 // J505:6
159 #define DEBUG_PIN_1 PIO_PD16_IDX
160 // J505:5
161 #define DEBUG_PIN_2 PIO_PD15_IDX
162 
163 static inline void hal_uart_rts_high(void){
164 	if (!simulate_flowcontrol) return;
165 	ioport_set_pin_level(DEBUG_PIN_2, IOPORT_PIN_LEVEL_HIGH);
166 	BOARD_USART->US_CR = US_CR_RTSEN;
167 }
168 static inline void hal_uart_rts_low(void){
169 	if (!simulate_flowcontrol) return;
170 	ioport_set_pin_level(DEBUG_PIN_2, IOPORT_PIN_LEVEL_LOW);
171 	BOARD_USART->US_CR = US_CR_RTSDIS;
172 }
173 
174 /**
175  */
176 static int hal_uart_dma_initialized = 0;
177 void hal_uart_dma_init(void)
178 {
179 	if (hal_uart_dma_initialized){
180 		log_info("hal_uart_dma_init already initialized");
181 		return;
182 	}
183 	hal_uart_dma_initialized = 1;
184 
185 	// debug
186 #ifdef DEBUG_PIN_1
187 	ioport_set_pin_dir(DEBUG_PIN_1, IOPORT_DIR_OUTPUT);
188 	ioport_set_pin_level(DEBUG_PIN_1, IOPORT_PIN_LEVEL_LOW);
189 #endif
190 #ifdef DEBUG_PIN_2
191 	ioport_set_pin_dir(DEBUG_PIN_2, IOPORT_DIR_OUTPUT);
192 	ioport_set_pin_level(DEBUG_PIN_2, IOPORT_PIN_LEVEL_LOW);
193 #endif
194 	// power on
195 	ioport_set_pin_dir(BLUETOOTH_CHP_EN, IOPORT_DIR_OUTPUT);
196 	ioport_set_pin_level(BLUETOOTH_CHP_EN, IOPORT_PIN_LEVEL_HIGH);
197 
198 	// reset
199 	ioport_set_pin_dir(BLUETOOTH_RESET, IOPORT_DIR_OUTPUT);
200 	ioport_set_pin_level(BLUETOOTH_RESET, IOPORT_PIN_LEVEL_LOW);
201 	mdelay(250);
202 	ioport_set_pin_level(BLUETOOTH_RESET, IOPORT_PIN_LEVEL_HIGH);
203 	mdelay(250);
204 
205 	/* Enable the peripheral clock in the PMC. */
206 	sysclk_enable_peripheral_clock(BOARD_ID_USART);
207 
208 	// configure Bluetooth USART
209 	const sam_usart_opt_t bluetooth_settings = {
210 		115200,
211 		US_MR_CHRL_8_BIT,
212 		US_MR_PAR_NO,
213 		US_MR_NBSTOP_1_BIT,
214 		US_MR_CHMODE_NORMAL,
215 		/* This field is only used in IrDA mode. */
216 		0
217 	};
218 
219 	/* Configure USART mode. */
220 	simulate_flowcontrol = 0;
221 	usart_init_rs232(BOARD_USART, &bluetooth_settings, sysclk_get_peripheral_hz());
222 	// Set RTS = 0 (normal mode)
223 	BOARD_USART->US_CR = US_CR_RTSEN;
224 
225 	/* Disable all the interrupts. */
226 	usart_disable_interrupt(BOARD_USART, ALL_INTERRUPT_MASK);
227 
228 	/* Enable TX & RX function. */
229 	usart_enable_tx(BOARD_USART);
230 	usart_enable_rx(BOARD_USART);
231 
232 	/* Configure and enable interrupt of USART. */
233 	NVIC_EnableIRQ(USART_IRQn);
234 
235 #ifdef USE_XDMAC_FOR_USART
236 
237 	// setup XDMAC
238 
239 	/* Initialize and enable DMA controller */
240 	pmc_enable_periph_clk(ID_XDMAC);
241 
242 	/* Enable XDMA interrupt */
243 	NVIC_ClearPendingIRQ(XDMAC_IRQn);
244 	NVIC_SetPriority( XDMAC_IRQn ,1);
245 	NVIC_EnableIRQ(XDMAC_IRQn);
246 
247 	// Setup XDMA Channel for USART TX
248 	xdmac_channel_set_destination_addr(XDMAC, XDMA_CH_UART_TX, (uint32_t)&BOARD_USART->US_THR);
249 	xdmac_channel_set_config(XDMAC, XDMA_CH_UART_TX,
250 		XDMAC_CC_TYPE_PER_TRAN |
251 		XDMAC_CC_DSYNC_MEM2PER |
252 		XDMAC_CC_MEMSET_NORMAL_MODE |
253 		XDMAC_CC_MBSIZE_SINGLE |
254 		XDMAC_CC_DWIDTH_BYTE |
255 		XDMAC_CC_SIF_AHB_IF0 |
256 		XDMAC_CC_DIF_AHB_IF1 |
257 		XDMAC_CC_SAM_INCREMENTED_AM |
258 		XDMAC_CC_DAM_FIXED_AM |
259 		XDMAC_CC_CSIZE_CHK_1 |
260 		XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_USART0_TX)
261 	);
262 	xdmac_channel_set_descriptor_control(XDMAC, XDMA_CH_UART_TX, 0);
263 	xdmac_channel_set_source_microblock_stride(XDMAC, XDMA_CH_UART_TX, 0);
264 	xdmac_channel_set_destination_microblock_stride(XDMAC, XDMA_CH_UART_TX, 0);
265 	xdmac_channel_set_datastride_mempattern(XDMAC, XDMA_CH_UART_TX, 0);
266 	xdmac_channel_set_block_control(XDMAC, XDMA_CH_UART_TX, 0);
267 	xdmac_enable_interrupt(XDMAC, XDMA_CH_UART_TX);
268 	xdmac_channel_enable_interrupt(XDMAC, XDMA_CH_UART_TX, XDMAC_CIE_BIE);
269 
270 	// Setup XDMA Channel for USART RX
271 	xdmac_channel_set_source_addr(XDMAC, XDMA_CH_UART_RX, (uint32_t)&BOARD_USART->US_RHR);
272 	xdmac_channel_set_config(XDMAC, XDMA_CH_UART_RX,
273 		XDMAC_CC_TYPE_PER_TRAN |
274 		XDMAC_CC_DSYNC_PER2MEM |
275 		XDMAC_CC_MEMSET_NORMAL_MODE |
276 		XDMAC_CC_MBSIZE_SINGLE |
277 		XDMAC_CC_DWIDTH_BYTE |
278 		XDMAC_CC_SIF_AHB_IF1 |
279 		XDMAC_CC_DIF_AHB_IF0 |
280 		XDMAC_CC_SAM_FIXED_AM |
281 		XDMAC_CC_DAM_INCREMENTED_AM |
282 		XDMAC_CC_CSIZE_CHK_1 |
283 		XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_USART0_RX)
284 	);
285 	xdmac_channel_set_descriptor_control(XDMAC, XDMA_CH_UART_RX, 0);
286 	xdmac_channel_set_source_microblock_stride(XDMAC, XDMA_CH_UART_RX, 0);
287 	xdmac_channel_set_destination_microblock_stride(XDMAC, XDMA_CH_UART_RX, 0);
288 	xdmac_channel_set_datastride_mempattern(XDMAC, XDMA_CH_UART_RX, 0);
289 	xdmac_channel_set_block_control(XDMAC, XDMA_CH_UART_RX, 0);
290 	xdmac_enable_interrupt(XDMAC, XDMA_CH_UART_RX);
291 	xdmac_channel_enable_interrupt(XDMAC, XDMA_CH_UART_RX, XDMAC_CIE_BIE);
292 #endif
293 }
294 
295 void hal_uart_dma_set_sleep(uint8_t sleep){
296 }
297 
298 void hal_uart_dma_set_block_received( void (*the_block_handler)(void)){
299 	rx_done_handler = the_block_handler;
300 }
301 
302 void hal_uart_dma_set_block_sent( void (*the_block_handler)(void)){
303 	tx_done_handler = the_block_handler;
304 }
305 
306 void hal_uart_dma_set_csr_irq_handler( void (*the_irq_handler)(void)){
307 	cts_irq_handler = the_irq_handler;
308 }
309 
310 int  hal_uart_dma_set_baud(uint32_t baud){
311 	/* Disable TX & RX function. */
312 	usart_disable_tx(BOARD_USART);
313 	usart_disable_rx(BOARD_USART);
314 	uint32_t res = usart_set_async_baudrate(BOARD_USART, baud, sysclk_get_peripheral_hz());
315 	if (res){
316 		log_error("hal_uart_dma_set_baud library call failed");
317 	}
318 
319 	/* Enable TX & RX function. */
320 	usart_enable_tx(BOARD_USART);
321 	usart_enable_rx(BOARD_USART);
322 
323 	log_info("set baud rate %u", (int) baud);
324 	return 0;
325 }
326 
327 int  hal_uart_dma_set_flowcontrol(int flowcontrol){
328 	log_info("hal_uart_dma_set_flowcontrol %u", flowcontrol);
329 	simulate_flowcontrol = flowcontrol;
330 	if (flowcontrol){
331 		/* Set hardware handshaking mode. */
332 		BOARD_USART->US_MR = (BOARD_USART->US_MR & ~US_MR_USART_MODE_Msk) | US_MR_USART_MODE_HW_HANDSHAKING;
333 		hal_uart_rts_high();
334 	} else {
335 		/* Set nomal mode. */
336 		BOARD_USART->US_MR = (BOARD_USART->US_MR & ~US_MR_USART_MODE_Msk) | US_MR_USART_MODE_NORMAL;
337 		// Set RTS = 0 (normal mode)
338 		BOARD_USART->US_CR = US_CR_RTSEN;
339 	}
340 	return 0;
341 }
342 
343 void hal_uart_dma_send_block(const uint8_t *data, uint16_t size){
344 
345 	tx_notify = 1;
346 
347 #ifdef USE_XDMAC_FOR_USART
348 	xdmac_channel_get_interrupt_status( XDMAC, XDMA_CH_UART_TX);
349 	xdmac_channel_set_source_addr(XDMAC, XDMA_CH_UART_TX, (uint32_t)data);
350 	xdmac_channel_set_microblock_control(XDMAC, XDMA_CH_UART_TX, size);
351 	xdmac_channel_enable(XDMAC, XDMA_CH_UART_TX);
352 #else
353 	if (bytes_to_write){
354 		log_error("send block, bytes to write %u", bytes_to_write);
355 		return;
356 	}
357     tx_buffer_ptr = (uint8_t *) data;
358     bytes_to_write = size;
359 	usart_enable_interrupt(BOARD_USART, US_IER_TXRDY);
360 #endif
361 }
362 
363 void hal_uart_dma_receive_block(uint8_t *data, uint16_t size){
364 
365 #ifdef DEBUG_PIN_1
366 	ioport_set_pin_level(DEBUG_PIN_1, IOPORT_PIN_LEVEL_HIGH);
367 #endif
368 
369 	hal_uart_rts_low();
370 
371 	rx_notify = 1;
372 
373 #ifdef USE_XDMAC_FOR_USART
374 	xdmac_channel_get_interrupt_status( XDMAC, XDMA_CH_UART_RX);
375 	xdmac_channel_set_destination_addr(XDMAC, XDMA_CH_UART_RX, (uint32_t)data);
376 	xdmac_channel_set_microblock_control(XDMAC, XDMA_CH_UART_RX, size);
377 	xdmac_channel_enable(XDMAC, XDMA_CH_UART_RX);
378 #else
379     rx_buffer_ptr = data;
380     bytes_to_read = size;
381     usart_enable_interrupt(BOARD_USART, US_IER_RXRDY);
382 #endif
383 }
384 
385 #ifdef USE_XDMAC_FOR_USART
386 void XDMAC_Handler(void)
387 {
388 	uint32_t dma_status;
389 	dma_status = xdmac_channel_get_interrupt_status(XDMAC, XDMA_CH_UART_TX);
390 	if (dma_status & XDMAC_CIS_BIS) {
391 		if (tx_notify){
392 			tx_notify = 0;
393 			tx_done_handler();
394 		}
395 	}
396 	dma_status = xdmac_channel_get_interrupt_status(XDMAC, XDMA_CH_UART_RX);
397 	if (dma_status & XDMAC_CIS_BIS) {
398 		hal_uart_rts_high();
399 		if (rx_notify){
400 			rx_notify = 0;
401 			rx_done_handler();
402 		}
403 	}
404 }
405 #else
406 void USART_Handler(void)
407 {
408 
409 #ifdef DEBUG_PIN_2
410 	// ioport_set_pin_level(DEBUG_PIN_2, IOPORT_PIN_LEVEL_HIGH);
411 #endif
412 
413 	/* Read USART status. */
414 	uint32_t ul_status = usart_get_status(BOARD_USART);
415 
416 	// handle ready to send
417 	if(ul_status & US_IER_TXRDY) {
418 		if (bytes_to_write){
419 			// send next byte
420 			usart_write(BOARD_USART, *tx_buffer_ptr);
421 			tx_buffer_ptr++;
422 			bytes_to_write--;
423 		} else {
424 
425 			// done. disable tx ready interrupt to avoid starvation here
426 			usart_disable_interrupt(BOARD_USART, US_IER_TXRDY);
427 			if (tx_notify){
428 				tx_notify = 0;
429 				tx_done_handler();
430 			}
431 		}
432 	}
433 
434 	// handle byte available for read
435 	if (ul_status & US_IER_RXRDY) {
436 		if (bytes_to_read){
437 			uint32_t ch;
438 			usart_read(BOARD_USART, (uint32_t *)&ch);
439 			*rx_buffer_ptr++ = ch;
440 			bytes_to_read--;
441 			if (bytes_to_read == 0){
442 
443 #ifdef DEBUG_PIN_1
444 			ioport_set_pin_level(DEBUG_PIN_1, IOPORT_PIN_LEVEL_LOW);
445 #endif
446 
447 				// done. disable rx ready interrupt, raise RTS
448 				hal_uart_rts_high();
449 				usart_disable_interrupt(BOARD_USART, US_IER_RXRDY);
450 				if (rx_notify){
451 					rx_notify = 0;
452 					rx_done_handler();
453 				}
454 			}
455 		} else {
456 			// shoult not happen, disable irq anyway
457 			usart_disable_interrupt(BOARD_USART, US_IER_RXRDY);
458 		}
459 	}
460 #ifdef DEBUG_PIN_2
461 	// ioport_set_pin_level(DEBUG_PIN_2, IOPORT_PIN_LEVEL_LOW);
462 #endif
463 
464 }
465 #endif
466 
467 void hal_tick_init()
468 {
469 	/* Configure systick for 1 ms */
470 	puts("Configure system tick to get 1ms tick period.\r");
471 	if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
472 		puts("-F- Systick configuration error\r");
473 		while (1);
474 	}
475 }
476 
477 void hal_tick_set_handler(void (*handler)(void)){
478 	if (handler == NULL){
479 		tick_handler = &dummy_handler;
480 		return;
481 	}
482 	tick_handler = handler;
483 }
484 
485 int  hal_tick_get_tick_period_in_ms(void){
486 	return 1;
487 }
488 
489 static const btstack_uart_block_t * uart_driver;
490 
491 static void phase2(int status){
492 
493     if (status){
494         printf("Download firmware failed\n");
495         return;
496     }
497 
498     printf("Phase 2: Main app\n");
499 
500     // init HCI
501     const hci_transport_t * transport = hci_transport_h4_instance(uart_driver);
502     // const btstack_link_key_db_t * link_key_db = btstack_link_key_db_fs_instance();
503     hci_init(transport, (void*) &transport_config);
504     hci_set_chipset(btstack_chipset_atwilc3000_instance());
505     // hci_set_link_key_db(link_key_db);
506 
507     // setup app
508     btstack_main(0, NULL);
509 }
510 
511 /**
512  *  \brief getting-started Application entry point.
513  *
514  *  \return Unused (ANSI-C compatibility).
515  */
516 // [main]
517 int main(void)
518 {
519 	/* Initialize the SAM system */
520 	sysclk_init();
521 	board_init();
522 
523 	/* Initialize the console uart */
524 	configure_console();
525 
526 	/* Output boot info */
527 	printf("BTstack on SAMV71 Xplained Ultra with ATWILC3000\n");
528 	printf("CPU %lu hz, peripheral clock %lu hz\n", sysclk_get_cpu_hz(), sysclk_get_peripheral_hz());
529 #ifdef USE_XDMAC_FOR_USART
530 	printf("Using XDMA for Bluetooth UART\n");
531 #else
532 	printf("Using IRQ driver for Bluetooth UART\n");
533 #endif
534 	printf("--\n");
535 
536 	// start with BTstack init - especially configure HCI Transport
537 	btstack_memory_init();
538 	btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
539 
540 	// enable full log output while porting
541 	hci_dump_open(NULL, HCI_DUMP_STDOUT);
542 
543 	// setup UART HAL + Run Loop integration
544 	uart_driver = btstack_uart_block_embedded_instance();
545 
546     // extract UART config from transport config, but disable flow control and use default baudrate
547     uart_config.baudrate    = HCI_DEFAULT_BAUDRATE;
548     uart_config.flowcontrol = 0;
549     uart_config.device_name = transport_config.device_name;
550     uart_driver->init(&uart_config);
551 
552     // phase #1 download firmware
553     printf("Phase 1: Download firmware\n");
554 
555     // phase #2 start main app
556     btstack_chipset_atwilc3000_download_firmware(uart_driver, transport_config.baudrate_init, transport_config.flowcontrol, atwilc3000_fw_data, atwilc3000_fw_size, &phase2);
557 
558 	// go
559 	btstack_run_loop_execute();
560 
561 	// compiler happy
562 	while(1);
563 }
564 #ifdef __cplusplus
565 }
566 #endif
567