原题:
代码:
为了简略,将注释部分 删除掉了。
main.c
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM4_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
LED_Display(0x00);//LED初始化
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
HAL_TIM_Base_Start_IT(&htim4);
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
HAL_UART_Receive_IT(&huart1,&rxdat,1);
while (1)
{
lcd_process();
key_process();
led_process();
if(rx_p!=0)
{
int temp=rx_p;
HAL_Delay(1);
if(temp==rx_p) uart_rx();
}
}
}
main函数主要是进行初始化,然后在while循环中调用lcd、按键、led、uart的函数实时判断完成其功能。
void lcd_process(void)
{
char text[30];
if(view==0)
{
Clear();
sprintf(text," Data ");
LCD_DisplayStringLine(Line1,(unsigned char*)text);
sprintf(text," CNBR:%d ",CNBR);
LCD_DisplayStringLine(Line3,(unsigned char*)text);
sprintf(text," VNBR:%d ",VNBR);
LCD_DisplayStringLine(Line5,(unsigned char*)text);
sprintf(text," IDLE:%d ",IDLE);
LCD_DisplayStringLine(Line7,(unsigned char*)text);
}else if(view==1)
{
Clear();
sprintf(text," Para ");
LCD_DisplayStringLine(Line1,(unsigned char*)text);
sprintf(text," CNBR:%.2lf ",P_CNBR);
LCD_DisplayStringLine(Line3,(unsigned char*)text);
sprintf(text," VNBR:%.2lf ",P_VNBR);
LCD_DisplayStringLine(Line5,(unsigned char*)text);
}
}
这是LCD的屏幕界面显示,利用Clear函数进行清屏,使得切换界面的时候不会闪烁,或者一次按键实现多次切屏功能。显示屏幕的内容就没什么多说的。
void key_process(void)
{
if(keys[0].single_flag==1)//按键B1 按下实现切换界面
{
view=(view+1)%2;
}else if(keys[1].single_flag==1)//按键B2 按下费率分别加0.5
{
if(view!=1) //只有在Para界面才有效,否则不改动数据
{
keys[1].single_flag=0;
return;
}
P_CNBR+=0.5;
P_VNBR+=0.5;
keys[1].single_flag=0;//防止一个按键实现多次功能
}else if(keys[2].single_flag==1)//按键B3 同B2 按下费率分别减0.5
{
if(view!=1)
{
keys[2].single_flag=0;
return;
}
P_CNBR-=0.5;
P_VNBR-=0.5;
keys[2].single_flag=0;
}else if(keys[3].single_flag==1)//按键B4
{
if(PA7_flag==false)//切换为2khz 20%占空比的脉冲输出
{
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,200);
}else //切换为低电平输出
{
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,0);
}
PA7_flag=!PA7_flag;
keys[3].single_flag=0;
}
}
这是按键的处理函数,根据每个按键按下的标志,来对应实现相应的逻辑。低电平输出,可以将占空比直接设置为0。
void led_process(void)
{
if(IDLE>=1&&PA7_flag)
{
LED_Display(0x03);
}else if(IDLE>=1&&!PA7_flag)
{
LED_Display(0x01);
}else if(IDLE<=0&&!PA7_flag)
{
LED_Display(0x00);
}else if(IDLE<=0&&PA7_flag)
{
LED_Display(0x02);
}
}
这是LED的显示函数。根据题目要求进行电灯。
可以分为四种情况:
1、有空余、PA7低电平 --- LED1 亮 LED2 灭
2、有空余、PA72Khz --- LED1 亮 LED2 亮
3、无空余、PA72Khz --- LED1 灭 LED2 亮
4、无空余、PA7低电平 --- LED1 灭 LED2 灭
void Clear(void)
{
if(keys[0].single_flag==1)
{
LCD_Clear(Black);
keys[0].single_flag=0;
}
}
主要是用来实现切换界面不闪烁。
void uart_rx(void)
{
char temp[20];
int flag=0;//判断停车场里是否有这个编号的车
if(rx_p>0)
{
if(rx_p==22)//收到了符合长度的数据
{
//读取
sscanf(rxdata,"%4s:%4s:%2d%2d%2d%2d%2d%2d",Car[car_p].car_type,Car[car_p].car_id,&Car[car_p].year,&Car[car_p].month,&Car[car_p].day,&Car[car_p].hour,&Car[car_p].minute,&Car[car_p].second);
//判断读取的数据有没有逻辑错误
if(isOK(Car[car_p])!=0)
{
sprintf(temp,"Error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t*)temp,strlen(temp),50);
rx_p=0;//很重要,每次退出前要清0
return;
}
//遍历车库,查找有没有同样编号的车
for(int i=0;i<car_p;i++)
{
if(!strcmp(Car[i].car_id,Car[car_p].car_id)&&i!=car_p)//如果有,就是出库
{
flag=1;
//计算时间
int ans=0;
ans+=(Car[car_p].year-Car[i].year)*365*24+(Car[car_p].month-Car[i].month)*30*24+(Car[car_p].day-Car[i].day)*24+(Car[car_p].hour-Car[i].hour);
if(Car[car_p].hour-Car[i].hour>0)
{
if((Car[car_p].minute*60+Car[car_p].second)-(Car[i].minute*60+Car[i].second)>0)
{
ans++;//不足一小时,按一小时计算
}
}
double feat=0;
if(Car[car_p].car_type[0]=='V')
{
feat=ans*P_VNBR;
VNBR--;
}else
{
feat=ans*P_CNBR;
CNBR--;
}
sprintf(temp,"%s:%s:%d:%.2lf\r\n",Car[car_p].car_type,Car[car_p].car_id,ans,feat);
HAL_UART_Transmit(&huart1,(uint8_t*)temp,strlen(temp),50);
IDLE++;
}
}
if(!flag)//没搜到,入场
{
if(IDLE==0)//如果满了
{
sprintf(temp,"Error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t*)temp,strlen(temp),50);
rx_p=0;
return;
}
if(Car[car_p].car_type[0]=='V')
{
VNBR++;
}else
{
CNBR++;
}
IDLE--;
}
car_p++;
}else
{
sprintf(temp,"Error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t*)temp,strlen(temp),50);
}
}
rx_p=0;//很重要
}
串口通信主要部分。大部分注释都写了,应该看得懂吧?
int isOK(struct car Car)
{
//类型
if(strcmp(Car.car_type,"CNBR"))
{
if(strcmp(Car.car_type,"VNBR"))
return 1;
}
//编号
if(strlen(Car.car_id)!=4)
return 2;
//时间
if(Car.month>12||Car.month<0)
return 3;
if(Car.day>31||Car.day<0)
return 4;
if(Car.hour>24||Car.hour<0)
return 5;
if(Car.minute>=60||Car.minute<0)
return 6;
if(Car.second>=60||Car.second<0)
return 7;
return 0;
}
判断数据的逻辑是否有误,无误返回0,否则返回错误的类型(其实是方便我debug)。
interrupt.c
#include "interrupt.h"
#include "usart.h"
struct key keys[4];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
keys[0].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
keys[1].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
keys[2].key_status=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
keys[3].key_status=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
//状态机,消抖
for(int i=0;i<4;i++)
{
switch(keys[i].judge_status)
{
case 0:
{
if(keys[i].key_status==0)
{
keys[i].judge_status=1;
keys[i].key_time=0;
}
}
break;
case 1:
{
if(keys[i].key_status==0)
{
keys[i].judge_status=2;
}else
{
keys[i].judge_status=0;
}
}
break;
case 2:
{
if(keys[i].key_status==0)
{
keys[i].key_time++;
if(keys[i].key_time>=70)
{
keys[i].long_flag=1;
}
}else
{
keys[i].judge_status=0;
if(keys[i].key_time<70)
{
keys[i].single_flag=1;
}
}
}
break;
}
}
}
}
char rxdata[30];//读取的数据的集合
uint8_t rxdat;//存储每次读取的数据
uchar rx_p;//标志读到第几位了
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断部分
{
rxdata[rx_p++]=rxdat;
HAL_UART_Receive_IT(&huart1,&rxdat,1);//每次读取1位数据
}
有关按键的部分可以参考【蓝桥杯嵌入式学习G431】模块三:KEY-CSDN博客
这里就不过多叙述了。
interrupt.h
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "main.h"
#include "stdbool.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
struct key
{
bool single_flag;
bool long_flag;
uint key_time;
uint key_status;
uint judge_status;
};
#endif
led.c
#include "led.h"
void LED_Display(uchar dsLED)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
有关LED的部分可以参考【蓝桥杯嵌入式学习G431】模块一:LED-CSDN博客
led.h
#ifndef _LED_H_
#define _LED_H_
#include "main.h"
void LED_Display(uchar dsLED);
#endif
lcd.c和lcd.h
这两个文件都是复制粘贴的官方文件。详情请参考【蓝桥杯嵌入式学习G431】模块二:LCD-CSDN博客
写完咯,有一说一,十二届简单,唯一难点就在串口的数据解析上。
----THE END-----
有关蓝桥杯嵌入式的模块知识请参考如下:
蓝桥杯嵌入式模块学习系列
【蓝桥杯嵌入式学习G431】模块五:USART-CSDN博客
(还有几个模块没更新~)
有关蓝桥杯嵌入式历届真题请参考如下: