蓝桥杯stm32学习笔记(三):点亮LED
Published in:2024-02-28 | category: 嵌入式
Words: 2.5k | Reading time: 9min | reading:

开发板芯片:stm32g4rbt6;HAL库开发

接着用(二)中配置好的工程模板

LED

LED电路

LED引脚:PC8-PC15
PD2:锁存器的控制端口

基本配置

使能需要的端口

依次使能PC8-PC15GPIO_Output

使能PD2GPIO_Output

配置初始引脚电平

PC8-PC15GPIO output level设置为High

WHY
左边是拉高的(高电平)((一)中有VDD的介绍)所以当右边是低电平时,有电流通过,LED被点亮。为了初始时是LED是熄灭状态,我们这里把引脚设置成高电平(High)
PD2:高电平使能,低电平关闭
选项解释
  • 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.cled.h到根目录bsp文件夹中

添加led.c到项目bsp文件夹中

添加led.h到Include Paths中

在main.h里定义类型简写

LED驱动代码

led.h

1
2
3
4
5
6
7
8
#ifndef _LED_H_
#define _LED_H_

#include "main.h"

void LED_Disp(uchar dsLED);

#endif
详细解释

这段代码是 C/C++ 中的预处理器指令,用于条件编译。在这段代码中:

  • #ifndef _LED_H_ 表示如果 _LED_H_ 这个宏未定义,则执行下面的代码。在这里,通常用来防止头文件的重复包含,确保头文件只被包含一次。
  • #define _LED_H_ 定义了宏 _LED_H_,用来标识这个头文件已经被包含。
  • #endif 表示条件编译的结束。
    下划线(_)在 C/C++ 中表示前缀保留给编译器和标准库来使用的标识符。在这里的 _LED_H_ 用来表示这个头文件的宏定义,以避免可能的命名冲突。

具体来讲,当第一次包含头文件时,该宏会被定义,防止再次包含;后续再次尝试包含同一头文件时,在文件一开始检查该宏是否已经定义,如果已经定义了,就不再进行实际的代码包含,从而避免了头文件的重复定义和引用。这是一种常见的防止头文件重复包含的做法,通常称为“头文件保护”,也可以使用其他形式的宏标识来实现同样的效果。

led.c

1
2
3
4
5
6
7
8
9
#include"led.h"

void LED_Disp(uchar dsLED)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
HAL_GPIO_WritePin
  • ①GPIO_TypeDef
    类型定义结构体,定义了一些寄存器的名称
    这段代码使用了宏定义来定义了一系列的GPIO端口,将它们映射到对应的基地址上,并将其类型转换为GPIO_TypeDef*类型。
    1
    2
    3
    4
    WHY转换为GPIO_TypeDef*类型:
    每个GPIO端口在微控制器中都有与之相关联的寄存器。这些寄存器通常用于
    配置和控制对应的GPIO端口,包括设置引脚的输入/输出状态、设置引脚的
    电平状态、配置引脚的中断等功能。
    AHB和APB
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    AHB(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_RESETGPIO_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
    #define IS_GPIO_PIN(__PIN__)        ((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U) &&\
    (((uint32_t)(__PIN__) & ~GPIO_PIN_MASK) == 0x00U))

    这段代码片段是用于定义一个宏(macro),用于验证给定的GPIO引脚
    是否有效。让我们来解释一下这段代码的含义:

    1. #define IS_GPIO_PIN(__PIN__):这里定义了一个名为
      IS_GPIO_PIN的宏,用于判断给定的GPIO引脚是否有效。
    2. ((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U)
      这部分代码是对给定引脚进行位运算,通过AND操作符&
      GPIO_PIN_MASK进行比较,判断是否至少有一个位与操作结果不为0。
    3. &&:这里使用逻辑与运算符,确保两个条件都满足。
    4. (((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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void led(int x,int a)
{
int pin;
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
switch(x)
{
case 1:pin=GPIO_PIN_8;break;
case 2:pin=GPIO_PIN_9;break;
case 3:pin=GPIO_PIN_10;break;
case 4:pin=GPIO_PIN_11;break;
case 5:pin=GPIO_PIN_12;break;
case 6:pin=GPIO_PIN_13;break;
case 7:pin=GPIO_PIN_14;break;
case 8:pin=GPIO_PIN_15;break;

}
if(a==0) HAL_GPIO_WritePin(GPIOC,pin,GPIO_PIN_SET);
if(a==1) HAL_GPIO_WritePin(GPIOC,pin,GPIO_PIN_RESET);



HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);


}

就是八个led,第一个参是led代号(1-8),第二个是开或关(0关,1开);
最后两行要开关寄存器,开了之后一瞬间就过去了,然后关上就行了(不细说了,没时间了)

led闪烁(引脚状态反转):

1
2
3
4
if(HAL_GetTick()-tempCnt>500) {
HAL_GPIO_TogglePin (GPIOC,GPIO_PIN_9);
tempCnt =HAL_GetTick();
}
Prev:
STM32考核作业
Next:
蓝桥杯stm32学习笔记(二):工程模板建立