- 实验目的 .2
- 实验板原理图.2
- GPIO寄存器…2
- GPIO控制函数…3
- 时钟使能函数…9
- 流水灯实现程序…10
- 本人做实验的尝试、问题与思考….11
- 使用HAL库编写的流水灯(STM32cubeMX配合KEIL编程)……12
通过对GPIO口的设置使LED灯亮灭,初步认识GPIO(通用输入输出寄存器)的性质和相关控制函数。对时钟使能有一定概念。 1. 可以看到,板上放置在一起的共有五个LED灯,可以看到,灯五右接电阻、电源左接地,并不受引脚控制,即开启开发板电源即自动点亮。 2. 灯1、2、3、4分与STM32引出的PD9、10、11、12相连。待会儿我们即控制该四个引脚输出高低电平达到控制灯亮灭的目的。 - STM32F407系列有GPIOA-GPIOK共11个GPIO接口,每个接口又引出0-15共十六个引脚,引脚即可以单一外接某一器件,也可以多接(这时选择读入输出信号的对象要使用复用功能选择,本点灯过程不接触)。
- 以下是我们要设置到的GPIO寄存器,关于GPIO所有寄存器的信息可以在官方提供的《STM32F4XX参考手册》中找到。
- GPIO端口模式寄存器(Mode Register)可控制各引脚使输入、输出、复用功能还是模拟模式,我们控制LED灯要使引脚处于输出模式。
- 端口输出类型寄存器(Output Type Register)可选择开漏或推挽模式。在推挽模式下,输出的高低电平与STM32对引脚的设置一致(与输出数据寄存器中设置的状态一致);而在开漏模式下,高电平输出为高阻态(可理解为非高或低电平或高低电平不确定不予响应)。们控制LED灯要使引脚处于推挽模式。
- 上下拉寄存器(PullUpPullDown Register),上拉时引脚高电平,下拉时引脚低电平,既不上拉也不下拉时为介于高低电平中的不定电压值。
- 输出速度寄存器(Output Speed Register),设置其输出频率。
- 输入数据寄存器和输出数据寄存器即设置为输入模式时数据的存入处和设置为输出模式时外部对数据进行读入处。这两个寄存器如图都是低十六位有效的(高十六位保留),一一对应每个GPIO的十六个引脚。
- 置位复位寄存器:我们修改输出数据寄存器时不直接对其操作,而是通过置位复位寄存器对它进行设置。
该寄存器低十六位相应位置1使输出数据寄存器对应位为1,高十六位相应位置1使对应位为0。这高低十六位置0对输出数据寄存器则无作用。 - 在哪儿查看这些函数的相关信息?控制函数定义在哪儿?
- STM官方提供了详细介绍所有标准库函数的文档(称为固件库),这个文档是相关程序自动生成的(生成原理不必了解),里面详细的提及了函数的定义、功能、对应参数的可选范围、宏定义以及他们的源代码都在哪些文件中。
- STM32F407的固件库名为
《STM32F40x/41x/427/437/429/439/401/410/411xE/412xG/413xx/423xx/446/469/479 DSP and Standard Peripherals Library》 Moudles->STM32F4xx_StdPeriph_Driver->GPIO->Functions 要使用的函数如下 可以看到函数被定义在stm32f4xx_gpio.c文件中。结构体类型和相应的宏分别被定义在stm32f4xx_gpio.h和stm32f4xx.h头文件中。 - 重点在看看GPIO_Init函数中的一个参数GPIO_InitTypeDef的结构体类型。包含了,GPIO_Mode、GPIO_Otype、GPIO_Pin、GPIO_PuPd、GPIO_Speed五个成员,对这五个成员可赋的实参已经在途中列出,例如GPIO_Pin的十五个引脚的宏定义。
- 看过上面的函数定义和结构体定义后,这三个函数的参数一目了然如上一般。
功能分别是对输出数据寄存器相应位置零、置一和反转。即在初始化GPIO口后,用这三个函数控制灯的亮灭。
以上的函数使完全能够控制灯了,但实际控制灯之前还有一件事要做,就是使能时钟。CPU是有工作时序的,可以理解为每一个操作都是单位时间的整数倍,设置好这个单位时间(对内的CPU工作,对外每类接口、外设的 这个单位时间未必相同),即时钟频率的倒数,放能工作。 但目前我们还未正式学到时钟的配置,所以只需要使能对应外设口的时钟,频率由STM32自动设置。 现在来看一下这个时钟使能函数 - 我们开启GPIOD对应的时钟,另一个参数为ENABLE。
- 现在我们做好了所有的准备工作,可以来跑流水灯了。
(函数部分由两个右括号没有截图下来,但不影响阅读)
- 由于子函数只有两个,所以就直接定义在main函数之上了。
- 子函数一个用于延时,一个用于点亮对应灯,延时等待亮一会儿,然后关闭。
- 主函数无非分为三个步骤,使能时钟、初始化需要用到的与灯相连的GPIO引脚和做完这些准备工作后正式开始控制灯的亮灭。
- 在我第一次点灯的时候,突发奇想能否使用c语言中所学的随机函数并联合数组
设置电灯序列。由随机函数设置一连串灯的亮灭控制。可控制的参数分别由选择的灯、灯是亮是灭、灯的颜色(当时使用的是F429的板子外接的灯是彩灯)。 那么这样,在每次RESET板子重启程序的时候,灯光的闪烁将是随机的,将构成一 支“灯光舞蹈”,那可能很有趣。 - 我满怀兴趣的去做了,但最终没有成功。根本原因是C语言中随机数本质上是伪随机数,采用的是UNIX时钟戳作为随机种子生成随机数,每个种子多次生成的结果是一样的,但由于时钟戳读取的当前时间(从1970年1月1日起的每一秒都是一个的时钟戳,本质上是一个浮点数)每秒都不一样,所以也算是随机数了。但STM32的标准库中并不包括时间戳读取函数,所以必然无法这样做了。
-
- 那么为什么会不包含这个函数呢?
其实这个问题很无聊,没有就是没有嘛,用不到所以没有。这个解释实际上,真的没毛病,相关的需要用到时间的操作通过系统时钟的控制和定时器可以完成,这个似乎并没有什么用处。另外我的一个看法是,本身芯片就只有1M甚至512KB的RAM,无用的函数越多,烧录进去时可用的空间越少。限于外部原因嵌入式编程代码精简是应该的。 - 后来我退而求其次,选择宏定义一个时间种子,手动修改,那么每个修改的数在程序中处理后也是一支不同的舞蹈了嘛。
- 你是否也有其他关于控制灯的想法呢?
- 如果用STM32cube来写就会稍微简单一些,而且在用标准库写例程时,我们仅仅使能了时钟函数,并没有配置相关的分、倍频等系数,在STM32cubeMX中,我们简单的来配置一下。
- 打开STM32CubeMX,创建一个新项目。
- 在搜索栏中找到我们的芯片STM32F407ZE系列,双击进入配置
- 在芯片图的右下角找到我们的PD9,PD10,PD11,PD12四个引脚,右键设置为输出模式。
- 在左边栏中找RCC设置,设置HSE为第三个选项,如图4
- 进入Clock Configuration,将时钟相关选择和系数因子设置如几个圈中,大致介绍在图中文字可见。
- 具体介绍要到后面学习RCC时钟控制器方具体学习。
- 进入Configuration选项的GPIO选项。设置PD9,10,11,12的状态都为低电平,推挽,上拉,低速模式,则程序烧入后立即全部灯都亮起来。
- 如图所示创建工程,最后点击Open Project打开创建的工程,则软件会自动用KEIL打开这个工程。
- 上面的main.c的程序是自动生成的同时并不是全部,我只截取了部分,可以看的出来,其中自动生成的注释非常的多。内容也比较多,主要关注下左边红点标注了的语句。
主函数里就三个部分,第一部分是根据我们在STM32CubeMx中配置的时钟关系来设置时钟的SystemClock_Config();函数的使用。第二部分是根据我们在设置的引脚状态来配置引脚的MX_GPIO_Init()的使用。第三个是一个while(1)的空循环。 - 这三条语句执行后的结果就是所有灯亮然后保持着直到RESET(灭了然后重启程序)或关闭电源。
- 我们再来看看定义在stm32f4xx_hal_gpio.c中的函数HAL_GPIO_WritePin()和HAL_GPIO_TogglePin()。前者你去生成该工程后,可以在MX_GPIO_Init()函数中发现它被调用。这两个函数是不是很眼熟,其功能与标准库中的GPIO_SetBits()和GPIO_ToggleBits()相同,只不过HAL库中生成的函数的取名带有
明显的HAL库的特征(如MX_GPIO_Init和HAL_GPIO_WritePin); - 在用STM32CubeMX生成工程后,这些个相关的头文件,c文件都被自动添加到了工程中,而要被用到函数,宏定义的都在这些个文件里。本人暂时没有找到类似标准库的固件库的HAL固件库文档,所以暂时采用找相关文件直接看里面的宏定义、函数代码的方式来了解程序。
流水灯需要添加的内容有:
1.找到HAL库下用来置位复位的函数,上图的HAL_GPIO_WritePin和HAL_GPIO_TogglePin就刚刚好够用。 - 做一个延时函数用于使灯亮一会儿后再灭之。(为了看的方便直接加在主函数下面且不截图上面的声明。)
完整的Word格式文档51黑下载地址:
http://www.51hei.com/bbs/dpj-152520-1.html
|