btstack/port/apollo2-em9304/btstack_port.c
2017-12-20 23:04:07 +01:00

535 lines
13 KiB
C

//
// 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 <stdio.h>
#include <unistd.h>
#include <errno.h>
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();
}