开启这一年单片机第一次基础培训,这次用了STM32F407ZG的板子。
一. 概述
代码烧入后通过按下按键实现相应功能
(KEY0控制跑马灯和闪烁的切换;KEY1不同占空比的切换;KEY2在不同占空比下频率的切换。灵敏度高,不同按键不同情况下可随时切换状态,但这次小题没有用到LCD,输出矩形波还得靠示波器。)
二.?功能简介
(1)跑马灯的实现
(2)通过按键实现跑马灯与闪烁的切换
(3)呼吸灯
(4)输出一个100HZ,占空比为50%的矩形波
(5)通过按键切换25%,90%占空比的矩形波
(6)通过按键切换不同频率的矩形波
三.?相关部分代码
1.宏定义
//读取按键宏定义
#define READ_KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)//读取引脚的电平状态
#define READ_KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)
#define READ_KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)
2.相关自定义函数声明
void run_led(void); //跑马灯与闪烁
void duty_ratio(void); //矩形波
uint8_t run_led_shift(); //跑马灯与闪烁之间模式切换
uint8_t duty_ratio_shift(); //矩形波不同占空比的转换
uint8_t choose_run(); //转到跑马灯
uint8_t choose_ratio(); //转到矩形波
3.相关函数
uint8_t choose=0;
uint8_t k=0, l=0, m=0, n=0, i=0;
static uint8_t key_up = 1; //按键松开标志
/* 跑马灯与闪烁 */
void run_led(void)
{
uint32_t time=0;
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1);
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
if( 1==i )
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, 0);
for(time=0; time<500; time++)
{
HAL_Delay(100);
if( run_led_shift()||choose_ratio() ) return;
}
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
for(time=0; time<500; time++)
{
HAL_Delay(100);
if( run_led_shift()||choose_ratio() ) return;
}
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
if( run_led_shift()||choose_ratio() ) return;
}
if( 0==i )
{
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
for(time=0; time<500; time++)
{
HAL_Delay(100);
if( run_led_shift()||choose_ratio() ) return;
}
if( run_led_shift()||choose_ratio() ) return;
}
}
/* 矩形波 */
void duty_ratio(void)
{
uint8_t j=0;
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, 1);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1);
if( 0==READ_KEY0 )
{
HAL_Delay(500);
if( 0==READ_KEY0 )
{
choose = 1;
return ;
}
}
if( 1==k ) /* 50% */
{
while(1)
{
if( choose_run() ) break;
if( duty_ratio_shift()) return;
if( 0==READ_KEY2 )
{
HAL_Delay(500);
if( 0==READ_KEY2)
{
m++;
m%=3;
}
while( 0==READ_KEY2 ){};
}
if( 0==m )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(501);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(501);
}
if( 1==m )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(1005);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(1005);
}
if( 2==m )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(250);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(250);
}
}
}
else if( 2==k ) /* 25% */
{
while(1)
{
if( choose_run()) break;
if( duty_ratio_shift()) return;
if( 0==READ_KEY2 )
{
HAL_Delay(500);
if( 0==READ_KEY2)
{
l++;
l%=3;
}
while( 0==READ_KEY2 ){};
}
if( 0==l )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(252);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(753);
}
if( 2==l )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(502);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(1510);
}
if( 1==l )
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(100);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(300);
}
}
}
else if( 3==k ) /* 90% */
{
while(1)
{
if( choose_run()) break;
if( duty_ratio_shift()) return;
if( 0==READ_KEY2 )
{
HAL_Delay(10);
if( 0==READ_KEY2)
{
l++;
l%=3;
}
while( 0==READ_KEY2 ){};
}
if(0==l)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(908);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(98);
}
if(1==l)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(360);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(40);
}
if(2==l)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(1812);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(198);
}
}
}
else if( 0==k ) //呼吸灯
{
while(1)
{
if( choose_run()) break;
if( duty_ratio_shift()) return;
for(j=0; j<150 ;j++)
{
if( duty_ratio_shift()||choose_run() )
{
return;
}
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 0);
HAL_Delay(j*10);
if( duty_ratio_shift()||choose_run() )
{
return;
}
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(1500-j*10);
}
for(j=150; j>0; j--)
{
if( duty_ratio_shift()||choose_run() )
{
return;
}
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_Delay(1500-j*10);
if( duty_ratio_shift()||choose_run() )
{
return;
}
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 0);
HAL_Delay(j*10);
}
}
}
else
{
return ;
}
}
/* 转到跑马灯模式 */
uint8_t choose_run()
{
if( READ_KEY0==0)
{
HAL_Delay(500);
if( READ_KEY0==0)
{
choose = 1;
}
while( READ_KEY0==0 ){};
return 1;
}
return 0;
}
/* 转到矩形波 */
uint8_t choose_ratio()
{
if( READ_KEY1==0)
{
HAL_Delay(500);
if( READ_KEY1==0)
{
choose = 2;
}
while( READ_KEY1==0 ){};
return 1;
}
return 0;
}
/* 跑马灯和闪烁转换 */
uint8_t run_led_shift()
{
if( READ_KEY0==0 )
{
HAL_Delay(500);
if( READ_KEY0==0 )
{
i++;
i%=2;
}
while( READ_KEY0==0 ){};
return 1;
}
return 0;
}
/* 矩形波的占空比切换 */
uint8_t duty_ratio_shift()
{
if( 0==READ_KEY1 )
{
HAL_Delay(500);
if( 0==READ_KEY1)
{
k++;
k%=4;
}
while( READ_KEY1==0 ){};
return 1;
}
return 0;
}
4.主函数
int main(void)
{
SystemClock_Config();
MX_GPIO_Init();
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, 1);
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1);
while (1)
{
// 跑马灯与闪烁
if (key_up && ( 0 == READ_KEY0 ))
{
HAL_Delay(500); //????
key_up = 0;
if (READ_KEY0 == 0)
choose = 1;
}
else if (READ_KEY0 == 1)
{
key_up = 1;
}
// 矩形波
if (key_up && ( 0 == READ_KEY1 ))
{
HAL_Delay(500); //????
key_up = 0;
if (READ_KEY1 == 0)
choose = 2;
}
else if (READ_KEY1 == 1)
{
key_up = 1;
}
/* 小菜单 */
switch( choose )
{
case 1:
run_led();
break;
case 2:
duty_ratio();
break;
default:
continue;
}
}
}
四.?所学与总结
1.检测按键是否松开
这个很重要,一开始没怎么注意,然后发现按键按下的时候一直检测,一不小心就跳到另一个模式去了,后边在高人指导下才了解到其重要性,加上之后程序运行的效果比之前好很多。总结了如下两种写法:
(1)加按键松开标志(学到了学长的代码)
uint8_t KEY_Scan(uint8_t mode) {
static uint8_t key_up = 1; //按键松开标志 这里这里这里!!!
if (mode)
{
key_up = 1; //支持连按
}
if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 1))
{
HAL_Delay(10); //去抖动
key_up = 0;
if (KEY0 == 0)
return KEY0_PRES;
if (KEY1 == 0)
return KEY1_PRES;
if (KEY2 == 1)
return KEY2_PRES;
}
else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 0)
{
key_up = 1;
}
return 0; //按键按下
}
(2)while辅助
uint8_t choose_run()
{
if( READ_KEY0==0)
{
HAL_Delay(500);
if( READ_KEY0==0)
{
choose = 1;
}
while( READ_KEY0==0 ){}; //这里这里这里!!!
return 1;
}
return 0;
}
2.?使用delay的按键灵敏度问题
(1)把延迟时间分成小段,如下:
for(time=0; time<500; time++)
{
HAL_Delay(100);
if( run_led_shift()||choose_ratio() ) return; //每循环一次判断一次
}
(2)改写delay函数( while )
3. 多用宏定义define及ifdef,能大大提高代码可读性、移植性等,大大简化代码。
4. switch和if else结合使用,也莫忘了 ( ?:)
5.?合理运用全局变量
这个小题不难,我写的逻辑也简单易懂,但代码过于冗杂写了很多行,后边想了一下,合理运用switch和define可以很好地解决很多问题,也能让代码少很多,下次注意,加油加油。
|