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

原题:

代码:

LCD:

void lcd_proc(void)
{
	char text[30];
	if(view==0)
	{
		sprintf(text,"        SHOP   ");
		LCD_DisplayStringLine(Line1,(uchar*)text);
		sprintf(text,"     X:%d   ",X_shop);
		LCD_DisplayStringLine(Line3,(uchar*)text);
		sprintf(text,"     Y:%d   ",Y_shop);
		LCD_DisplayStringLine(Line4,(uchar*)text);
	}else if(view==1)
	{
		sprintf(text,"        PRICE   ");
		LCD_DisplayStringLine(Line1,(uchar*)text);
		sprintf(text,"     X:%.1lf   ",X_price);
		LCD_DisplayStringLine(Line3,(uchar*)text);
		sprintf(text,"     Y:%.1lf   ",Y_price);
		LCD_DisplayStringLine(Line4,(uchar*)text);
	}else if(view==2)
	{
		sprintf(text,"        REP   ");
		LCD_DisplayStringLine(Line1,(uchar*)text);
		sprintf(text,"     X:%d   ",X_rep);
		LCD_DisplayStringLine(Line3,(uchar*)text);
		sprintf(text,"     Y:%d   ",Y_rep);
		LCD_DisplayStringLine(Line4,(uchar*)text);
	}
}

LCD的显示界面跟着题目要求来即可,注意行数、空格数、保留几位小数点的要求。

建议是:不管用不用的上,都可以为了保险,去官方提供的LCD文件中,将

进行修改,详情请参考:蓝桥杯嵌入式第十四届模拟赛第一期--程序设计部分-CSDN博客

 KEY:

void key_proc(void)
{
	if(key[0].single_flag==1)//切换界面
	{
		view=(view+1)%3;
		LCD_Clear(Black);
		key[0].single_flag=0;
	}else if(key[1].single_flag==1)//对X商品进行操作
	{
		if(view==0)
		{
			X_shop++;
			if(X_shop>=X_rep+1)
				X_shop=0;
		}else if(view==1)
		{
			X_price+=0.1;
			if(X_price>=2.1)
				X_price=1.0;
			HAL_Delay(10);
			eeprom_write(0x02,(uchar)(X_price*10));
			HAL_Delay(10);
		}else if(view==2)
		{
			X_rep++;
			HAL_Delay(10);
			eeprom_write(0x00,X_rep);
			HAL_Delay(10);
		}
		key[1].single_flag=0;
	}else if(key[2].single_flag==1)//对Y商品进行操作
	{
		if(view==0)
		{
			Y_shop++;
			if(Y_shop>=Y_rep+1)
				Y_shop=0;
		}else if(view==1)
		{
			Y_price+=0.1;
			if(Y_price>=2.1)
				Y_price=1.0;
			HAL_Delay(10);
			eeprom_write(0x03,(uchar)(Y_price*10));
			HAL_Delay(10);
		}else if(view==2)
		{
			Y_rep++;
			HAL_Delay(10);
			eeprom_write(0x01,Y_rep);
			HAL_Delay(10);
		}
		key[2].single_flag=0;
	}else if(key[3].single_flag==1)//确认购买
	{
		if(view==0)
		{
			X_rep-=X_shop;
			Y_rep-=Y_shop;
			
			eeprom_write(0x01,Y_rep);
			HAL_Delay(10);
			eeprom_write(0x00,X_rep);
			HAL_Delay(10);
			
			//调节占空比
			__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,300);
			pwm_Tick=LED1_Tick=uwTick;
			
			//LED2闪烁
			if(X_rep==0&&Y_rep==0)
			{
				LED2=1;
			}else 
				LED2=0;
			
			//LED1点亮
			LED1=1;
			
			//串口
			char text[30];
			sprintf(text,"X:%d,Y:%d,Z:%.1lf\r\n",X_shop,Y_shop,X_shop*X_price+Y_shop*Y_price);
			HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
			
			X_shop=Y_shop=0;
		}
		key[3].single_flag=0;
	}
}

在按键中,注意及时清除按键的标志位,防止一个操作导致多个事情发生。在按键的处理过程中,同时结合了对数据存入eeprom中的操作。也控制了LED1和LED2的亮灭与闪烁。

LED:

void led_proc(void)
{
	//HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
	if(uwTick-LED2_Tick>=100&&LED2==1)
	{
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_9);
		LED2_Tick=uwTick;
	}
	
	if(LED1==1)
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
	}
	
	if(uwTick-LED1_Tick>=5000)
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
	}
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

采用SysTick进行定时,分别定义两个变量记录两个灯的开始时间,将与uwTick(1ms增加1)的差进行判断,例如LED2闪烁,如果二者差距100以上,也就是100ms,就反转LED2,并且把当时的uwTick存入变量中,以便进行下一次判断,如此循环,就能实现定时闪烁。

PWM:

void control(void) 
{
	if(uwTick-pwm_Tick>=5000)
	{
		__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,50);
	}
}

方法和LED差不多,满足条件时先将占空比调成20%,5s一到,就调回5%。

void rx_uart(void)
{
	char text[30];
	if(rx_p>0)
	{
		if(rx_p==1)
		{
			char rx;
			sscanf(rxdata,"%c",&rx);
			if(rx=='?')
			{
				sprintf(text,"X:%.1lf,Y:%.1lf\r\n",X_price,Y_price);
				HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
			}else
			{
				sprintf(text,"ERROR\r\n");
				HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
			}
		}else
		{
			sprintf(text,"ERROR\r\n");
			HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
		}
		rx_p=0;
		memset(rxdata,0,50);
	}
		
}

串口模块化的发送就好了。重点是记得rx_p和rxdata的清除。

中断:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
		key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i=0;i<4;i++)
		{
			switch(key[i].key_judge)
			{
				case 0:
				{
					if(key[i].key_sta==0)
					{
						key[i].key_judge=1;
						key[i].time=0;
					}
					break;
				}
				case 1:
				{
					if(key[i].key_sta==0)
					{
						key[i].key_judge=2;
					}else
					{
						key[i].key_judge=0;
					}
					break;
				}
				case 2:
				{
					
					if(key[i].key_sta==0)
					{
						key[i].time++;
					}else
					{
						if(key[i].time<=70)
						{
							key[i].single_flag=1;
							key[i].long_flag=0;
						}
						else
						{
							key[i].long_flag=1;
							key[i].single_flag=0;
						}
						key[i].key_judge=0;
					}
					break;
				}
			}
		}
	}
}


char rxdata[30];
uint8_t data;
int rx_p=0;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	rxdata[rx_p++]=data;
	HAL_UART_Receive_IT(&huart1,&data,1);
}

按键中断采取状态机消抖的方式。

EEPROM:

在官方给的i2c文件中,添加两个函数:

uchar eeprom_read(uchar addr)
{
	uchar dat=0;
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	dat=I2CReceiveByte();
	I2CSendNotAck();
	I2CStop();
	return dat;
}

void eeprom_write (uchar addr , uchar dat)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CSendAck();
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();
}

前者可以读取一个地址中的数据,后者可以在某个地址上写入数据。在连续的两个读或者写的函数之间一定要加入延时函数,大概10ms就够了。

uchar value=eeprom_read(0x04);
	HAL_Delay(10);
	if(value!=12)
	{
		eeprom_write(0x00,10);
		HAL_Delay(10);
		eeprom_write(0x01,10);
		HAL_Delay(10);
		eeprom_write(0x02,10);
		HAL_Delay(10);
		eeprom_write(0x03,10);
		HAL_Delay(10);
		eeprom_write(0x04,12);
		HAL_Delay(10);
	}
	
	X_rep=eeprom_read(0x00);//
	HAL_Delay(10);
	Y_rep=eeprom_read(0x01);
	HAL_Delay(10);
	X_price=(double)eeprom_read(0x02)/10;
	HAL_Delay(10);
	Y_price=(double)eeprom_read(0x03)/10;//
	HAL_Delay(10);

用于判断设备是不是第一次上电,采取标志位的方法,题目用到的地址为0-3,可以在地址4上假设一个标志位,如果这个地址的数据为12,说明此时设备不是第一次上电,读取地址0-3中的数据并且存入变量之中。如果数据不是12,说明设备是第一次上电,先将初始值写入地址中,再将标志位12写入地址中,表明此后将不是第一次上电。

  ----THE END-----

有关蓝桥杯嵌入式的模块知识请参考如下:

蓝桥杯嵌入式模块学习系列

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

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

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

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

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

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

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


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

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

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

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

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

蓝桥杯嵌入式第十四届模拟赛第一期--程序设计部分-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值