STM32工作笔记0030---编写跑马灯实验--使用库函数
技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152
下面是mini版的
跑马灯硬件连接
这个是硬件的原理图,可以看到
战舰版的:
LED0连接到了PB5引脚,这个连接到哪个引脚
可以搜索,直接搜LED0就可以了。
LED0,咱们在跑马灯的文档中搜索
可以看到LED0连接到了PB5引脚。
可以看到文档中有,说明,LED0连接PB5引脚,然后右侧,他是通过一个510欧姆的上拉电阻,拉到VCC 3.3v
同样LED1也是这样,通过上拉电阻,拉高电压。
LED1也可以搜索到:
他是连接到了PE5引脚。
mini版的,LED0连接到了PA8引脚
LED1连接到了PD2引脚。
然后,这里硬件连接,我们用的:
是推挽输出,因为推挽输出可以输出高低电平,前面有说过。
他的原理是这样的
当PE5输出低电平0的时候,LED1右侧,会被推挽输出拉到0低电平,然后,右边又接入了
一个上拉电阻,拉到vcc3.3 电压,这个时候形成了 压差,这样电流就会,由高电压,流入低电平
LED1就会亮。
相对的,当PE5输出高电平1的时候,这个时候,上拉电阻,也是VCC3.3v 高压,就不会形成
压差,LED1就不会亮。
也就是输出高电平就会灭,低电平就会亮。
战舰版的:
有两个LED灯。
可以看到这两个LED是连接了两个电阻的。
同时也可以看到有个孔,就是通过这个过孔最终连接到IO口上的。
这个就是双层板,意思就是,板子的正面可以走电路,反面也可以走电路,
这个几层板,主要是用来布线用的,因为板子越复杂,功能越多,需要的
线路就越多,那么,单单的板子上的一面可能没办法,把这么多多线路布好,
那么就通过过孔,把这个线路,连接到反面,或者,第三层,第四层上去。
这个过孔就是,比如,这个孔都是在一个导体面上的,在导体面上,同时连接着
导体,并且孔的内部也是导电的,然后这个孔,一直导电,通道板子的反面,
在反面,也有导体,这样就把这个线路,通过板子的反面来进行连接了,然后
连接到对应个器件,或者芯片的引脚,再通过过孔,连接到板子的正面,最终
连接到芯片某个引脚上。
可以看到,最终,这个LED1就是通过过孔,连接到了对应的芯片引脚上。
mini版的led位置在左侧
然后来看,要用到的库函数:
打开战舰版的,跑马灯实验来看一看:
然后这里,如果要用操作IO,这几个文件是必须的。
misc.c
stm32f10x_gpio.c 操作io口的
stm32f10x_rcc.c 关于时钟的
stm32f10x_usart.c 这个操作usart口的,用不到可以不引入。
stm32f10x_dbgmcu.c这个是调试用的,用不到也可以不引用。
这个文件是初始化LED的代码。
然后先来看看:
STM32F10X_GPIO.h头文件,这个文件中有很多的宏定义。
然后:
可以看到里面有很多函数,咱们来挨个看这些函数
先看第一个
这个函数可以用来设置,GPIO口的 输入输出模式,速度,和上拉,还是下拉
在STM32F10X_GPIO.h头文件中找到这个函数,然后
右键,goto definition of gpio_init就可以定位到,实现的地方。
如果报错可能是:
1.没有编译,
2.下面的设置没做
可以看到,这个方法主要是控制了
CRL这个寄存器
可以看到他还有控制,BSRR,BRR寄存器,这个主要是用来
控制上下拉的
实际上它最终是用来控制ODR寄存器。
这个方法内部不说,后面会说怎么用这个方法。
初始化以后,可以来读取输入IO口电平,读取输出IO电平,设置输出电平。
这个如果有外设,通过IO口,输入电平,可以用这个函数来读取输入电平。
当,开发版有往这个IO输出数据的时候,咱们可以通过这个函数,读取,CPU往这个IO口输出的电平信息,
因为这个CPU输出电平的时候,有可能不是咱们自己做的程序输出的,如果需要做分析,就需要读取,这个电平。
这个主要是用前两个函数,
这里先通过初始化函数,设置为推挽输出,然后再通过setbits和resetbits这两个来设置高低电平,
setbits设置为高电平,然后resetbit设置为低电平。
先看setbits,这个可以看到他设置的是低16位的引脚。
操作的是端口设置清除寄存器。
这个resetbits这个输出低电平,这歌可以看到他是操作BRR,端口清除寄存器。操作的也是低16位。
可以看到官方的库,操作的其实也是寄存器。
这个也印证了这一点。
先看初始化函数。
去看代码:
这个初始化函数。
可以看到,这个函数,有三个参数,第一个这个结构体指针。
选中goto definition。
可以看到,这个结构体定义了7个寄存器。用的是32位的,每个寄存器是32位。
然后再回去看;
这里有个对参数的有效性进行判断。
选中,gotodefindition
可以看到,他就是对7组GPIO口进行了验证。
所以第一个参数,就是选中用哪组IO口。比如GPIOA,就传入GPIOA
再看第二个参数:
定位到这个结构体。右键。
这里结构体中第一个,是int16,这个可以用来定义,是哪个组中的,哪个IO口,一组io口有16个IO口。
这里用来具体用哪个IO口
第二个参数用来选择,3种速度种的一种速度。
第三个参数用来选择,模式,8中输入输出模式中的一种。
下面是个案例,说明怎么用:
这里,先指定用哪组IO,比如这里指定的GPIOB,然后传入结构体指针,先定义
结构体,然后分别设置,结构体的,引脚也就是IO,然后输入输出模式,然后是速度。
然后再看看后面的参数是怎么来的
首先,看IS_GPIO_PIN这里有个对IO口的有效性判断,可以右键进去看
可以看到他的定义,GPIO_PIN_0这里参数,我们用的是GPIO_PIN_5
然后,我们看结构体中的关于速度的定义。
可以看到,里面是定义的三种,代表的是10m,2m,50m。
然后再看第三个参数
可以看到对应的8种输入输出模式,_AIN模拟输入
_IN_FLOATING这个是浮空输入
_IPD 下拉输入
_IPU上拉输入
1、普通推挽输出(GPIO_Mode_Out_PP):
2、普通开漏输出(GPIO_Mode_Out_OD):
复用推挽输出(GPIO_Mode_AF_PP):用作串口的输出。
复用开漏输出(GPIO_Mode_AF_OD):用在IIC。
注意推挽可以直接输出高低电平,一般用来点击数字设备。
这里,我们选择的是推挽复用输出模式。
然后还有两个读取输入函数
这里第一个函数,是从GPIO_ODR寄存器中输出数据寄存器中读取数据,第一个参数指定那一组IO口,然后第二个参数指定具体的IO口。
然后第二个函数,是读取一组的IO口,指定一组IO就可以,比如GPIOA,这个是一次性读取16个IO口,
可以看到他也是用了UInt16一个16位的值。
然后这里的读取输出数据也是一样的。只是名字不一样,操作的寄存器不一样,这个操作输出数据寄存器。
然后重点看,设置输出电平函数。
主要用到前两个。可以看到参数是,第一个,哪组io,第二个是具体的哪个io
然后开始写这个跑马灯实验。
这里,使用以前,首先要使能时钟,这个使能时钟后面会讲。
这里先要知道,使用所有的外设之前几乎都要先使能时钟。
这个使能时钟,实际上是操作寄存器,然后把对应的位,设置为1,这样就开启了。
如果没有使能时钟, 那么这个外设是没有被开启,也就无法被使用的。
然后,这里使能用的是一个时钟函数,他在下面的位置。
可以看到他实际上是操作的
APB2ENR这样的一个寄存器
然后再去看一下,他的参数,他也是先判断这个参数是否是有效的。
可以看到他的宏定义
可以看到他有这么多参数,也就是如果我们使能GPIOA这个io口,就把RCC_APB2Periph_GPIOA这个参数传入就可以了。
第二个参数是个state
可以看到,他要么disable,要么是enable,也就是说要么是使能,要么是不使能。
是一个枚举类型,枚举类型编程的时候用的比较多。
这个是第一步使能IO口,然后第二部就是初始化Io口模式
是输入,输出,上拉,下拉,还有速度是什么,
第三步就是通过_setbits和resetbits来控制是输入高电平,还是低电平
然后手把手来写这个程序:
找到以前写的库函数版的template程序,这个可以从正点原子提供的程序中复制。
打开工程新建分组harEware
然后在对应的文件夹中也新建这个文件夹
然后再新建这个hareware下面新建一个LED,这个是用来做按键处理的。
这里面一般就有个.c .h文件了。
然后在这里面新建LED.C LED.H文件
然后咱们要在这里面添加led初始化的代码
首先在led.h文件中写入:
#ifndef __LED_H
#defind __LED_H
//1.这里是注释,ifndef的意思是,没有定义的时候就定义这个
//,这个是用来避免重复引用的,这里设置只要是用户没有引用这里
//就引用下面这些内容,如果引用了就不会再引用了。
void LED_Init(volid);//2.声明这个函数
#endif
然后再在led.c中写入:
#include "LED.H"
void LED_Init(void)
{
//这里实现头文件中的函数。
}
然后,这个时候这个led.c文件还没有添加到工程中去,先去添加
然后可以看到led.c的引入led.h的部分报错,这个是因为还没有吧led.h的路径引入进来。
这个时候再引入led.h文件。
点击魔术棒,的c、c++选项卡,includepath中添加。
注意这里创建文件的时候,最好用小写的名字led.c,led.h,然后
创建的时候最好用mdk5这个软件来创建,不要直接用文本编辑器创建。
可能会报错。
然后就是点击edit,去Config中去设置一下编码为gb2312
再写代码。
可以看到他有个警告
是因为代码最后需要有个回车,这样就不报这个错误了。这个是mdk的一个bug吧。
然后进行第一步使能,去fwlib中找到,rcc.h时钟控制的文件,然后找到使能函数
找到这个函数copy过来
这里如果不清楚他又哪些参数可以右键,去定义
然后去看第一个参数的定义。
然后可以通过校验的地方,查看第一个参数都是有哪些。具体的值
右键定义。
然后,这里战舰版我们用GPIOB把这个参数复制过来就可以了。
第二个参数,我们说过他是enable,还是disable,这个我们用enable,复制过来就行。
来看一下完成后的led.h
#ifndef __LED_H
#define __LED_H
//1.这里是注释,ifndef的意思是,没有定义的时候就定义这个
//,这个是用来避免重复引用的,这里设置只要是用户没有引用这里
//就引用下面这些内容,如果引用了就不会再引用了。
void LED_Init(void);//2.声明这个函数
#endif
还有led.c
#include "led.h"
#include "stm32f10x.h"//2使能需要引入头文件
void LED_Init(void)
{
//1.这里实现头文件中的函数。
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//3.同样的方法再去使能RCC_APB2Periph_GPIOE
//2.注意这里使能的时候可以看到
//#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
//#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
//这样的话,可以用一个或运算,不用写两行了就。
//3.可以这样
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);
//由于刚开始,还是分开写。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//4.然后再调用初始化io口的函数
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//这个函数在stm32f10x_gpio.h中有
//然后再用go 定义的方式去查看,参数
//这里我们传入GPIOB
//然后第二个参数,这里
//同样的方法去找定义中去看。去stm32f10x_gpio.h这个文件中去找到
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//然后,再去看他们的参数定义就可以,可以看到第二个参数是,结构体,这里需要定义结构体
//普通推挽输出(GPIO_Mode_Out_PP):
//普通开漏输出(GPIO_Mode_Out_OD):
//复用推挽输出(GPIO_Mode_AF_PP):用作串口的输出。
//复用开漏输出(GPIO_Mode_AF_OD):用在IIC。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
//具体的每个=后面的内容都可以通过去定义查看的方式找到。
GPIO_Init(GPIOB,&GPIO_InitStructure);//5.因为第二个参数要求传入指针,所以这里加上&
//7.这里还需要设置初始的高电平,也就是默认设置为LED灯LED0不点亮。
//这里用到
//void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//这个在stm32f10x_gpio.h中可以找到。
//参数从定义中找。
GPIO_SetBits(GPIOB,GPIO_Pin_5);
//5.然后再把GPIOE初始化
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //这里之所以用5这个引脚,是因为前面有看过电路图
//6.战舰版的LED1 连接的PE5 LED0连接的PB5引脚,所以这里
//所以这里是第5个针脚,然后用的是GPIO的io口是,GPIOB 和GPIOE这两个IO口组
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
//这样初始化就做好了。
//8.设置LED1,初始化为不点亮。
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
上面这个文件是有问题的,要把结构体的声明放到最上面,要不会报错,因为结构体声明,不能
放到,可执行代码的下面。
然后再去写main.c里面原来的内容可以都删除。
原来的先备份一下。
#include "stm32f10x.h"
/************************************************
ALIENTEK 精英STM32F103开发板实验0
工程模板
注意,这是手册中的新建工程章节使用的main文件
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com
关注微信公众平台微信号:"正点原子",免费获取STM32资料。
广州市星翼电子科技有限公司
作者:正点原子 @ALIENTEK
************************************************/
void Delay(u32 count)
{
u32 i=0;
for(;i<count;i++);
}
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
Delay(3000000);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
Delay(3000000);
}
}
然后自己写。
然后编写:
#include "stm32f10x.h"//1.首先引入顶层函数
#include "led.h" //2.初始化led
#include "delay.h" //3.延时函数
int main(void)
{
delay_init();//4.初始化延时函数,这个是在system这个文件夹中,提供的工具函数
LED_Init();//5.初始化led
while(1){
//6.战舰版,把引脚5,IO口GPIOB ,GPIOE设置为高电平,这个时候LED灯关闭
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
delay_ms(500);//7.延时后
//6.战舰版,把引脚5,IO口GPIOB ,GPIOE设置为低电平,这个时候LED灯亮起
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
delay_ms(500);
}
}
编译可以看到有错误
注意这个错误的意思是:
声明结构体,不能放到可执行代码的后面,也就是,像这种结构体的声明,要放到代码的头部,才行。
需要修改一下:
led.c
可以看到放到上面后再编译就没错了
这个是完整的代码
#include "led.h"
#include "stm32f10x.h"//2使能需要引入头文件
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//1.这里实现头文件中的函数。
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//3.同样的方法再去使能RCC_APB2Periph_GPIOE
//2.注意这里使能的时候可以看到
//#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
//#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
//这样的话,可以用一个或运算,不用写两行了就。
//3.可以这样
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);
//由于刚开始,还是分开写。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//4.然后再调用初始化io口的函数
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//这个函数在stm32f10x_gpio.h中有
//然后再用go 定义的方式去查看,参数
//这里我们传入GPIOB
//然后第二个参数,这里
//同样的方法去找定义中去看。去stm32f10x_gpio.h这个文件中去找到
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//然后,再去看他们的参数定义就可以,可以看到第二个参数是,结构体,这里需要定义结构体
//普通推挽输出(GPIO_Mode_Out_PP):
//普通开漏输出(GPIO_Mode_Out_OD):
//复用推挽输出(GPIO_Mode_AF_PP):用作串口的输出。
//复用开漏输出(GPIO_Mode_AF_OD):用在IIC。
//GPIO_InitTypeDef GPIO_InitStructure; 注意这个定义要放最上面,因为结构体不能声明在可执行代码的下面。这样会报错的。
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
//具体的每个=后面的内容都可以通过去定义查看的方式找到。
GPIO_Init(GPIOB,&GPIO_InitStructure);//5.因为第二个参数要求传入指针,所以这里加上&
//7.这里还需要设置初始的高电平,也就是默认设置为LED灯LED0不点亮。
//这里用到
//void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//这个在stm32f10x_gpio.h中可以找到。
//参数从定义中找。
GPIO_SetBits(GPIOB,GPIO_Pin_5);
//5.然后再把GPIOE初始化
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //这里之所以用5这个引脚,是因为前面有看过电路图
//6.战舰版的LED1 连接的PE5 LED0连接的PB5引脚,所以这里
//所以这里是第5个针脚,然后用的是GPIO的io口是,GPIOB 和GPIOE这两个IO口组
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
//这样初始化就做好了。
//8.设置LED1,初始化为不点亮。
GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
然后,打开摄像头,可以看到这是初始状态,接下来,把程序通过flymcu下载到开发版中去。
找到编译好的hex文件,然后点击开始编程
看看效果,两个灯同时一起亮,一起灭。
上一篇: SpringSecurity实现基于数据库的身份验证
下一篇: CentOS7手动配置静态IP地址