为了减少篇幅,各模块的初始化均在模块配置中,可以随时去翻看博客,还有function里面的函数,将不再罗列出来,只是截图展示。
基本要求
1.1 使用 CT117E 嵌入式竞赛板,完成试题功能的程序设计与调试;
1.2 设计与调试过程中,可参考组委会提供的“资源数据包”;
1.3 Keil 工程文件以准考证号命名,完成设计后,提交完整、可编译的 Keil工程文件到服务器。
硬件框图
通过按键设置定时时间,启动定时器后,开始倒计时;计时过程中,可以暂停、取消定时器。在定时时间内,按要求输出 PWM 信号和控制 LED 指示灯。系统框图如图 1 所示:
功能描述
1、LCD 显示
LCD 显示存储位置、定时时间和当前状态。系统预留 5 个存储位置用于存储常用的定时时间。当定时器停止时,当前状态为 Standby;当系统正在设置时间时,当前状态为 Setting;当定时器运行时,当前状态为 Running,定时器暂停时,当前状态为 Pause。
2、按键功能
系统使用 4 个按键,B1、B2、B3 和 B4。
按键 B1 为存储位置切换键。每按一次,存储位置依次以 1、2、3、4、5循环切换,切换后定时时间设定为当前位置存储的时间。
按键 B2 为时间位置(时、分、秒)切换键和存储键。短按 B2 键进入时间设置状态。每次短按 B2 键,设置位置以时、分、秒循环切换,并突出显示(高亮)当前位置;设置完后,长按 B2 键(超过 0.8 秒)把设置的时间存储到当前的存储位置,并推出设置状态。如果是临时设置定时时间,则不需存储,直接按定时器启动按键。
按键 B3 为时、分、秒(按键 B2 确定当前位置)数字增加键。每短按B3 一次,数字递增一次;按住 B3 超过 0.8 秒,则数字快速递增,直到松开B3 按键。数字递增时,超出范围则从头循环。
按键 B4 为定时器启动键。短按 B4,定时器启动,开始运行;运行期间短按 B4,暂停定时器,再短按 B4,恢复定时器运行;长按 B4(超过 0.8 秒),则取消定时器运行,回到 Standby 状态。
3、PWM 输出和 LED 显示
定时器运行时,PA6 口输出 PWM 信号,同时 LED 灯(LD1)以 0.5 秒的频率闪烁。PWM 信号频率为 1KHz,占空比为 80%。
定时器停止或暂停时,停止输入 PWM 信号,LED 灯灭。
4、定时时间存储
设定好的定时时间存储在 EEPROM 中。
掉电重启后,显示存储位置 1 的定时时间。
需要用的模块:LCD、IIC、四个按键、LED、TIM3(PA
6)
LCD:显示-----》Display();
IIC:读取和存储信息到EEPROM-----》Write_Time();Read_Time();
按键:对参数进行调整-----》KEY_Handle();KEY_Scan();
LED:提示作用-----》LED();
TIM3:初始化
(本次赛题主要是考代码逻辑,特别是按键处理的方法。在模块方面用的比较少)
显示功能如下:
Location表示设置时间的地址,Location为4时,表示不设置时间。(单独点亮一个字符,方法如下,若有其他方法,欢迎评论)
mode表示定时器的四种状态:
0:Standby
1:Setting
2:Running
3:Pause
void Dispaly(void)
{
sprintf((char *)str," N0 %d",signal + 1);
LCD_DisplayStringLine(Line1, str);
if(Location == 4)//设置部分
{
LCD_ClearLine(Line4);
LCD_SetTextColor(Black);
sprintf((char *)str," %02d: %02d: %02d",Now_Time[0],Now_Time[1],Now_Time[2]);
LCD_DisplayStringLine(Line4, str);
}
else if(Location == 0)
{
LCD_SetTextColor(Black);
sprintf((char *)str," : %.2d: %.2d",Now_Time[1],Now_Time[2]);
LCD_DisplayStringLine(Line4, str);
LCD_SetTextColor(Red);
LCD_DisplayChar(Line4,210, Now_Time[0]/10 + 0x30);
LCD_DisplayChar(Line4,195, Now_Time[0]%10 + 0x30);
LCD_SetTextColor(Black);
}
else if(Location == 1)
{
LCD_SetTextColor(Black);
sprintf((char *)str," %.2d: : %.2d",Now_Time[0],Now_Time[2]);
LCD_DisplayStringLine(Line4, str);
LCD_SetTextColor(Red);
LCD_DisplayChar(Line4,150, Now_Time[1]/10 + 0x30);
LCD_DisplayChar(Line4,135, Now_Time[1]%10 + 0x30);
LCD_SetTextColor(Black);
}
else if(Location == 2)
{
LCD_SetTextColor(Black);
sprintf((char *)str," %.2d: %.2d: ",Now_Time[0],Now_Time[1]);
LCD_DisplayStringLine(Line4, str);
LCD_SetTextColor(Red);
LCD_DisplayChar(Line4,60, Now_Time[2]/10 + 0x30);
LCD_DisplayChar(Line4,45, Now_Time[2]%10 + 0x30);
LCD_SetTextColor(Black);
}
if(mode == 0)
LCD_DisplayStringLine(Line7, " Standby ");
else if(mode == 1)
LCD_DisplayStringLine(Line7, " Setting ");
else if(mode == 2)
LCD_DisplayStringLine(Line7, " Running ");
else if(mode == 3)
LCD_DisplayStringLine(Line7, " Pause ");
}
void Write_Time(void)
{
IIC_Write(0x00,Hour[0]); HAL_Delay(10);
IIC_Write(0x01,Minute[0]);HAL_Delay(10);
IIC_Write(0x02,Second[0]);HAL_Delay(10);
IIC_Write(0x10,Hour[1]); HAL_Delay(10);
IIC_Write(0x11,Minute[1]);HAL_Delay(10);
IIC_Write(0x12,Second[1]);HAL_Delay(10);
IIC_Write(0x20,Hour[2]); HAL_Delay(10);
IIC_Write(0x21,Minute[2]);HAL_Delay(10);
IIC_Write(0x22,Second[2]);HAL_Delay(10);
IIC_Write(0x30,Hour[3]); HAL_Delay(10);
IIC_Write(0x31,Minute[3]);HAL_Delay(10);
IIC_Write(0x32,Second[3]);HAL_Delay(10);
IIC_Write(0x40,Hour[4]); HAL_Delay(10);
IIC_Write(0x41,Minute[4]);HAL_Delay(10);
IIC_Write(0x42,Second[4]);HAL_Delay(10);
}
void Read_Time(void)
{
Hour[0] = IIC_Read(0x00);HAL_Delay(10);
Minute[0] = IIC_Read(0x01);HAL_Delay(10);
Second[0] = IIC_Read(0x02);HAL_Delay(10);
Hour[1] = IIC_Read(0x10);HAL_Delay(10);
Minute[1] = IIC_Read(0x11);HAL_Delay(10);
Second[1] = IIC_Read(0x12);HAL_Delay(10);
Hour[2] = IIC_Read(0x20);HAL_Delay(10);
Minute[2] = IIC_Read(0x21);HAL_Delay(10);
Second[2] = IIC_Read(0x22);HAL_Delay(10);
Hour[3] = IIC_Read(0x30);HAL_Delay(10);
Minute[3] = IIC_Read(0x31);HAL_Delay(10);
Second[3] = IIC_Read(0x32);HAL_Delay(10);
Hour[4] = IIC_Read(0x40);HAL_Delay(10);
Minute[4] = IIC_Read(0x41);HAL_Delay(10);
Second[4] = IIC_Read(0x42);HAL_Delay(10);
}
由于本实验牵扯到长按(大于0.8s)和短按,并且长按是需要实时操作对应变量,所以长按的操作需要检测到马上执行。不能等按完了才执行。所以这里用0.2s的延时,次数加到4以后就进行长按操作。
变量声明:
Now_Time是当前显示的时间。
Time是存储的时间。
考虑到有暂时设定时间且不进行存储的情况,后面也引入了Aim_Time,定时结束的目标时间。
uint8_t KEY_Scan(uint8_t mode)
{
static uint8_t flag=1;
uint8_t count = 0;
if(mode) flag = 1;
if(flag &&(KEY_B1 == 0 || KEY_B2 == 0 || KEY_B3 == 0 || KEY_B4== 0 ))
{
HAL_Delay(10);
flag = 0;
if (KEY_B1 == 0) return B1_Press;
else if (KEY_B2 == 0)
{
while(KEY_B2 == 0){
HAL_Delay(200);
count++;//每0.2s加一
}
if(count <= 4) return B2_Press;
else return B2_Press_Long;
}
else if (KEY_B3 == 0)
{
while(KEY_B3 == 0){
Dispaly();
HAL_Delay(200);
count++;//每0.2s加一
if(count > 4)
{
Now_Time[Location] = Now_Time[Location]+1;
Now_Time[0] = Now_Time[0]%24;
Now_Time[1] = Now_Time[1]%60;
Now_Time[2] = Now_Time[2]%60;
Hour[signal]= Now_Time[0];
Minute[signal] = Now_Time[1];
Second[signal] = Now_Time[2];
}
}
if(count <= 4) return B3_Press;
}
else if (KEY_B4 == 0)
{
while(KEY_B4 == 0){
HAL_Delay(200);
count++;//每0.2s加一
}
if(count <= 4) return B4_Press;
else return B4_Press_Long;
}
}else if(KEY_B1 == KEY_B2 == KEY_B3 == KEY_B4 == 1) flag = 1;
return 0;
}
按键 B1 为存储位置切换键。每按一次,存储位置依次以 1、2、3、4、5循环切换,切换后定时时间设定为当前位置存储的时间。(signal 变化)
按键 B2 为时间位置(时、分、秒)切换键和存储键。短按 B2 键进入时间设置状态。每次短按 B2 键,设置位置以时、分、秒循环切换,并突出显示(高亮)当前位置;设置完后,长按 B2 键(超过 0.8 秒)把设置的时间存储到当前的存储位置,并推出设置状态。如果是临时设置定时时间,则不需存储,直接按定时器启动按键。(Location变化)
按键 B3 为时、分、秒(按键 B2 确定当前位置)数字增加键。每短按B3 一次,数字递增一次;按住 B3 超过 0.8 秒,则数字快速递增,直到松开B3 按键。数字递增时,超出范围则从头循环。(Now_Time变化)
按键 B4 为定时器启动键。短按 B4,定时器启动,开始运行;运行期间短按 B4,暂停定时器,再短按 B4,恢复定时器运行;长按 B4(超过 0.8 秒),则取消定时器运行,回到 Standby 状态。(mode 变化)
void KEY_Handle(uint8_t Key)
{
if(Key == B1_Press)
{
Location = 4;
signal++;
signal = signal % 5;
Now_Time[0] = Hour[signal];
Now_Time[1] = Minute[signal];
Now_Time[2] = Second[signal];
}
else if(Key == B2_Press)
{
mode = 1;
if(Location == 4)
Location = 0;
else
{
Location++;
Location = Location % 3;
}
}
else if(Key == B2_Press_Long)
{
Location = 4;
mode = 0;
Hour[signal]= Now_Time[0];Minute[signal] = Now_Time[1];Second[signal] = Now_Time[2];
Write_Time();
}
else if(Key == B3_Press)
{
Now_Time[Location] = Now_Time[Location]+1;
Now_Time[0] = Now_Time[0]%24;
Now_Time[1] = Now_Time[1]%60;
Now_Time[2] = Now_Time[2]%60;
}
else if(Key == B4_Press)
{
if(mode == 1)
{
mode = 2;
HAL_TIM_Base_Start(&htim3);
Location = 4;
Aim_Time[signal] = Now_Time[0];
Aim_Time[signal] = Now_Time[1];
Aim_Time[signal] = Now_Time[2];
Now_Time[0] = Now_Time[1] = Now_Time[2] = 0;
}
else if(mode == 2) mode = 3;//暂停
else
{
Aim_Time[signal] = Hour[signal];
Aim_Time[signal] = Minute[signal];
Aim_Time[signal] = Second[signal];
mode = 2;
HAL_TIM_Base_Start(&htim3);
}
}
else if(Key == B4_Press_Long)
{
mode = 0;
}
}
void LED(void);每进入一次LD1变换一次(TOGGLE函数似乎用不了,本人也不清楚)
void LED_OFF(void);关闭所有LED
void LED(void)
{
static uint16_t i;
i++;
if(i % 2)HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
else
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
}
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_OFF(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
时间加1函数
定时到目标时间的结束标志mode = 0;
void ADD(void)
{
Now_Time[2]++;
if(Now_Time[2] >= 60)
{
Now_Time[2] = 0;
Now_Time[1]++;
}
if(Now_Time[1] >= 60)
{
Now_Time[1] = 0;
Now_Time[0]++;
}
Now_Time[0] = Now_Time[0] % 24;
if(Now_Time[0] == Aim_Time[0] && Now_Time[1] ==Aim_Time[1] && Now_Time[2] == Aim_Time[2]) mode = 0;
}
LCD_Init();
LCD_Clear(White);
LCD_SetTextColor(Black);
I2CInit();
IIC_Write(0x00,0);HAL_Delay(10);
IIC_Write(0x01,0);HAL_Delay(10);
IIC_Write(0x02,0);HAL_Delay(10);
IIC_Write(0x10,0);HAL_Delay(10);
IIC_Write(0x11,1);HAL_Delay(10);
IIC_Write(0x12,0);HAL_Delay(10);
IIC_Write(0x20,5);HAL_Delay(10);
IIC_Write(0x21,20);HAL_Delay(10);
IIC_Write(0x22,00);HAL_Delay(10);
IIC_Write(0x30,13);HAL_Delay(10);
IIC_Write(0x31,14);HAL_Delay(10);
IIC_Write(0x32,52);HAL_Delay(10);
IIC_Write(0x40,23);HAL_Delay(10);
IIC_Write(0x41,59);HAL_Delay(10);
IIC_Write(0x42,59);HAL_Delay(10);
LED_OFF();
HAL_Delay(100);
Read_Time();
HAL_Delay(1000);
Dispaly();
key = KEY_Scan(0);
KEY_Handle(key);
if(mode == 2 && num == 1)
{
LED();
}
else if(mode != 2)
{
HAL_TIM_Base_Stop(&htim3);
LED_OFF();
}
if(nCount == 1 && mode == 2)
{
nCount = 0;
ADD();
}
上面的num和nCount在哪变了,在哪计时呢?
就在滴答定时器里面咯。
大家打开stm32g4xx_it.c
if(mode == 2)
{
i++;j++;
}
if(i >= 1000)
{
nCount = 1;
i = 0;
}
if(j >= 500)
{
num = 1;
j = 0;
}
以上就是本次赛题的程序设计部分(变量定义部分,本人没有罗列出来嗷),在代码简洁方面本人做的不是很好,如有更好的设计方法可以在评论区留言。互相学习。
代码改了好久,比第一次写的时候精简多了。。。太难了。。。(PS: 仍有不足,欢迎指导)
还等什么,赶快自己写一下,测试一下代码。实测有效,嘿嘿