浏览量: 474 次浏览

STM32 GPIO应用之led灯、数码管

2019年4月10日 0 作者 Nie Hen

这节主要是GPIO的应用 包括点亮led灯和数码管使用。使用CubeMax进行配置使用keil5编写代码。

应用演示

GPIO介绍

General Purpose Input Output (通用输入/输出)简称为GPIO
许多设备或电路只要求有开/关两种状态就够了,比如LED的亮与灭。对这些设备的控制,在嵌入式微处理器上通常提供了一种“通用可编程I/O端口”,也就是GPIO。

对GPIO的配置一般有
① 浮空输入_IN_FLOATING
② 带上拉输入_IPU
③ 带下拉输入_IPD
④ 模拟输入_AIN
⑤ 开漏输出_OUT_OD
⑥ 推挽输出_OUT_PP
⑦ 复用功能的推挽输出_AF_PP
⑧ 复用功能的开漏输出_AF_OD
通常有5种方式使用某个引脚功能,它们的配置方式如下:
1、作为普通GPIO输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。  
2、作为普通模拟输入:配置该引脚为模拟输入模式,同时不要使能该引脚对应的所有复用功能模块。
3、作为内置外设的输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时使能该引脚对应的某个复用功能模块。
4、作为普通GPIO输出:根据需要配置该引脚为推挽输出或开漏输出,同时不要使能该引脚对应的所有复用功能模块。
5、作为内置外设的输出:根据需要配置该引脚为复用推挽输出或复用开漏输出,同时使能该引脚对应的所有复用功能模块。
如果有多个复用功能模块对应同一个引脚,只能使能其中之一,其它模块保持非使能状态。

GPIO使用的一些函数

重要函数:
1个初始化函数:

void HAL_GPIO_Init(GPIO_TypeDef  GPIOx, GPIO_InitTypeDef GPIO_Init);

1个读取输入电平函数:

 GPIO_PinStateHAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

1个设置输出电平函数:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin, GPIO_PinStatePinState);

1个电平翻转函数:

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

1个引脚电平锁定函数:

HAL_StatusTypeDefHAL_GPIO_LockPin(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

点亮led灯

led灯的原理很简单,通电有电流通过就有亮,没有电流就不亮。
1. 首先 查看板子的原理图,看led灯所在GPIO口对应的管脚
我所使用的板子 led部分对应管脚 C1 C2 C3 A0 A1 A2 A3 A4
enter description here

  1. 打开cubemx 选择板子对应的芯片 对相应管脚进行配置
    这里写一个三个led灯点亮的项目 使用A0 A1 A2 三个管脚
    选择要控制的管脚 左键点击 选择GPIO_Output 输出功能
    enter description here
    选择之后会变那个管脚颜色会变化
    enter description here

    可以选择配置时钟或者跳过 (下一篇文章写配置时钟过程)

    1. 配置工程
      添加工程名字 位置 以及 使用的IDE
      enter description here
      然后点击 GENRATE CODE 合成代码

    4.使用keil5 打开代码
    (打开代码第一节中写过)
    打开main.c 找到主函数
    enter description here

    一般添加的代码都在while循环中执行
    可以添加

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,0);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,0);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,0);
HAL_Delay(500);

(不用写这些代码 生成hex文件 并传入板子 也会亮 ,因为默认cubemx设置的低电平而led是低电平有效)
使用writepin函数给三个引脚低电平 ,0就是低电平 1是高电平 然后HAL_Delay延时函数里面参数单位为ms。

低电平有效是共阳极,意思就是阳极电平一直不变时高电平,加上低电平的话就有了电势差就有电流通过。而加上高电平的话,没有电势差也就没有了电流。
同理高电平有效就是共阴极。阴极电平一直不变是低电平。

MX_GPIO_Init函数 里面是GPIO的初始化
enter description here
(该图片不是该项目截图)
5 编译程序 烧写程序
将程序写好进行编译 编译通过就会生成一个hex文件,将该文件找到并使用烧写工具 写入板子。
(第一节有详细过程)
然后就会看到 三个led灯变亮

#### 使用数码管
让数码管显示数字,并能够从0到9999依次递增

原理
数码管的原理跟led很类似。可以理解为很多的小的led灯
常见有一位数码管和四位数码管
enter description here
先来看一位数码管接线图
enter description here
a-g dp 八个引脚分别控制着相应的码段,如果是共阴极 给g高电平其余低电平 那就是只有一道横线,如果给g低电平其余高电平,那呈现的就是0 加上右下角的点。

四位数码管跟一位的类型,不过接线复杂了点,理解起来还是简单的
enter description here =800*400

a-g dp控制的依旧是跟单个数码管一样
但是 A1 A2 A3 A4 控制的分别是 四个数码管
如果是共阴极 高电平有效。给A1 g高电平,其余低电平 那样显示的就是第一个数码管的一道横线亮。
如果给A1 g低电平 其余高电平,那显示的就是第一个数码管暗,其余三个数码管都显示0和点
动态显示
我们平常用到的数码管几乎都是动态显示。意思就是一次显示一个循环显示,只要相差时间不是很大,人眼就分辨不出来是循环显示的。人大脑有视觉暂留功能,所以有这种现象。同样动态显示能够节省内存,耗电等。(显示屏也是动态显示的)所以程序设计的时候要参考这个。
看原理图
打开该板子对应的原理图
enter description here =600*400

可以看到控制四个数码管分别是 D2 B4 B6 B7
a-f 以及dp 为 C6 C8 —A15 (图上左边一排)
配置cubemx
把使用到的引脚 共12个 都初始化为GPIO_OUTPUT
enter description here =400*400

这样就配置完成了
然后可以配置项目名称,位置,IDE,合成代码

编写代码
如果直接编译,烧写hex文件,显示的是四个数码管全亮
我们还需要根据我们的需求更改代码。

  1. 编写GPIO口对应的数组 ,便于调用
    struct GPIO_PACK{
        GPIO_TypeDef * port;
        uint16_t pin;
    };
    struct GPIO_PACK segs[8]= {
    {GPIOA,GPIO_PIN_15},
    {GPIOA,GPIO_PIN_11},
    {GPIOC,GPIO_PIN_9},
    {GPIOC,GPIO_PIN_7},
    {GPIOC,GPIO_PIN_8},
    {GPIOA,GPIO_PIN_12},
    {GPIOA,GPIO_PIN_8},
    {GPIOC,GPIO_PIN_6},
    }   ;
    struct GPIO_PACK bits[4]={
    {GPIOD,GPIO_PIN_2},
    {GPIOB,GPIO_PIN_4},
    {GPIOB,GPIO_PIN_6},
    {GPIOB,GPIO_PIN_7},
    };
  1. 编写段码
    段码指的是 讲单个数码管上dp a-g 高低电平用1和0代替,按照dp g f e d c b a组成的一串二进制数值,再转换为十六进制
    比如 十六进制值 c0就是 1100 0000 代表的是dp 和g 高电平 灭 其他亮 形成的是数字0
    同理 0-9 分别是 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e

写入数组

uint8_t shuzu[]={
    0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,
    0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
};

3.将段码转换为实际的对引脚控制
下面的这个函数实现的是 让特定一个数码管显示特定的值

void DisplayOnebit(uint8_t digtal,uint8_t bit){
        uint8_t i;
        for(i=0;i<8;i++){
            HAL_GPIO_WritePin(segs[i].port,segs[i].pin,(GPIO_PinState)(shuzu[digtal]&(0x01<<i)));
        }
        for(i=0;i<4;i++){
            HAL_GPIO_WritePin(bits[i].port,bits[i].pin,(GPIO_PinState)(i!=bit));
        }
}

使用writePin函数能够给GPIO脚高低电位,使用了移位的方式来解析段码。
传入的参数 digtal 是要显示的数字 。bit 是要显示的是第几位数码管
调用 DisplayOnebit(9,1);
结果就会是 第二位数码管显示数字9 其他数码管暗 。

控制多个数码管显示多位数

void DisplayDigtal(uint16_t digtal){
    int a=0;
    while(a<50)
    {
    a++;
    DisplayOnebit(digtal%10,0);
    HAL_Delay(5);
    if (digtal>=10){
    DisplayOnebit(digtal/10%10,1);
    HAL_Delay(5);
    if (digtal>=100){
    DisplayOnebit(digtal/100%10,2);
    HAL_Delay(5);
    if (digtal>=1000){
    DisplayOnebit(digtal/1000%10,3);
    HAL_Delay(5);

                }
            }
        }
    }
}

传入一个值 就会直接显示 输入的值 里面调用的是DisplayOnbit 函数 只不过是分配了一个每个数码管应该显示的值 并加了延时。
调用 DisplayDigtal(999);
结果会有三个数码管显示9 另一个数码管是暗的

4.控制数码管显示
前面上一步操作可以理解成前期准备条件。是放到主函数外面的,方便调用。
真正根据应用场景需求编写的代码 是在这里的。

在main函数中
while循环里 添加以下代码

DisplayDigtal(displya_data++);
        if(displya_data>9999)
            displya_data=0;

这里需要注意 需要前面对display_data 这个变量进行声明

uint16_t displya_data=0;

上面代码很简单就是变量从0一直增加并依次显示,当变量达到9999时需要重新赋值为0.
然后编译 运行,烧写程序就可以了。
效果

完整工程链接。GitHub