目录
- 一、用寄存器地址操作的方式控制流水灯闪烁
- 二、用标准库的方式控制流水灯闪烁
- 1、新建一个标准库工程
- 2、程序代码编写
- 3、编译、下载、结果
- 三、用HAL库+ 按键中断方式控制流水灯运行的暂停、恢复
- 1、STM32CubeMX新建工程
- 2、代码编写
- 3、上板展示
一、用寄存器地址操作的方式控制流水灯闪烁
用寄存器地址操作的方式点亮led流水灯之前已经完成,详细可以参考这篇博客寄存器地址操作:STM32F103的流水灯点亮
二、用标准库的方式控制流水灯闪烁
1、新建一个标准库工程
(1)点击顶上project,选择新建一个工程;
(2)先专门建一个文件夹(stm32f103)来存放工程,在此文件夹内再新建一个文件夹(std-waterled)来存放本次流水灯工程,再给本次工程起个名,并保存;
(3)选择芯片型号,点击OK
这里会弹出一个Keil软件新建工程小助手,我们暂时用不到,叉掉就好;
(4)为新的空白工程添加一些必要文件
Step1:添加启动文件。打开刚刚新建的文件夹(std-waterled),在此文件夹内再新建一个文件夹(Start)用来存放启动文件,目的是防止文件太多,不美观。所有的.s启动文件都可以去网上找到。
再在Start文件夹内添加标准库核心头文件、系统时钟初始化文件、CMSIS内核文件。
最终添加完毕后Start文件夹里面所有文件如下:
Step2:回到Keil,把Step1添加的所有文件复制到工程里面。选中Source Group 1,单击一下,更名为Start。
双击Start,添加必要文件。文件类型选择All files,添加后缀为md.s的启动文件(flash为中容量大小,适用f101、f102、f103),以及标准库核心头文件、系统时钟初始化文件、CMSIS内核文件。
添加完毕后,Start目录下有以下几个文件:
Step3:在工程选项里面添加Start文件的头文件路径,否则软件找不到.h文件。点击魔术棒,点击C/C++,点击Include Paths最右边的三个点。
点击新建,再点击三个点。
选择Start文件夹就成功添加进来了,之后点击OK。会返回到C/C++选择里面,再点击OK,这样就添加成功了。
(5)添加一个main.c
在std-waterled文件夹里面添加一个User文件夹,用来存放main.c。右键Target1,点击Add Group,将添加的New Group重命名为User
右键User,点击添加新文件,选择.c文件,命名为main,路径选择User文件夹,点击Add。这样我们就添加成功了,同样的也可以在工程文件夹里面的User文件夹里面找到这个main.c文件。
最后不要忘了在工程选项里面添加User目录的路径,方法跟添加Start一样。
(6)添加库函数
Step1:在std-waterled里面新建一个文件夹(Library)来存放库函数。将库函数的源文件.c以及头文件.h全部拷贝到Library文件夹里面去。
Step2:回到Keil,右键Target 1,添加组,重命名为Library。右键Library,添加已有文件,将Step1里面拷贝的库函数文件全部添加进去。
Step3:此时库函数还不能直接使用,需要添加库配置文件conf.h。在User文件夹里面添加以下三个文件,it结尾的两个文件是中断服务函数文件,我也顺带添加了。回到Keil软件把这三个文件添加到User组里面。
Step4:点击魔术棒,点击C/C++,添加宏定义USE_STDPERIPH_DRIVER。这是由于头文件stm32f10x.h有一个条件编译。
Step5:最后不要忘了在工程选项里面添加Library目录的路径,方法跟添加Start一样。
(7)验证工程:在main.c输入一个简单的程序,点击全量编译,显示0错误0警告就说明建的工程没有问题。
到这里我们基于库函数的工程就建好了!!!
2、程序代码编写
本次基于标准库的流水灯点亮能用到的函数如下:
在主函数里面如何编写程序呢?以RCC_APB2PeriphClockCmd(APB2外设时钟控制,所有的GPIO端口都在这条总线上)为例。
(1)在库函数文件里面将这个函数复制到主函数里面,右键这个函数跳到其定义
(2)简单读一下函数功能说明。功能是开启或关闭APB2总线上的外设时钟
这个函数有两个参数,第一个可选一堆,这里我们根据自己要使用的端口来选择,我要使用GPIOA,选择RCC_APB2Periph_GPIOA;第二个只能选ENABLE/DISABLE,开启时钟选择ENABLE。
将这两个参数复制到主函数里面去就完成APB2外设时钟函数的编写了。
最后完整的main.c如下:
#include"stm32f10x.h "//软件延时,实物测量t=7200000大概为1svoidDelay(uint32_tt){while(t--);}intmain(){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//APB2外设时钟打开GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO初始化结构体变量GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;//PA0,PA1,PA2三个引脚GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出速度50MhzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据结构体配置,初始化GPIOA端口对应引脚while(1){//PA0亮,PA0输出高电平,其余为低GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);GPIO_WriteBit(GPIOA,GPIO_Pin_2,Bit_RESET);Delay(7200000);//PA1亮,PA1输出高电平GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);GPIO_WriteBit(GPIOA,GPIO_Pin_2,Bit_RESET);Delay(7200000);//PA2亮,PA2输出高电平GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);GPIO_WriteBit(GPIOA,GPIO_Pin_2,Bit_SET);Delay(7200000);}}3、编译、下载、结果
点击魔术棒,点击Debug,选择调试器(我是ST-Lin),点击设置settings
在设置里面注意勾选以下选择。同时在Flash Download里面勾选Reset and Run,这样下载程序就自动跑起来不用复位。
点击编译,确定无误后,下载到板上
上板结果:
问:LED灯的亮/灭周期是通过软件循环延时完成的,其准确周期大致是多少呢?
通过板子实物测量,t=7200000时候大概周期为1s
三、用HAL库+ 按键中断方式控制流水灯运行的暂停、恢复
写法跟标准库一样,先确定要用到HAL库的函数,然后右键找到函数定义,确定参数。
1、STM32CubeMX新建工程
(1)打开STM32CubeMX软件,点击Start My project from MCU进入
(2)搜索芯片型号STM32F103C8T6
双击
(3)SYS配置,Debug下拉选择Serial Wire
(4)RCC配置,高速时钟下拉选择外部晶振
(5)GPIO设置。点击PA0、PA2、PA3设置为GPIO_Output,点击PC13设置为GPIO_EXTI13。同时点击左边的PC13那一行,设置为下降沿外部中断、内部上拉。
(6)NVIC设置。勾选开启中断
(7)时钟配置72Mhz。点击Clock Configuration,在红色框位置输入72,点击回车,会询问你,点击OK。
(8)点击Project Manager,给工程起个名,下面选择MDk-ARM,后面的版本根据Keil版本选择;进入code Generator,勾选。完成之后就可以点击右上角GENERATE CODE创建Keil工程了。
2、代码编写
在主函数main.c内编写代码
(1)在/* USER CODE BEGIN PV/ ~ /USER CODE BEGIN PV */里面编写全局变量
代码如下:
uint8_trun_flag=0;//0运行,1暂停uint8_texti_trig_flag=0;//中断标记uint8_tled_step=0;//流水灯步骤 0,1,2(2)中断回调函数放在/* USER CODE BEGIN 4 */内
代码如下:
voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_Pin==GPIO_PIN_13){exti_trig_flag=1;}}(3)while(1)里面的流水灯逻辑放在/* USER CODE BEGIN 3 */内
代码如下:
//处理中断请求if(exti_trig_flag==1){HAL_Delay(20);if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){run_flag=!run_flag;}exti_trig_flag=0;}if(run_flag==0){//全部灯先熄灭HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);switch(led_step)//状态机判断流水灯步骤,即哪个灯亮{case0:HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);break;case1:HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);break;case2:HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);break;}HAL_Delay(1000);led_step++;if(led_step>=3){led_step=0;}}// run_flag ==1 什么都不做,保持IO口原来电平3、上板展示
外部中断上板结果
上板可以发现流水灯初始按照PA0、PA1、PA2顺序循环点亮,每盏灯点亮时长为1秒。使用杜邦线瞬时短接PC13引脚与GND时触发外部中断,流水灯立刻暂停,保持当前正在点亮的LED状态不变;再次瞬时短接PC13与GND,再次触发中断,流水灯从暂停位置继续恢复流水循环。