开发板芯片:stm32g4rbt6;HAL库开发
接着用(二)中配置好的工程模板
LED
LED电路LED引脚:PC8-PC15
PD2
:锁存器的控制端口
基本配置
使能需要的端口
依次使能PC8-PC15
为GPIO_Output
使能PD2
为GPIO_Output
配置初始引脚电平
把PC8-PC15
的GPIO output level
设置为High
WHY
选项解释
- Pin Name(引脚名称): 引脚的标识符,用于区分不同的 GPIO 引脚。
- Signal on Pin(引脚的信号): 描述该引脚的信号状态,如高电平或低电平。
- GPIO output level(GPIO 输出电平): GPIO 引脚的输出电平,即引脚输出的信号状态。
- GPIO mode(GPIO 模式): GPIO 引脚的模式描述,如输入模式或输出模式。
- GPIO Pull-up/Pull-down(GPIO 上拉/下拉): 描述是否启用了引脚的上拉或下拉电阻。
- Maximum output speed(最大输出速度): GPIO 引脚的最大输出速度。
- Fast Mode(快速模式): 是否启用了 GPIO 的快速模式。
- User Label(用户标签): 用户自定义的引脚标签。
- Modified(修改): 描述引脚是否被修改过。
生成工程
点击右上角”GENERATE CODE”,生成工程
新建文件
新建两个文件
保存为led.c
和led.h
到根目录bsp
文件夹中
添加led.c到项目bsp文件夹中
添加led.h到Include Paths中
在main.h里定义类型简写
LED驱动代码
led.h
1 |
|
详细解释
这段代码是 C/C++ 中的预处理器指令,用于条件编译。在这段代码中:
#ifndef _LED_H
_ 表示如果_LED_H_
这个宏未定义,则执行下面的代码。在这里,通常用来防止头文件的重复包含,确保头文件只被包含一次。#define _LED_H_
定义了宏_LED_H_
,用来标识这个头文件已经被包含。#endif
表示条件编译的结束。
下划线(_
)在 C/C++ 中表示前缀保留给编译器和标准库来使用的标识符。在这里的_LED_H_
用来表示这个头文件的宏定义,以避免可能的命名冲突。
具体来讲,当第一次包含头文件时,该宏会被定义,防止再次包含;后续再次尝试包含同一头文件时,在文件一开始检查该宏是否已经定义,如果已经定义了,就不再进行实际的代码包含,从而避免了头文件的重复定义和引用。这是一种常见的防止头文件重复包含的做法,通常称为“头文件保护”,也可以使用其他形式的宏标识来实现同样的效果。
led.c
1 |
|
HAL_GPIO_WritePin
①GPIO_TypeDef
类型定义结构体,定义了一些寄存器的名称这段代码使用了宏定义来定义了一系列的GPIO端口,将它们映射到对应的基地址上,并将其类型转换为GPIO_TypeDef*类型。1
2
3
4WHY转换为GPIO_TypeDef*类型:
每个GPIO端口在微控制器中都有与之相关联的寄存器。这些寄存器通常用于
配置和控制对应的GPIO端口,包括设置引脚的输入/输出状态、设置引脚的
电平状态、配置引脚的中断等功能。AHB和APB1
2
3
4
5
6
7
8
9
10
11AHB(Advanced High-performance Bus), 高速总线,用来接高速外设的。
APB (Advanced Peripheral Bus) 低速总线,用来接低速外设的。
总线(Bus)
是计算机各种功能部件之间传送信息的 公共通信干线
如果说 主板(Mother Board)是一座城市,那么 总线
就像是城市里的公共汽车(bus),
能按照固定行车路线,传输来回不停运作的比特(bit)
按照计算机所传输的 信息种类,计算机的总线
可以划分为 数据总线、地址总线 和 控制总线,
分别用来传输数据、数据地址和控制信号
uint16_t
无符号短整型,占用16位(即2个字节)的内存空间。在C和C++中,uint16_t通常用来表示无符号的16位整数。这样定义便于代码的可读性和移植性,因为不同平台下这样的数据类型大小可能会有所不同,这样可以确保uint16_t始终表示一个16位的无符号整数。③ GPIO_PinState
1
2
3
4
5
6
7
8/**
* @brief GPIO Bit SET and Bit RESET enumeration
*/
typedef enum
{
GPIO_PIN_RESET = 0U,
GPIO_PIN_SET
} GPIO_PinState;这段代码定义了一个枚举类型
GPIO_PinState
,其中包含了两个枚举常量GPIO_PIN_RESET
和GPIO_PIN_SET
。在这个枚举类型中,GPIO_PIN_RESET
被赋值为0,而GPIO_PIN_SET
未被显式赋值,它将自动被赋值为上一个枚举常量的值加1,即为1。这种简单的枚举类型通常用于表示开关状态、传感器状态等具有两种状态的情况。GPIO_PIN_RESET
表示GPIO引脚复位状态,GPIO_PIN_SET
表示GPIO引脚设置状态。④ assert_param(IS_GPIO_PIN(GPIO_Pin));
IS_GPIO_PIN()
1
2这段代码片段是用于定义一个宏(macro),用于验证给定的GPIO引脚
是否有效。让我们来解释一下这段代码的含义:#define IS_GPIO_PIN(__PIN__)
:这里定义了一个名为IS_GPIO_PIN
的宏,用于判断给定的GPIO引脚是否有效。((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U)
:
这部分代码是对给定引脚进行位运算,通过AND操作符&
与GPIO_PIN_MASK
进行比较,判断是否至少有一个位与操作结果不为0。&&
:这里使用逻辑与运算符,确保两个条件都满足。(((uint32_t)(__PIN__) & ~GPIO_PIN_MASK) == 0x00U))
:
这部分代码将给定的引脚与取反的GPIO_PIN_MASK
进行AND操作,
判断是否所有位均为0。
assert_param()
简单地讲,断言就是对某种假设条件进行检查。 在 C 语言中,断言被定义
为宏的形式(assert(expression)),而不是函数,其原型定义在
<assert.h> 文件中。其中,assert 将通过检查表达式 expression
的值来决定是否需要终止执行程序。也就是说,如果表达式 expression
的值为假(即为 0),那么它将首先向标准错误流 stderr打印一条
出错信息,然后再通过调用 abort 函数终止程序运行;否则,assert
无任何作用。
⑤ GPIO_PIN_RESET
GPIO_PinState里的一个枚举常量:GPIO_PIN_RESET被赋值为0,表示GPIO引脚复位状态
⑥ GPIOx->BSRR
点运算符( . )的左边操作数是一个结果为结构的表达式;箭头运算符( -> )的左边的操作数是一个指向结构体的指针。
ORD,BSRR,BRR寄存器的作用是对已经初始化后的 IO 口输出高、低电平。ODR寄存器可读可写,32位,既能控制管脚为高电平,也能控制管脚为低电平。GPIO管脚对于位写1为高电平,写 0 为低电平。(低 16 位用于设置 GPIO 口对应位输出高/低电平。高 16 位保留地址,读写无效。)
BSRR寄存器 称为端口位设置/清除寄存器,只写寄存器,32位,既能控制管脚为高电平,也能控制管脚为低电平 ,对寄存器高 16位 写1 对应管脚为低电平,对寄存器低16位写1对应管脚为高电平。写 0 ,无动作
BRR寄存器称为端口位清除寄存器,只写寄存器,32位,只能改变管脚状态为低电平 ,寄存器管脚对应位写 1 相应管脚会变为低电平。写 0 无动作。(BRR低 16 位用于设置 GPIO 口对应位输出低电平。高 16 位保留地址,读写无效。)
所以理论上来讲,BRR 寄存器的功能(低16位)和 BSRR 寄存器高 16 位的功能是一样的。
(未完待续)等这些弄完了系统学习一下stm32g431的寄存器什么的⑦ GPIO_PIN_RESET
这段代码片段看起来是在STM32的嵌入式程序中用来控制GPIO引脚输出状态的。让我来解释一下这段代码的功能:
PinState != GPIO_PIN_RESET: 这个条件判断语句检查了一个名为PinState的变量是否不等于GPIO_PIN_RESET。这里预期PinState是一个表示 GPIO 引脚状态(高电平或低电平)的变量。
如果PinState不等于GPIO_PIN_RESET,则执行下面的代码块:
GPIOx->BSRR = (uint32_t)GPIO_Pin;:这行代码是在将指定的GPIO引脚置高。 BSRR(Bit Set Reset Register)是一个寄存器,通过写入相应的引脚位来将指定的引脚置高。如果PinState等于GPIO_PIN_RESET,则执行下面的代码块:
GPIOx->BRR = (uint32_t)GPIO_Pin;:这行代码是在将指定的GPIO引脚置低。BRR(Bit Reset Register)是一个寄存器,通过写入相应的引脚位来将指定的引脚置低。
BTW,上面那个led.c代码是课里写的,我不太喜欢用,感觉不太好用,我就用我学51的时候用的led函数思想写了一个(我考核的时候就用的自己的,很直观,而且没出什么问题)
led.c
1 | void led(int x,int a) |
就是八个led,第一个参是led代号(1-8),第二个是开或关(0关,1开);
最后两行要开关寄存器,开了之后一瞬间就过去了,然后关上就行了(不细说了,没时间了)
led闪烁(引脚状态反转):
1 | if(HAL_GetTick()-tempCnt>500) { |