如何点亮STM32中的LED灯?(点亮LED不是目的,学会如何使用GPIO才是真正的目的)
1、 先看懂原理图(本实验使用的是野火开发板,使用其他开发板原理相同)
根据原理图可知,三个LED的阳极接在3.3V电源端,为高电平,所以只要让阴极端接低电平就可以点亮LED灯。
这里介绍一种特殊的LED灯,三色全彩LED灯,将红、绿、蓝三原色集合到一个LED灯上,有4个管脚(其中一个管脚为公共端,接地),通过颜色比例混合,可以发出256种颜色。
那么如何才能让阴极端接低电平,也就是让PB5/PB0/PB1=0。
下面通过实验来进行演示:点亮红、绿、蓝三种颜色LED灯,并让它们间隔闪烁。
PB5代表的含义?
PB5=GPIOB_Pin_5
这里就引申出GPIO是什么?
GPIO就是通用输入输出端口,也就是IO口,通俗来讲就是芯片上的引脚,LED与芯片上的引脚相连,只要让芯片上的引脚输出0,则连通到PB5也为0,就可以点亮LED灯。
在STM32F103VET6芯片中共有5组GPIO口,每组有16个引脚,因此学会如何使用GPIO就学会了一大半STM32。
相比51,在使用STM32的时候,需要初始化配置GPIO,下图为GPIO硬件结构框图,对应GPIO不同的工作模式:
通过配置GPIO的工作模式,可以执行不同的功能,有执行输入输出高/低电平、通信、时钟(脉冲信号)、ADC等。
GPIO的具体工作模式后面我会写一些内容专门介绍,此处我们只要知道点亮LED灯,需要将GPIO的工作模式配置成推挽输出模式(Push-Pull)。
配置成推挽输出模式,是因为要从MCU中输出一个0给PB5,而这种工作模式可以真正地输出高/低电平。
LED是接在芯片外部的设备,简称“外设”,每一个外设在芯片中都有一个时钟与之对应,如果要使用外设,就要先打开时钟。
那么时钟是什么?
时钟就相当于口哨,用来下命令。
我们需要怎么做才能打开时钟?
由于GPIO控制的外设是挂接在APB2总线上的,所以利用官方提供的库函数就打开APB2上的时钟,具体实操见下方代码,下面是一幅芯片系统框图。
打开时钟之后,就来通过代码讲解下如何使用GPIO?(本例以官方标准库编程为例,不讲解更接近于底层的寄存器编程。)
1、初始化GPIO
这里先花几分钟时间了解下GPIO常用库函数,ST官方会提供库函数文档,查找资料时也助于大家提高英语能力。
GPIO_Init() :初始化GPIO库函数
在使用任何库函数之前,都需要进行初始化。
函数原型:void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct)
GPIO_InitTypeDef 结构体定义在文件“stm32f10x_goio.h”
typedef struct
{
u16 GPIO_Pin;
GPIO_Speed_TypeDef GPIO_Speed;
GPIO_Mode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
GPIO_Pin参数负责选择待设置的引脚,使用“|”操作符可以一次选择多个引脚
GPIO_Pin_5; //选中引脚5
GPIO_Pin_All; //选中全部引脚
GPIO_Speed用来设置GPIO引脚的速率
GPIO_Speed_50MHz; //最高输出速率为50MHz
速度配置函数具体实现在stm32f10x.h文件中,这里我们只要调用函数名就可以
其他函数都在对应的函数库文件中,我们只要写出函数名,学会调用就可以
这里我先说下,我会先把所有代码都放在main.c文件中,然后直接运行,这样容易理解,后面再进行模块化编程,那样会看起来更像一个工程。
2、写主代码
#include "stm32f10x.h"//调用stm32f10x.h头文件
/*延时函数*/
void delay(u32 i)
{
while(i--);
}
/*GPIO初始化*/
void LED_GPIO_Init(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体,利用的是stm32f10x_gpio.h文件库*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启GPIOX的外设时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择要控制的GPIOX的引脚*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
/*调用库函数,初始化GPIOX*/
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*关闭所有LED灯,置位1,为了实现从0到1的过程*/
GPIO_SetBits(GPIOB,GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1);
}
/*实现LED点亮的主代码函数*/
int main(void)
{
LED_GPIO_Init();/*调用上面的GPIO初始化函数*/
while(1)/*死循环是为了保证程序一直在运行*/
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//复位函数,将PB5设置为0,点亮红灯
delay(9999999);//延时函数调用
GPIO_SetBits(GPIOB,GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1);//置位函数,将PB5=PB1=PB0设置为1,关闭所有灯
delay(9999999);
GPIO_ResetBits(GPIOB,GPIO_Pin_0);//点亮绿灯
delay(9999999);
GPIO_SetBits(GPIOB,GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1);
delay(9999999);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);//点亮蓝灯
delay(9999999);
GPIO_SetBits(GPIOB,GPIO_Pin_5 | GPIO_Pin_0 | GPIO_Pin_1);
delay(9999999);
}
}
3、配置魔术棒
写好了代码之后,还有一步至关重要的步骤是:配置魔术棒
如果魔术棒没有配置好,会出现很多问题,尤其是找不到库文件,也就无法使用官方提供的库函数。
所谓魔术棒,就是一个长的像魔术棒的小图标
配置魔术棒就是添加库文件的路径,打开魔术棒,在C/C++这个选项卡中添加所用到的头文件路径就可以,就不用继续在main.c上面添加头文件了
至于Output中hex文件输出、Debug中的仿真器配置都比较简单,这里就不详细阐述了。
hex文件是在串口下载会使用到,而我们这里使用SWD仿真器可以直接在keil页面点击LOAD下载,有助于我们直接调试代码。
最后我再介绍下,一个工程具体包含哪些文件?
每个模块的代码会存放到对应的文件中,使得代码更加简洁、易懂、美观,下面通过一幅图来展示,相比文字,这样也更加直观。
关于官方库文件的获取可以百度寻找,或者找我要。
下图为整个工程的框架图(这是在编写代码之前就要建立好的):
代码模块化就是把LED_GPIO_Init()函数放在了led.c这个文件中,使用时直接调用就可以,我就不把代码复制到这里演示了。
当代码很多时,一定要将代码模块化,这样可以容易找到想要的模块代码,方便调试。
关于延时函数这里解释下:写的比较简单就和main.c放在一起了,而且这里有一个小Bug:如果把延时函数放在主函数后面,就会出现warning: function declared implicitly,放在前面就不会出现,这里暂时不深究了,后面会用到RTC(实时时钟)实现精确延时。
还有一个注意警告是: warning: #1-D: last line of file ends without a newline,说的是代码的最后一行没有以新的一行作为结束,我们只要在最后一行再敲几下回车键就可以解决这个警告。
作者能力和水平有限,文章难免存在错误和纰漏,请大佬不吝赐教,非常欢迎大家与小白杨进行技术交流。