您的位置:首页 > 其它

对STM32 HAL库的一些思考(一)SPI通信的数据格式问题

2017-11-08 20:21 746 查看
众所周知,STM32是一款性价比比较高的ARM芯片,并且它拥有极为丰富的外设,方便实现大部分的功能。2014年,意法半导体公司推出HAL(Hardware Abstracted Library)和配套的STM32CubeMX,更是让STM32的开发变得易如反掌,使得繁复的初始化代码仅需简单配置即可完成,这是一次重大的变革。然而,HAL库中的某些功能的确让人摸不着头脑,比如我们今天的主角SPI。

关于SPI

SPI是Motorola推出的一种通信接口,它具有很多优点,这里不赘述,但是在HAL中,使用这个模块对应的库函数并非易事,那么问题出在哪里呢?

SPI发送16位数据的问题

根据参考手册,STM32的SPI拥有一个16位的数据寄存器,记为DR,但是我们看HAL库的函数,以轮询发送为例

/**
* @brief  Transmit an amount of data in blocking mode.
* @param  hspi pointer to a SPI_HandleTypeDef structure that contains
*               the configuration information for SPI module.
* @param  pData pointer to data buffer
* @param  Size amount of data to be sent
* @param  Timeout Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)


可以看到,传入该函数的数据指针是八位的,但是在C语言中,指针的类型是不可以混用的,于是问题来了,如何充分利用这个16位的DR寄存器呢?

在解决这个问题之前,先看一小段C语言代码

#include "stdio.h"
#include "stdint.h"

uint16_t re(uint8_t* p)
{
uint16_t i =  *(uint16_t*)p;
return (i);
}

int main(void)
{
uint16_t i = 0x1234;
uint8_t* p = &i;

printf_s("0x%x \r\n", re(p));

return (0);
}


这段代码展示了如何处理不同类型的指针,如果将
uint16_t
的指针强行解释为
uint8_t
,则新指针的数据为原指针的低八位,不过此时原有数据在内存中存储没有发生变化,因此我们仍可以用原有指针取出数据;同时,这种转换在C语言中是不安全的行为,常常产生警告,然而这种转换如果作为表达式参数传入函数中,则是合法的,所以以上代码的输出为0x1234。

理解了这一点后,就能很方便的理解SPI的函数了,在该函数源码中,关于16bit发送的部分如下:

/* Transmit data in 16 Bit mode */
if(hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
if((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01))
{
hspi->Instance->DR = *((uint16_t *)pData);
pData += sizeof(uint16_t);
hspi->TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if(__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)pData);
pData += sizeof(uint16_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick()-tickstart) >=  Timeout)))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}


可以看到对DR寄存器赋值时,采用了类型转换的方式,保证从内存中取出的数据为16位。

如果需要使用SPI发送16位数据,可以按照下例:

int main(void)
{
uint16_t arr[]={...};
HAL_SPI_Transmit(&hspi1, (uint8_t* )arr, sizeof(arr), HAL_MAX_DELAY);
}


通过双机通信或者将数据写入flash,可以看到数据是正确的,不过依然建议对Flash或者E2PROM,采取单字节读写方式,安全可控。

全文完
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  stm32 硬件 通信