【STM32】HAL库调用HAL_SPI_Transmit出现HartFault问题

问题描述:

HAL库调用硬件SPI发送函数HAL_SPI_Transmit会导致程序进入HartFault

// flash 页大小(字节)
#define FLASH_SPI_PAGE_SIZE       (256)

static uint8_t write_buffer[FLASH_SPI_PAGE_SIZE + 4];

spi1_write(W25QXX_CHANNEL, (uint8_t *)write_buffer, (length + 4));

/**
 * @brief SPI写入
 * @param *txBuffer 发送字节集
 * @param txLength 发送长度
 * @return spi发送的长度
 * */
uint32_t spi1_write(SPIChannel channel, uint8_t *txBuffer, uint32_t txLength)
{
  if(channel == W25QXX_CHANNEL)
  {
    W25QXX_CHANNEL_ON;
  }

  HAL_SPI_Transmit(&hspi2, txBuffer, txLength, 500);

  if(channel == W25QXX_CHANNEL)
  {
    W25QXX_CHANNEL_OFF;
  }

  return txLength;
}

原因分析:

Debug发现进入HartFault之前最后一句话是

在这里插入图片描述
是该揭开它真正面纱的时候了:我们再看出现问题的那条语句:

hspi->Instance->DR = *((uint16_t *)pData);

再看我们数组的定义:uint8_t
出现问
题这条语句把我们定义的uint8_t 数组转换成了uint16_t 同时进行半字的操作(同时操作两个byte)。这样看确实提高了执行效率,但是却也埋下了隐患。

产生这样的问题,我们就不得不扯得更远一点,arm内核对数据的非对齐数据访问。

Arm对内存的访问支持字(4byte)、半字(2byte)、字节(1byte)的直接访问,但是呢他们是有一定的要求的:

存取字时要求地址按对齐,也就是地址要是4的整数倍,如0x0000、0x0004、0x0008(该地址只是举例,mcu的地址分配请参考具体手册的地址映射图)
存取半字是要求地址按半字对齐,也就是地址是2的倍数,这样假如通过0x0001、0x0003这样非2倍数的地址来读取一个半字就会产生错误
存取字节简单,只要地址不超范围就可以

这么看来是不是有点清晰了,我们出现错误的地方不就是在操作一个半字(uint16占用两个byte也就是半字),那么进入到了hardfault应该就是操作了非半字对齐的地址。

下面我们将write_buffer地址打印出来:

rtt_printf("0x%08x\n",write_buffer);

在这里插入图片描述
很显然,这个地址并不是非半字对齐的地址。


解决方案:

将write_buffer定义成uint32_t即四字节对齐即可。
static uint32_t write_buffer[(FLASH_SPI_PAGE_SIZE + 4 )>>2];

在这里插入图片描述

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 使用stm32f401hal库进行spi_dma通信的示例步骤如下: 1. 首先,需要初始化SPIDMA的配置。可以使用HAL库提供的函数进行初始化。具体的初始化函数为: - SPI的初始化函数:`HAL_SPI_Init(SPI_HandleTypeDef *hspi)` - DMA的初始化函数:`HAL_DMA_Init(DMA_HandleTypeDef *hdma)` 2. 接下来,配置SPI的参数,包括数据传输模式、数据位长度、主从模式、时钟极性和相位等等。 3. 配置DMA的参数,包括数据传输方向、数据传输大小、地址增量模式和传输完毕后的回调函数等等。 4. 初始化SPIDMA模块,并启动DMA传输。具体的函数为: - 启动SPI传输:`HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)` - 启动DMA传输:`HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)` 5. 在回调函数中,可以进行一些后续的操作,例如数据处理或者处理传输完成后的标志位。 6. 当数据传输完成后,需要停止DMA传输。可以使用以下函数: - 停止DMA传输:`HAL_DMA_Stop(DMA_HandleTypeDef *hdma)` 通过以上的步骤,就可以使用stm32f401hal库进行spi_dma通信的配置和使用了。 ### 回答2: stm32f401是意法半导体(STMicroelectronics)推出的一款高性能微控制器,具有丰富的外设资源。其中,SPI(Serial Peripheral Interface,串行外设接口)是一种常用的通信接口,用于实现与其他外设的数据交换。 在stm32f401中,HAL库是STMicroelectronics针对其系列微控制器推出的一个硬件抽象层(Hardware Abstraction Layer),用于简化开发者对硬件的访问和控制。使用HAL库编写代码时,可以选择使用SPI DMA(Direct Memory Access,直接内存访问)功能来提高数据交换效率。 下面给出一个使用HAL库进行SPI DMA通信的示例: 首先,需要初始化SPI外设和DMA控制器。通过调用HAL_SPI_Init()函数和HAL_DMA_Init()函数进行相应的初始化配置。 然后,需要创建一个DMA传输完成的回调函数。在该函数中,可以进行相应的数据处理。可以通过调用HAL_DMA_RegisterCallback()函数注册回调函数,当DMA传输完成时会自动调用该函数。 接下来,通过调用HAL_SPI_TransmitReceive_DMA()函数进行SPI DMA传输。在函数参数中,需要提供发送缓冲区、接收缓冲区以及数据长度等信息。该函数会自动触发DMA传输,并在传输完成后调用之前注册的回调函数。 最后,在主函数中,可以调用HAL_SPI_TransmitReceive_DMA()函数进行SPI DMA传输。通过检查传输是否完成,可以确定数据是否顺利传输完成。 通过以上步骤,就可以实现使用stm32f401的HAL库进行SPI DMA通信了。使用SPI DMA可以提高数据传输效率,减少CPU的负担,适用于需要高速数据传输的应用场景。 ### 回答3: 使用STM32F401HAL库实现SPI_DMA通信的示例步骤如下: 1.首先,要使用SPI_DMA通信功能,需要初始化SPI配置和DMA通道。例如,使用SPI1和DMA1通道2来传输数据。 2.配置SPI参数。这包括设置数据传输模式(全双工、半双工等)、数据帧大小、时钟极性和相位等。例如,设置SPI1为全双工模式,数据帧大小为8位,时钟极性为空闲时钟为低电平,采样时钟为空闲时钟的第1个周期。 3.启用SPI功能。调用HAL_SPI_Init()函数来初始化SPI1。 4.配置DMA通道。设置DMA的数据传输方向,源地址、目的地址和数据长度等。例如,设置DMA1通道2为从SPI1数据寄存器读取数据,并将其传输到目标地址。 5.启动DMA传输。调用HAL_SPI_Transmit_DMA()函数来启动传输。 6.等待传输完成。可以使用HAL_DMA_PollForTransfer()函数来检查DMA传输是否完成。 7.处理接收到的数据。可以在传输完成后,使用HAL_SPI_Receive_DMA()函数来接收从SPI接收到的数据。然后可以对接收到的数据进行进一步的处理。 8.关闭SPIDMA。在数据传输完成后,调用HAL_SPI_DeInit()和HAL_DMA_DeInit()函数来关闭SPIDMA。 以上就是一个简单的STM32F401HAL库使用SPI_DMA通信的例子。实际应用中,可以根据具体需求进行参数配置和数据处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值