使用STM32 CubeMX和HAL库实现按键控制流水灯亮灭
目录
一、在CubeMX创建项目
已配置好java环境和CubeMX。
CubeMX官网下载传送门:CubeMX – 工具与软件
1.第一步
2.第二步
3.第三步
将CubeMX中的"DEBUG"栏目设置为"Serial Wire"
意味着你选择了Serial Wire Debug(SWD)接口作为调试接口。
SWD是一种调试接口协议,通常用于与ARM Cortex-M微控制器进行调试和编程交互。
当你选择SWD接口时,它将作为与目标芯片之间的调试通信通道。SWD接口使用两根线进行通信:SWDIO(Serial Wire Debug I/O)和SWCLK(Serial Wire Debug Clock)。SWDIO用于数据传输和调试信息的收发,而SWCLK用于时钟同步。
通过使用SWD接口进行调试,你可以实现以下功能:
总之,选择将CubeMX中的"DEBUG"栏目设置为"Serial Wire"意味着你选择了SWD接口作为调试接口,以便进行嵌入式软件的调试和开发。
4.第四步
在STM32F103C8T6微控制器中,GPIO
口是通过APB2
总线进行控制和访问的。
在STM32F103C8T6微控制器中,HSE(High-Speed External)
和LSE(Low-Speed External)
分别用于控制不同的外部时钟源。
-
HSE
(高速外部时钟): - 主时钟(SYSCLK):HSE可以作为主时钟源,提供较高的时钟频率。通过适当的配置和分频系数,可以将HSE的频率作为系统时钟(SYSCLK)。
- PLL(锁相环)输入:HSE可以作为PLL的输入时钟源,用于生成更高的时钟频率。通过配置PLL参数,可以将HSE的频率倍增并输出为SYSCLK。
-
LSE
(低速外部时钟): - RTC(实时时钟):LSE被用作STM32F103C8T6的RTC模块的时钟源。RTC模块用于提供实时时钟和日历功能,可以用于时间戳记录、定时器和闹钟功能等。
注意:STM32F103C8T6微控制器还可以使用内部时钟源(
HSI
和LSI
)作为主时钟和RTC时钟源。HSI
是内部高速时钟,而LSI
是内部低速时钟。选择使用HSE
和LSE
作为时钟源是根据具体需求和应用场景来决定的。
在STM32F103C8T6微控制器中,APB2总线的时钟并不直接由HSE(高速外部时钟)控制。APB2总线的时钟源可以选择来自系统时钟(SYSCLK)或者从PLL(锁相环)输出的时钟。
在STM32F103C8T6中,HSE可以作为主时钟(SYSCLK)的输入源之一,通过PLL倍频后输出。主时钟(SYSCLK)可以进一步分频得到APB2总线的时钟。
具体来说,时钟源的配置如下:
1. 如果HSE被配置为主时钟(SYSCLK)的输入源,那么APB2总线的时钟也将来自HSE通过PLL的输出。
2. 如果HSE没有被配置为主时钟(SYSCLK)的输入源,而是使用内部时钟源(如HSI),那么APB2总线的时钟将与主时钟(SYSCLK)相同,来自HSI或者通过PLL倍频后输出的HSI。
因此,APB2总线的时钟源是与主时钟(SYSCLK)相关的,并不直接由HSE控制。配置主时钟(SYSCLK)的时钟源,间接地影响了APB2总线的时钟频率。
所以,为了通过APB2控制GPIO口,这里选择将将HSE
设置为Crystal/Ceramic Resonator
。
5.第五步
根据需要配置相应的管脚,点击并选择管脚模式即可。这里选择GPIO_Output
,输出模式。
我在A8的地方加了一个按键,准备用来做中断控制的实验,选的是GPIO_EXTI8
6.第六步
由上面提供的资料,如果HSE被配置为主时钟(SYSCLK)的输入源,那么APB2总线的时钟也将来自HSE通过PLL的输出。
所以在时钟架构这个页面选择PLLCLK。
7.第七步
生成代码
二、代码编写实现流水灯
1.编写代码并实现
因为重新构建了好几次这个工程,为了方便起见我把管脚改成了这样的设置,别的不变。
用下面这段代码替换主函数中的while(1){}。
while (1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//PA4亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);//PC15亮灯
HAL_Delay(1000);//延时1s
}
对HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
语句的解释:
是GPIO输出的HAL库函数,可以设定或改变当前引脚电平值
HAL_GPIO_WritePin(GPIO_TypeDef* GPIOX,Uint16_t GPIO_Pin,GPIO_PinState pinstate);
GPIOX代表目标引脚的端口号,如GPIOA
GPIO_Pin代表目标引脚的引脚号
pinstate代表当前引脚的高低电平,高电平(GPIO_PIN_SET)、低电平(GPIO_PIN_RESET)。
运行结果如下
2.观察GPIO端口的输出波形
-
在keil中点击魔法棒,进入Target界面,设置晶振为8MHz
-
进入Debug界面进行如下设置,最后点击OK
-
点击Debug进入调试,打开逻辑分析仪(Logic Analyzer),选择要观察的引脚
-
观察波形
动态图如下(图片上传有大小限制所以加了点倍速):
可以观察到引脚为低电平的灯亮,高电平的灯不亮,高低电平转换周期(LED闪烁周期)为1s左右。
三、代码编写实现按键控制流水灯亮灭
1.新建工程
(1)配置中断函数
在CubeMX中将GPIO模式(GPIO mode)设置为"上升和下降都可触发"(Rising and Falling Edge Triggered)意味着你配置了GPIO引脚的输入触发方式,使其能够在引脚电平上升沿和下降沿发生变化时触发中断或事件。
当你将GPIO模式设置为"上升和下降都可触发"时,GPIO引脚会监测其输入电平的变化,并在引脚电平从低电平(0)变为高电平(1)或从高电平(1)变为低电平(0)时触发相应的中断或事件。
这种触发方式常用于需要对输入信号的变化做出实时响应的应用场景。例如,当一个按钮或开关被按下或释放时,你可以配置相应的GPIO引脚为"上升和下降都可触发"模式,从而使得微控制器能够在按钮按下或释放时立即检测到并执行相应的中断服务程序或处理逻辑。
通过CubeMX配置GPIO模式为"上升和下降都可触发",它将生成相应的初始化代码,以使你能够轻松地设置和使用这种触发方式,并在中断或事件回调函数中处理GPIO引脚的变化。
在GPIO引脚上选择Pull-up或Pull-down电阻的目的是为了确保在引脚未连接到外部设备或信号时,引脚的电平保持在已定义的状态(高电平或低电平),从而避免电平浮动或不确定性。
当GPIO引脚未连接到外部设备时,引脚可能会处于高阻抗状态,这可能导致电平不确定或受到干扰。通过选择Pull-up或Pull-down电阻,可以将引脚拉向预定义的电平,以确保引脚在未连接时有一个已知的状态。
选择Pull-up电阻意味着将引脚连接到Vcc(电源正电压)或逻辑高电平,而选择Pull-down电阻意味着将引脚连接到GND(地)或逻辑低电平。具体选择哪种电阻取决于你的应用需求和电路设计。
一些常见的应用情况包括:
通过选择适当的Pull-up或Pull-down电阻,你可以提高GPIO引脚的可靠性和稳定性,减少电平浮动带来的干扰,并确保在引脚未连接时具有可预测的电平状态。
2.进入NVIC界面设置中断优先级
在CubeMX中,将EXTI线[9:5]的中断设置为"Enabled"表示启用了EXTI线9到EXTI线5之间的中断功能。
将中断优先级设置为1的目的是确保该中断具有较高的优先级,以优先处理该中断。
中断优先级决定了当多个中断同时发生时,哪个中断具有更高的优先级并首先得到处理。较低的优先级值表示较高的优先级。
将中断优先级设置为1意味着将该中断设置为较高的优先级。这对于需要及时响应和处理的重要中断非常重要。例如,如果你有一个实时任务需要在最短的时间内得到响应和处理,你可以将该任务的中断优先级设置为1,以确保它能够尽快得到处理。
3.配置时钟
4.生成工程文件
2.编写代码并实现

这里我设置的是根据A8的不同中断控制流水灯亮灭
代码逻辑如下:
首先,通过调用HAL_GPIO_ReadPin函数来读取引脚GPIOA的状态(GPIO_PIN_8)。
如果引脚状态为GPIO_PIN_SET,即高电平,则将标志位flag设置为1,表示高电位。
如果引脚状态为GPIO_PIN_RESET,即低电平,则将标志位flag设置为0,表示低电位。
首先定义一个标志位代表高低电平。
uint16_t flag=0;
根据代码逻辑编写中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_8){//获取A8的电位
GPIO_PinState pinState = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8);//高电位
if(pinState==GPIO_PIN_SET)//高电位流水灯亮
{
flag=1;//高电位
}
else if(pinState==GPIO_PIN_RESET)//低电位流水灯灭
{
flag=0;//低电位
}
}
}
将流水灯控制代码加入到主函数中
while (1)
{
if(flag==1){
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//PA4亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);//PC15亮灯
HAL_Delay(1000);//延时1s
}
else if(flag==0){
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(5000);//延时5s
}
}
实现结果: