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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35872856/article/details/78482785

众所周知,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,采取单字节读写方式,安全可控。

全文完

展开阅读全文

没有更多推荐了,返回首页