CMSIS-ARM_DRIVER架构下的SPI通信-1数据“收发”
·
这一切的一切都是因为这六行代码引起的惨案
void Spi_transferU16Data(STR_SpiStruct *pxSpi, STR_GpioStruct *pCs, uint16_t *pDataOut, uint16_t len, uint16_t *pDataIn)
{
while(pxSpi->pxDriver->GetStatus().busy == 1) {}
pxSpi->pxDriver->Transfer(pDataOut, pDataIn, len);
while(pxSpi->pxDriver->GetStatus().busy == 1) {}
}
这是一个SPI U16数据收发的函数,pDataOut是数据发送缓存区,pDataIn是数据接收缓存区。看似非常简单,但我想GO进去看Transfer底层的代码是如何写的,于是就有了今天的文章。
首先,我查了F407系列的SPI部分的中文手册,查到了比较有用的SPI通信框图以及收发数据的框图,如下。
SPI通信框图:

全双工发送和接收过程:(SPE位负责使能和关闭SPI)

然后,又在工程文档中,FIND ALL 了很长时间,找到了Transfer的原型函数。但是发现,数据处理似乎并不在这个函数当中,而是一个以 RXNEIE 接收中断函数中去处理的数据信息。于是乎,我紧接着又FIND ALL 出来了SPI的中断服务函数,都贴到下面了。
官方"收发"代码例程:
static int32_t SPI_Transfer (const void *data_out, void *data_in, uint32_t num, const SPI_RESOURCES *spi) {
if ((data_out == NULL) || (data_in == NULL) || (num == 0U)) { return ARM_DRIVER_ERROR_PARAMETER; }
if ((spi->info->state & SPI_CONFIGURED) == 0U) { return ARM_DRIVER_ERROR; }
if ( spi->info->status.busy) { return ARM_DRIVER_ERROR_BUSY; }
// Check if receive and transmit pins available
if ((spi->io.miso == NULL) || (spi->io.mosi == NULL)) {
return ARM_DRIVER_ERROR;
}
// Update SPI statuses
spi->info->status.busy = 1U;
spi->info->status.data_lost = 0U;
spi->info->status.mode_fault = 0U;
// Save transfer info
spi->xfer->rx_buf = (uint8_t *)((uint32_t)data_in);
spi->xfer->tx_buf = (uint8_t *)((uint32_t)data_out);
spi->xfer->num = num;
spi->xfer->rx_cnt = 0U;
spi->xfer->tx_cnt = 0U;
#ifdef __SPI_DMA //启用DMA的话走下面这部分逻辑
if ((spi->rx_dma != NULL) || (spi->tx_dma != NULL)) {
// DMA mode
if (spi->rx_dma != NULL) {
// Prepare DMA to receive RX data
spi->rx_dma->hdma->Init.PeriphInc = DMA_PINC_DISABLE;
spi->rx_dma->hdma->Init.MemInc = DMA_MINC_ENABLE;
if (spi->reg->CR1 & SPI_CR1_DFF) {
// 16 - bit data frame
spi->rx_dma->hdma->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
spi->rx_dma->hdma->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
} else {
// 8 - bit data frame
spi->rx_dma->hdma->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
spi->rx_dma->hdma->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
}
// Initialize and start SPI RX DMA Stream
if (HAL_DMA_Init (spi->rx_dma->hdma) != HAL_OK) { return ARM_DRIVER_ERROR; }
if (HAL_DMA_Start_IT (spi->rx_dma->hdma, (uint32_t)(&spi->reg->DR), (uint32_t)spi->xfer->rx_buf, num) != HAL_OK) {
return ARM_DRIVER_ERROR;
}
// Disable Rx Not Empty interrupt, in DMA mode it is used only to allow data lost detection
// before reception with DMA is started
spi->reg->CR2 &= ~SPI_CR2_RXNEIE;
// RX Buffer DMA enable
spi->reg->CR2 |= SPI_CR2_RXDMAEN;
}
if (spi->tx_dma != NULL) {
// Prepare DMA to send TX data
spi->tx_dma->hdma->Init.PeriphInc = DMA_PINC_DISABLE;
spi->tx_dma->hdma->Init.MemInc = DMA_MINC_ENABLE;
if (spi->reg->CR1 & SPI_CR1_DFF) {
// 16 - bit data frame
spi->tx_dma->hdma->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
spi->tx_dma->hdma->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
} else {
// 8 - bit data frame
spi->tx_dma->hdma->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
spi->tx_dma->hdma->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
}
// Initialize and start SPI TX DMA Stream
if (HAL_DMA_Init (spi->tx_dma->hdma) != HAL_OK) { return ARM_DRIVER_ERROR; }
if (HAL_DMA_Start_IT (spi->tx_dma->hdma, (uint32_t)spi->xfer->tx_buf, (uint32_t)(&spi->reg->DR), num) != HAL_OK) {
return ARM_DRIVER_ERROR;
}
// TX Buffer DMA enable
spi->reg->CR2 |= SPI_CR2_TXDMAEN;
}
} else
#endif
{
// Interrupt mode
spi->reg->CR2 |= SPI_CR2_RXNEIE;//使能接收中断
SPI_TxData (spi->reg, spi->xfer);//输出第一个发送字节,启动SPI时钟移位
}
return ARM_DRIVER_OK;
}
这里我们没有启用DMA传输数据的话,比较核心的代码在最后几行:
spi->reg->CR2 |= SPI_CR2_RXNEIE;//使能接收中断
SPI_TxData (spi->reg, spi->xfer);//输出第一个发送字节

第一个发送数据写入到SPI_DR寄存器后,RXNE置1,此时满足RXNE = 1 且 RXNEIE = 1 ,所以就进入SPI的中断服务函数。
SPI中断服务函数:
void SPI_IRQHandler (const SPI_RESOURCES *spi)
{
uint8_t data_8bit;
uint16_t data_16bit;
uint32_t event, sr;
event = 0U;
// Save status register
sr = spi->reg->SR;
if ((sr & (SPI_SR_OVR | SPI_SR_UDR | SPI_SR_MODF)) != 0U) {
if ((sr & SPI_SR_OVR) != 0U) {
// Clear Overrun flag by reading data and status register
if ((spi->reg->CR1 & SPI_CR1_DFF) == 0U) {
// 8-bit data frame
data_8bit = *(volatile uint8_t *)(&spi->reg->DR);
if (spi->xfer->rx_cnt < spi->xfer->num) {
if (spi->xfer->rx_buf != NULL) {
*(spi->xfer->rx_buf++) = data_8bit;
}
}
} else {
// 16-bit data frame
data_16bit = *(volatile uint16_t *)(&spi->reg->DR);
if (spi->xfer->rx_cnt < spi->xfer->num) {
if (spi->xfer->rx_buf != NULL) {
*(spi->xfer->rx_buf++) = (uint8_t) data_16bit;
*(spi->xfer->rx_buf++) = (uint8_t)(data_16bit >> 8U);
}
}
}
spi->xfer->rx_cnt++;
sr = spi->reg->SR;
spi->info->status.data_lost = 1U;
event |= ARM_SPI_EVENT_DATA_LOST;
}
if ((sr & SPI_SR_UDR) != 0U) {
// Underrun flag is set
spi->info->status.data_lost = 1U;
event |= ARM_SPI_EVENT_DATA_LOST;
}
if ((sr & SPI_SR_MODF) != 0U) {
// Mode fault flag is set
spi->info->status.mode_fault = 1U;
// Write CR1 register to clear MODF flag
spi->reg->CR1 = spi->reg->CR1;
event |= ARM_SPI_EVENT_MODE_FAULT;
}
}
else {//正常收发分支
if (((sr & SPI_SR_RXNE) != 0U) && ((spi->reg->CR2 & SPI_CR2_RXNEIE) != 0U)) {
// RX buffer Not Empty
event = SPI_RxData (spi->reg, spi->xfer);
if (event == 0U) {
event = SPI_TxData (spi->reg, spi->xfer);
}
else {
if (event == ARM_SPI_EVENT_TRANSFER_COMPLETE) {
spi->info->status.busy = 0U;
}
}
if ((event & ARM_SPI_EVENT_DATA_LOST) != 0U) {
spi->info->status.data_lost = 1U;
}
}
}
// Send event
if ((event != 0U) && ((spi->info->cb_event != NULL))) {
spi->info->cb_event(event);
}
}
#ifdef __SPI_DMA_TX
static void SPI_TX_DMA_Complete(const SPI_RESOURCES *spi) {
if ((__HAL_DMA_GET_COUNTER(spi->tx_dma->hdma) != 0) && (spi->xfer->num != 0)) {
// TX DMA Complete caused by transfer abort
return;
}
spi->xfer->tx_cnt = spi->xfer->num;
}
#endif
中断服务函数正常收发分支如下:
else {
if (((sr & SPI_SR_RXNE) != 0U) && ((spi->reg->CR2 & SPI_CR2_RXNEIE) != 0U))
{
// RXNE=1 且接收中断使能,收到完整1字节
event = SPI_RxData (spi->reg, spi->xfer);
if (event == 0U) {
// 接收未完成,继续填充下一笔发送数据
event = SPI_TxData (spi->reg, spi->xfer);
}
else {
// SPI_RxData 返回传输完成事件:所有字节收发完毕
if (event == ARM_SPI_EVENT_TRANSFER_COMPLETE) {
spi->info->status.busy = 0U; // 核心:清除忙标志
}
}
if ((event & ARM_SPI_EVENT_DATA_LOST) != 0U) {
spi->info->status.data_lost = 1U;
}
}
}
核心执行顺序(中断触发一次完整流程)
SPI_RxData():读取 DR,存入 rx_buf,rx_cnt 自增;- 若
rx_cnt == num:返回ARM_SPI_EVENT_TRANSFER_COMPLETE; - 若没收完:返回
event=0。
- 若
- 没收完数据 → 调用
SPI_TxData()写下一个发送字节;- 当下一次 TXE 硬件置 1,会再次进入中断、再次走这套逻辑。
- 收完全部数据:
- event 为传输完成,执行
spi->info->status.busy = 0U; - 上层
while(GetStatus().busy)循环退出
- event 为传输完成,执行
总结:整套驱动触发逻辑以 RXNE 接收中断为驱动主线。
附上 TX和RX函数
static uint32_t SPI_TxData (SPI_TypeDef *reg, SPI_TRANSFER_INFO *xfer) {
uint32_t event;
uint16_t data_16bit;
uint8_t data_8bit;
if (xfer->tx_cnt < xfer->num) {
if ((reg->CR1 & SPI_CR1_DFF) == 0U) {
if (xfer->tx_buf != NULL) {
data_8bit = *(xfer->tx_buf++);
} else {
data_8bit = (uint8_t)xfer->def_val;
}
// Write data to data register
*(volatile uint8_t *)(®->DR) = data_8bit;
} else {
if (xfer->tx_buf != NULL) {
data_16bit = *(xfer->tx_buf++);
data_16bit |= *(xfer->tx_buf++) << 8U;
} else {
data_16bit = (uint16_t)xfer->def_val;
}
// Write data to data register
*(volatile uint16_t *)(®->DR) = data_16bit;
}
xfer->tx_cnt++;
event = 0U;
}
else {
// Unexpected transfer, data lost
event = ARM_SPI_EVENT_DATA_LOST;
}
return (event);
}
static uint32_t SPI_RxData (SPI_TypeDef *reg, SPI_TRANSFER_INFO *xfer) {
uint32_t event;
uint16_t data_16bit;
uint8_t data_8bit;
if (xfer->rx_cnt < xfer->num) {
if ((reg->CR1 & SPI_CR1_DFF) == 0U) {
// 8-bit data frame
data_8bit = *(volatile uint8_t *)(®->DR);
if (xfer->rx_buf != NULL) {
*(xfer->rx_buf++) = data_8bit;
}
} else {
// 16-bit data frame
data_16bit = *(volatile uint16_t *)(®->DR);
if (xfer->rx_buf != NULL) {
*(xfer->rx_buf++) = (uint8_t) data_16bit;
*(xfer->rx_buf++) = (uint8_t)(data_16bit >> 8U);
}
}
xfer->rx_cnt++;
if (xfer->rx_cnt == xfer->num) {
// Transfer completed
event = ARM_SPI_EVENT_TRANSFER_COMPLETE;
}
else {
event = 0U;
}
}
else {
// Unexpected transfer, data lost
// Read DR to flush it
*(volatile uint8_t *)(®->DR);
event = ARM_SPI_EVENT_DATA_LOST;
}
return (event);
}
更多推荐


所有评论(0)