谨以此文和我去年前的一篇蓝桥杯单片机的教程构成电子类的青铜双壁.
国信长天单片机竞赛训练之原理图讲解及常用外设原理(遗失的章节-零)_昊月光华的博客-优快云博客
目录
*串口的DMA+空闲中断不定长接受任意类型的数据(高效且简洁)
时钟树

串口重定向:printf输出
注意:
- 勾选微库.若发现根本进不了main函数里面一般是这个原因.
- 包含#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;
}

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

被折叠的 条评论
为什么被折叠?



