一.题目要求

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.出库的操作:

            发送同一个车辆类型和车辆编号,时间大于入库时间即可出库

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐