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