问题描述:
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];