stm32f103c8t6按键控制程序开始和结束
时间: 2025-07-02 17:17:01 浏览: 10
<think>我们正在使用STM32F103C8T6微控制器,通过按键控制程序的启动和停止。这里的关键点在于按键的检测和状态管理。通常,我们会使用一个全局变量作为标志(flag)来表示程序的运行状态。按键被按下时,切换这个标志的状态。步骤:1.初始化按键对应的GPIO(通常设置为输入模式,使用上拉或下拉电阻,或者使用外部上拉/下拉)2.在程序中循环检测按键状态(或者使用外部中断,但这里我们先使用轮询方式)3.设计一个状态标志,例如`programRunning`,初始状态为停止(比如0表示停止,1表示运行)4.当检测到按键按下时,改变`programRunning`的状态(注意消抖)5.根据`programRunning`的状态,执行或停止相应的程序功能注意:按键消抖是必要的,可以通过延时或软件定时器实现。下面是一个简单的示例代码(使用HAL库,轮询方式):假设按键连接在PC13(STM32F103C8T6开发板常见的按键引脚,如蓝色药丸板),低电平有效(即按键按下时引脚为低电平,因为通常外部有上拉电阻)。我们使用一个简单的状态机来避免按键的抖动和重复触发。代码结构如下:
```#include"stm32f1xx_hal.h"
//定义状态标志volatile uint8_tprogramRunning=0;//0:停止,1:运行//按键引脚定义#define KEY_PINGPIO_PIN_13#define KEY_PORT GPIOC//按键状态uint8_tkeyState=1;//初始状态为高电平(未按下)
uint8_tlastKeyState =1; //上一次的按键状态intmain(void) {HAL_Init();SystemClock_Config();//初始化按键引脚为输入上拉模式__HAL_RCC_GPIOC_CLK_ENABLE();GPIO_InitTypeDefGPIO_InitStruct= {0};GPIO_InitStruct.Pin =KEY_PIN;GPIO_InitStruct.Mode =GPIO_MODE_INPUT;GPIO_InitStruct.Pull =GPIO_PULLUP;HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct);while(1) {//读取按键状态keyState= HAL_GPIO_ReadPin(KEY_PORT,KEY_PIN);//检测下降沿(按键按下)if(lastKeyState==1 &&keyState==0){//消抖,延时一段时间再检测HAL_Delay(20); //20ms消抖keyState =HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN);if (keyState==0){ //确认按下programRunning= !programRunning; //切换运行状态//等待按键释放(避免连续触发)while (HAL_GPIO_ReadPin(KEY_PORT,KEY_PIN)==0);HAL_Delay(20);//释放消抖}}lastKeyState= keyState;//更新上一次按键状态//根据程序状态执行相应操作if (programRunning) {//执行程序功能//例如:点亮LED等} else{//停止状态,可以执行停止后的操作(如关闭外设等)}}
}```但是,上面的代码在while循环中使用了延时消抖,这可能会影响其他任务的执行。在实际应用中,我们可能会使用非阻塞的方式,比如通过系统滴答定时器(SysTick)或硬件定时器来计时消抖。另一种更高效的方式是使用外部中断来检测按键,并在中断服务函数中处理状态切换(但中断中同样需要进行消抖处理,通常通过设置标志,在主循环中处理)。下面给出使用外部中断的方式:1.配置按键引脚为外部中断模式(下降沿触发)2.在中断服务函数中设置一个按键按下的标志,并记录时间3.在主循环中检测这个标志,并进行消抖和状态切换代码示例(中断方式):```#include "stm32f1xx_hal.h"volatileuint8_t programRunning =0;
volatile uint8_tkeyPressed=0;//按键按下标志volatile uint32_tkeyPressTime =0; //记录按下时间#defineKEY_PIN GPIO_PIN_13#defineKEY_PORTGPIOCint main(void){HAL_Init();SystemClock_Config();//初始化按键引脚为外部中断模式__HAL_RCC_GPIOC_CLK_ENABLE();GPIO_InitTypeDef GPIO_InitStruct ={0};GPIO_InitStruct.Pin= KEY_PIN;GPIO_InitStruct.Mode= GPIO_MODE_IT_FALLING;//下降沿触发GPIO_InitStruct.Pull =GPIO_PULLUP;HAL_GPIO_Init(KEY_PORT,&GPIO_InitStruct);//配置中断优先级HAL_NVIC_SetPriority(EXTI15_10_IRQn,0,0);HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);while(1) {if(keyPressed){//消抖:判断按下时间是否超过20ms(这里简化处理,实际可以用HAL_GetTick()计算时间差)//注意:在中断中已经等待了20ms,所以这里可以直接处理//但是,我们可以在中断中只设置标志,在主循环中消抖//这里我们改变策略:在中断中只记录按下事件,主循环中延时消抖后再次确认HAL_Delay(20);if(HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) ==GPIO_PIN_RESET){ //再次确认按下programRunning= !programRunning;//等待按键释放while(HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) ==GPIO_PIN_RESET);}keyPressed =0;//清除标志}//根据programRunning执行相应操作// ...}}
//外部中断服务函数void EXTI15_10_IRQHandler(void){if(__HAL_GPIO_EXTI_GET_IT(KEY_PIN) !=RESET) {__HAL_GPIO_EXTI_CLEAR_IT(KEY_PIN);keyPressed =1; //设置按键按下标志}}
```但是,上面的中断方式中,我们又在主循环中使用了延时,这仍然会阻塞主循环。更好的方法是使用状态机和非阻塞的定时器来消抖。考虑到时间,我们给出一个使用状态机的非阻塞轮询方式(推荐):我们可以在主循环中每隔一段时间(比如10ms)检测一次按键,然后通过状态机来消抖。状态机设计:状态0:等待按下状态1:检测到按下,等待稳定(延时20ms)状态2:确认按下,等待释放状态3:释放消抖代码示例:
```#include "stm32f1xx_hal.h"volatileuint8_t programRunning =0;#defineKEY_PIN GPIO_PIN_13#defineKEY_PORTGPIOC//按键状态机typedefenum {KEY_STATE_IDLE,//空闲,等待按下KEY_STATE_PRESS_DELAY,//按下消抖KEY_STATE_PRESSED,//已按下(稳定)KEY_STATE_RELEASE_DELAY//释放消抖} KeyState;KeyStatekeyState= KEY_STATE_IDLE;
uint32_tkeyTimer=0;//用于计时intmain(void) {HAL_Init();SystemClock_Config();//初始化按键__HAL_RCC_GPIOC_CLK_ENABLE();GPIO_InitTypeDef GPIO_InitStruct ={0};GPIO_InitStruct.Pin= KEY_PIN;GPIO_InitStruct.Mode= GPIO_MODE_INPUT;GPIO_InitStruct.Pull= GPIO_PULLUP;HAL_GPIO_Init(KEY_PORT, &GPIO_InitStruct);uint32_tlastTick= HAL_GetTick();while(1) {uint32_tcurrentTick= HAL_GetTick();//每10ms执行一次按键检测if (currentTick- lastTick >=10) {lastTick =currentTick;uint8_t keyValue= HAL_GPIO_ReadPin(KEY_PORT,KEY_PIN);//读取按键值switch(keyState){case KEY_STATE_IDLE:if (keyValue==0){ //按键按下(低电平)keyState= KEY_STATE_PRESS_DELAY;keyTimer =currentTick; //记录当前时间}break;case KEY_STATE_PRESS_DELAY:if(keyValue ==1) {//在消抖期间按键释放,认为是抖动keyState= KEY_STATE_IDLE;} elseif (currentTick- keyTimer >=20) {//消抖时间到keyState= KEY_STATE_PRESSED;}break;case KEY_STATE_PRESSED://这里可以执行按键按下的动作,但我们希望等到释放后再切换状态if(keyValue ==1) {//按键释放keyState= KEY_STATE_RELEASE_DELAY;keyTimer =currentTick;}break;caseKEY_STATE_RELEASE_DELAY:if (keyValue==0){ //再次按下,忽略(在释放消抖期间又按下,认为是抖动)//可以不做处理,或者重新进入按下消抖} elseif (currentTick- keyTimer >=20) {//消抖完成//切换程序运行状态programRunning= !programRunning;keyState= KEY_STATE_IDLE;}break;default:keyState =KEY_STATE_IDLE;break;}}//根据programRunning执行程序功能if (programRunning) {//运行状态} else{//停止状态}}
}```以上代码使用了状态机和非阻塞方式,每10ms检测一次按键,消抖时间为20ms。这样主循环中的其他任务不会因为按键检测而被长时间阻塞。根据实际需求,你可以将按键检测放在定时器中断中,或者使用RTOS的任务来处理。最后,根据用户的问题,我们生成了以下相关问题:
§§相关问题§§1.如何优化STM32按键检测以降低功耗?2.除了按键控制,还有哪些方式可以控制程序的启动和停止?3.在STM32中,如何使用外部中断实现按键的双击和长按功能?</think>### 基于STM32F103C8T6的按键控制程序启停实现
#### 一、硬件设计要点
1. **按键连接**:
- 推荐使用GPIO引脚(如PA0)连接按键,外部配置10kΩ上拉电阻
- 按键另一端接地,按下时产生低电平信号
- 电路示例:
```
VDD ── 10kΩ ──┬── PA0
│
BUTTON ───────┴── GND
```
2. **消抖处理**:
- 机械按键会产生5-10ms抖动[^1]
- 推荐软件消抖(延时检测)或硬件RC滤波
#### 二、软件实现(使用HAL库)
```c
#include "stm32f1xx_hal.h"
// 状态标志定义
volatile uint8_t programRunning = 0; // 0:停止 1:运行
uint32_t lastPressTime = 0; // 记录上次按键时间
int main(void) {
HAL_Init();
SystemClock_Config();
// 按键GPIO初始化(PA0)
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
while (1) {
// 检测按键按下(低电平有效)
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
// 消抖处理(20ms延时)
HAL_Delay(20);
// 确认按键仍被按下
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
// 防连按处理(300ms间隔)
if (HAL_GetTick() - lastPressTime > 300) {
programRunning = !programRunning; // 切换运行状态
lastPressTime = HAL_GetTick();
// 等待按键释放
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
}
}
}
// 根据状态执行程序
if (programRunning) {
// 此处添加需要运行的功能代码
// 示例:LED闪烁
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
} else {
// 停止状态的处理(如关闭外设)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
}
}
}
```
#### 三、关键实现技术
1. **状态机控制**:
- 使用`programRunning`全局标志位控制程序状态
- 按键触发状态翻转:`programRunning = !programRunning`
2. **消抖优化**:
- 双重检测机制(延时前后各检测一次)
- 防连按设计(300ms间隔锁定)
3. **中断优化方案**(可选):
```c
// 中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_0) {
if (HAL_GetTick() - lastPressTime > 300) {
programRunning = !programRunning;
lastPressTime = HAL_GetTick();
}
}
}
```
#### 四、调试建议
1. 使用LED指示当前状态:
- 运行状态:LED闪烁
- 停止状态:LED常灭
2. 串口输出状态信息:
```c
printf("Program state: %s\n", programRunning ? "RUNNING" : "STOPPED");
```
3. 用示波器检测按键信号质量
> **注意事项**:
> - 按键检测需在主循环中持续执行
> - 长时间任务应拆分为状态机避免阻塞按键检测
> - 低功耗场景需配置GPIO唤醒功能[^2]
阅读全文
相关推荐














