// // BTstack port for Apolle 2 EVB with EM9304 shield // #include "am_mcu_apollo.h" #include "am_bsp.h" #include "am_util.h" #include "am_devices_em9304.h" //***************************************************************************** // // Insert compiler version at compile time. // //***************************************************************************** #define STRINGIZE_VAL(n) STRINGIZE_VAL2(n) #define STRINGIZE_VAL2(n) #n #ifdef __GNUC__ #define COMPILER_VERSION ("GCC " __VERSION__) #elif defined(__ARMCC_VERSION) #define COMPILER_VERSION ("ARMCC " STRINGIZE_VAL(__ARMCC_VERSION)) #elif defined(__KEIL__) #define COMPILER_VERSION "KEIL_CARM " STRINGIZE_VAL(__CA__) #elif defined(__IAR_SYSTEMS_ICC__) #define COMPILER_VERSION __VERSION__ #else #define COMPILER_VERSION "Compiler unknown" #endif //***************************************************************************** // // IOM SPI Configuration for EM9304 // //***************************************************************************** const am_hal_iom_config_t g_sEm9304IOMConfigSPI = { .ui32ClockFrequency = AM_HAL_IOM_8MHZ, .ui32InterfaceMode = AM_HAL_IOM_SPIMODE, .ui8WriteThreshold = 20, .ui8ReadThreshold = 20, .bSPHA = 0, .bSPOL = 0, }; //***************************************************************************** // // UART configuration settings. // //***************************************************************************** am_hal_uart_config_t g_sUartConfig = { .ui32BaudRate = 115200, .ui32DataBits = AM_HAL_UART_DATA_BITS_8, .bTwoStopBits = false, .ui32Parity = AM_HAL_UART_PARITY_NONE, .ui32FlowCtrl = AM_HAL_UART_FLOW_CTRL_NONE, }; //***************************************************************************** // // Initialize the UART // //***************************************************************************** void uart_init(uint32_t ui32Module) { // // Make sure the UART RX and TX pins are enabled. // am_bsp_pin_enable(COM_UART_TX); am_bsp_pin_enable(COM_UART_RX); // // Power on the selected UART // am_hal_uart_pwrctrl_enable(ui32Module); // // Start the UART interface, apply the desired configuration settings, and // enable the FIFOs. // am_hal_uart_clock_enable(ui32Module); // // Disable the UART before configuring it. // am_hal_uart_disable(ui32Module); // // Configure the UART. // am_hal_uart_config(ui32Module, &g_sUartConfig); // // Enable the UART FIFO. // am_hal_uart_fifo_config(ui32Module, AM_HAL_UART_TX_FIFO_1_2 | AM_HAL_UART_RX_FIFO_1_2); // // Enable the UART. // am_hal_uart_enable(ui32Module); } //***************************************************************************** // // Disable the UART // //***************************************************************************** void uart_disable(uint32_t ui32Module) { // // Clear all interrupts before sleeping as having a pending UART interrupt // burns power. // am_hal_uart_int_clear(ui32Module, 0xFFFFFFFF); // // Disable the UART. // am_hal_uart_disable(ui32Module); // // Disable the UART pins. // am_bsp_pin_disable(COM_UART_TX); am_bsp_pin_disable(COM_UART_RX); // // Disable the UART clock. // am_hal_uart_clock_disable(ui32Module); } //***************************************************************************** // // Initialize the EM9304 BLE Controller // //***************************************************************************** void am_devices_em9304_spi_init(uint32_t ui32Module, const am_hal_iom_config_t *psIomConfig) { if ( AM_REGn(IOMSTR, ui32Module, CFG) & AM_REG_IOMSTR_CFG_IFCEN_M ) { return; } #if defined(AM_PART_APOLLO2) am_hal_iom_pwrctrl_enable(ui32Module); #endif // // Setup the pins for SPI mode. // am_bsp_iom_spi_pins_enable(ui32Module); // // Set the required configuration settings for the IOM. // am_hal_iom_config(ui32Module, psIomConfig); // Enable spi am_hal_iom_enable(ui32Module); } void configure_em9304_pins(void) { am_bsp_pin_enable(EM9304_CS); am_bsp_pin_enable(EM9304_INT); am_hal_gpio_out_bit_set(AM_BSP_GPIO_EM9304_CS); am_hal_gpio_int_polarity_bit_set(AM_BSP_GPIO_EM9304_INT, AM_HAL_GPIO_RISING); am_hal_gpio_int_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); am_hal_gpio_int_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); } void em9304_init(void) { // // Assert RESET to the Telink device. // am_hal_gpio_pin_config(AM_BSP_GPIO_EM9304_RESET, AM_HAL_GPIO_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_EM9304_RESET); // // Setup SPI interface for EM9304 // configure_em9304_pins(); am_devices_em9304_spi_init(AM_BSP_EM9304_IOM, &g_sEm9304IOMConfigSPI); // // Delay for 20ms to make sure the em device gets ready for commands. // am_util_delay_ms(5); // // Enable the IOM and GPIO interrupt handlers. // am_hal_gpio_out_bit_set(AM_BSP_GPIO_EM9304_RESET); am_util_delay_ms(20); } // hal_cpu.h implementation #include "hal_cpu.h" void hal_cpu_disable_irqs(void){ am_hal_interrupt_master_disable(); } void hal_cpu_enable_irqs(void){ am_hal_interrupt_master_enable(); } void hal_cpu_enable_irqs_and_sleep(void){ am_hal_interrupt_master_enable(); __asm__("wfe"); // go to sleep if event flag isn't set. if set, just clear it. IRQs set event flag } // hal_time_ms.h #include "hal_time_ms.h" uint32_t hal_time_ms(void){ return am_hal_stimer_counter_get(); } /** * Use USART_CONSOLE as a console. * This is a syscall for newlib * @param file * @param ptr * @param len * @return */ #include #include #include int _write(int file, char *ptr, int len); int _write(int file, char *ptr, int len){ #if 1 uint8_t cr = '\r'; int i; if (file == STDOUT_FILENO || file == STDERR_FILENO) { for (i = 0; i < len; i++) { if (ptr[i] == '\n') { am_hal_uart_char_transmit_polled( AM_BSP_UART_PRINT_INST, cr ); } am_hal_uart_char_transmit_polled( AM_BSP_UART_PRINT_INST, ptr[i]); } return i; } errno = EIO; return -1; #else return len; #endif } int _read(int file, char * ptr, int len){ (void)file; (void)ptr; (void)len; return -1; } int _close(int file){ (void)file; return -1; } int _isatty(int file){ (void)file; return -1; } int _lseek(int file){ (void)file; return -1; } int _fstat(int file){ (void)file; return -1; } void * _sbrk(intptr_t increment){ return (void*) -1; } // hal_em9304_spi.h #include "hal_em9304_spi.h" static void (*hal_em9304_spi_transfer_done_callback)(void); static void (*hal_em9304_spi_ready_callback)(void); #if (0 == AM_BSP_EM9304_IOM) void am_iomaster0_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(0, false); am_hal_iom_int_clear(0, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(0, ui32IntStatus); } #endif #if defined(AM_PART_APOLLO2) #if (5 == AM_BSP_EM9304_IOM) void am_iomaster5_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(5, false); am_hal_iom_int_clear(5, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(5, ui32IntStatus); } #endif #endif void am_gpio_isr(void) { uint64_t ui64Status; // // Check and clear the GPIO interrupt status // ui64Status = am_hal_gpio_int_status_get(true); am_hal_gpio_int_clear(ui64Status); // // Check to see if this was a wakeup event from the BLE radio. // if ( ui64Status & AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT) ) { if (hal_em9304_spi_ready_callback){ (*hal_em9304_spi_ready_callback)(); } } } void hal_em9304_spi_enable_ready_interrupt(void){ am_hal_gpio_int_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); } void hal_em9304_spi_disable_ready_interrupt(void){ am_hal_gpio_int_disable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); } void hal_em9304_spi_set_ready_callback(void (*done)(void)){ hal_em9304_spi_ready_callback = done; } int hal_em9304_spi_get_ready(void){ return am_hal_gpio_input_read() & AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT); } void hal_em9304_spi_init(void){ hal_em9304_spi_disable_ready_interrupt(); } void hal_em9304_spi_deinit(void){ hal_em9304_spi_disable_ready_interrupt(); } void hal_em9304_spi_set_transfer_done_callback(void (*done)(void)){ hal_em9304_spi_transfer_done_callback = done; } void hal_em9304_spi_set_chip_select(int enable){ if (enable){ am_hal_gpio_out_bit_clear(AM_BSP_GPIO_EM9304_CS); } else { am_hal_gpio_out_bit_set(AM_BSP_GPIO_EM9304_CS); } } void hal_em9304_spi_transceive(const uint8_t * tx_data, uint8_t * rx_data, uint16_t len){ // TODO: handle tx_data/rx_data not aligned // TODO: support non-blocking full duplex uint32_t ui32ChipSelect = 0; // TODO: Use Full Duplex with Interrupt callback // NOTE: Full Duplex only supported on Apollo2 // NOTE: Enabling Full Duplex causes am_hal_iom_spi_write_nq to block (as bytes ready returns number of bytes written) // AM_REGn(IOMSTR, ui32Module, CFG) |= AM_REG_IOMSTR_CFG_FULLDUP(1); am_hal_iom_spi_fullduplex_nq(AM_BSP_EM9304_IOM, ui32ChipSelect, (uint32_t *) tx_data, (uint32_t *) rx_data, len, AM_HAL_IOM_RAW); (*hal_em9304_spi_transfer_done_callback)(); return; } void hal_em9304_spi_transmit(const uint8_t * tx_data, uint16_t len){ // TODO: handle tx_data/rx_data not aligned uint32_t ui32ChipSelect = 0; am_hal_iom_spi_write_nb(AM_BSP_EM9304_IOM, ui32ChipSelect, (uint32_t *) tx_data, len, AM_HAL_IOM_RAW, hal_em9304_spi_transfer_done_callback); } void hal_em9304_spi_receive(uint8_t * rx_data, uint16_t len){ // TODO: handle tx_data/rx_data not aligned // TODO: support non-blocking full duplex uint32_t ui32ChipSelect = 0; am_hal_iom_spi_read_nb(AM_BSP_EM9304_IOM, ui32ChipSelect, (uint32_t *) rx_data, len, AM_HAL_IOM_RAW, hal_em9304_spi_transfer_done_callback); } int hal_em9304_spi_get_fullduplex_support(void){ return 0; } //***************************************************************************** // // Main // //***************************************************************************** // EM 9304 SPI Master HCI Implementation const uint8_t hci_reset_2[] = { 0x01, 0x03, 0x0c, 0x00 }; #include "btstack_event.h" #include "btstack_memory.h" #include "btstack_run_loop.h" #include "btstack_run_loop_embedded.h" #include "hci_dump.h" static btstack_packet_callback_registration_t hci_event_callback_registration; int btstack_main(int argc, char ** argv); // main.c static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(size); UNUSED(channel); if (packet_type != HCI_EVENT_PACKET) return; switch(hci_event_packet_get_type(packet)){ case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; printf("BTstack up and running.\n"); break; default: break; } } int main(void) { // // Set the clock frequency. // am_hal_clkgen_sysclk_select(AM_HAL_CLKGEN_SYSCLK_MAX); // // Set the default cache configuration // am_hal_cachectrl_enable(&am_hal_cachectrl_defaults); // // Configure the board for low power operation. // am_bsp_low_power_init(); // // Initialize the printf interface for UART output. // am_util_stdio_printf_init((am_util_stdio_print_char_t)am_bsp_uart_string_print); // // Configure and enable the UART. // uart_init(AM_BSP_UART_PRINT_INST); // // Reboot and configure em9304. // em9304_init(); am_hal_interrupt_enable(AM_HAL_INTERRUPT_GPIO); // // Enable IOM SPI interrupts. // am_hal_iom_int_clear(AM_BSP_EM9304_IOM, AM_HAL_IOM_INT_CMDCMP | AM_HAL_IOM_INT_THR); am_hal_iom_int_enable(AM_BSP_EM9304_IOM, AM_HAL_IOM_INT_CMDCMP | AM_HAL_IOM_INT_THR); #if (0 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER0); #elif (5 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER5); #endif // Start System Timer (only Apollo 2) am_hal_stimer_config(AM_HAL_STIMER_LFRC_1KHZ); am_hal_stimer_counter_clear(); // start with BTstack init - especially configure HCI Transport btstack_memory_init(); btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); // init HCI hci_init(hci_transport_em9304_spi_instance(btstack_em9304_spi_embedded_instance()), NULL); // hci_dump_open( NULL, HCI_DUMP_STDOUT ); // inform about BTstack state hci_event_callback_registration.callback = &packet_handler; hci_add_event_handler(&hci_event_callback_registration); // hand over control to btstack_main().. // turn on! // hci_power_control(HCI_POWER_ON); btstack_main(0, NULL); btstack_run_loop_execute(); }