一、前言
Nordic的SPI串行外设主要分成两类,SPIM
(基于EasyDMA的主机) SPIS
(基于EasyDMA的从机)
ps:如有错误,请留言指正,谢谢
二、工作原理
功能特点
- 四种模式(模式0 - 模式3关联到不同的极性与相位)
- EasyDMA进行RAM数据与寄存器的传输
- 可映射到任意的GPIO引脚
原理框图
四种工作模式
数据通信
如下图
- SPI通信通过
START
任务进行触发,最终通过STOP
结束任务,通信过程中会产生相关事件- 当指定
TXD.MAXCNT
寄存器中TXD缓存发送完成后会生成ENDTX
事件- 当指定
RXD.MAXCNT
寄存器中RXD缓存接收完成后生成ENDRX
事件STOP
停止任务将会触发STOPPED
事件- 当SPI被停止时,数据未发完货未接收完成,也会触发对应的事件
ENDTX
ENDRX
三、寄存器
- SHORTS:
END_START
位写1使能循环模式,写0禁用循环模式;(发送接收结束事件后自动触发START
任务) - INTENSET:中断使能寄存器,可配置
STOPPED
ENDRX
END
ENDTX
STARTED
事件中断,写1有效 - INTENCLR:中断禁用寄存器,可配置
STOPPED
ENDRX
END
ENDTX
STARTED
事件中断,写1有效 - SPIM:SPI外设使能寄存器
- PLSEL.SCK:关联SCK引脚,低4位有效
- PLSEL.MOSI:关联MOSI引脚,低4位有效
- PLSE.MISO:关联MISO引脚,低4位有效
- FREQUENCY:SPI通信速率选择
125k
250k
500k
1M
2M
4M
8M
- RXD.PTR:接收数据寄存器,指向RXD接收缓存
- RXD.MAXCNT:接收数据缓存区最大长度
- RXD.AMOUNT:最近一次数据通信的接收传输字节长度
- RXD.LIST:EasyDMA列表类型,写1使用arraylist接收缓存,触发
START
任务时无需更新PTR数据指针寄存器,类似DMA串口中双缓存循环读取 - TXD.PTR:发送数据寄存器,指向TXD发送缓存
- TXD.MAXCNT:需要发送数据缓存的最大长度
- TXD.AMOUNT:最近一次数据通信的发送传输字节长度
- TXD.LIST:EasyDMA列表类型,写1使用arraylist发送缓存,触发
START
任务时无需更新PTR数据指针寄存器,类似DMA串口中双缓存循环发送 - CONFIG:配置寄存器,主要进行配置发送高位在前还是低位在前,通信的极性与相位
四、相关接口
SPIM的驱动目录在modules\nrfx\drivers\src\nrfx_spim.c中
- nrfx_spim_init:初始化spim,形参
nrfx_spim_config_t p_config
传入引脚的映射关系与SPI的参数(频率、工作模式、高低位传输);函数指针nrfx_spim_evt_handler_t handler
用于定义事件回调(数据的发送与接收)
nrfx_err_t nrfx_spim_init(nrfx_spim_t const * const p_instance,
nrfx_spim_config_t const * p_config,
nrfx_spim_evt_handler_t handler,
void * p_context)
- nrfx_spim_uninit:禁用spim外设,系统进入低功耗的时候可以调用
void nrfx_spim_uninit(nrfx_spim_t const * const p_instance)
- nrfx_spim_xfer:数据传输接口,形参
nrfx_spim_xfer_desc_t const * p_xfer_desc
定义了发送TX与接收RX的缓存与长度
nrfx_err_t nrfx_spim_xfer(nrfx_spim_t const * const p_instance,
nrfx_spim_xfer_desc_t const * p_xfer_desc,
uint32_t flags)
五、代码实例
打开工程目录 examples\peripheral\spi,选择pca10040
贴上例程代码,更改宏定义NRF_DRV_SPI_DEFAULT_CONFIG
更新SPIM的配置,填充m_tx_buf
来更新发送的数据,m_rx_buf
为接收到的数据
/*
* 默认的SPIM的参数
*/
#define NRF_DRV_SPI_DEFAULT_CONFIG \
{ \
.sck_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.mosi_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.miso_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \
.orc = 0xFF, \
.frequency = NRF_DRV_SPI_FREQ_4M, \
.mode = NRF_DRV_SPI_MODE_0, \
.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \
}
int main(void)
{
bsp_board_init(BSP_INIT_LEDS); //初始化LED
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG; //缺省参数
spi_config.ss_pin = SPI_SS_PIN; //关联SPI引脚
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL)); //执行初始化
NRF_LOG_INFO("SPI example started.");
while (1)
{
// Reset rx buffer and transfer done flag
memset(m_rx_buf, 0, m_length); //清缓存
spi_xfer_done = false; //清状态
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, m_length, m_rx_buf, m_length)); //开始传输
while (!spi_xfer_done) //等待传输完成
{
__WFE();
}
NRF_LOG_FLUSH();
bsp_board_led_invert(BSP_BOARD_LED_0); //指示灯
nrf_delay_ms(200);
}
}