花生了什么树694 2023-06-05 22:21 采纳率: 0%
浏览 69
已结题

使用按键和串口,记录按键在不消抖的情况下触发的次数

使用按键和串口,记录按键在不消抖的情况下触发的次数
stm32

img

  • 写回答

9条回答 默认 最新

  • qllaoda 2023-06-05 23:10
    关注

    按键用中断方式,中断一次,给一个全局变量累加一次,主循环里定期输出这个全局变量的值,通过观察这个值的变化情况就可以看到抖动次数
    代码通过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);
        }
    }
    
    
    
    评论 编辑记录
  • 少林and叔叔 2023-06-06 09:24
    关注

    使用外部中断做计数就好了,如果按键按下是低电平,就计算下降沿的个数,并1s上传一次下降沿个数通过串口!

    评论
  • 虚白在敲代码 2023-06-06 11:54
    关注
    #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也会短暂点亮,表示按钮被触发。

    评论
  • 独处东汉 2023-06-08 13:45
    关注

    假设你使用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; // 按键弹起,清一下次数
        }
      }
    }
      
    
    评论
  • CSDN-Ada助手 CSDN-AI 官方账号 2023-06-12 14:32
    关注
    不知道你这个问题是否已经解决, 如果还没有解决的话:
    • 这有个类似的问题, 你可以参考下: https://ask.csdn.net/questions/7523908
    • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:【源码详解~按键状态机~简洁易懂】1.单个按键实现短按长按的功能(基于STM32)
    • 除此之外, 这篇博客: STM32串口发送和接收多个数据教程基于气体传感器实战中的 一、串口的数据接收和发送 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:

      在这里我想把串口这一部分内容好好说一下,从事stm32开发已经好几年了,自以为对stm32已经掌握的很好了,后来才发现自己只是浮于表面,没有好好的深入学习stm32的底层

    • 您还可以看一下 Abel小智老师的嵌入式开发系统学习路线 从基础到项目 精品教程 工程师必备课程 物联网课程中的 STM32什么时候使用IO外部中断?小节, 巩固相关知识点
    • 以下回答来自chatgpt:

      解决方案:

      记录按键在没有消抖的情况下触发的次数,需要在按键按下时通过中断服务函数实现记录。具体步骤如下:

      步骤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'到单片机,单片机会将记录的按键响应次数返回给电脑端。需要注意的是,如果单片机一次内接收到的数据量较大,需要进行数据缓存处理,避免数据溢出或者丢失。


    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论
  • yy64ll826 2023-06-12 16:32
    关注

    可以借鉴下

    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没有按下
    }
    
    
    评论
  • Leodong. 2023-06-06 16:07
    关注

    该回答通过自己思路及引用到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 引脚的值,否则可能会出现误触发。

    希望这个代码可以帮助你实现使用按键和串口记录按键在不消抖的情况下触发的次数。


    如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

    评论
  • MR_Bone 2023-06-07 10:58
    关注

    根据您的要求,您想要使用按键和串口记录在不消抖的情况下按键触发的次数。以下是一个大致的步骤来实现这个功能:

    1. 连接硬件:将按键与STM32微控制器的相应引脚连接起来,确保正确的连接和电气特性匹配。
    2. 初始化串口:在STM32上初始化串口通信功能,包括设置波特率、数据位、停止位等参数。这样可以实现与计算机的串口通信。
    3. 初始化按键:配置STM32上的GPIO引脚为输入模式,并设置中断触发条件。您可以选择适合您的具体硬件和需求的触发方式,例如上升沿触发、下降沿触发等。
    4. 编写中断服务程序:当按键触发中断时,中断服务程序将被调用。在中断服务程序中,您可以编写代码来记录按键触发的次数,并通过串口将次数发送给计算机。
    5. 串口通信:使用适当的串口通信函数(例如UART)将按键触发次数发送给计算机。您需要编写代码来格式化数据并通过串口发送。
    6. 计算机端接收数据:在计算机上,您需要使用串口通信软件(例如Tera Term、PuTTY等)来接收和监视来自STM32的数据。确保串口设置匹配,并解析接收到的数据以获取按键触发次数。

    请注意,具体的实现细节和代码将取决于您使用的具体硬件平台和编程语言。以上步骤提供了一个大致的指导,您可以根据自己的需求和硬件平台进行适当的调整和实现。

    评论
  • GIS工具开发 2023-06-12 14:55
    关注
    1. 配置 STM32 的 GPIO 引脚为输入模式,并使能 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);
    
    1. 定义一个变量来存储按键触发的次数。
    uint16_t key_counter = 0;
    
    1. 在主程序中循环检测按键是否被按下。
    while (1) {
      if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == RESET) {  // 检测按键是否被按下
        while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == RESET);  // 等待按键松开
        key_counter++;  // 记录按键触发次数
      }
    }
    
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 6月12日
  • 创建了问题 6月5日

悬赏问题

  • ¥15 定义了函数,但是无法根据函数定义触发器
  • ¥20 5变量卡诺图化简得出与非门电路图
  • ¥20 位置依赖的碱基序列独热编码
  • ¥15 Python爬取交通拥堵指数数据
  • ¥15 使用vba抓取重定向网页问题
  • ¥20 付费需求测试程序(细谈)。
  • ¥15 为什么这段c++代码会报这么多语法错误?
  • ¥20 如何利用C语言实现用最小二乘法选配两个经验公式
  • ¥50 vue-codemirror如何对指定行 指定位置的 字符进行背景颜色或者字体颜色的修改?
  • ¥30 遇到一个的问题,请教各位