原题:
代码:
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】模块五:USART-CSDN博客
(还有几个模块没更新~)
有关蓝桥杯嵌入式历届真题请参考如下: