学习笔记(1)stm32h743和ADXL355的SPI通信,基于cubeMX和keil5MDK平台,采用HAL库编程,代码已模块化处理

今天是一个特别的日子,为什么呢?哈哈,因为从大四做毕业设计的时候,接触CSDN,到现在一整年了,终于创作了我的第一篇博客,内心难念有些激动!

首先声明,我不是嵌入式领域的技术大牛,只是普通的研一新生,在我的学习过程中,认为有价值拿出来分享,亦或是有意义采用这种方式进行纪念或记录的学习经验,所以有了这一篇博客,希望可以帮到有需要的人,好了言归正传。

想要成功实现stm32h743和ADXL355的SPI通信,前提是,你知道什么是SPI(不胜详略),配置通信参数的时候不会满头雾水;还要熟悉HAL库,关于SPI各个函数and参数的定义;然后,读ADXL355的datasheet,数据手册上会有寄存器列表,从上面可以知道“传感器的配置方法,数据的读取地址等”。要耐心,充分的准备是成功的关键。

关于什么是spi,stm32h7的HAL库SPI部分的介绍,下面这位博主已经写的非常棒了!我就不赘述了。这个是安富莱公司的教程,正点原子H7是没有写SPI的。

【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API_Simon223的博客-CSDN博客

进行完之前的理论学习之后,正式利用STM32H7进行ADXL355的数据读取了。

在此之前,不妨先过目下面这篇博客,也是我在写STM32H7时候的主要参考!这个是基于STM32F103RC的,原理是类似的。

STM32HAL库实现ADXL355加速度传感器简单记步功能(SPI)_Miller@yuan的博客-CSDN博客_adxl355测加速度程序

下面开始讲解我的具体配置过程:

新建CubeMX工程,选择对应芯片。 进入工程,第一步,使能RCC,选择外部时钟(HES)晶体/陶瓷震荡源,然后如下配置时钟树,可参考正点原子H7教程。

使能usart1,修改usart1的引脚至PA9,PA10,使能NVIC,开启usart1的中断,prior设置为3。

使能spi2,(1,2,3都行) ,我把引脚的默认值修改了。

 !!!!请注意,这里踩坑了,用SPI2的NSS作为片选,我设置的是硬件控制,输出,上拉,但是不能用,不知原因。所以一般选择soft_nss模式,此时该端口未用,可以用于正常IO输出。

下图可参考。

接着,弄了两个LED灯的GPIO,随便玩的。

选择MDK,勾选Dont generat the main(),生成代码。

接下来,一起来康康生成的代码是什么样子的,因为是第一篇博客,而且我也是第一次尝试用cubeMX+keil5MDK一起使用,所以内容包含但不限于SPI配置:

首先,生成的main.h中,有在配置LED灯的IO口时,起的别名,别的基本上没啥。

 

 再看main.c,绝大多数配置代码都在这了,为了节省篇幅,就只看这段声明吧,我都打好注释了。

SPI句柄的参数配置,记得按照ADLX355的时钟极性修改。

SPI_HandleTypeDef hspi2;  //初始化SPI用的结构体(SPI句柄)

UART_HandleTypeDef huart1;  //UART句柄


/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);     //系统时钟初始化函数,留在main.c中,折叠起来不碍眼,直接调用
static void MX_GPIO_Init(void);      //GPIO初始化函数,稍后搬到GPIO模块中
static void MX_SPI2_Init(void);       //SPI2初始化函数,稍后搬到SPI模块中
static void MX_USART1_UART_Init(void);//USART1初始化函数,稍后搬到UART模块中

 然后,打开stm32h7xx_hal_msp.c,可以看到以下函数。

插入题外话,HAL库和标准库的区别是,例如,标准库配置SPI只需要在一个INIT函数中写清配置信息,而HAL库会把涉及硬件的配置和常规功能配置分开,前文的MX_SPI2_INIT是对SPI的通信模式、速度、校验格式等进行设置,这些设置在M7系列的各种芯片都是通用的,甚至可以兼容到M3,而HAL_SPI_MspInit是进行SPI通信端口的配置,顺便在这个函数里帮你使能了SPI的时钟和SPI用到的GPIO的时钟。这样,在你需要将这个工程一直到同系列的其他芯片上时,只需要修改Mspinit函数里的内容就可以了。

再多说一点,为什么这些函数可以写在这个文件里,因为你的MX_SPI2Init在main.c,maic.c和该文件同时包含main.h,而且main.h包含"stm32h7xx_hal.h",所以MX_SPI2Init可以到HAL_SPI_Init,HAL_SPI_Init可以调用到HAL_SPI_MspDInit,因此我们在模块化设计的时候,把HAL_SPI_MspInit和MX_SPI2Init放在一个模块中,然后在main.c中可以成功调用,若要在其他模块中调用,则该模块应当包含main.h或直接包含stm32h7xx_hal.h。

void HAL_MspInit(void)
{
....  这个是初始化函数,留在这个文件里就行,其他的函数 我都习惯性搬走。
}


void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
(... 这是重写的SPI_Mspinit函数,因为是_week函数,就是给你重写的。)
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hspi->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */

  /* USER CODE END SPI2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI
    */

    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);
  }
  }


void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
...
  }
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)

接下来,直接上我的spi.c和spi.h!!为了可以折叠函数,所以我截图了。想必大家也知道这些函数是从哪里复制而来的了。记得复制配置SPI2的句柄定义。

下面没有折叠的函数,是基于HAL给提供的SPI通信函数,重写的用于读写字节的函数。

spi的全双工的通信-写入n个字节:

1,片选拉低,用我们重写的通信函数,要先写入地址,返回值是你写地址的过程中,MOSI线上的数据,可以不要。

2,再次调用通信函数,写入1个字节的数据,再次调用,写入第2个字节的数据.....片选拉高。

spi的全双工的通信-读取n个字节:

1,片选拉低,用我们重写的通信函数,要先写入地址,返回值是你写地址的过程中,MOSI线上的数据,可以不要。

2,再次调用通信函数,写入0xff或者0x00(根据从机的要求),此时的返回值,就是你读取的第1个字节的数据,再次调用函数写入0xff,返回值为第2个字节.....片选拉高。

!!!如何让从机知道你要读or写,要对原始地址进行处理:左移一位,右边自动补0,为写;左移一位,和0x01或,相当于最低为置1,为读;有些小伙伴会有些疑惑,为什么要在最低位作为读写标志位,标志位位于地址帧的最前位,区别于最高位,最前位我将其定义为:接受方接到地址帧的首个bit。具体看下图,一目了然。

图片来源于:

SPI原理超详细讲解---值得一看_Z小旋-CSDN博客_spi通信的详细讲解

 到这里,内容基本上写完了。我相信,现在,你已经可以利用STM32H7读取ADLX355的数据了,可能还不限于ADLX355

哦对了,还有最重要的一件事!重写printf函数,这样你就可以按照你熟悉的printf方式进行输出了。建议写到uart模块中,记得包含 stdio.h。

#include "stdio.h"

int fputc(int ch, FILE *f)  //֘¶¨ϲ Printf º¯ʽ
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
    return ch;
}

 俗话说,送佛送到西,我上传了整个工程文件,如果需要可以下载,定个4.9元钱吧,一瓶饮料的价钱。

STM32H743IITX和ADLX355BZ的SPI接口通信,MDK工程。-C文档类资源-CSDN下载

如果想自己写,那我把我的配置方法贴出来供大家参考!

最最最最最后!数据手册说ADXL355支持最大100MHz的SPI通信时钟,但是我用25MHz的时候数据就开始出错,后面找原因,是杜邦线太长了,SPI的通信距离最好在厘米级,进入分米级之后,会限制通信速率。可以开动脑筋想一想,如何尽可能地优化通信可靠性,提示:修改SPI句柄。

当你再次用CubeMX生成工程之后,你的自建模块依然在,但其余修改过的地方会复原,除非你写到它要你写到的地方,看着有点乱,所以我会把该工程另存到其他地方保护起来。然后需要配置其他外设的时候,用CubeMX重新生成代码后,搬移到模块中,或者直接用句柄配置。个人认为这样的CubeMX+keil5的编程方式较好。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wql_njust

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值