I am currently working on nRF52DK and TCAN4550 using SPI communication. Regardless the SPI slave which I am using, I have some doubts with SPI Nordic library (I am using 17.0.2 SDK).
It I provided three ways to implement SPI communications:
- SPI Master driver
- SPI driver
- SPI HAL
I am using SPI Master Driver, even if it is based on legacy driver “SPI/SPIM peripheral driver”. Therefore, is it better to use SPI HAL?? I tried without success….
I have implemented my read/write functions using a non-blocking transfer function “nrf_drv_spi_transfer”. It works and I am able to read/write CAN frames by sending and receiving SPI commands.
My other doubt begin now, I have configured a GPIO HW interrupt in order to know when I receive a new CAN frame, and afterwards read using SPI library to get that frame. If I use a non-blocking transfer inside of a GPIO handler, my program breaks due to a handler inside of another handler… It is normal. I saw this other topic Is nrf_drv_spi_transfer interrupt safe? In which Nordic support recommended “SPI transaction manager library” but is a post 3 years old… so… I am not sure if it continues being the best answer.
You can find the related code below.
main.c
/** * @file main.c * @date 30 april 2021 * @brief Main file */ #include "nrf_drv_spi.h" #include "app_util_platform.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "boards.h" #include "app_error.h" #include <string.h> #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "nrf_drv_gpiote.h" #include "SPI.h" #include "tcan4x5x/TCAN4550.h" #include "EBM_TCAN4550.h" #ifdef ARDUINO_0_PIN #define CAN_INT ARDUINO_0_PIN #endif extern void TCAN4550_device_init(void); extern void send_CAN_frame(uint16_t ID, uint8_t DLC, uint8_t* data); extern void read_CAN_frame_due_interrupt(uint16_t* ID, uint8_t* DLC, uint8_t* data); static volatile bool frame_received; /** * @brief GPIO interrupt handler * @param pin interrupt number * @param action polarity */ void canIntHandler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { NRF_LOG_INFO("[main::canIntHandler] GPIO interrupt: %d.", pin); uint16_t ID; uint8_t DLC; uint8_t data[8]; read_CAN_frame_due_interrupt(&ID, &DLC, data); if (ID == 0x0AA) // Example of how you can do an action based off a received address { // Do something bsp_board_led_invert(BSP_BOARD_LED_1); } bsp_board_led_invert(BSP_BOARD_LED_1); } /** * @brief GPIO initial configuration */ void gpio_init(void) { ret_code_t err_code; err_code = nrf_drv_gpiote_init(); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true); in_config.pull = NRF_GPIO_PIN_PULLUP; err_code = nrf_drv_gpiote_in_init(CAN_INT, &in_config, canIntHandler); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_in_event_enable(CAN_INT, true); } /** * @brief Main function * @return value */ int main(void) { bsp_board_init(BSP_INIT_LEDS); APP_ERROR_CHECK(NRF_LOG_INIT(NULL)); NRF_LOG_DEFAULT_BACKENDS_INIT(); spi_init(); gpio_init(); TCAN4550_device_init(); uint8_t data[8] = {0,1,2,3,4,5,6,7}; TCAN4x5x_MCAN_ClearInterruptsAll(); //testTCAN4550(); while (1) { send_CAN_frame(111, 8, data); bsp_board_led_invert(BSP_BOARD_LED_0); nrf_delay_ms(200); NRF_LOG_FLUSH(); } }
#define WAIT_FOR_TRANSMIT while (!spi_xfer_done) { __WFE(); } void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void * p_context) { spi_xfer_done = true; } void spi_init(void) { nrf_drv_spi_config_t spi_config; spi_config.sck_pin = SPI_SCK_PIN, \ spi_config.mosi_pin = SPI_MOSI_PIN, \ spi_config.miso_pin = SPI_MISO_PIN, \ spi_config.ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \ spi_config.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \ spi_config.orc = 0xFF, \ spi_config.frequency = NRF_DRV_SPI_FREQ_1M, \ spi_config.mode = NRF_DRV_SPI_MODE_0, // It is the only mode which is supported by TCAN4550 spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \ nrf_drv_spi_init(&spi0, &spi_config, spi_event_handler, NULL); //nrf_drv_spi_init(&spi0, &spi_config, NULL, NULL); nrf_gpio_cfg_output(SPI_SS_PIN); } void spi_transfer(uint8_t *tx_data, unsigned tx_len, uint8_t *rx_data, unsigned rx_len) { spi_xfer_done = false; memset(rx_data, 0, m_length); memset(m_rx_buf, 0, m_length); ret_code_t ret = nrf_drv_spi_transfer(&spi0, tx_data, tx_len, m_rx_buf, rx_len); WAIT_FOR_TRANSMIT if(ret != NRF_SUCCESS) { NRF_LOG_INFO("[SPI::spi_transfer] Transfer fail."); } memcpy(rx_data, m_rx_buf,8); }
Which is the best way to manage the SPI transfer asynchronously? I have to configure the "nrf_drv_spi_transfer" as blocking transfer?
Many thanks in advanced and regards,
Javier