蓝桥杯嵌入式第十二届省赛第一场
·
一.题目要求
1.设计要求

2.界面显示


3.按键控制

4.串口收发




5.LED灯和初始化

二.代码实现
1.main中
#include "user.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
HAL_UART_Receive_IT(&huart1,&rx_data,1);
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
while (1)
{
key_pro();
lcd_pro();
led_pro();
if(rx_pointer > 0)
{
uart_rx_pro();
}
}
}
2.user.c中
#include "user.h"
struct Car_info Car[8]; //8个车位信息
uint8_t key_value,key_down,key_up,key_old = 0;
void key_scan(void)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET)
key_value = 1;
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET)
key_value = 2;
else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET)
key_value = 3;
else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET)
key_value = 4;
else key_value = 0;
key_down = key_value & (key_value ^ key_old);
key_up = ~key_value & (key_value ^ key_old);
key_old = key_value;
}
uint8_t page_index = 1;
uint32_t key_tick = 0;
uint8_t pwm_sta = 0;
double CNBR_price = 3.50,VNBR_price = 2.50;
void key_pro(void)
{
if(uwTick - key_tick < 10)
return;
key_tick = uwTick;
key_scan();
if(key_down == 1)
{
page_index ++;
LCD_Clear(Black);
if(page_index > 2)page_index = 1;
}
else if(key_down == 4)
{
pwm_sta ++;
if(pwm_sta > 1)pwm_sta = 0;
if(!pwm_sta)TIM17 -> CCR1 = 0;
else TIM17 -> CCR1 = 100;
}
if(page_index == 2)
{
if(key_down == 2)
{
CNBR_price += 0.5;
VNBR_price += 0.5;
}
else if(key_down == 3)
{
if(CNBR_price > 0.001)CNBR_price -= 0.5;
if(VNBR_price > 0.001)VNBR_price -= 0.5;
}
}
}
extern UART_HandleTypeDef huart1;
int fputc(int ch, FILE *f)
{
uint8_t c = (uint8_t)ch;
HAL_UART_Transmit(&huart1,&c,1,50);
return ch;
}
#define RX_BUF_SIZE 64
#define RX_TIMEOUT 100
uint8_t rx_pointer,rx_data,rx_buf[RX_BUF_SIZE];
uint8_t CNBR = 0,VNBR = 0,IDLE = 8; //车类型,IDLE是空位余量
uint32_t rx_last_time = 0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(rx_pointer < RX_BUF_SIZE - 1)
{
rx_buf[rx_pointer++] = rx_data;
rx_last_time = uwTick; //更新最后接收数据的时间
}
HAL_UART_Receive_IT(&huart1,&rx_data,1);
}
//函数功能:检测输入的格式。参数:字符串数组
uint8_t Check_Format(uint8_t *rx_str)
{
uint8_t i;
if(rx_pointer != 22) //长度不够,不正确
{
return 0;
}
else if(rx_pointer == 22)
{
if((rx_buf[0] == 'V' || rx_buf[0] == 'C') && rx_buf[1] == 'N' &&
rx_buf[2] == 'B' && rx_buf[3] == 'R' && rx_buf[4] == ':' && rx_buf[9] == ':')
{
for(i = 10;i < 22; i++)
{
if(rx_buf[i] > '9' || rx_buf[i] < '0') //时间每位不是1到9之间的字符,不正确
{
return 0;
}
}
//格式正确
return 1;
}
else //不是CNBR或VNBR和冒号:,不正确
{
return 0;
}
}
return 0x11; //随便返回,不然有警告
}
//函数功能:提取字符串函数。参数:目标字符串和源字符串,起始提取位置,提取长度
void Substr(uint8_t *d_str,uint8_t *s_str,uint8_t location,uint8_t length)
{
uint8_t i = 0;
for(i = 0;i < length;i ++)
{
d_str[i] = s_str[location + i];
}
d_str[length] = '\0'; //结束字符占一个位置
}
//判断车是否在库
uint8_t isExist(uint8_t *str)
{
uint8_t i = 0;
for(i = 0;i < 8;i ++)
{
if(Car[i].NotEmpty ==1 && strcmp((const char*)str,(const char*)Car[i].ID) == 0) //在库
return i;
}
return 0xFF; //库中没有这辆车,只要不是1到8的数就好
}
uint8_t FindLocation(void)
{
uint8_t i = 0;
for(i = 0;i < 8;i ++)
{
if(Car[i].NotEmpty == 0) //没有这辆车
return i;
}
return 0xFF; //库中没有这辆车,只要不是1到8的数就好
}
// 辅助函数:判断是否为闰年
bool is_leap_year(uint16_t year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// 辅助函数:计算从2000-01-01到给定日期的总天数
uint32_t calculate_days(uint16_t year, uint8_t month, uint8_t day)
{
uint32_t days = 0;
for (uint16_t y = 2000; y < year; y++)
{
days += is_leap_year(y) ? 366 : 365;
}
for (uint8_t m = 1; m < month; m++)
{
switch (m)
{
case 1: days += 31; break;
case 2: days += is_leap_year(year) ? 29 : 28; break;
case 3: days += 31; break;
case 4: days += 30; break;
case 5: days += 31; break;
case 6: days += 30; break;
case 7: days += 31; break;
case 8: days += 31; break;
case 9: days += 30; break;
case 10: days += 31; break;
case 11: days += 30; break;
case 12: days += 31; break;
}
}
days += day - 1; // 从1日开始计算
return days;
}
void uart_rx_pro(void)
{
if (uwTick - rx_last_time > RX_TIMEOUT && rx_pointer > 0)
{
if (Check_Format(rx_buf) == 0)
{
printf("Error\r\n");
}
else
{
uint8_t year_temp = (rx_buf[10] - '0') * 10 + (rx_buf[11] - '0');
uint8_t month_temp = (rx_buf[12] - '0') * 10 + (rx_buf[13] - '0');
uint8_t day_temp = (rx_buf[14] - '0') * 10 + (rx_buf[15] - '0');
uint8_t hour_temp = (rx_buf[16] - '0') * 10 + (rx_buf[17] - '0');
uint8_t minute_temp = (rx_buf[18] - '0') * 10 + (rx_buf[19] - '0');
uint8_t second_temp = (rx_buf[20] - '0') * 10 + (rx_buf[21] - '0');
// 初步时间校验
if (year_temp > 99 || month_temp > 12 || day_temp > 31 ||
hour_temp > 23 || minute_temp > 59 || second_temp > 59)
{
printf("Error\r\n");
}
else //时间正确
{
// 详细月份和天数校验
uint8_t max_day;
if (month_temp == 4 || month_temp == 6 || month_temp == 9 || month_temp == 11)
{
max_day = 30;
}
else if (month_temp == 2)
{
uint16_t full_year = 2000 + year_temp;
max_day = is_leap_year(full_year) ? 29 : 28;
}
else
{
max_day = 31;
}
if (day_temp == 0 || day_temp > max_day)
{
printf("Error\r\n");
}
else //时间准确
{
uint8_t Car_Type[5], Car_ID[5];
Substr(Car_Type, rx_buf, 0, 4);
Substr(Car_ID, rx_buf, 5, 4);
uint8_t exist_idx = isExist(Car_ID);
// 入库
if (exist_idx == 0xFF)
{
uint8_t loc = FindLocation();
if (loc == 0xFF)
{
printf("Error\r\n");
}
else
{
memcpy(Car[loc].Type, Car_Type, 4);
memcpy(Car[loc].ID, Car_ID, 4);
Car[loc].Year_in = year_temp;
Car[loc].Month_in = month_temp;
Car[loc].Day_in = day_temp;
Car[loc].Hour_in = hour_temp;
Car[loc].Minute_in = minute_temp;
Car[loc].Second_in = second_temp;
Car[loc].NotEmpty = 1;
if (Car_Type[0] == 'C') CNBR++;
else if (Car_Type[0] == 'V') VNBR++;
IDLE--;
// printf("OK\r\n");
}
}
else // 出库
{
struct Car_info *car = &Car[exist_idx];
uint16_t in_year = 2000 + car->Year_in;
uint16_t out_year = 2000 + year_temp;
// 计算时间差
uint32_t in_seconds = calculate_days(in_year, car->Month_in, car->Day_in) * 24 * 60 * 60 +
car->Hour_in * 60 * 60 + car->Minute_in * 60 + car->Second_in;
uint32_t out_seconds = calculate_days(out_year, month_temp, day_temp) * 24 * 60 * 60 +
hour_temp * 60 * 60+ minute_temp * 60 + second_temp;
if (out_seconds <= in_seconds)
{
// printf("Error: Invalid Out Time\r\n");
printf("Error\r\n");
}
else
{
uint32_t total_seconds = out_seconds - in_seconds;
uint32_t billed_hours = (total_seconds + 3599) / 3600; // 向上取整
if(car->Type[0] == 'C')
{
printf("%s:%s:%d%d%d%d%d%d\r\n",Car->Type,Car->ID,Car->Year_in,Car->Month_in,
Car->Day_in,Car->Hour_in ,Car ->Minute_in,Car ->Second_in);
printf("%s:%s:%d%d%d%d%d%d\r\n",Car->Type,Car->ID,year_temp,month_temp,
day_temp,hour_temp ,minute_temp,second_temp);
printf("%s:%s:%d:%.2lf\r\n",Car->Type,Car->ID,billed_hours,(billed_hours * VNBR_price));
}
else
{
printf("%s:%s:%d%d%d%d%d%d\r\n",Car->Type,Car->ID,Car->Year_in,Car->Month_in,
Car->Day_in,Car->Hour_in ,Car ->Minute_in,Car ->Second_in);
printf("%s:%s:%d%d%d%d%d%d\r\n",Car->Type,Car->ID,year_temp,month_temp,
day_temp,hour_temp ,minute_temp,second_temp);
printf("%s:%s:%d:%.2lf\r\n",Car->Type,Car->ID,billed_hours,(billed_hours * VNBR_price));
}
car->NotEmpty = 0;
if (car->Type[0] == 'C') CNBR--;
else VNBR--;
IDLE++;
}
}
}
}
}
rx_pointer = 0;
memset(rx_buf, 0, sizeof(rx_buf));
}
}
void led_pro(void)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
if(pwm_sta == 1)HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET); //不输出低电平
else HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET);
if(IDLE > 0)HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET); //有空位
else HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
char buf[30];
uint32_t lcd_tick = 0;
void lcd_pro(void)
{
if(uwTick - lcd_tick < 150)
return;
lcd_tick = uwTick;
switch(page_index)
{
case 1:
{
LCD_DisplayStringLine(Line1,(u8 *)" Data ");
sprintf(buf," CNBR:%d ",CNBR);
LCD_DisplayStringLine(Line3,(u8 *)buf);
sprintf(buf," VNBR:%d ",VNBR);
LCD_DisplayStringLine(Line5,(u8 *)buf);
sprintf(buf," IDLE:%d ",IDLE);
LCD_DisplayStringLine(Line7,(u8 *)buf);
}break;
case 2:
{
LCD_DisplayStringLine(Line1,(u8 *)" Para ");
sprintf(buf," CNBR:%.2f ",CNBR_price);
LCD_DisplayStringLine(Line3,(u8 *)buf);
sprintf(buf," VNBR:%.2f ",VNBR_price);
LCD_DisplayStringLine(Line5,(u8 *)buf);
}break;
}
}
3.user.h中
#ifndef __USER_H
#define __USER_H
#include "main.h"
#include "stdio.h"
#include "string.h"
#include "stdbool.h"
void key_pro(void);
void lcd_pro(void);
void led_pro(void);
int fputc(int ch, FILE *f);
void uart_rx_pro(void);
extern uint8_t rx_data;
extern uint8_t rx_pointer;
struct Car_info
{
uint8_t Type[5];
uint8_t ID[5];
uint8_t Year_in;
uint8_t Month_in;
uint8_t Day_in;
uint8_t Hour_in;
uint8_t Minute_in;
uint8_t Second_in;
bool NotEmpty; //0表示空位,1表示这个这位有车
};
#endif
三.总结
1.串口的逻辑:
检查车辆信息格式是否正确(长度,停车类型和时间是否是数字字符) ---> 对时间进行由粗到细的判断(年月日时分秒的合理性,及闰年的判断) ---> (入库) 看车库中是否有该车,没有则入库,并记录入库时间 ---> (出库)车库有这个车,则计算出库与入库的时间差,然后计费,串口打印入库信息,出库信息和费用
2.闰年:
普通年可以被4整除但不能被100整除;世纪年可以被400整除(2000是闰年,1999不是)
3.出库的操作:
发送同一个车辆类型和车辆编号,时间大于入库时间即可出库
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)