百度云:https://pan.baidu.com/s/1feVfEzPXrLFPB18flcg4Ew?pwd=6666
提取码:6666
目录
1.赛题
2.分析及配置
直接浏览一遍观察用到的引脚和模块。
按键、LED、LCD、PWM、EEPROM。
看原理图直接配置即可,配置完毕直接生成代码。
2.1 RCC配置
2.2 SYS配置
2.3 按键配置
2.4 EEPROM配置
配置为输出直接一直官方的历程即可。
2.5 PWM配置
题目要设置为1KHz,我们这里方便设置占空比把计数值设为100-1,时钟源为80MHz,它要经过两次相除的到1KHz。
80M/100/x=1K
则 x = 800
所以我们分频为800-1,计数值为100-1
占空比为80
2.6LCD及LED的配置
用到的引脚配置为输出即可,LCD的显示移植官方历程。
3.编写代码及解释
3.1 数据定义
typedef struct
{
unsigned char Set_Time;//判断那个高亮
int Time_H;//显示的时间
int Time_M;//显示的分钟
int Time_S;//显示的小时
unsigned char Place;//存储的位置
unsigned char State;//定时器运行状态
unsigned char LED_Flag;
unsigned char Time_Count;
}Data_TypeDef;
Data_TypeDef Data;
typedef struct
{
unsigned int Value;//按键的值
unsigned char State;//判断按键进行到那一步
bool Short_Flag;//短按标志位
bool Long_Flag;//长按标志位
unsigned int Time;//按键按下的时间
}Key_TypeDef;
Key_TypeDef B[4];//四个按键
//屏幕显示的数字
extern uc16 ASCII_Table[];
3.2 LCD的移植
找到官方给的LCD,打开它
放到自己对应的工程中。
放进去之后添加即可。
双击Application/User/Core找到你的lcd.c添加
用记事本打开main.c,可以找到初始化以及显示的函数,其他的功能到对应的.c和.h文件查看
3.3 EEPROM的移植
找到HAL_08_EE打开,打开Inc和Src找到i2c.h和i2c.c放到自己工程对应的Inc和Src即可
然后双击添加
打开记事本打开对应的main.c
可以找到
3.4 界面显示的编写
我们更改了一个函数进行局部的显示,以及高亮的改变。
void Show(void)
{
char arr[30];
//显示存储位置
sprintf(arr," No %d ",Data.Place);
LCD_DisplayStringLine(Line1, (uint8_t *)arr);
//因为LCD和LED公用引脚所以这里加了一点空格,防止数据前面出现点点
LCD_DisplayStringLine(Line4, (uint8_t *)" ");
//显示时间
if(Data.Set_Time == 1)//高亮小时
{
//取十位和各位传入参数
LCD_DrawChar1(Line4,6,Data.Time_H/10,Blue);
LCD_DrawChar1(Line4,7,Data.Time_H%10,Blue);
}
else
{
LCD_DrawChar1(Line4,6,Data.Time_H/10,Black);
LCD_DrawChar1(Line4,7,Data.Time_H%10,Black);
}
if(Data.Set_Time == 2)//高亮分钟
{
LCD_DrawChar1(Line4,10,Data.Time_M/10,Blue);
LCD_DrawChar1(Line4,11,Data.Time_M%10,Blue);
}
else
{
LCD_DrawChar1(Line4,10,Data.Time_M/10,Black);
LCD_DrawChar1(Line4,11,Data.Time_M%10,Black);
}
if(Data.Set_Time == 3)//高亮秒
{
LCD_DrawChar1(Line4,14,Data.Time_S/10,Blue);
LCD_DrawChar1(Line4,15,Data.Time_S%10,Blue);
}
else
{
LCD_DrawChar1(Line4,14,Data.Time_S/10,Black);
LCD_DrawChar1(Line4,15,Data.Time_S%10,Black);
}
//显示定时器状态
if(Data.State == 0)//当定时器停止时
LCD_DisplayStringLine(Line7, (uint8_t *)" Standby ");
if(Data.State == 1)//当系统正在设置时间时
LCD_DisplayStringLine(Line7, (uint8_t *)" Setting ");
if(Data.State == 2)//当定时器运行时
LCD_DisplayStringLine(Line7, (uint8_t *)" Running ");
if(Data.State == 3)//定时器暂停时
LCD_DisplayStringLine(Line7, (uint8_t *)" Pause ");
}
这里我们更改了官方给的LCD函数。点开lcd.c
找到
复制一下改成
void LCD_DrawChar1(u8 Xpos, u16 Ypos,unsigned char Number,unsigned int BackColor)
{
//Y = 319时显示在最左边,每减16就会右移1个字符
Ypos = 319-16*Ypos;
uc16 *c;
//我们显示的是数组中的数据,这里16对应的是0
c = &ASCII_Table[(16+Number) * 24];
u32 index = 0, i = 0;
u8 Xaddress = 0;
Xaddress = Xpos;
LCD_SetCursor(Xaddress, Ypos);
for(index = 0; index < 24; index++)
{
LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
for(i = 0; i < 16; i++)
{
if((c[index] & (1 << i)) == 0x00)
{
/*这里改成形参,因为我们要改变高亮*/
LCD_WriteRAM(BackColor);
}
else
{
LCD_WriteRAM(White);
}
}
Xaddress++;
LCD_SetCursor(Xaddress, Ypos);
}
}
因为是有高亮所以我们增加了一个形参,因为显示的是数字我们把原来的指针换成了数字。
根据
这个函数我们知道了第三个参数原来是传的一个数组的地址,一个一个的试一试的话可以知道,Ascii=16时显示的是0,也就是17显示1,一次类推。或者直接看数组也会知道16对应的是0
所以我们
我们把指针写到了里面传入的参数还是一样的,Number就代表要显示的数字。
根据
这里我们就可以判断
因为要高亮所以我们也有颜色的形参
3.5 按键
找到
右击进入对应的头文件,然后滑到最下面
找到
找到void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);复制一下编写回调函数。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4)
{
Time();//定时减时间
LED();//定时闪烁LED
B[0].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
B[1].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
B[2].Value = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
B[3].Value = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i=0;i<4;i++)
{
switch(B[i].State)
{
case 0: if(B[i].Value == 0)
{
B[i].State = 1;
B[i].Time = 0;
}
break;
case 1: if(B[i].Value == 0)
B[i].State = 2;
else
B[i].State = 0;
break;
case 2: if(B[i].Value == 1)
{
//10ms进来一次,0.8s = 800ms
//进来80次,也就是10ms*80 = 800ms = 0.8s
if(B[i].Time<80)
B[i].Short_Flag = 1;
else
B[i].Long_Flag = 1;
B[i].State = 0;
}
else
{
B[i].Time++;
if(i == 2)
if(B[2].Time>=80)//如果是按键B3长按就进入
Set_Time();//进行时间连加
}
break;
default:break;
}
}
}
}
找到stm32g4xx_hal_gpio.c右击找到对应的头文件,滑倒最后可以找到HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
按键功能的编写
void Key_Handle(void)
{
if(B[0].Short_Flag == 1)
{
//进来之后把能初始化的都初始化,以免出现bug
Data.Set_Time = 0;//取消高亮
Data.State = 0;//状态设为取消
Data.Place++;
if(Data.Place>5)
Data.Place = 1;
if(Data.Place == 1)
{
//读取EEPROM的值
Data.Time_H = x24c02_read(0);HAL_Delay(10);
Data.Time_M = x24c02_read(1);HAL_Delay(10);
Data.Time_S = x24c02_read(2);
}
if(Data.Place == 2)
{
Data.Time_H = x24c02_read(3);HAL_Delay(10);
Data.Time_M = x24c02_read(4);HAL_Delay(10);
Data.Time_S = x24c02_read(5);
}
if(Data.Place == 3)
{
Data.Time_H = x24c02_read(6);HAL_Delay(10);
Data.Time_M = x24c02_read(7);HAL_Delay(10);
Data.Time_S = x24c02_read(8);
}
if(Data.Place == 4)
{
Data.Time_H = x24c02_read(9);HAL_Delay(10);
Data.Time_M = x24c02_read(10);HAL_Delay(10);
Data.Time_S = x24c02_read(11);
}
if(Data.Place == 5)
{
Data.Time_H = x24c02_read(12);HAL_Delay(10);
Data.Time_M = x24c02_read(13);HAL_Delay(10);
Data.Time_S = x24c02_read(14);
}
B[0].Short_Flag = 0;
}
if(B[1].Short_Flag == 1)
{
Data.State = 1;//当系统正在设置时间时
Data.Set_Time++;
if(Data.Set_Time>3)
Data.Set_Time = 1;
B[1].Short_Flag = 0;
}
if(B[1].Long_Flag == 1)
{
//数据进行清除
Data.State = 0;//状态设置为取消
Data.Set_Time = 0;//取消高亮
if(Data.Place == 1)
{
//存储EEPROM
x24c02_write(0,Data.Time_H);HAL_Delay(10);
x24c02_write(1,Data.Time_M);HAL_Delay(10);
x24c02_write(2,Data.Time_S);
}
if(Data.Place == 2)
{
x24c02_write(3,Data.Time_H);HAL_Delay(10);
x24c02_write(4,Data.Time_M);HAL_Delay(10);
x24c02_write(5,Data.Time_S);
}
if(Data.Place == 3)
{
x24c02_write(6,Data.Time_H);HAL_Delay(10);
x24c02_write(7,Data.Time_M);HAL_Delay(10);
x24c02_write(8,Data.Time_S);
}
if(Data.Place == 4)
{
x24c02_write(9,Data.Time_H);HAL_Delay(10);
x24c02_write(10,Data.Time_M);HAL_Delay(10);
x24c02_write(11,Data.Time_S);
}
if(Data.Place == 5)
{
x24c02_write(12,Data.Time_H);HAL_Delay(10);
x24c02_write(13,Data.Time_M);HAL_Delay(10);
x24c02_write(14,Data.Time_S);
}
B[1].Long_Flag = 0;
}
if(B[2].Short_Flag == 1)
{
Set_Time();
B[2].Short_Flag = 0;
}
if(B[3].Short_Flag == 1)
{
switch(Data.State)
{
//当定时器停止时,打开定时器
case 0:Data.State = 2;break;
//如果为设置模式,直接运行
//并且清除高亮
case 1:Data.State = 2;Data.Set_Time=0;break;
//当定时器运行时,暂停定时器
case 2:Data.State = 3;break;
//当定时器暂停时,运行定时器
case 3:Data.State = 2;break;
default:break;
}
B[3].Short_Flag = 0;
}
if(B[3].Long_Flag == 1)
{
//定时器关闭
Data.State = 0;
Data.Set_Time=0;//清除高亮
B[3].Long_Flag = 0;
}
}
3.6 PWM的编写
根据标志位开启和关闭PWM即可用。
void PWM(void)
{
switch(Data.State)
{
//当定时器停止时,关闭PWM
case 0: HAL_TIM_PWM_Stop(&htim16,TIM_CHANNEL_1);break;
//当定时器运行时,开启PWM
case 2:HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);break;
//当定时器暂停时,关闭PWM
case 3:HAL_TIM_PWM_Stop(&htim16,TIM_CHANNEL_1);break;
default:break;
}
}
找到stm32g4xx_hal_tim.c打开,右击到对应的头文件,滑到最后。
对于参数的填写可以按F12跳转到该函数,上方就会出现参数。
3.7 LED的编写
void LED(void)
{
//定时器运行时
if(Data.State == 2)
{
Data.LED_Flag++;
//0.5s = 500ms >> 计数值为50
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
if(Data.LED_Flag>50&&Data.LED_Flag<100)
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
if(Data.LED_Flag>100)
Data.LED_Flag = 0;
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
//定时器取消和暂停时
else if((Data.State == 0)||(Data.State == 3))
{
Data.LED_Flag = 0;
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
}
根据标志位改变灯的变化即可。
对应的函数到stm32g4xx_hal_gpio.c,右击到对应的头文件,滑倒最后。
和上面一样参数可以按F12进行跳转填写 。
3.8 时间编写
我们把时间递减放到了按键的中断里10ms一次计数一百就开始递减。
void Time(void)
{
if(Data.State == 2)
{
if(Data.Time_H==0&&Data.Time_M==0&&Data.Time_S==0)
Data.State = 0;
else
{
Data.Time_Count++;
//十毫秒进来一次,进100次为1s
if(Data.Time_Count>100)
{
Data.Time_Count = 0;
if(Data.Time_S>0)
Data.Time_S--;
else
{
if(Data.Time_M>0)
{
Data.Time_S = 59;
Data.Time_M--;
}
else
{
if(Data.Time_H>0)
{
Data.Time_H--;
Data.Time_M = 59;
Data.Time_S = 59;
}
else
{
Data.Time_H = 0;
Data.Time_M = 0;
Data.Time_S = 0;
Data.State = 0;
}
}
}
}
}
}
}
3.9 初始化
开启一些定时器和LCD的初始化,以及设置变量的初始值,和EEPROM的读取。
void Init(void)
{
//开启定时器中断
HAL_TIM_Base_Start_IT(&htim4);
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
//初始化存储位置
Data.Place = 1;
//读取时间
Data.Time_H = x24c02_read(0);
Data.Time_M = x24c02_read(1);
Data.Time_S = x24c02_read(2);
//因为我们显示时间的时候是局部的,所以这里直接显示 :
LCD_DisplayStringLine(Line4, (uint8_t *)" : : ");
}
HAL_TIM_Base_Start_IT和上面的一样找到stm32g4xx_hal_tim.c对应的头文件在最后面。
4.结果展示
5.结尾
到此蓝桥杯嵌入式九届省赛完毕,其中需要修改LCD的一个函数所以在练习过程中,要了解一下官方给的历程方便改写。在编写过程中我们先移植再编写,写一个模块就验证一个模块。当全部写完了之后也要看一下是否存在bug,然后再进行改写。代码水平有限还请见谅。
如有错误还请指正,谢谢。