文章目录
一、MDK5 Keil5中使用标准固件的STM32工程的建立过程
1、下载STM32F10x标准外设库
- 在STM32标准外设库-官方下载地址,选择3.5.0版本下载
- 将下载好的压缩包解压到keil文件夹中保存。
2、STM32工程建立
-
首先新建工程文件夹,打开新建的工程文件夹,在文件夹中再新建以下文件夹
-
在CMSIS文件夹中新建startup文件夹,并存放已经下载完成的官方固件库中的如下文件
需要拷贝的文件位置为STM32F10x_StdPeriph_Lib_V3.5.0 > Libraries > CMSIS > CM3 > DeviceSupport >ST >STM32F10x
与STM32F10x_StdPeriph_Lib_V3.5.0 > Libraries > CMSIS > CM3 > CoreSupport
-
在CMSIS > startup文件中存放启动文件,启动文件位于下载的压缩包中STM32F10x_StdPeriph_Lib_V3.5.0 > Libraries > CMSIS > CM3 > DeviceSupport >ST >STM32F10x > startup > arm
-
Doc文件夹用于存放客户说明文件
文件位于STM32F10x_StdPeriph_Lib_V3.5.0 > Project > STM32F10x_StdPeriph_Template > TrueSTUDIO > STM3210B-EVAL
-
Libraries文件夹存放库文件
库文件位于STM32F10x_StdPeriph_Lib_V3.5.0 > Libraries > STM32F10x_StdPeriph_Driver
-
Project文件夹用于存放自己建立的Keil5工程文件
-
User文件夹存放一部分STM32F10x_StdPeriph_Lib_V3.5.0 > Project > STM32F10x_StdPeriph_Template中的文件,并新建main.c文件
3.新建Keil工程
-
打开Keil5,点击Project新建工程,工程目录选择新建的Fwlib_Template > Project文件夹并设置工程文件名
-
选择STM32F103C8
-
直接关掉下一个弹窗,在工程建立后右击target1,选择Add Group,新建如下文件夹
-
双击文件夹添加文件
在STARTUP文件夹中添加启动文件夹
在USER文件夹中添加main.c和system_stm32f10x.c
在FWLIB文件夹中添加Libraries > inc和Libraries > src中的文件
同理将其他文件夹中的文件添加到工程中
-
添加完成后点击魔法棒,选择C/C++,在Define中添加宏定义USE_STDPERIPH_DRIVER,用于编译头文件#include"stm32f10x.h",之后如下图依次点击,将包含头文件的文件夹添加进来,可以让工程优先在添加进来的文件夹中搜索头文件,最后点击OK保存。
二、基于标准外设库的LED流水灯
1、硬件设计
2、软件设计
(1)编程思路
- 使能GPIO端口时钟;
- 初始化GPIO目标引脚为推挽输出模式;
- 编写程序控制GPIO引脚输出高、低电平。
(2)代码编写
-
1.设置GPIO口时钟,处于使能状态
在标准库函数中,只需要调用开启时钟的函数即可。在STM32中,调用APB2使能配置的函数为
void RCC_APB2PeriphResetCmd(u32 RCC_APB2Periph, FunctionalState NewState)
第一个参数 uint32 RCC_APB2Periph,是指定的端口,我们这里使用GPIOA,故在主函数中写 RCC_APB2Periph_GPIOA。第二个参数是指定端口使能还是失能,我们选择使能,填上 ENABLE。对于A、B、C三个端口的完整调用如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PA,PB,PC端口时钟
-
2.配置GPIO端口模式
在GPIO端口配置中,有一个GPIO_Init函数,专门用来设置端口状态的。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
第一个参数指定端口,我们填入GPIOA。第二个参数是一个结构体变量取地址。是结构体变量的声明,GPIO_InitStruct是结构体变量名。我们跳转GPIO_InitTypeDef定义如下
typedef struct { uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; }GPIO_InitTypeDef;
可知结构体中含有三个参数。GPIO_Pin是指定具体引脚;GPIO_Mode是指定模式,也就是是输入还是输出,怎样输入输出。GPIO_Speed是指定输出的速度。
设置结构体内参数时一定要按照结构体的格式规范写入,打开PA5,PB10,PC14引脚,设置为推挽输出模式,输出速度为50MHZ。最后调用GPIO_Init函数,配置完成。
//定义一个GPIO_InitTypeDef类型的结构体 GPIO_InitTypeDef GPIO_InitStructure; //选择要控制的GPIO引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PA4 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //LED1-->PB9 端口配置 GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz GPIO_SetBits(GPIOB,GPIO_Pin_10); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //LED2-->PC13 端口配置, 推挽输出 GPIO_Init(GPIOC, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
-
3.利用控制高低电平变化的函数实现流水灯
使用GPIO_SetBits函数和GPIO_ResetBits函数设置端口引脚的高低电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
-
4.延时函数
delay.h
#ifndef __DELAY_H #define __DELAY_H #include "stm32f10x.h" // Device header //Delay.h void Delay_us(uint32_t us); void Delay_ms(uint32_t ms); void Delay_s(uint32_t s); #endif
delay.c
#include "stm32f10x.h" //Delay.c /** * @brief 微秒级延时 * @param xus 延时时长,范围:0~233015 * @retval 无 */ void Delay_us(uint32_t xus) { SysTick->LOAD = 72 * xus; //设置定时器重装值 SysTick->VAL = 0x00; //清空当前计数值 SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器 while(!(SysTick->CTRL & 0x00010000)); //等待计数到0 SysTick->CTRL = 0x00000004; //关闭定时器 } /** * @brief 毫秒级延时 * @param xms 延时时长,范围:0~4294967295 * @retval 无 */ void Delay_ms(uint32_t xms) { while(xms--) { Delay_us(1000); } } /** * @brief 秒级延时 * @param xs 延时时长,范围:0~4294967295 * @retval 无 */ void Delay_s(uint32_t xs) { while(xs--) { Delay_ms(1000); } }
-
5.完整主函数
main.c
#include "stm32f10x.h" #include "delay.h" int main(void) { //定义一个GPIO_InitTypeDef类型的结构体 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PA,PB,PC端口时钟 //选择要控制的GPIO引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PA4 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz //调用库函数初始化GPIO GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA4 GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA4 输出高 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //LED1-->PB9 端口配置 GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz GPIO_SetBits(GPIOB,GPIO_Pin_10); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //LED2-->PC13 端口配置, 推挽输出 GPIO_Init(GPIOC, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz GPIO_SetBits(GPIOC,GPIO_Pin_14); //PC13 输出高 while(1) { GPIO_ResetBits(GPIOA,GPIO_Pin_5); //PA5拉低,亮 GPIO_SetBits(GPIOB,GPIO_Pin_10); //PB10拉高,暗 GPIO_SetBits(GPIOC,GPIO_Pin_14); //PC14拉高,暗 Delay_ms(300); //延时300ms GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA5拉高,暗 GPIO_ResetBits(GPIOB,GPIO_Pin_10); //PB10拉低,亮 GPIO_SetBits(GPIOC,GPIO_Pin_14); //PC14拉高,暗 Delay_ms(300); //延时300ms GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA5拉高,暗 GPIO_SetBits(GPIOB,GPIO_Pin_10); //PB10拉高,暗 GPIO_ResetBits(GPIOC,GPIO_Pin_14); //PC14拉低,亮 Delay_ms(300); //延时300ms } }
(3)观察端口波形
在没有示波器条件下,可以使用Keil的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点
-
点开魔术棒,在Target中修改晶振为8.0MHZ
-
再点开Debug,修改以下选项
-
修改完成后,点击打开调试界面,然后打开逻辑分析仪
-
在逻辑分析仪的左上角有一个Set Up选项,点击它。
-
添加引脚
在弹出的窗口中,我们新建观察引脚。添加完成后,一定要把每个的Display Type改成比特。
快捷方式添加引脚
输入PORTX.xx(X为A.B…;xx为引脚号,比如:PORTA.4)然后回车。 -
之后点击左上角运行图标
-
得到如下波形
根据观察结果我们看到,波形都是连续变化的。PA5处LED灯先点亮,经过0.3s端口处电平由低变为高同时PB10的端口电平由高变低,LED灯点亮,每次端口的低电平持续时间为0.3s。
(4)硬件实现
四、参考资料
(1) STM32固件使用手册
(2) keil仿真和使用示波器调波形