蓝桥杯嵌入式第十二届省赛--程序设计部分

原题:

代码:

为了简略,将注释部分 删除掉了。

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】模块一:LED-CSDN博客

【蓝桥杯嵌入式学习G431】模块二:LCD-CSDN博客

【蓝桥杯嵌入式学习G431】模块三:KEY-CSDN博客

【蓝桥杯嵌入式学习G431】模块四:PWM-CSDN博客

【蓝桥杯嵌入式学习G431】模块五:USART-CSDN博客

(还有几个模块没更新~)


有关蓝桥杯嵌入式历届真题请参考如下:

蓝桥杯嵌入式历届真题系列

蓝桥杯嵌入式第十二届省赛--程序设计部分-CSDN博客

蓝桥杯嵌入式第十三届省赛第一场--程序设计部分-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值