目录
2. GPIO_SetBits、GPIO_ResetBits 函数
五、流水灯程序思路,Led.h 和、Led.c 和main.c函数编写
一、创建工程、添加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.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm
在保存的工程路径中创建一个文件夹,将该启动文件复制进去
将中容量启动文件拷贝进去:
在Keil中工程中添加该文件:
右键Target1 ,选择Manage Project Items
添加START分组
添加完后在START文件中添加我们刚才复制过来的启动文件
进入START文件夹后,需要将最下方文件格式改为 ALL files:
然后选择启动文件
选择添加和保存
完成后成功添加上了该启动文件:
3. 添加核心文件、库文件
1. 同样在工程目录下创建一个文件夹Library ,用来存放库文件
在下载的固件库文件中找到标准库:
路径为:
\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries
将该文件下的头文件夹和源文件夹拷贝到所创的文件夹中
同样在Keil5工程下添加好上面的文件(方法和上面一样,不再赘述),只需要将源文件添加进来即可:
成功将库文件添加到该工程下
2. 在文件夹中创建一个CORE文件夹,从来存放核心文件:
在固件库中找到core_cm3.h、core_cm3.h 复制到创建的CORE目录下,这两个文件在固件库中的路径为:\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport
复制到刚才创建的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.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
后面三个文件路径:STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_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)。
参考文献:
keil5在波形仿真的时候出现 Unknown Signal. 的解决方法_keil5 unknown signal-CSDN博客