蓝桥杯嵌入式STM32G431RBT6竞赛指南与模板——最后的绝唱

本文详细介绍了STM32单片机在竞赛环境下的应用,涵盖了ADC的DMA多通道采样、串口的DMA接收、定时器输入捕获用于频率和占空比测量、动态更改PWM频率的方法,以及IIC接口与DS18B20和DHT11温度传感器的交互。此外,还讨论了按键状态机的实现和动态点灯等实用技巧。

谨以此文和我去年前的一篇蓝桥杯单片机的教程构成电子类的青铜双壁.

国信长天单片机竞赛训练之原理图讲解及常用外设原理(遗失的章节-零)_昊月光华的博客-优快云博客

 

 

目录

时钟树

串口重定向:printf输出

动态点灯(点灯大师)

按键(常用状态机)

同一时刻对多个按键按下进行判断

系统滴答定时器Systick(了解)

*ADC的DMA多通道采样+平均滤波处理(多通道使用)

*串口的DMA+空闲中断不定长接受任意类型的数据(高效且简洁)

定时器通道的输入捕获(采集频率和占空比)

运行期间动态更改PWM的频率

PWM波的生成

IIC读写epprom

扩展板之ds18b20温度传感器

扩展板之dht11温度传感器

扩展板之三位数码管

扩展板之ADC按键

扩展板之三轴加速度传感器

扩展板之光敏电阻DO&AO

扩展板之两路AD采集

 扩展板之频率测量PUS1,PUS2,PWM1,PWM2


时钟树

串口重定向:printf输出

注意:

  1. 勾选微库.若发现根本进不了main函数里面一般是这个原因.
  2. 包含#include "stdio.h"

int fputc(int ch,FILE * f )
{
    HAL_UART_Transmit(&huart1,  (uint8_t *)&ch,1,0xff);
    
    return ch;
    
}

动态点灯(点灯大师)

u8 LEDINDEX[]= {0X00,1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7};
u8 LEDDT[]={0,0,0,0,0,0,0,0,0};
void led_display(u8 ds)
{
    HAL_GPIO_WritePin(GPIOC,0xff<<8,GPIO_PIN_SET);
     HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    
    
     HAL_GPIO_WritePin(GPIOC,ds<<8,GPIO_PIN_RESET);
     HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
       HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
     
}
void led_scan(){
    int ds = 0;
    for(int i = 0 ;i < 4;i++)
    {
        ds|=LEDINDEX[LEDDT[i]];
        
    }
    led_display(ds);
}

运行期间只需要更改LEDDT的值就行,LEDDT没有顺序,表示最多同时点亮的灯的数目。

按键(常用状态机)

(状态机,假设同时只有一个按键按下,无论长按还是短按都只判断按下,记作一次)这个代码只能一次性判断一个按键按下,无法对同一时刻多个按键按下进行判断,一般这个代码就够用了)

使用定时器7,配置为20ms进入一次回调函数。这种我认为是日常中比较常用的按键操作,因为以前对51单片机按键扫描的记忆,故回顾了下。

#define ReadB1  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define ReadB2  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define ReadB3  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define ReadB4  HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
u8 key_state= 0;
u8 rkey = 0;
u8 key = 0;
void  HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    if(htim->Instance == TIM2)
    {
        LedFlusht++;
        if(time++ == 1000)
        {
            time = 0;
            LEDDT[1] = LEDDT[1]?0:1;
            
            
        }
        
    
        
    }
    else if(htim->Instance == TIM7)
    {
        
        //执行按键扫描
        switch(key_state)
        {
            
            case 0:
                if(!ReadB1 || !ReadB2 ||!ReadB3 || !ReadB4)
                {
                    key_state = 1;
  
                }
                break;
            case 1:
                  if(!ReadB1 || !ReadB2 ||!ReadB3 || !ReadB4)
                {
                    key_state = 2;
                    if(!ReadB1) key = 1;
                    else if(!ReadB2) key=2;
                    else if(!ReadB3) key=3;
                    else if(!ReadB4) key =4;
                    rkey = key;
  
                }
                else 
                {
                    
                    key_state = 0;
                    key = 0;
                    
                }
                break;
            case 2:
                rkey = 0;//在只需要判断按下之后,不管长按短按(有没有松开)都试做按下一次
                if(!ReadB1 || !ReadB2 ||!ReadB3 || !ReadB4)
                {
                    
                }
                else 
                {
                    key = 0;
                    key_state= 0;
                    
                }
                break;
            
            
        }
        
        
        keyAction();
        
        
        
        
    }
    
    
}

同一时刻对多个按键按下进行判断

原理:把按键定义成一个结构体变量:


typedef struct Key{
    u16 keytime;
    u8 keystate;
    bool sflag;
    bool lflag;
    bool state;

}Key;

按键扫描:(按下小于500ms属于短按,大于500ms属于长按) ,按键扫描函数单独用一个定时器,每10ms产生一次中断去执行,这也就是为什么keytime+=10的原因.

#define ReadB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define ReadB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define ReadB3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define ReadB4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)

 void key_scan()
{
    
    keys[0].state =  READB1;
    keys[1].state =  READB2;
    keys[2].state =  READB3;
    keys[3].state =  READB4;
    
    for(int i = 0 ; i < 4 ;i++)
    {
        switch(keys[i].keystate)
        {
            
            case 0:
                 if( !keys[i].state){
                     keys[i].keystate =1;
                     
                 }
                break;
            case 1:
                  if(!keys[i].state){
                     keys[i].keystate =2;
                     
                 }
                  else 
                      keys[i].keystate = 0;
                break;
            case 2:
                if(!keys[i].state)
                {
                    
                    keys[i].keytime+=10;
                   
                }
                else 
                {
                        if(keys[i].keytime > 500)
                        {
                            keys[i].lflag = 1;
                        }
                        else 
                            keys[i].sflag = 1;
                        
                        
                        keys[i].keytime = 0;
                        keys[i].keystate=0;
                        
                    
                    
                }    
                break;
            default:
                break;
            
            
        }   
    }  
}

//串口测试代码
void keywork(void)
{
    
    if(keys[0].sflag)
    {
        
        printf("0\r\n");
        keys[0].sflag = 0;
        
    }
    else if(keys[0].lflag)
    {
        
        printf("long 0\r\n");
        keys[0].lflag = 0;
    }
     if(keys[1].sflag)
    {
        
        printf("1\r\n");   keys[1].sflag = 0;
        
    }
    
    
      else if(keys[1].lflag)
    {
        
        printf("long 1\r\n");
        keys[1].lflag = 0;
    }
  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值