第12届 蓝桥杯 嵌入式设计与开发项目

本文介绍了STM32单片机的学习与实践,重点在于串口通信的难点及解决方法。作者通过实现键值处理、LED控制、LCD显示和串口接收中断等功能,展示了如何处理接收到的车流量数据,包括数据合法性检查、车类型判断、时间计算等。通过这些操作,实现了对进出车辆的计费和时间管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为把STM32这块单片机的资源都学完了,也就找了份真题来做做,在网上记录一下,一开始自己做是完完全全卡在串口那里了,完全不知道怎么做,之后去看了一下教程,虽然但是,在里面还是学到了很多的知识点的。

 

 

这一届,除了串口那一部分比较难,其它都还挺简单的了。。

用到的变量

/* 按键变量*/
__IO uint32_t key_uwTick;
uint8_t ucKey_Val,ucKey_Down,ucKey_Up,ucKey_Old;

/* LED变量*/
__IO uint32_t led_uwTick;
uint8_t ucled;

/* LCD变量*/
__IO uint32_t lcd_uwTick;
uint8_t lcd_disp_string[21];

/* 串口相关变量*/
__IO uint32_t usart_uwTick;
//uint8_t Tx_dat[20];
uint8_t Rx_dat;
uint8_t Rx_buf[200];//用于接收22个字符的缓冲
uint8_t Rx_count;//索引,记录接收了多少个数据
uint8_t str_str[40];

_Bool interface;
uint8_t CNBR;
uint8_t VNBR;
uint8_t IDEL = 8;
float CNBR_Price = 3.50;
float VNBR_Price = 2.00;
_Bool PA7_Staus;


typedef struct{
	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 min_in;
	uint8_t sec_in;
	_Bool notEmpty;
} Car_Data_Storage_Type;

/* 数据库构建完毕,用于存储8个进来的车的信息*/
Car_Data_Storage_Type Car_Data_Storage[8];

先把外设的函数和.c文件写好 

led_key.c

#include "led_key.h"

void Led_Disp(uint8_t ucled)
{
	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);
	
	HAL_GPIO_WritePin(GPIOC,ucled<<8,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

uint8_t Key_Scans(void)
{
	uint8_t ucKey_val = 0;
	if(key1 == Key_Down)
		ucKey_val = 1;
	if(key2 == Key_Down)
		ucKey_val = 2;
	if(key3 == Key_Down)
		ucKey_val = 3;
	if(key4 == Key_Down)
		ucKey_val = 4;
	
	return ucKey_val;
}

 外设的处理函数

void Key_Proc(void)
{
	if(uwTick - key_uwTick < 50)
		return;
	key_uwTick = uwTick;
	ucKey_Val = Key_Scans();
	ucKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
	ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);
	ucKey_Old = ucKey_Val;
	
	switch(ucKey_Down)
	{
		case 1:
			interface ^= 0x1;
			break;
		case 2: 
			if(interface)
			{
				VNBR_Price += 0.5f;
				CNBR_Price += 0.5f;	
			}
			break;
		case 3: 
			if(interface)
			{
				if((VNBR_Price - 0.5f)> 0)
				{
					VNBR_Price -= 0.5f;
					CNBR_Price -= 0.5f;	
				}
			}
			break;
		case 4: 
			PA7_Staus ^= 0x1;
			if(PA7_Staus)
				__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 100);//强制配置成PWM电平
			else
				__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 0);
			break;
	}
}

void Led_Proc(void)
{
	if(uwTick - led_uwTick < 200)
		return;
	led_uwTick = uwTick;
	
	if(IDEL)
		ucled |= 0x01;
	else
		ucled &= (~0x01);
	if(PA7_Staus)
		ucled |= 0x02;
	else
		ucled &= (~0x02);
	
	Led_Disp(ucled);
}

void Lcd_Proc(void)
{
	if(uwTick - lcd_uwTick < 200)
		return;
	lcd_uwTick = uwTick;
	
	if(interface == 0)
	{
		LCD_DisplayStringLine(Line0,(u8 *)"        Data        ");
		sprintf((char *)lcd_disp_string,"    CNBR:%d          ",CNBR);
		LCD_DisplayStringLine(Line4,lcd_disp_string);
		sprintf((char *)lcd_disp_string,"    VNBR:%d          ",VNBR);
		LCD_DisplayStringLine(Line6,lcd_disp_string);
		sprintf((char *)lcd_disp_string,"    IDEL:%d          ",IDEL);
		LCD_DisplayStringLine(Line8,lcd_disp_string);
	}
	else
	{
		LCD_DisplayStringLine(Line0,(u8 *)"        Pata        ");
		sprintf((char *)lcd_disp_string,"    CNBR:%.2f       ",CNBR_Price);
		LCD_DisplayStringLine(Line4,lcd_disp_string);
		sprintf((char *)lcd_disp_string,"    VNBR:%.2f       ",VNBR_Price);
		LCD_DisplayStringLine(Line6,lcd_disp_string);
		LCD_DisplayStringLine(Line8,(u8 *)"                    ");
	}
}

 在main函数中初始化

LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
	
__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 0);//强制配置成低电平
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);		//PA7

/* 串口接收中断打开*/
	HAL_UART_Receive_IT(&huart1,(uint8_t *)(&Rx_dat),1);//执行完这个之后会进入下面的中断回调函数

之后就是处理串口了,首先得判断接受的数据个数和格式是否合法,再通过判断车的类型判断车有没有进(这时候就需要从那一串发送的数据中提取车的类型和id),如果车还没进入,就得把车放进去,让车的类型++,同时空位要--;如果车已经进来了,就要判断车在里面的时间了,这里用的方法是把时间全都转换成秒,再相减,得到结果再通过串口发送出去,之后还得让车的类型-1,空位+1,最后清空该位置的所有内容,全清为0。

第一步:判断发送的22个字符是否合法。

_Bool CheckCmd(uint8_t* str)
{
	if(Rx_count != 22)
		return 0;
	if(((str[0]=='C')||(str[0]=='V'))&&(str[1]=='N')&&(str[2]=='B')&&(str[3]=='R')&&(str[4]==':')&&(str[9]==':'))
	{
		uint8_t i;
		for(i = 10; i < 22; i++)
		{
			if((str[i]>'9')||(str[i]<'0'))
				return 0;
		}
		return 1;//表示接受的数据没问题
	}
}

第二步:判断车的类型(所以要先从长的字符串中提取短的字符串,也即是车的类型)

void substr(uint8_t* d_str,uint8_t* s_str,uint8_t loc,uint8_t len)
{
	uint8_t i = 0;
	for(i = 0; i < len; i++)
	{
		d_str[i] = s_str[loc+i];
	}
	d_str[len] = '\0';
}

第三步:判断车是否在里面(判断车的id)

uint8_t isExist(uint8_t* str)
{
	uint8_t i = 0;
	for(i = 0; i < 8; i++)
	{
		if((strcmp((const char *)str,(const char *)Car_Data_Storage[i].id)) == 0)//字符串匹配,表示库里面有这辆车(车在库里)
		{
			return i;
		}
	}
	return 0xff; //车不在库里,返回0xff
}

第四步:如果车不在里面,判断哪个位置有空位

uint8_t findLocate(void)
{
	uint8_t i = 0;
	for(i = 0; i < 8; i++)
	{
		if(Car_Data_Storage[i].notEmpty == 0)
			return i;
	}
	return 0xff;
}

串口处理函数

void Usart_Proc(void)
{
	if(uwTick - usart_uwTick < 100)
		return;
	usart_uwTick = uwTick;
//	sprintf((char *)lcd_disp_string,"    test:%d          ",CheckCmd(Rx_buf));
//	LCD_DisplayStringLine(Line9,lcd_disp_string);
	
	if(CheckCmd(Rx_buf))//数据个数和格式是否合法
	{
		uint8_t car_id[5];
		uint8_t car_type[5];
		uint8_t year_temp,month_temp,day_temp,hour_temp,min_temp,sec_temp;
		year_temp = ((Rx_buf[10] - '0')*10) + (Rx_buf[11] - '0');
		month_temp = (((Rx_buf[12] - '0')*10) + (Rx_buf[13] - '0')); 	
		day_temp = (((Rx_buf[14] - '0')*10) + (Rx_buf[15] - '0')); 	
		hour_temp = (((Rx_buf[16] - '0')*10) + (Rx_buf[17] - '0')); 	
		min_temp = (((Rx_buf[18] - '0')*10) + (Rx_buf[19] - '0')); 	
		sec_temp = (((Rx_buf[20] - '0')*10) + (Rx_buf[21] - '0')); 
		if((month_temp>12)||(day_temp>31)||(hour_temp>23)||(min_temp>59)||(sec_temp>59))//验证日期和时间是否合法
		{
			goto SEND_ERROR;
		}
		
		substr(car_id,Rx_buf,5,4);//提取车的id
		substr(car_type,Rx_buf,0,4);//提取车的类型
		
//		sprintf((char *)lcd_disp_string, "   test:%x",isExist(car_id));
//		LCD_DisplayStringLine(Line9, lcd_disp_string);						
		
		/**  车还没进入*/
		if(isExist(car_id) == 0xff)
		{
			uint8_t locate = findLocate();//找到哪个地方是空的
			if(locate == 0xff)
			{
				goto SEND_ERROR;
			}
			
			substr(Car_Data_Storage[locate].type,car_type,0,4);//把当前车的类型存入	
			substr(Car_Data_Storage[locate].id,car_id,0,4);//把当前车的id存入
			Car_Data_Storage[locate].year_in = year_temp;
			Car_Data_Storage[locate].month_in = month_temp;
			Car_Data_Storage[locate].day_in = day_temp;
			Car_Data_Storage[locate].hour_in = hour_temp;
			Car_Data_Storage[locate].min_in = min_temp;
			Car_Data_Storage[locate].sec_in = sec_temp;
			Car_Data_Storage[locate].notEmpty = 1;
			
			if(Car_Data_Storage[locate].type[0] == 'C')
				CNBR++;
			else if(Car_Data_Storage[locate].type[0] == 'V')
				VNBR++;
			
			IDEL--;
			
			
		}
		
		/** 车已经在库里,进来了*/
		else if(isExist(car_id) != 0xff)
		{
			int64_t Second_derta;//核算小时的差值	
			uint8_t in_locate = isExist(car_id);//记录在数据库中的位置
			
			if(strcmp((const char *)car_type,(const char *)Car_Data_Storage[in_locate].type) != 0) //不匹配
			{
				goto SEND_ERROR;
			}
			
			Second_derta = (year_temp - Car_Data_Storage[in_locate].year_in)*365*24*60*60 + (month_temp - Car_Data_Storage[in_locate].month_in)*30*24*60*60+\
				(day_temp - Car_Data_Storage[in_locate].day_in)*24*60*60 + (hour_temp - Car_Data_Storage[in_locate].hour_in)*60*60 + \
				(min_temp - Car_Data_Storage[in_locate].min_in)*60 + (sec_temp - Car_Data_Storage[in_locate].sec_in);
			if(Second_derta < 0)//出去的时间超前进去的时间
			{
				goto SEND_ERROR;
			}
			
			Second_derta = (Second_derta + 3599)/3600;
			sprintf((char *)str_str,"%s:%s:%d:%.2f\r\n",Car_Data_Storage[in_locate].type,Car_Data_Storage[in_locate].id,(uint32_t)Second_derta,Second_derta*(Car_Data_Storage[in_locate].id[0] == 'C' ? CNBR_Price:VNBR_Price));
			HAL_UART_Transmit(&huart1,(uint8_t *)str_str,sizeof(str_str),50);
			
			if(Car_Data_Storage[in_locate].type[0] == 'C')
				CNBR--;
			else if(Car_Data_Storage[in_locate].type[0] == 'V')
				VNBR--;
			
			IDEL++;
			
			memset(&Car_Data_Storage[in_locate],0,sizeof(Car_Data_Storage[in_locate]));//清空该位置所有内容,为0
		}
		
		
		
		goto CMD_YES;
		
		SEND_ERROR:
			sprintf((char *)str_str, "Error\r\n");
			HAL_UART_Transmit(&huart1,(unsigned char *)str_str, sizeof(str_str), 50);
		
		CMD_YES:
		memset(&Rx_buf[0],0,sizeof(Rx_buf));
			Rx_count = 0;
		
	}
	
}

当然,我们发送的数据得存起来。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	Rx_buf[Rx_count] = Rx_dat;
	Rx_count++;
	
	HAL_UART_Receive_IT(&huart1,(uint8_t *)(&Rx_dat),1);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wu小燕呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值