基于固件库的STM32 LED流水灯

目录

 

一、创建工程、添加STM32标准外设库文件

        1.  创建工程

        2.  添加启动项和标准库文件

        3. MDK环境配置及编译

二、基于库函数的流水灯程序编写流程

三、时钟使能函数库函数介绍

四、GPIO库函数介绍

1.     GPIO_Init 函数     

2.    GPIO_SetBits、GPIO_ResetBits 函数

五、流水灯程序思路,Led.h 和、Led.c 和main.c函数编写 

六、仿真查看延时时间周期和实物效果

           1. 用示波器查看延时时间

  2. 实物效果

七、总结


 

一、创建工程、添加STM32标准外设库文件

        1.  创建工程

                   创建一个新工程,如下,里面没有包含任何文件

                  1. 点击Project

b7434cb1aac948c8bc68acf6ce81822a.png

                 2. 填写工程文件名

f560ff41c9a6485b96c2ff83f14439b4.png

                 3. 选择stm32F103C8设备(根据自己的芯片型号选择)

f0b3b076d6c144aca602040c5a3efab8.png

 

            4. 最后生成了一个新的空文件

c76819d5af374d5888e1583e50202d04.png

          2.  添加启动项和标准库文件

                   1.  如何下载固件库

                        可参考连接:

 STM32官方固件库下载并且新建自己的工程_stm32固件库下载_一只学习萌的博客-CSDN博客

                        下载后是这样的文件夹:

7fb82fcff4244c60a35e6c83d5b79e43.png

                   2.  添加stm32标准库的启动文件

                     上面的操作,我们还只是建了一个框架,还需要添加启动代码,以及.c 文件等。这里我们先介 绍一下启动代码:启动代码是一段和硬件相关的汇编代码。是必不可少的!这代码主要作用如 下:

                        1、堆栈(SP)的初始化;

                        2、初始化程序计数器(PC);

                        3、设置向量表异常事件的入口地 址;

                        4、调用 main 函数。

                   stm32标准库的启动文件根据Flash容量大小一般分为三种:                   
                           startup_stm32f10x_ld.s
                           startup_stm32f10x_md.s
                           startup_stm32f10x_hd.s
其中,ld.s适用于小容量 产品;md.s适用于中等容量产品;hd适用于大容量产品;
这里的容量是指FLASH的大小.判断方法如下:
                        小容量:FLASH≤32K
                        中容量:64K≤FLASH≤128K
                        大容量:256K≤FLASH

                   在添加标准库之前需要查看自己芯片的FLASH容量大小,本次实验使用的芯片型号是stm32f103zec8t6最小系统板,查阅资料该新芯片的FASH容量为64k,因此需要添加的启动项文件是中容量版本:startup_stm32f10x_md.s

                   在该文件夹中找到启动文件的位置路径为:
\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm

                 e77d96b734324511aca26cddd3525a9e.png

                   在保存的工程路径中创建一个文件夹,将该启动文件复制进去

4add2609a7fa40898f30d41067134cd5.png

                将中容量启动文件拷贝进去:

5964a60acc9c44818d1189c3f41c800a.png

               在Keil中工程中添加该文件:

               右键Target1 ,选择Manage Project Itemsa3c00c0c6cbf481483676691492f3687.png

               添加START分组e1ded04b7c174a759ed9c46353649642.png

14cb9edd9de44253958dadedaf9922ae.png

           添加完后在START文件中添加我们刚才复制过来的启动文件

           进入START文件夹后,需要将最下方文件格式改为 ALL files:

               5479d654462e4e7191ac837bb7f88f4a.png

          然后选择启动文件

9d91ea5ea7f444389fc498a4b30107e2.png

         选择添加和保存

         完成后成功添加上了该启动文件:                 

        dffcf6cfb49a403bafbd424611a31fde.png                

         3.  添加核心文件、库文件

              1.  同样在工程目录下创建一个文件夹Library ,用来存放库文件

524a4d2278fb44268f94b50b23ebc131.png

              在下载的固件库文件中找到标准库:

               路径为:

               \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries

 

ab35013fe54844fba821d6700a3d97f3.png

         将该文件下的头文件夹和源文件夹拷贝到所创的文件夹中

         42a91577156f46d0bd9c7a3a99dff1c7.png

      同样在Keil5工程下添加好上面的文件(方法和上面一样,不再赘述),只需要将源文件添加进来即可:     
7521fbcdd0ea421ba6bb2eb0812fa452.png

 

   成功将库文件添加到该工程下

             2. 在文件夹中创建一个CORE文件夹,从来存放核心文件:

87f22b774eb5497ea0b65cc6ead9bcce.png

               在固件库中找到core_cm3.h、core_cm3.h 复制到创建的CORE目录下,这两个文件在固件库中的路径为:\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport
22cdeae3162a4de2a366258e9321c1bd.png

             复制到刚才创建的CORE文件夹:
1494b19803fd42d9a2f265de9f885edb.png

           然后在Keil下,添加到工程下:

3820b15b933946cbb6f09c3f8c30ea51.png

          3.  添加设备支持文件

               创建名为Divice文件,将stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h、stm32f10x_conf.h、stm32f10x_it.c和stm32f10x_it.h拷贝到当前文件

d83bcba578f740b89423052016195322.png

              前面三个文件在固件库中的地址为:

          \STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x

   6deb89f57ed94661ac9a91e934e736f7.png

53595795fa0d4f299e7d6fb54230f009.png

         后面三个文件路径:STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template

 

        543ce6b0ac8f43a48b40c0d23c638c51.png
         拷贝到Divece文件夹里面:

e2f71986b10b42d785beef4f83683ff8.png

         同样在Keil里面将该文件添加到工程下:

4329844c996c45ae973fb0772f0b1b66.png

        到此,所需文件添加完成了

         

  3. MDK环境配置及编译

         57766830bdf24ad486ecad6813643a99.png

 在第五步,我们需要把引用到的头文件加进去,不然源文件中无法找到头文件:

5a2febf2afab4689a7488a076e733eaa.png

 

42fa469a9db44e029ab843f7daead6e2.png

如果不添加头文件路径,则会出现一下报错:

495876c5243744a3bbf3ff1f3a4451ef.png

创建一个主函数:

a5d4bcc66b2a40329b29051f9c216efa.png

        编译:

a7b4462db834445e916b64d9029235fd.png

没有报错,就成功了

二、基于库函数的流水灯程序编写流程

           

        STM32 的IO 口相比51 而言要复杂得多,所以使用起来也困难很多。首先STM32 的IO 口
可以由软件配置成如下8 种模式:

                        1、输入浮空
                        2、输入上拉、
                        3、输入下拉
                        4、模拟输入
                        5、开漏输出
                        6、推挽输出
                        7、推挽式复用功能
                        8、开漏复用功能

         由于我们实验所需的是流水灯实验,因此使用的GPIO模式为推挽输出

        在上次实验中,我们通过寄存器操作了GPIO端口,实现了流水灯的效果。通过上次的实验,我们知道了操作一个IO口需要的三个步骤:

            1. 使能时钟(能修改GPIO的寄存器状态)

            2. 端口配置(选择上述8种模式之一,并且选择输出速度)

            3. 端口初始化(配置端口的初始状态,输出高电平或者低电平)

三、时钟使能函数库函数介绍

            根据上面的步骤,首先是需要时钟使能,在配置STM32外设的时候,任何时候都要先使能该外设的时钟,GPIO是挂载在APB2总线的外设。

            关于时钟使能的函数在库函数中的stm32f10x_rcc.h 和 stm32f10x_rcc.c 库函数中

           stm32的总线有AHB总线,APB1总线和APB2总线

           通过寄存器的实验我们知道GPIO端口连接在APB2的总线上,因此需要使能APB2总线

           在stm32中固件库中,APB2时钟使能函数为:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

          第一个参数是 RCC_APB2Priph 即使能APB2总线上的外围设备,查看一下有哪些外围设备:

e0a5b71a0ca548f1acfe123fa43cb0e9.png

        可以看出有很多外围设备,如GPIO、AFIO、ADC、TIM、USART、SPI等

        第二个参数的功能是配置该外设的状态,分为使能或者不使能(ENABLE / DISABLE)

       通过如下配置GPIOA端口使能:

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	

   

四、GPIO库函数介绍

            GPIO相关的函数和定义分布在stm32f10x_gpio,c  和  stm32f10x_gpio.h文件种

1.     GPIO_Init 函数     

          在固件库开发中,操作寄存器CRH和CRL来配置IO口的模式和速度是通过GPIO初始化函数实现的:

void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct)

           可见,该函数主要有两个参数结构体:GPIOx,以及GPIO_InitStruct

           GPIOx是用来指定GPIO的,取值范围为A~G  共8组端口

           GPIO_InitTypeDef * 这个结构体是用指明端口、端口模式和端口速度的

           在stm32f10x_gpio.h中找到该函数,查看该结构体定义:

f967fcfb2a754a40ba97071feba02ad3.png

e4cd28da5d1c48e6af2c3085b3c79fc6.png

其中有一个无符号16位整型变量、两个结构体,我们分别查看一下定义:

GPIO_Pin 从0-15 16个端口

ffab9baa682043228c4823daa5849ab0.pngGPIO_Mode 共8种模式,如下:

b14a3d3fecea44f78440788ec92fc742.png

GPIO_Speed共3种输出速度:10Mhz、2Mhz 和 50Mhz

0950eef29a604fc689852ba00cee4743.png

下面通过一个完整的函数完成一个IO的配置:

 GPIO_InitTypeDef  GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //第五个端口
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出模式
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //输出速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //GPIOB的第5端口推挽输出50Mhz

这样,我们就通过GPIO_Init函数完成了端口的配置!

2.    GPIO_SetBits、GPIO_ResetBits 函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

 这两个函数的作用是将 GPIOx的 某个端口设为高电平或者低电平

 GPIO_SetBits 高电平

 GPIO_ResetBits 低电平

 该函数可以用于初始化指定端口最初的状态,以及在后面主程序中更改端口状态

五、流水灯程序思路,Led.h 和、Led.c 和main.c函数编写 

 初始化程序:

          首先完成使能APB2时钟,使能GPIOA端口

          其次端口初始化配置,将GPIOA 的0-7 八个端口设置为推挽输出50Mhz的模式

          延时程序:

          本实验采用的是软件延时的方法,因此精度并不高

          主程序思路:

            本实验采用的是共阴极连接方法,即LED灯阴极接地,阳极接PA0-7 8个端口,那么通过GPIO_SetBits、和GPIO_ResetBits两个函数就能控制灯亮和灯灭。

             流水灯的思路是先让PA0端口输出1(即PA0灯亮),然后延时,再让所有灯灭,让PA1端口输出1,再延时,所有灯灭,再延时,让所有灯灭,让PA2灯亮……一直循环到PA7灯亮,然后延时,再让所有灯灭,到PA1灯亮,周而复始,从而实现流水灯效果,程序流程图如下:
         

                      69e9fcbecb2a4b448c0dea87435bc57a.png

  led.c程序编写,用于初始化端口模式

#include "led.h"
void GPIOA_Init()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//APB2 GPIOA时钟使能
  	GPIO_InitTypeDef GPIOA_Structure;//创建CPIO结构体
	  GPIOA_Structure.GPIO_Pin=GPIO_Pin_All; //所有端口
	  GPIOA_Structure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	  GPIOA_Structure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度为50Mhz
    GPIO_Init(GPIOA,&GPIOA_Structure);//将GPIOA所有端口设置为50Mhz的推挽输出模式	
}

   这里的作用是使能GPIOA的全部端口输出模式为50Mhz推完输出

   led.h程序编写

#ifndef LED_H
#define LED_H
void GPIOA_Init();
#endif

  在工程下创建HARDWARE,将led.c源文件添加进去

 79688c80d91f487e8854bc529db97c66.png

main.c编写:

#include "stm32f10x.h"
#include "led.h"
void delay(unsigned int  time)//软件延时
{
 unsigned int i=0;
	while(time--)
	{
   i=12000;
   while(i--);		
	}
}

int main()
{
  GPIOA_Init();//初始化端口
	
	while(1)
	{
	  GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_0); //PA0为1
		GPIO_SetBits(GPIOA,GPIO_Pin_0); //PA0为1
	  delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_1); //PA1为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_2); //PA2为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_3); //PA3为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_4); //PA4为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA5为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_6); //PA6为1
		delay(800);//延时
		GPIO_ResetBits(GPIOA,GPIO_Pin_All);//全部为0
		GPIO_SetBits(GPIOA,GPIO_Pin_7); //PA7为1
		delay(800);//延时
	
	}
	
	
}

六、仿真查看延时时间周期和实物效果

      1. 用示波器查看延时时间

            Keil5仿真配置:

e536ba1f6266492fbb0ecca94c62b439.png

 

25c3f403600b4f7ba4fe68908fe7bec2.png

点击仿真:

f1fb222f5221414c829a1356aea82c5d.png

点击分析窗口

a1e2b97b3c9f4c54af6697b8bb669c3b.png 点击Setup:

21095e071f4f4f74be0bdb86c4de2d23.png

在Setup Logic Analyzer中添加端口

如需PA1端口则输入   PORTA.1

将Display Type 设置为bit

12b8fa1abfbc4da08f7276174bc9ac6b.png

查看波形:

fcb21ef687e841caab8ebf03ddc1319c.png

a9c5be67468f4d95a8ecb6f1a75f5867.png

 分析波形:

1. 发现PA1-PA7分别在前一个端口高电平持续时间过后,从低电平到高电平

2. 每一个端口高电平持续时间大约为0.7s 

3. 波形是周而复始的

  2. 实物效果

e723080bf20d4c849fa7a7f8ee8aed40.gif

七、总结

      1.  通过库函数的开发优点: 通过库函数实现流水灯实验,在视觉和操作上,更加简单,因为操作寄存器需要完成什么操作就需要找到对应的寄存器,修改寄存器的状态,这导致我们每次去写代码的时候都需要去查阅资料,太浪费时间和精力了。库函数将一些寄存器的操作封装起来,我们只需要调用函数去完成我们想要的操作,而函数的功能又是显而易见的,如GPIO_SetBits函数,明显就是将某个端口置1,因此库函数开发的优点在于方便我们记忆,使得开发效率提高。就像是机器码和汇编语言的关系一样,汇编语言是机器码的封装,便于程序员记忆。

     2.  这篇文章是从一个空项目到一个具体项目,个人认为最为繁琐的地方就是固件库的添加,因为首先需要下载固件库,然后需要将固件库中的启动文件、设备支持文件、核心文件等必须文件加入到我们所创工程,在此过程中,我们需要知道这些文件的位置,以及如何添加。添加到工程文件目录后,还需要在Keil5中添加头文件等,还需要配置Keil5环境。

    3.  由于是通过软件延时的方式,时间延时肯定是不准确的,因此通过软件仿真,用示波器查看高电平持续时间大约为0.7s, 和实物进行对比,大体上是差不多的,因为是用软件延时的方式,所以准确的延时并不知道,且通过软件仿真查看的延时结果是否有误差也并不知道,如果需要准确的延时,那么需要用到定时器(TIM)。

 

参考文献:

               STM32启动文件分析-CSDN博客

keil5在波形仿真的时候出现 Unknown Signal. 的解决方法_keil5 unknown signal-CSDN博客

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值