STM32实现跑马灯实验共11页文档.docx
《STM32实现跑马灯实验共11页文档.docx》由会员分享,可在线阅读,更多相关《STM32实现跑马灯实验共11页文档.docx(21页珍藏版)》请在冰豆网上搜索。
STM32实现跑马灯实验共11页文档
6.1STM32IO简介
要练说,先练胆。
说话胆小是幼儿语言发展的障碍。
不少幼儿当众说话时显得胆怯:
有的结巴重复,面红耳赤;有的声音极低,自讲自听;有的低头不语,扯衣服,扭身子。
总之,说话时外部表现不自然。
我抓住练胆这个关键,面向全体,偏向差生。
一是和幼儿建立和谐的语言交流关系。
每当和幼儿讲话时,我总是笑脸相迎,声音亲切,动作亲昵,消除幼儿畏惧心理,让他能主动的、无拘无束地和我交谈。
二是注重培养幼儿敢于当众说话的习惯。
或在课堂教学中,改变过去老师讲学生听的传统的教学模式,取消了先举手后发言的约束,多采取自由讨论和谈话的形式,给每个幼儿较多的当众说话的机会,培养幼儿爱说话敢说话的兴趣,对一些说话有困难的幼儿,我总是认真地耐心地听,热情地帮助和鼓励他把话说完、说好,增强其说话的勇气和把话说好的信心。
三是要提明确的说话要求,在说话训练中不断提高,我要求每个幼儿在说话时要仪态大方,口齿清楚,声音响亮,学会用眼神。
对说得好的幼儿,即使是某一方面,我都抓住教育,提出表扬,并要其他幼儿模仿。
长期坚持,不断训练,幼儿说话胆量也在不断提高。
本章将要实现的是控制ALIENTEK战舰STM32开发板上的两个LED实现一个类似跑马灯的效果,该实验的关键在于如何控制STM32的IO口输出。
了解了STM32的IO口如何输出的,就可以实现跑马灯了。
通过这一章的学习,你将初步掌握STM32基本IO口的使用,而这是迈向STM32的第一步。
死记硬背是一种传统的教学方式,在我国有悠久的历史。
但随着素质教育的开展,死记硬背被作为一种僵化的、阻碍学生能力发展的教学方式,渐渐为人们所摒弃;而另一方面,老师们又为提高学生的语文素养煞费苦心。
其实,只要应用得当,“死记硬背”与提高学生素质并不矛盾。
相反,它恰是提高学生语文水平的重要前提和基础。
这一章节因为是第一个实验章节,所以我们在这一章将讲解一些知识为后面的实验做铺垫。
为了小节标号与后面实验章节一样,这里我们不另起一节来讲。
其实,任何一门学科都离不开死记硬背,关键是记忆有技巧,“死记”之后会“活用”。
不记住那些基础知识,怎么会向高层次进军?
尤其是语文学科涉猎的范围很广,要真正提高学生的写作水平,单靠分析文章的写作技巧是远远不够的,必须从基础知识抓起,每天挤一点时间让学生“死记”名篇佳句、名言警句,以及丰富的词语、新颖的材料等。
这样,就会在有限的时间、空间里给学生的脑海里注入无限的内容。
日积月累,积少成多,从而收到水滴石穿,绳锯木断的功效。
在讲解STM32的GPIO之前,首先打开我们光盘的第一个固件库版本实验工程跑马灯实验
工程(光盘目录为:
“4,程序源码\标准例程-V3.5库函数版本\实验1跑马灯/USER/LED.Uv2”),可以看到我们的实验工程目录:
图6.1.1跑马灯实验目录结构
接下来我们逐一讲解一下我们的工程目录下面的组以及重要文件。
①组FWLib下面存放的是ST官方提供的固件库函数,里面的函数我们可以根据需要添加和删除,但是一定要注意在头文件stm32f10x_conf.h文件中注释掉删除的源文件对应的头文件,这里面的文件内容用户不需要修改。
②组CORE下面存放的是固件库必须的核心文件和启动文件。
这里面的文件用户不需要修改。
③组SYSTEM是ALIENTEK提供的共用代码,这些代码的作用和讲解在第五章都有讲解,大家可以翻过去看下。
④组HARDWARE下面存放的是每个实验的外设驱动代码,他的实现是通过调用FWLib下面的固件库文件实现的,比如led.c里面调用stm32f10x_gpio.c里面的函数对led进行初始化,这里面的函数是讲解的重点。
后面的实验中可以看到会引入多个源文件。
⑤组USER下面存放的主要是用户代码。
但是system_stm32f10x.c文件用户不需要修改,同时stm32f10x_it.c里面存放的是中断服务函数,这两个文件的作用在3.1节有讲解,大家可以翻过去看看。
Main.c函数主要存放的是主函数了,这个大家应该很清楚。
针对第①步中怎么随意添加和删除固件库文件,这里我们稍微讲解一下。
首先从上面的图中可以看到,stm32f10x_gpio.c源文件下面include了好几个头文件,其中有一个stm32f10x_conf.h,这个文件会被每个固件库源文件引用。
我们可以打开看看里面的内容:
图6.1.2stm32f10x_conf文件内容
从图中可以看出,在头文件stm32f10x_conf.h文件中,我们包含了四个.h头文件,那是因为我们的FWLib组下面引入了相应的4个.c源文件。
同时大家记住,后面三个源文件stm32f10x_rcc.c,stm32f10x_usart.c以及misc.c在每个实验基本都需要添加。
在这个实验中,因为LED是关系到STM32的GPIO,所以我们增加了stm32f10x_gpio.c和头文件stm32f10x_gpio.h的引入。
添加和删除固件库源文件的步骤是:
1.在stm32f10x_conf.h文件引入需要的.h头文件。
这些头文件在每个实验的目录\STM32F10x_FWLib\inc下面都有存放。
2.在FWLib下面加入步骤一中引入的.h头文件对应的源文件。
记住最好一一对应,否则就有可能会报错。
这些源文件在每个实验的\STM32F10x_FWLib\src目录下面都有存放。
添加方法请参考3.3节的内容。
最后我们讲解一下这些组之间的层次结构:
图6.1.3代码层次结构图
从层次图中可以看出,我们的用户代码和HARDWARE下面的外设驱动代码再不需要直接
操作寄存器,而是直接或间接操作官方提供的固件库函数。
但是后面我们的为了让大家更全面方便的了解外设,我们会增加重要的外设寄存器的讲解,这样对底层知识更加了解,方便我们深入学习固件库。
准备内容我们就讲解到这里,接下来我们就要进入我们跑马灯实验的讲解部分了。
这里需要说明一下,我们在讲解固件库之前会首先对重要寄存器进行一个讲解,这样是为了大家对寄存器有个初步的了解。
大家学习固件库,并不需要记住每个寄存器的作用,而只是通过了解寄存器来对外设一些功能有个大致的了解,这样对以后的学习也很有帮助。
首先要提一下,在固件库中,GPIO端口操作对应的库函数函数以及相关定义在文件stm32f10x_gpio.h和stm32f10x_gpio.c中。
STM32的IO口相比51而言要复杂得多,所以使用起来也困难很多。
首先STM32的IO口可以由软件配置成如下8种模式:
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏复用功能
每个IO口可以自由编程,但IO口寄存器必须要按32位字被访问。
STM32的很多IO口都是5V兼容的,这些IO口在与5V电平的外设连接的时候很有优势,具体哪些IO口是5V兼容的,可以从该芯片的数据手册管脚描述章节查到(I/OLevel标FT的就是5V电平兼容的)。
STM32的每个IO端口都有7个寄存器来控制。
他们分别是:
配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的锁存寄存器LCKR。
大家如果想要了解每个寄存器的详细使用方法,可以参考《STM32中文参考手册V10》P105~P129。
CRL和CRH控制着每个IO口的模式及输出速率。
STM32的IO口位配置表如表6.1.4所示:
表6.1.4STM32的IO口位配置表
STM32输出模式配置如表6.1.5所示:
表6.1.5STM32输出模式配置表
接下来我们看看端口低配置寄存器CRL的描述,如图6.1.6所示:
图6.1.6端口低配置寄存器CRL各位描述
该寄存器的复位值为0X44444444,从图6.1.4可以看到,复位值其实就是配置端口为浮空136
输入模式。
从上图还可以得出:
STM32的CRL控制着每组IO端口(A~G)的低8位的模式。
每个IO端口的位占用CRL的4个位,高两位为CNF,低两位为MODE。
这里我们可以记住几个常用的配置,比如0X0表示模拟输入模式(ADC用)、0X3表示推挽输出模式(做输出口用,50M速率)、0X8表示上/下拉输入模式(做输入口用)、0XB表示复用输出(使用IO口的第二功能,50M速率)。
CRH的作用和CRL完全一样,只是CRL控制的是低8位输出口,而CRH控制的是高8位输出口。
这里我们对CRH就不做详细介绍了。
下面我们讲解一下怎样通过固件库设置GPIO的相关参数和输出。
GPIO相关的函数和定义分布在固件库文件stm32f10x_gpio.c和头文件stm32f10x_gpio.h文件中。
在固件库开发中,操作寄存器CRH和CRL来配置IO口的模式和速度是通过GPIO初始化函数完成:
voidGPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct);
这个函数有两个参数,第一个参数是用来指定GPIO,取值范围为GPIOA~GPIOG。
第二个参数为初始化参数结构体指针,结构体类型为GPIO_InitTypeDef。
下面我们看看这个结构体的定义。
首先我们打开我们光盘的跑马灯实验,然后找到FWLib组下面的stm32f10x_gpio.c文件,定位到GPIO_Init函数体处,双击入口参数类型GPIO_InitTypeDef后右键选择“Gotodefinitionof…”可以查看结构体的定义:
typedefstruct
{uint16_tGPIO_Pin;
GPIOSpeed_TypeDefGPIO_Speed;
GPIOMode_TypeDefGPIO_Mode;
}GPIO_InitTypeDef;
下面我们通过一个GPIO初始化实例来讲解这个结构体的成员变量的含义。
通过初始化结构体初始化GPIO的常用格式是:
GPIO_InitTypeDefGPIO_InitStructure;
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;//速度50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure);//根据设定参数配置GPIO
上面代码的意思是设置GPIOB的第5个端口为推挽输出模式,同时速度为50M。
从上面初始化代码可以看出,结构体GPIO_InitStructure的第一个成员变量GPIO_Pin用来设置是要初始化哪个或者哪些IO口;第二个成员变量GPIO_Mode是用来设置对应IO端口的输出输入模式,这些模式是上面我们讲解的8个模式,在MDK中是通过一个枚举类型定义的:
typedefenum
{GPIO_Mode_AIN=0x0,//模拟输入
GPIO_Mode_IN_FLOATING=0x04,//浮空输入
GPIO_Mode_IPD=0x28,//下拉输入
GPIO_Mode_IPU=0x48,//上拉输入
GPIO_Mode_Out_OD=0x14,//开漏输出
GPIO_Mode_Out_PP=0x10,//通用推挽输出
GPIO_Mode_AF_OD=0x1C,//复用开漏输出
GPIO_Mode_AF_PP=0x18//复用推挽
}GPIOMode_TypeDef;
第三个参数是IO口速度设置,有三个可选值,在MDK中同样是通过枚举类型定义:
typedefenum
GPIO_Speed_10MHz=1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
这些入口参数的取值范围怎么定位,怎么快速定位到这些入口参数取值范围的枚举类型,在我们上面章节4.7的“快速组织代码”章节有讲解,不明白的朋友可以翻回去看一下,这里我们就不重复讲解,在后面的实验中,我们也不再去重复讲解怎么定位每个参数的取值范围的方法。
IDR是一个端口输入数据寄存器,只用了低16位。
该寄存器为只读寄存器,并且只能以16位的形式读出。
该寄存器各位的描述如图6.1.7所示:
图6.1.7端口输入数据寄存器IDR各位描述
要想知道某个IO口的电平状态,你只要读这个寄存器,再看某个位的状态就可以了。
使用起来是比较简单的。
在固件库中操作IDR寄存器读取IO端口数据是通过GPIO_ReadInputDataBit函数实现的:
uint8_tGPIO_ReadInputDataBit(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin)
比如我要读GPIOA.5的电平状态,那么方法是:
GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);
返回值是1(Bit_SET)或者0(Bit_RESET);
ODR是一个端口输出数据寄存器,也只用了低16位。
该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前IO口的输出状态。
而向该寄存器写数据,则可以控制某个IO口的输出电平。
该寄存器的各位描述如图6.1.8所示:
图6.1.8端口输出数据寄存器ODR各位描述
在固件库中设置ODR寄存器的值来控制IO口的输出状态是通过函数GPIO_Write来实现的:
voidGPIO_Write(GPIO_TypeDef*GPIOx,uint16_tPortVal);
该函数一般用来往一次性一个GPIO的多个端口设值。
BSRR寄存器是端口位设置/清除寄存器。
该寄存器和ODR寄存器具有类似的作用,都可以用来设置GPIO端口的输出位是1还是0。
下面我们看看该寄存器的描述如下图:
图6.1.9端口位设置/清除寄存器BSRR各位描述
该寄存器通过举例子可以很清楚了解它的使用方法。
例如你要设置GPIOA的第1个端口值为1,那么你只需要往寄存器BSRR的低16位对应位写1即可:
GPIOA->BSRR=1<<1;
如果你要设置GPIOA的第1个端口值为0,你只需要往寄存器高16位对应为写1即可:
GPIOA->BSRR=1<<(16+1)
该寄存器往相应位写0是无影响的,所以我们要设置某些位,我们不用管其他位的值。
BRR寄存器是端口位清除寄存器。
该寄存器的作用跟BSRR的高16位雷同,这里就不做详细讲解。
在STM32固件库中,通过BSRR和BRR寄存器设置GPIO端口输出是通过函数
GPIO_SetBits()和函数GPIO_ResetBits()来完成的。
voidGPIO_SetBits(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin);
voidGPIO_ResetBits(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin)
在多数情况下,我们都是采用这两个函数来设置GPIO端口的输入和输出状态。
比如我们要设置GPIOB.5输出1,那么方法为:
GPIO_SetBits(GPIOB,GPIO_Pin_5);
反之如果要设置GPIOB.5输出位0,方法为:
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO相关的函数我们先讲解到这里。
虽然IO操作步骤很简单,这里我们还是做个概括性的总结,操作步骤为:
1)使能IO口时钟。
调用函数为RCC_APB2PeriphClockCmd()。
2)初始化IO参数。
调用函数GPIO_Init();
3)操作IO。
操作IO的方法就是上面我们讲解的方法。
上面我们讲解了STM32IO口的基本知识以及固件库操作GPIO的一些函数方法,下面我们来讲解我们的跑马灯实验的硬件和软件设计。
6.2硬件设计
本章用到的硬件只有LED(DS0和DS1)。
其电路在ALIENTEK战舰STM32开发板上默认是已经连接好了的。
DS0接PB5,DS1接PE5。
所以在硬件上不需要动任何东西。
其连接原理图如图6.2.1下:
图6.2.1LED与STM32连接原理图
6.3软件设计
跑马灯实验我们主要用到的固件库文件是:
stm32f10x_gpio.c/stm32f10x_gpio.h
stm32f10x_rcc.c/stm32f10x_rcc.h
misc.c/misc.h
stm32f10x_usart/stm32f10x_usart.h
其中stm32f10x_rcc.h头文件在每个实验中都要引入,因为系统时钟配置函数以及相关的外设时钟使能函数都在这个其源文件stm32f10x_rcc.c中。
stm32f10x_usart.h和misc.h头文件在我们SYSTEM文件夹中都需要使用到,所以每个实验都会引用。
在stm32f10x_conf.h文件里面,我们注释掉其他不用的头文件,只引入以下头文件:
#include"stm32f10x_gpio.h"
#include"stm32f10x_rcc.h"
#include"stm32f10x_usart.h"
#include"misc.h"
首先,找到之前3.3节新建的Template工程,在该文件夹下面新建一个HARDWARE的文件夹,用来存储以后与硬件相关的代码,然后在HARDWARE文件夹下新建一个LED文件夹,用来存放与LED相关的代码。
如图6.3.1所示:
图6.3.1新建HARDWARE文件夹
然后我们打开USER文件夹下的LED.Uv2工程(如果是使用的上面新建的工程模板,那么就是Template.Uv2,大家可以将其重命名为LED.Uv2),按按钮新建一个文件,然后保存在HARDWARE->LED文件夹下面,保存为led.c。
在该文件中输入如下代码:
#include"led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LEDIO初始化
voidLED_Init(void)
GPIO_InitTypeDefGPIO_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;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB.5输出高
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//LED1-->PE.5推挽输出
GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_5);/PE.5输出高
该代码里面就包含了一个函数voidLED_Init(void),该函数的功能就是用来实现配置PB5和PE5为推挽输出。
这里需要注意的是:
在配置STM32外设的时候,任何时候都要先使能该外设的时钟!
GPIO是挂载在APB2总线上的外设,在固件库中对挂载在APB2总线上的外设时钟使能是通过函数RCC_APB2PeriphClockCmd()来实现的。
对于这个入口参数设置,在我们前面的“快速组织代码”章节已经讲解很清楚了。
看看我们的代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE);//使能GPIOB,GPIOE端口时钟
这行代码的作用是使能APB2总线上的GPIOB和GPIOE的时钟。
在配置完时钟之后,LED_Init配置了GPIOB.5和GPIOE.5的模式为推挽输出,并且默认输出1。
这样就完成了对这两个IO口的初始化。
函数代码是:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//LED0-->GPIOB.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);//GPIOB.5输出高
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//LED1-->GPIOE.5推挽输出
GPIO_Init(GPIOE,&GPIO_InitStructure);//初始化GPIOE.5
GPIO_SetBits(GPIOE,GPIO_Pin_5);//GPIOE.5输出高
这里需要说明的是,因为GPIOB和GPIOE的IO口的初始化参数都是设置在结构体变量
GPIO_InitStructure中,因为两个IO口的模式和速度都一样,所以我们只用初始化一次,在GPIOE.5的初始化的时候就不需要再重复初始化速度和模式了。
最后一行代码:
GPIO_SetBits(GPIOE,GPIO_Pin_5);
的作用是在初始化中将GPIOE.5输出设置为高。
保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。
在led.h中输入如下代码:
#ifndef__LED_H
#define__LED_H
#include"sys.h"
//LED端口定义#defineLED0PBout(5)//DS0
#defineLED1PEout(5)//DS1
voidLED_Init(void);//初始化
#endif
这段代码里面最关键就是2个宏定义:
#defineLED0PBout(5)//DS0
#defineLED1PEout(5)//DS1
这里使用的是位带操作来实现操作某个IO口的1个位的,关于位带操作前面第五章5.2.1已经有介绍,这里不再多说。
需要说明的是,这里同样可以使用固件库操作来实现IO口操作。
如下:
GPIO_SetBits(GPIOB,GPIO_Pin_5);//设置GPIOB.5输出1,等同LED0=1;
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//设置GPIOB.5输出0,等同LED0=0;
有兴趣的朋友不妨修改我们的位带操作为库函数直接操作,这样也有利于学习。
将led.h也保存一下。
接着,我们在ManageComponents管理里面新建一个HARDWARE的组,并把led.c加入到这个组里面,如图6.3.2所示:
图6.3.2给工程新增HARDWARE组
单击OK,回到工程,然后你会发现在Project