STM32 HAL库SPI+DMA接收数据的配置和使用方法

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同时配置,否则无法成功。
新人第一次发帖,有漏洞之处请各位网友指出

### SPI DMA HAL 实现与用法 在嵌入式系统中,DMA(直接存储器访问)用于高效的数据传输而无需CPU干预。对于SPI通信而言,使用DMA可以显著提高性能并减少CPU负载。 #### 使用STM32CubeMX配置SPI_DMA 通过图形化工具STM32CubeMX来初始化外设是非常常见的做法。该软件允许开发者轻松设置SPI接口及其关联的DMA通道。一旦完成这些基本设定,生成的基础代码会自动处理大部分底层细节[^1]。 ```c // 配置SPIDMA句柄结构体 static void MX_SPI1_Init(void) { hspi.Instance = SPI1; hspi.Init.Mode = SPI_MODE_MASTER; hspi.Init.Direction = SPI_DIRECTION_2LINES; ... if (HAL_SPI_Init(&hspi) != HAL_OK) { Error_Handler(); } } static void MX_DMA_Init(void) { __HAL_RCC_DMA1_CLK_ENABLE(); hdma_spi_tx.Instance = DMA1_Channel3; hdma_spi_rx.Instance = DMA1_Channel2; // 初始化DMA流/通道 if (HAL_DMA_Init(&hdma_spi_tx) != HAL_OK || HAL_DMA_Init(&hdma_spi_rx) != HAL_OK) { Error_Handler(); } // 关联DMA到对应的SPI句柄上 __HAL_LINKDMA(&hspi, hdmatx, hdma_spi_tx); __HAL_LINKDMA(&hspi, hdmarx, hdma_spi_rx); } ``` #### 发送接收函数示例 当涉及到实际发送或接收数据时,`HAL_SPI_TransmitReceive_DMA()` 函数提供了简单的方法来进行全双工操作: ```c uint8_t tx_buffer[] = {0x01, 0x02}; uint8_t rx_buffer[2]; if(HAL_SPI_TransmitReceive_DMA(&hspi, tx_buffer, rx_buffer, sizeof(tx_buffer))!= HAL_OK){ /* Transfer error in transmission/reception process */ Error_Handler(); } ``` 此方法启动异步读取过程,在后台执行直到所有字节都被传送完毕。在此期间,应用程序可以在等待回调通知的同时继续其他任务。 为了确保正确同步以及响应任何可能发生的错误条件,建议实现相应的中断服务程序(ISR),并在其中调用适当的API如 `HAL_SPI_IRQHandler()`, 这样能够及时更新状态标志位或者触发特定事件处理器。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值