//***************************************************************************** // //! @file am_devices_em9304.c //! //! @brief Support functions for the EM Micro EM9304 BTLE radio. // //***************************************************************************** //***************************************************************************** // // Copyright (c) 2020, Ambiq Micro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // Third party software included in this distribution is subject to the // additional license terms as defined in the /docs/licenses directory. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // This is part of revision 2.4.2 of the AmbiqSuite Development Package. // //***************************************************************************** #include #include #include #include "am_bsp.h" #include "am_devices_em9304.h" #include "am_util_debug.h" #include "am_util_stdio.h" #include "am_util_delay.h" //***************************************************************************** // // Macro definitions // //***************************************************************************** #ifndef HCI_APOLLO_USE_CHIPID_FOR_MAC #define HCI_APOLLO_USE_CHIPID_FOR_MAC true #endif #define USE_IOM_NONBLOCKING // SPI header byte #define EM9304_SPI_HEADER_TX 0x42 #define EM9304_SPI_HEADER_RX 0x81 #define EM9304_STS1_READY_VALUE 0xC0 // EM9304 timeout value. #define AM_DEVICES_EM9304_TIMEOUT 12000 // Assume worst case cold start counter (1.2 sec) // Length of RX buffer #define EM9304_BUFFER_SIZE 256 #if AM_APOLLO3_GPIO // Select the EM9304 -> Set the CSN to low level #define EM9304_SPISLAVESELECT() am_hal_gpio_state_write(AM_BSP_GPIO_EM9304_CS, AM_HAL_GPIO_OUTPUT_CLEAR); // Deselect the EM9304 -> Set the CSN to high level #define EM9304_SPISLAVEDESELECT() am_hal_gpio_state_write(AM_BSP_GPIO_EM9304_CS, AM_HAL_GPIO_OUTPUT_SET); // Indicates the EM9304 RDY pin state #define EM9304_RDY_INT() (am_hal_gpio_input_read(AM_BSP_GPIO_EM9304_INT)) #else // AM_APOLLO3_GPIO // Select the EM9304 -> Set the CSN to low level #define EM9304_SPISLAVESELECT() am_hal_gpio_out_bit_clear(AM_BSP_GPIO_EM9304_CS) // Deselect the EM9304 -> Set the CSN to high level #define EM9304_SPISLAVEDESELECT() am_hal_gpio_out_bit_set(AM_BSP_GPIO_EM9304_CS) // Indicates the EM9304 RDY pin state #define EM9304_RDY_INT() (am_hal_gpio_input_read() & AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)) #endif // AM_APOLLO3_GPIO #if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2) // SPI lock when a transmission is in progress static uint8_t spiTxInProgress; uint16_t hciTrSerialRxIncoming(uint8_t *pBuf, uint16_t len); static volatile bool gIomDone = false; //***************************************************************************** // // 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, }; //***************************************************************************** // // EM9304 Device Configuration // //***************************************************************************** const am_devices_em9304_t g_sEm9304 = { .ui32IOMModule = AM_BSP_EM9304_IOM, .ui32IOMChipSelect = AM_BSP_GPIO_EM9304_CS, .ui32DREADY = AM_BSP_GPIO_EM9304_INT }; #if defined(USE_IOM_NONBLOCKING) //***************************************************************************** // // IOM write complete callback // //***************************************************************************** static void iom_write_complete(void) { gIomDone = true; } #endif //***************************************************************************** // // Interrupt handler for IOM0 // //***************************************************************************** #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 //***************************************************************************** // // Interrupt handler for IOM0 // //***************************************************************************** #if (1 == AM_BSP_EM9304_IOM) void am_iomaster1_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(1, false); am_hal_iom_int_clear(1, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(1, ui32IntStatus); } #endif #if defined(AM_PART_APOLLO2) //***************************************************************************** // // Interrupt handler for IOM5 // //***************************************************************************** #if (2 == AM_BSP_EM9304_IOM) void am_iomaster2_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(2, false); am_hal_iom_int_clear(2, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(2, ui32IntStatus); } #endif //***************************************************************************** // // Interrupt handler for IOM5 // //***************************************************************************** #if (3 == AM_BSP_EM9304_IOM) void am_iomaster3_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(3, false); am_hal_iom_int_clear(3, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(3, ui32IntStatus); } #endif //***************************************************************************** // // Interrupt handler for IOM5 // //***************************************************************************** #if (4 == AM_BSP_EM9304_IOM) void am_iomaster4_isr(void) { uint32_t ui32IntStatus; // // Read and clear the interrupt status. // ui32IntStatus = am_hal_iom_int_status_get(4, false); am_hal_iom_int_clear(4, ui32IntStatus); // // Service FIFO interrupts as necessary, and call IOM callbacks as // transfers are completed. // am_hal_iom_int_service(4, ui32IntStatus); } #endif //***************************************************************************** // // Interrupt handler for IOM5 // //***************************************************************************** #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 //***************************************************************************** // // Checks to see if this processor is a Rev B2 device. // //***************************************************************************** static bool isRevB2(void) { // // Check to make sure the major rev is B and the minor rev is zero. // if ( (AM_REG(MCUCTRL, CHIPREV) & 0xFF) == (AM_REG_MCUCTRL_CHIPREV_REVMAJ_B | AM_REG_MCUCTRL_CHIPREV_REVMIN_REV2) ) { return true; } else { return false; } } #endif //***************************************************************************** // //! @brief Start a transmit transaction to the EM9304. //! //! @param psDevice is a pointer to a device structure describing the EM9304. //! //! This function handles the initial handshake of asserting the SPI CE and //! then waiting for the RDY signal from the EM. It then executes a transmit //! command to the EM9304 and receives the number of bytes that can be accepted. //! //! @return Number of bytes that can be written to EM9304. // //***************************************************************************** uint8_t am_devices_em9304_tx_starts(const am_devices_em9304_t *psDevice) { // Indicates that a SPI transfer is in progress spiTxInProgress = 1; am_hal_iom_buffer(2) sCommand; am_hal_iom_buffer(2) sStas; sCommand.bytes[0] = EM9304_SPI_HEADER_TX; sCommand.bytes[1] = 0x0; // Select the EM9304 EM9304_SPISLAVESELECT(); // Wait EM9304 RDY signal or timeout for (uint32_t i = 0; i < AM_DEVICES_EM9304_TIMEOUT; i++) { if (EM9304_RDY_INT()) { break; } am_util_delay_us(100); } if (!EM9304_RDY_INT()) { return 0; } #if defined(AM_PART_APOLLO2) // // Full-Duplex operation is only supported for Apollo2 B2 Silicon. // if (isRevB2()) { // // Write to the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_fullduplex(psDevice->ui32IOMModule, 0, sCommand.words, sStas.words, 2, AM_HAL_IOM_RAW)) { return 0; } // // Check that the EM9304 is ready. // if (sStas.bytes[0] != EM9304_STS1_READY_VALUE) { // Error EM9304_SPISLAVEDESELECT(); am_util_debug_printf("HCI TX Failed to starts\n"); am_util_debug_printf("%d, %d\n", sStas.bytes[0], sStas.bytes[1]); return 0; } return sStas.bytes[1]; } else #endif { // // Write to the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_write(psDevice->ui32IOMModule, 0, sCommand.words, 1, AM_HAL_IOM_RAW)) { return 0; } // // Read from the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_read(psDevice->ui32IOMModule, 0, sStas.words, 1, AM_HAL_IOM_RAW)) { return 0; } return sStas.bytes[0]; } } //***************************************************************************** // //! @brief End a transmit transaction to the EM9304. //! //! @param None. //! //! This function handles the completion of a transmit to the EM9304. After //! the IOM has completed the transaction, the CE is deasserted and the RDY //! interrupt is renabled. //! //! @return None. // //***************************************************************************** void am_devices_em9304_tx_ends(void) { am_hal_iom_poll_complete(g_sEm9304.ui32IOMModule); // Deselect the EM9304 EM9304_SPISLAVEDESELECT(); // Indicates that the SPI transfer is finished spiTxInProgress = 0; } //***************************************************************************** // //! @brief EM9304 write function. //! //! @param psDevice is a pointer to a device structure describing the EM9304. //! @param type is the HCI command. //! @param pui8Values is the HCI packet to send. //! @param ui32NumBytes is the number of bytes to send (including HCI command). //! //! This function perform a write transaction to the EM9304. //! //! @return None. // //***************************************************************************** void am_devices_em9304_block_write(const am_devices_em9304_t *psDevice, uint8_t type, uint8_t *pui8Values, uint32_t ui32NumBytes) { am_hal_iom_buffer(EM9304_BUFFER_SIZE) sData; uint8_t em9304BufSize = 0; uint8_t hci_type_sent = 0; if ((ui32NumBytes + 1) <= EM9304_BUFFER_SIZE) { for ( uint32_t i = 0; i < ui32NumBytes; ) { em9304BufSize = am_devices_em9304_tx_starts(psDevice); uint32_t len = (em9304BufSize < (ui32NumBytes - i)) ? em9304BufSize : (ui32NumBytes - i); if (len > 0) // check again if there is room to send more data { if (hci_type_sent == 0) { sData.bytes[0] = type; memcpy(&(sData.bytes[1]), pui8Values, len - 1); i += len -1; hci_type_sent = 1; } else { memcpy(&(sData.bytes[0]), pui8Values + i, len); i += len; } #if defined(USE_IOM_NONBLOCKING) gIomDone = false; if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_write_nb(psDevice->ui32IOMModule, 0, sData.words, len, AM_HAL_IOM_RAW, iom_write_complete)) { return; } for (uint32_t i = 0; i < AM_DEVICES_EM9304_TIMEOUT; i++) { if (!gIomDone) { // // Sleep while waiting for the IOM transaction to finish. // am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); } if (gIomDone) { break; } am_util_delay_us(25); } #else if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_write(psDevice->ui32IOMModule, 0, sData.words, len, AM_HAL_IOM_RAW)) { return; } #endif } am_devices_em9304_tx_ends(); } } else { am_util_debug_printf("HCI TX Error (STATUS ERROR) Packet Too Large\n"); } } //***************************************************************************** // //! @brief EM9304 read function. //! //! @param psDevice is a pointer to a device structure describing the EM9304. //! @param pui8Values is the buffer to receive the HCI packet. //! @param ui32NumBytes is the number of bytes to send (including HCI command). //! //! This function a read transaction from the EM9304. //! //! @return Number of bytes read. // //***************************************************************************** uint32_t am_devices_em9304_block_read(const am_devices_em9304_t *psDevice, uint32_t *pui32Values, uint32_t ui32NumBytes) { am_hal_iom_buffer(2) sCommand; am_hal_iom_buffer(2) sStas; uint8_t ui8RxBytes; uint8_t spiRxTotalBytesCount = 0; sCommand.bytes[0] = EM9304_SPI_HEADER_RX; sCommand.bytes[1] = 0x0; // // Check if the SPI is free // if ( spiTxInProgress ) { // // TX in progress -> Ignore RDY interrupt // am_util_debug_printf("HCI TX in progress\n"); return 0; } // // Check if they are still data to read // if ( !EM9304_RDY_INT() ) { // No data //am_util_debug_printf("HCI No data\n"); return 0; } // // Select the EM9304 // EM9304_SPISLAVESELECT(); #if defined(AM_PART_APOLLO2) // // Full-Duplex operation is only supported for Apollo2 B2 Silicon. // if (isRevB2()) { // // Write to the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_fullduplex(psDevice->ui32IOMModule, 0, sCommand.words, sStas.words, 2, AM_HAL_IOM_RAW)) { return 0; } // // Check that the EM9304 is ready. // if ( sStas.bytes[0] != EM9304_STS1_READY_VALUE ) { // Error EM9304_SPISLAVEDESELECT(); am_util_debug_printf("HCI RX Error (STATUS ERROR) EM9304 Not Ready\n"); am_util_debug_printf("%d, %d\n", sStas.bytes[0], sStas.bytes[1]); return 0; } // // Set the number of bytes to receive. // ui8RxBytes = sStas.bytes[1]; } else #endif { // // Write to the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_write(psDevice->ui32IOMModule, 0, sCommand.words, 1, AM_HAL_IOM_RAW)) { return 0; } // // Read from the IOM. // if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_read(psDevice->ui32IOMModule, 0, sStas.words, 1, AM_HAL_IOM_RAW)) { return 0; } // // Set the number of bytes to receive. // ui8RxBytes = sStas.bytes[0]; } while ( EM9304_RDY_INT() && (spiRxTotalBytesCount < ui8RxBytes) && (ui8RxBytes != 0)) { uint32_t len = 1; if ( (ui8RxBytes - spiRxTotalBytesCount) >= EM9304_BUFFER_SIZE ) { // // Error. Packet too large. // am_util_debug_printf("HCI RX Error (STATUS ERROR) Packet Too Large\n"); am_util_debug_printf("%d, %d\n", sStas.bytes[0], sStas.bytes[1]); return 0; } else { len = ui8RxBytes; } #if defined(USE_IOM_NONBLOCKING) gIomDone = false; if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_read_nb(psDevice->ui32IOMModule, 0, pui32Values, len, AM_HAL_IOM_RAW, iom_write_complete)) { return 0; } for (uint32_t i = 0; i < AM_DEVICES_EM9304_TIMEOUT; i++) { if (!gIomDone) { // // Sleep while waiting for the IOM transaction to finish. // am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); } if (gIomDone) { break; } am_util_delay_us(25); } #else if (AM_HAL_IOM_SUCCESS != am_hal_iom_spi_read(psDevice->ui32IOMModule, 0, pui32Values, len, AM_HAL_IOM_RAW)) { return 0; } #endif spiRxTotalBytesCount = len; } // Deselect the EM9304 EM9304_SPISLAVEDESELECT(); return spiRxTotalBytesCount; } //***************************************************************************** // //! @brief EM9304 SPI/IOM initialization function. //! //! @param ui32Module is the IOM module to be used for EM9304. //! @param psIomConfig is the configuration information for the IOM. //! //! This function initializes the IOM for operation with the EM9304. //! //! @return None. // //***************************************************************************** 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); #if defined(USE_IOM_NONBLOCKING) // // Enable interrupts. // am_hal_iom_int_clear(ui32Module, AM_HAL_IOM_INT_CMDCMP | AM_HAL_IOM_INT_THR); am_hal_iom_int_enable(ui32Module, AM_HAL_IOM_INT_CMDCMP | AM_HAL_IOM_INT_THR); #if AM_CMSIS_REGS #if AM_BSP_EM9304_IOM > 5 #error Invalid AM_BSP_EM9304_IOM #endif NVIC_EnableIRQ(IOMSTR0_IRQn + AM_BSP_EM9404_IOM); #else // AM_CMSIS_REGS #if (0 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER0); #elif (1 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER1); #elif (2 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER2); #elif (3 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER3); #elif (4 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER4); #elif (5 == AM_BSP_EM9304_IOM) am_hal_interrupt_enable(AM_HAL_INTERRUPT_IOMASTER5); #endif #endif // AM_CMSIS_REGS // // Reset the IOM Done flag. // gIomDone = false; #endif } //***************************************************************************** // //! @brief EM9304 SPI/IOM wakeup function. //! //! @param ui32Module is the IOM module to be used for EM9304. //! //! This function restores the IOM operation after sleep. //! //! @return None. // //***************************************************************************** void am_devices_em9304_spi_awake(uint32_t ui32Module) { if ( AM_REGn(IOMSTR, ui32Module, CFG) & AM_REG_IOMSTR_CFG_IFCEN_M ) { return; } #if defined(AM_PART_APOLLO2) am_hal_iom_power_on_restore(ui32Module); #endif // // Setup the pins for SPI mode. // am_bsp_iom_spi_pins_enable(ui32Module); // Enable spi am_hal_iom_enable(ui32Module); } void am_devices_em9304_configure_spi_sleep(void) { // // Re-configure SPI pins during sleep // #if AM_APOLLO3_GPIO #if (0 == AM_BSP_EM9304_IOM) am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM0_SCK, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM0_MISO, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM0_MOSI, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_state_write(AM_BSP_GPIO_IOM0_SCK, AM_HAL_GPIO_OUTPUT_CLEAR); am_hal_gpio_state_write(AM_BSP_GPIO_IOM0_MISO, AM_HAL_GPIO_OUTPUT_CLEAR); am_hal_gpio_state_write(AM_BSP_GPIO_IOM0_MOSI, AM_HAL_GPIO_OUTPUT_CLEAR); #elif (5 == AM_BSP_EM9304_IOM) am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM5_SCK, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM5_MISO, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM5_MOSI, g_AM_HAL_PIN_OUTPUT); am_hal_gpio_state_write(AM_BSP_GPIO_IOM5_SCK, AM_HAL_GPIO_OUTPUT_CLEAR); am_hal_gpio_state_write(AM_BSP_GPIO_IOM5_MISO, AM_HAL_GPIO_OUTPUT_CLEAR); am_hal_gpio_state_write(AM_BSP_GPIO_IOM5_MOSI, AM_HAL_GPIO_OUTPUT_CLEAR); #endif #else // AM_APOLLO3_GPIO #if (0 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM0_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM0_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM0_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM0_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM0_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM0_MOSI); #elif (1 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM1_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM1_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM1_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM1_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM1_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM1_MOSI); #elif (2 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM2_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM2_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM2_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM2_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM2_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM2_MOSI); #elif (3 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM3_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM3_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM3_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM3_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM3_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM3_MOSI); #elif (4 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM4_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM4_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM4_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM4_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM4_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM4_MOSI); #elif (5 == AM_BSP_EM9304_IOM) am_hal_gpio_pin_config(AM_BSP_GPIO_IOM5_SCK, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM5_MISO, AM_HAL_PIN_OUTPUT); am_hal_gpio_pin_config(AM_BSP_GPIO_IOM5_MOSI, AM_HAL_PIN_OUTPUT); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM5_SCK); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM5_MISO); am_hal_gpio_out_bit_clear(AM_BSP_GPIO_IOM5_MOSI); #endif #endif // AM_APOLLO3_GPIO } //***************************************************************************** // //! @brief EM9304 SPI/IOM sleep function. //! //! @param ui32Module is the IOM module to be used for EM9304. //! //! This function prepares the IOM for sleep. //! //! @return None. // //***************************************************************************** void am_devices_em9304_spi_sleep(uint32_t ui32Module) { if ( !(AM_REGn(IOMSTR, ui32Module, CFG) & AM_REG_IOMSTR_CFG_IFCEN_M) ) { return; } am_hal_iom_disable(ui32Module); #if defined(AM_PART_APOLLO) am_bsp_iom_spi_pins_disable(ui32Module); #else am_hal_iom_power_off_save(ui32Module); am_devices_em9304_configure_spi_sleep(); #endif } //***************************************************************************** // //! @brief Configure EM9304 pins. //! //! @param None. //! //! This function initializes the GPIOs for communication with the EM9304. //! //! @return None. // //***************************************************************************** void am_devices_em9304_config_pins(void) { #if AM_APOLLO3_GPIO am_hal_gpio_pinconfig(AM_BSP_GPIO_EM9304_CS, g_AM_HAL_GPIO_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_EM9304_INT, g_AM_HAL_GPIO_INPUT); am_hal_gpio_state_write(AM_BSP_GPIO_EM9304_CS, AM_HAL_GPIO_OUTPUT_SET); // Note - interrupt polarity is handled by the pin configuration. AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT)); #else // AM_APOLLO3_GPIO 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)); #endif // AM_APOLLO3_GPIO } void am_devices_em9304_enable_interrupt(void) { #if AM_APOLLO3_GPIO AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_enable(AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT)); #else // AM_APOLLO3_GPIO am_hal_gpio_int_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); #endif // AM_APOLLO3_GPIO } void am_devices_em9304_disable_interrupt(void) { #if AM_APOLLO3_GPIO AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_disable(AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT)); #else // AM_APOLLO3_GPIO am_hal_gpio_int_disable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_EM9304_INT)); #endif // AM_APOLLO3_GPIO } #endif // defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2) #if (defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)) //***************************************************************************** // // Global variables. // //***************************************************************************** typedef struct { uint32_t ui32Module; void *pIomHandle; bool bOccupied; } am_devices_iom_em9304_t; am_devices_iom_em9304_t gAmEm9304[AM_DEVICES_EM9304_MAX_DEVICE_NUM]; //***************************************************************************** // // EM9304 Device Configuration // //***************************************************************************** const am_devices_em9304_t g_sEm9304 = { .ui32IOMModule = AM_BSP_EM9304_IOM, .ui32IOMChipSelect = AM_BSP_GPIO_EM9304_CS, .ui32DREADY = AM_BSP_GPIO_EM9304_INT }; am_hal_iom_config_t g_sIomEm9304Cfg = { .eInterfaceMode = AM_HAL_IOM_SPI_MODE, .ui32ClockFreq = AM_HAL_IOM_1MHZ, .ui32NBTxnBufLength = 0, .pNBTxnBuf = NULL, }; //***************************************************************************** // //! @brief Configure EM9304 pins. //! //! @param None. //! //! This function initializes the GPIOs for communication with the EM9304. //! //! @return None. // //***************************************************************************** void am_devices_em9304_config_pins(void) { #if AM_APOLLO3_GPIO am_hal_gpio_pinconfig(AM_BSP_GPIO_EM9304_CS, g_AM_HAL_GPIO_OUTPUT); am_hal_gpio_pinconfig(AM_BSP_GPIO_EM9304_INT, g_AM_HAL_GPIO_INPUT); am_hal_gpio_state_write(AM_BSP_GPIO_EM9304_CS, AM_HAL_GPIO_OUTPUT_SET); // Note - interrupt polarity is handled by the pin configuration. AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear( AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT)); am_hal_gpio_interrupt_enable(AM_HAL_GPIO_MASKBIT(pGpioIntMask, AM_BSP_GPIO_EM9304_INT)); #else // AM_APOLLO3_GPIO 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)); #endif // AM_APOLLO3_GPIO } //***************************************************************************** // //! @brief EM9304 SPI/IOM initialization function. //! //! @param ui32Module is the IOM module to be used for EM9304. //! @param psIomConfig is the configuration information for the IOM. //! //! This function initializes the IOM for operation with the EM9304. //! //! @return None. // //***************************************************************************** uint32_t am_devices_em9304_init(uint32_t ui32Module, am_devices_em9304_config_t *pDevConfig, void **ppHandle, void **ppIomHandle) { void *pIomHandle; am_hal_iom_config_t stIOMEM9304Settings; uint32_t ui32Index = 0; // Allocate a vacant device handle for ( ui32Index = 0; ui32Index < AM_DEVICES_EM9304_MAX_DEVICE_NUM; ui32Index++ ) { if ( gAmEm9304[ui32Index].bOccupied == false ) { break; } } if ( ui32Index == AM_DEVICES_EM9304_MAX_DEVICE_NUM ) { return AM_DEVICES_EM9304_STATUS_ERROR; } if ( (ui32Module > AM_REG_IOM_NUM_MODULES) || (pDevConfig == NULL) ) { return AM_DEVICES_EM9304_STATUS_ERROR; } // // Configure the IOM pins. // am_bsp_iom_pins_enable(ui32Module, AM_HAL_IOM_SPI_MODE); // // Configure the EM9304 pins // am_devices_em9304_config_pins(); // // Enable fault detection. // #if AM_APOLLO3_MCUCTRL am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_FAULT_CAPTURE_ENABLE, 0); #else // AM_APOLLO3_MCUCTRL am_hal_mcuctrl_fault_capture_enable(); #endif // AM_APOLLO3_MCUCTRL stIOMEM9304Settings = g_sIomEm9304Cfg; stIOMEM9304Settings.ui32NBTxnBufLength = pDevConfig->ui32NBTxnBufLength; stIOMEM9304Settings.pNBTxnBuf = pDevConfig->pNBTxnBuf; stIOMEM9304Settings.ui32ClockFreq = pDevConfig->ui32ClockFreq; // // Initialize the IOM instance. // if (am_hal_iom_initialize(ui32Module, &pIomHandle) || am_hal_iom_power_ctrl(pIomHandle, AM_HAL_SYSCTRL_WAKE, false) || am_hal_iom_configure(pIomHandle, &stIOMEM9304Settings) || am_hal_iom_enable(pIomHandle)) { return AM_DEVICES_EM9304_STATUS_ERROR; } gAmEm9304[ui32Index].bOccupied = true; gAmEm9304[ui32Index].ui32Module = ui32Module; *ppIomHandle = gAmEm9304[ui32Index].pIomHandle = pIomHandle; *ppHandle = (void *)&gAmEm9304[ui32Index]; // // Return the status. // return AM_DEVICES_EM9304_STATUS_SUCCESS; } //***************************************************************************** // //! @brief EM9304 SPI/IOM de-initialization function. //! //! @param pHandle - Device handle# //! //! @return status. // //***************************************************************************** uint32_t am_devices_em9304_term(void *pHandle) { am_devices_iom_em9304_t *pIom = (am_devices_iom_em9304_t *)pHandle; if ( pIom->ui32Module > AM_REG_IOM_NUM_MODULES ) { return AM_DEVICES_EM9304_STATUS_ERROR; } // Disable the pins am_bsp_iom_pins_disable(pIom->ui32Module, AM_HAL_IOM_SPI_MODE); // // Disable the IOM. // am_hal_iom_disable(pIom->pIomHandle); // // Disable power to and uninitialize the IOM instance. // am_hal_iom_power_ctrl(pIom->pIomHandle, AM_HAL_SYSCTRL_DEEPSLEEP, true); am_hal_iom_uninitialize(pIom->pIomHandle); // Free this device handle pIom->bOccupied = false; // // Return the status. // return AM_DEVICES_EM9304_STATUS_SUCCESS; } #endif // defined(AM_PART_APOLLO3)