基于固件库的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

                 2. 填写工程文件名

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

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

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

                   1.  如何下载固件库

                        可参考连接:

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

                        下载后是这样的文件夹:

                   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.0LibrariesCMSISCM3DeviceSupportSTSTM32F10xstartuparm

                 

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

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

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

               右键Target1 ,选择Manage Project Items

               添加START分组

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

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

               

          然后选择启动文件

         选择添加和保存

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

                        

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

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

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

               路径为:

               STM32F10x_StdPeriph_Lib_V3.5.0Libraries

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

         

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

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

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

               在固件库中找到core_cm3.h、core_cm3.h 复制到创建的CORE目录下,这两个文件在固件库中的路径为:STM32F10x_StdPeriph_Lib_V3.5.0LibrariesCMSISCM3CoreSupport

             复制到刚才创建的CORE文件夹:

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

          3.  添加设备支持文件

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

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

          STM32F10x_StdPeriph_Lib_V3.5.0LibrariesCMSISCM3DeviceSupportSTSTM32F10x

   

         后面三个文件路径:STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_Template

        
         拷贝到Divece文件夹里面:

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

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

         

  3. MDK环境配置及编译

         

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

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

创建一个主函数:

        编译:

没有报错,就成功了

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

           

        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总线上的外围设备,查看一下有哪些外围设备:

        可以看出有很多外围设备,如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中找到该函数,查看该结构体定义:

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

GPIO_Pin 从0-15 16个端口

GPIO_Mode 共8种模式,如下:

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

下面通过一个完整的函数完成一个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灯亮,周而复始,从而实现流水灯效果,程序流程图如下:
         

                      

  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源文件添加进去

 

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仿真配置:

点击仿真:

点击分析窗口

 点击Setup:

在Setup Logic Analyzer中添加端口

如需PA1端口则输入   PORTA.1

将Display Type 设置为bit

查看波形:

 分析波形:

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

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

3. 波形是周而复始的

  2. 实物效果

七、总结

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

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

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

参考文献:

               STM32启动文件分析-CSDN博客

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