使用按键和串口,记录按键在不消抖的情况下触发的次数
stm32
按键用中断方式,中断一次,给一个全局变量累加一次,主循环里定期输出这个全局变量的值,通过观察这个值的变化情况就可以看到抖动次数
代码通过CHATGPT生成,我就没仔细研究了,自己稍微改改吧
#include "stm32f10x.h"
#include "stdio.h"
// 定义用于计数的变量
volatile uint32_t pulseCount = 0;
void USART2_Init(void) {
// 启用USART2和GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART2的引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART2的波特率
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
// 启用USART2
USART_Cmd(USART2, ENABLE);
}
void SysTick_Init(void) {
// 配置系统时钟为72MHz
SystemInit();
// 设置SysTick定时器的重装载值
SysTick->LOAD = 72000000; // 1秒钟的计数值
// 设置SysTick定时器的时钟源为内部时钟,并启动定时器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk;
}
void EXTI_Configuration(void) {
// 启用AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 配置IO口为浮空输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置EXTI中断线路,PA0作为按键输入
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发中断
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
void NVIC_Configuration(void) {
// 配置中断优先级分组为组1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 配置EXTI中断通道
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
// 计数加1
pulseCount++;
}
}
void USART_SendString(const char* str) {
while (*str) {
// 等待发送缓冲区为空
while ((USART2->SR & USART_SR_TXE) == 0);
// 将字符发送到USART2
USART2->DR = *str++;
// 等待发送完成
while ((USART2->SR & USART_SR_TC) == 0);
}
}
int main(void) {
USART2_Init();
SysTick_Init();
EXTI_Configuration();
NVIC_Configuration();
while (1) {
// 等待1秒钟
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
// 重装载SysTick定时器
SysTick->VAL = 0;
// 将计数值转换为字符串
char buffer[16];
sprintf(buffer, "Count: %lu\r\n", pulseCount);
// 发送计数值到串口
USART_SendString(buffer);
}
}
使用外部中断做计数就好了,如果按键按下是低电平,就计算下降沿的个数,并1s上传一次下降沿个数通过串口!
#include "stm32f4xx.h"
#include <stdbool.h>
//定义GPIO引脚和UART端口的常量
#define BUTTON_PIN 0
#define LED_PIN 7
#define UART_TX_PIN 9
#define UART_RX_PIN 10
#define UART_BAUDRATE 9600
// 用于跟踪按钮状态和触发器计数的全局变量
static bool button_state = false;
static uint32_t button_triggers = 0;
// 功能原型
static void setup_gpio(void);
static void setup_uart(void);
static void send_uart(const char* message);
static void handle_button(void);
int main(void)
{
// 初始化硬件外围设备
setup_gpio();
setup_uart();
// 通过UART发送初始消息
send_uart("Press the button to start counting.");
while (true) {
// 检查按钮状态并处理任何更改
handle_button();
// 如果按钮被触发,则更新计数并通过UART发送消息
if (button_state) {
button_state = false;
button_triggers++;
send_uart("Button triggered. Count is now %d.", button_triggers);
}
}
}
static void setup_gpio(void)
{
// 启用GPIOA和USART1外围时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 将引脚PA0配置为具有下拉电阻器的输入
GPIOA->MODER &= ~(GPIO_MODER_MODE0_Msk);
GPIOA->PUPDR |= GPIO_PUPDR_PUPD0_1;
// 将引脚PA7配置为LED的输出
GPIOA->MODER &= ~(GPIO_MODER_MODE7_Msk);
GPIOA->MODER |= GPIO_MODER_MODE7_0;
// 为UART引脚启用备用功能模式
GPIOA->MODER &= ~(GPIO_MODER_MODE9_Msk | GPIO_MODER_MODE10_Msk);
GPIOA->MODER |= GPIO_MODER_MODE9_1 | GPIO_MODER_MODE10_1;
GPIOA->AFR[1] |= (7 << GPIO_AFRH_AFSEL9_Pos) | (7 << GPIO_AFRH_AFSEL10_Pos);
}
static void setup_uart(void)
{
//启用UART时钟并重置其设置
RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
// 设置波特率,启用USART1,并启用变送器
USART1->BRR = HAL_RCC_GetHCLKFreq() / UART_BAUDRATE;
USART1->CR1 |= USART_CR1_UE | USART_CR1_TE;
}
static void send_uart(const char* message, ...)
{
char buffer[256];
va_list args;
va_start(args, message);
vsnprintf(buffer, sizeof(buffer), message, args);
va_end(args);
for (int i = 0; buffer[i] != '\0'; i++) {
USART1->DR = buffer[i];
while ((USART1->SR & USART_SR_TXE) == 0);
}
}
static void handle_button(void)
{
// 如果按钮当前被按下且没有抖动,则将状态设置为“已触发”
if ((GPIOA->IDR & (1 << BUTTON_PIN)) == 0 && !button_state) {
// 等待一小段延迟,以防止抖动快速触发按钮多次
uint32_t delay = 10000;
while (delay-- > 0);
// 检查按钮是否仍处于按下状态,如果是,则将其状态设置为“已触发”
if ((GPIOA->IDR & (1 << BUTTON_PIN)) == 0) {
GPIOA->BSRR |= (1 << LED_PIN);
button_state = true;
}
}
// 如果按钮当前已释放,请将其状态设置为“未触发”
else if ((GPIOA->IDR & (1 << BUTTON_PIN)) != 0) {
GPIOA->BRR |= (1 << LED_PIN);
button_state = false;
}
}
此代码假定你的按钮连接到PA0,LED连接到PA7,UART模块连接到引脚PA9和PA10。该代码持续监控按钮状态,并在每次按下和释放按钮时增加一个全局“button_triggers”变量,而不发生抖动。然后,它通过UART发送一条消息,让用户知道按钮被触发以及当前计数是多少。LED也会短暂点亮,表示按钮被触发。
假设你使用HAL库进行编程,那么我下面展示的代码将对你理解按键有些帮助。
找一个工程模板,复制粘贴下面的代码现象观察,祝您调试好运。
main.c中的代码
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#define KEY1_Pin GPIO_PIN_1
#define KEY1_GPIO_Port GPIOA
void my_gpio_set(void){
__HAL_RCC_GPIOA_CLK_ENABLE(); // 假设按键使用是A口时钟
GPIO_InitStruct.Pin = KEY1_Pin; // 引脚
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 上拉输入
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
uint16_t key_cnt = 0; // 定义一个按键计算用于记录按键按下的电平次数
int main(void)
{
HAL_Init(); // HAL库初始化
SystemClock_Config(); // 系统时钟配置
MX_GPIO_Init(); // gpio初始化
MX_USART1_UART_Init(); // 串口初始化
my_gpio_set();
while(1)
{
// 在主循环中一直等待按键按下
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
{
printf("key down\r\n"); // 发送给串口进行观察
printf("key down count = %d\r\n",key_cnt++); // 发送给串口进行观察
HAL_Delay(20); // 简单延迟进行消抖处理
}
else
{
key_cnt = 0; // 按键弹起,清一下次数
}
}
}
不知道你这个问题是否已经解决, 如果还没有解决的话:在这里我想把串口这一部分内容好好说一下,从事stm32开发已经好几年了,自以为对stm32已经掌握的很好了,后来才发现自己只是浮于表面,没有好好的深入学习stm32的底层
解决方案:
记录按键在没有消抖的情况下触发的次数,需要在按键按下时通过中断服务函数实现记录。具体步骤如下:
步骤1:配置GPIO引脚的输入输出状态
在STM32的GPIO寄存器中,通过设置输入输出状态位(MODER)来配置GPIO引脚的输入输出状态。例如,若要设置PC13引脚为输入状态,则需设置MODER对应位为00,代码如下:
GPIOC->MODER &= ~(3<<(132)); //清零 GPIOC->MODER |= 0<<(132); //设置PC13为输入状态
步骤2:配置GPIO引脚的上拉下拉状态(可选)
在GPIO寄存器中,通过设置前置上拉/下拉电阻(PUPDR)来配置GPIO引脚的上拉下拉状态。例如,在PC13引脚的输入状态下开启拉电阻,可以设置PUPDR对应位为10,代码如下:
GPIOC->PUPDR &= ~(3<<(132)); //清零 GPIOC->PUPDR |= 2<<(132); //设置PC13为上拉输入
步骤3:配置按键对应的中断线
STM32的每个GPIO引脚都可以配置成相应的输入中断线,从而实现中断的响应。仍以PC13引脚为例,在中断控制器(EXTI)中配置PC13为中断线,代码如下:
GPIOC->AFR[1] &= ~(0xf<<04); //清零 GPIOC->AFR[1] |= 0<<04; //选择AF0 EXTI->IMR |= 1<<13; //开启PC13对应的中断线 EXTI->FTSR |= 1<<13; //设置PC13为下降沿触发
步骤4:编写中断服务函数
在中断控制器中配置了GPIO引脚的中断线之后,还需编写中断服务函数(IRQn),来实现中断的响应。中断服务函数需要进行相关的处理,例如计数器加1等操作。代码如下:
//计数器 static uint32_t cnt = 0;
//中断服务函数 void EXTI15_10_IRQHandler(void) { if (EXTI->PR & (1 << 13)) //判断是否为PC13引脚中断 { if (!(GPIOC->IDR & (1 << 13))) //判断是否为按键按下 { ++cnt; //计数器加1 } EXTI->PR |= (1 << 13); //清除中断标志位 } }
步骤5:串口输出按键响应次数
将计数器所记录的按键响应次数通过串口输出到电脑进行查看。这里需要用到STM32的串口通信模块,通过串口通信模块将记录的按键响应次数输出到电脑端。
下面给出一份串口中断服务函数(带代码注释)供参考:
//串口中断服务函数 void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断是否接收到数据 { //读取接收到的数据 uint8_t recv_data = USART_ReceiveData(USART1);
//根据协议判断接收到的数据类型
switch (recv_data)
{
case 'c': //请求按键响应次数
USART_SendString("Key responses: ");
USART_SendInt(cnt);
USART_SendString("\r\n");
break;
default:
break;
}
}
}
上述中断服务函数实现了一个简单的协议,当电脑端发送字符'c'到单片机,单片机会将记录的按键响应次数返回给电脑端。需要注意的是,如果单片机一次内接收到的数据量较大,需要进行数据缓存处理,避免数据溢出或者丢失。
可以借鉴下
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;
if(mode==1)key_up=1;
if(key_up&& KEY按下)
{
delay_ms(10);
key_up=0;
if(KEY确实按下)
{
return KEY_Value;
}
}else if(KEY没有按下)key_up=1
return没有按下
}
该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
以下是一个简单的 STM32 代码,使用按键和串口记录按键在不消抖的情况下触发的次数。代码中使用了外部中断和定时器中断来检测按键的状态,并使用串口将按键触发次数发送到 PC。
首先,连接按键到 MCU 的 GPIO 引脚上,并将引脚配置为输入模式。在程序中,我们假设按键连接到了 GPIOA
的第 0 号引脚上,可以使用以下代码进行 GPIO 的初始化:
// 初始化 GPIO
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);
然后,使用外部中断检测按键的状态。可以使用 HAL_GPIO_EXTI_GetFlag()
函数检测是否发生了外部中断,并使用 HAL_GPIO_EXTI_IRQHandler()
函数处理外部中断。在中断处理函数中,可以统计按键触发的次数,并使用串口将次数发送到 PC。以下是示例代码:
// 定义按键触发次数计数器
volatile uint32_t g_keyCount = 0;
// GPIO 中断处理函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// 检测按键状态
if (HAL_GPIO_ReadPin(GPIOA, GPIO_Pin) == GPIO_PIN_RESET) {
// 如果按键被按下,增加计数器的值
g_keyCount++;
// 将计数器的值发送到 PC
char buf[20];
sprintf(buf, "key count: %lu\r\n", g_keyCount);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);
}
}
// 定时器中断处理函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 检测按键状态
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
// 如果按键被按下,增加计数器的值
g_keyCount++;
// 将计数器的值发送到 PC
char buf[20];
sprintf(buf, "key count: %lu\r\n", g_keyCount);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), HAL_MAX_DELAY);
}
}
int main(void)
{
// 初始化 GPIO 和 UART
// ...
// 配置外部中断和定时器中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_TIM_Base_Start_IT(&htim6);
while (1)
{
// ...
}
}
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
在程序中,我们使用了一个定时器中断,定时检测按键的状态。可以使用 HAL_TIM_Base_Start_IT()
函数启动定时器,并在定时器中断处理函数中检测按键的状态。需要注意的是,在定时器中断处理函数中,需要使用 HAL_GPIO_ReadPin()
函数检测按键的状态,而不是直接读取 GPIO 引脚的值,否则可能会出现误触发。
希望这个代码可以帮助你实现使用按键和串口记录按键在不消抖的情况下触发的次数。
如果以上回答对您有所帮助,点击一下采纳该答案~谢谢
根据您的要求,您想要使用按键和串口记录在不消抖的情况下按键触发的次数。以下是一个大致的步骤来实现这个功能:
请注意,具体的实现细节和代码将取决于您使用的具体硬件平台和编程语言。以上步骤提供了一个大致的指导,您可以根据自己的需求和硬件平台进行适当的调整和实现。
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOx, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_Init(GPIOx, &GPIO_InitStructure);
uint16_t key_counter = 0;
while (1) {
if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == RESET) { // 检测按键是否被按下
while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == RESET); // 等待按键松开
key_counter++; // 记录按键触发次数
}
}