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