STM32 HAL库SPI+DMA接收数据的配置和使用方法
- 材料
材料
- STM32F767
- stm32CodeMX
第一次使用HAL库做SPI+DMA的接收实验,一开始做的时候网上没有多少资料,踩了一些坑,也是第一次写博客,分享一下自己的经历,让网友少踩一些坑。
在使用SPI+DMA的时候,由于SPI协议的特性,主机(stm32)需要产生SCK并且同时接收和发送数据,所以配置DMA的时候,不能只配置SPI的接收DMA,需要发送和接收都一起设置。我一开始做的时候,就是只配置了接收的DMA,调试了好久都不能成功,起初还以为是HAL库的问题,到处加一些什么读写寄存器,清楚中断标志之类的。后来我又同时配置了发送和接收的DMA才成功。
先上代码(代码是Stm32CodeMx生产的,步骤就不列出来的,也可以自己写,都差不多的):
//配置DMA为单次模式
void NPEC_SPI3_DMA_RXconfig(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();//DMA1时钟使能
__HAL_LINKDMA(&SPI3_Handler,hdmarx,SPI3RxDMA_Handler); //将DMA与SPI2联系起来(发送DMA)
//Rx DMA配置
SPI3RxDMA_Handler.Instance = DMA1_Stream0; //数据流选择
SPI3RxDMA_Handler.Init.Channel = DMA_CHANNEL_0; //通道选择
SPI3RxDMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY; //外设到存储器
SPI3RxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
SPI3RxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
SPI3RxDMA_Handler.Init.PeriphDataAlignment=DMA_MDATAALIGN_BYTE; //外设数据长度:8位
SPI3RxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
SPI3RxDMA_Handler.Init.Mode=DMA_NORMAL; //外设流控模式
SPI3RxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
SPI3RxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
HAL_DMA_DeInit(&SPI3RxDMA_Handler);
HAL_DMA_Init(&SPI3RxDMA_Handler);
__HAL_LINKDMA(&SPI3_Handler,hdmatx,SPI3TxDMA_Handler); //将DMA与SPI2联系起来(发送DMA)
//Tx DMA配置
SPI3TxDMA_Handler.Instance = DMA1_Stream5; //数据流选择
SPI3TxDMA_Handler.Init.Channel = DMA_CHANNEL_0; //通道选择
SPI3TxDMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH; //外设到存储器
SPI3TxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
SPI3TxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
SPI3TxDMA_Handler.Init.PeriphDataAlignment=DMA_MDATAALIGN_BYTE; //外设数据长度:8位
SPI3TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
SPI3TxDMA_Handler.Init.Mode=DMA_NORMAL; //外设流控模式
SPI3TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
SPI3TxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
HAL_DMA_DeInit(&SPI3TxDMA_Handler);
HAL_DMA_Init(&SPI3TxDMA_Handler);
HAL_NVIC_SetPriority( DMA1_Stream5_IRQn, 1, 1 ); //发送DMA中断优先级
HAL_NVIC_EnableIRQ( DMA1_Stream5_IRQn );
HAL_NVIC_SetPriority( DMA1_Stream0_IRQn, 1, 1 ); //接收DMA中断优先级
HAL_NVIC_EnableIRQ( DMA1_Stream0_IRQn );
}
void SPI3_Init( void )
{
SPI3_Handler.Instance = SPI3; //SP3
SPI3_Handler.Init.Mode = SPI_MODE_MASTER; //设置SPI工作模式,设置为主模式
SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES; //设置SPI单向或者双向的数据模式:SPI设置为双线模式
SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_HIGH; //串行同步时钟的空闲状态为高电平
SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI3_Handler.Init.NSS = SPI_NSS_SOFT; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; //定义波特率预分频的值:波特率预分频值为256
SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE; //关闭TI模式
SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
SPI3_Handler.Init.CRCPolynomial = 7; //CRC值计算的多项式
HAL_SPI_Init( &SPI3_Handler );//初始化
NPEC_SPI3_DMA_RXconfig();
return;
}
//需要一直发送或者接收就在回调里再调用一次接收或读取函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
HAL_SPI_Receive_DMA(&hspi3, recv_buf, 5); //这样会一直接收,这里需要注意不能调用HAL_SPI_STOP()函数,这个是连续模式才有用的。
}
void DMA1_Stream0_IRQHandler(void) //进入公用中断函数以后HAL库会自动的清除中断标志位,不需要我们操心,如果需要在中断之中做事,就直接在回调函数中写就可以
{
HAL_DMA_IRQHandler(&hdma_spi3_rx);
}
void DMA1_Stream5_IRQHandler(void) //这里发送和接收要同时配置,否则中断标志位无法清除干净
{
HAL_DMA_IRQHandler(&hdma_spi3_tx);
}
一定要发送和接收DMA同时配置,否则无法成功。
新人第一次发帖,有漏洞之处请各位网友指出