蓝桥杯嵌入式第十届赛题自我实现总结
* 存储器读 */if (ucNum)elseI2C_Stop();/* 存储器写 */I2C_Stop();使用的是软件i2c。
赛题内容如下:




【【等琳啊】最新视频来袭,快来看看吧!】 https://www.bilibili.com/video/BV13kwWeJE1u/?share_source=copy_web&vd_source=05fd25ae792a7d31219c6b244bda064c
首先,使用cubemx配置好比赛所需要的一些设置(一般你们都有模板,用你们模板配置好的东西就可以了,这个并没有定时器部分的配置内容)
其次(进入代码部分)
本人习惯于先弄页面显示部分。这个赛题的页面分为两个页面,一个为main页面显示模拟输出电压值和状态;另一个为设置页面,用来设置电压上限值,电压下限值,电压上限提醒灯,电压下限提醒灯的大小。
则第一个页面代码如下:
sprintf((char*)ucLcd," Main");
LCD_DisplayStringLine(Line0,ucLcd);
sprintf((char*)ucLcd," Volt:%3.2f",(float)v_value/4096.0*3.3);
LCD_DisplayStringLine(Line3,ucLcd);
sprintf((char*)ucLcd," Status:%s",state_mode);
LCD_DisplayStringLine(Line4,ucLcd);
if((float)v_value/4096.0*3.3>(float)v_max/10.0)
{
sprintf(state_mode,"Upper ");
}
else if((float)v_value/4096.0*3.3<(float)v_min/10.0)
{
sprintf(state_mode,"Lower ");
}
else
{
sprintf(state_mode,"Normal ");
}
其中使用sprintf来将字符串输入到uclcd里面(uclcd要不直接是char类型,要不和代码一样强制转化成char*类型),模拟电压输出后面讲。随后就是状态显示切换,我用的是字符串后面+空格,这样转换的时候,不会多一个l字符(这是一个很蠢的办法!)
第二个页面代码如下(博主只能粗糙的使用改背景颜色这个方法来实现高亮显示,引以为戒):
LCD_SetBackColor(Red);
sprintf((char*)ucLcd," Setting");
LCD_DisplayStringLine(Line0,ucLcd);
if(setting==1)
{
LCD_SetBackColor(Green);
sprintf((char*)ucLcd,"Max Volt:%3.1f",(float)v_max/10.0);
LCD_DisplayStringLine(Line1,ucLcd);
}
else
{
LCD_SetBackColor(Red);
sprintf((char*)ucLcd,"Max Volt:%3.1f",(float)v_max/10.0);
LCD_DisplayStringLine(Line1,ucLcd);
}
if(setting==2)
{
LCD_SetBackColor(Green);
sprintf((char*)ucLcd,"Min Volt:%3.1f",(float)v_min/10.0);
LCD_DisplayStringLine(Line2,ucLcd);
}
else
{
LCD_SetBackColor(Red);
sprintf((char*)ucLcd,"Min Volt:%3.1f",(float)v_min/10.0);
LCD_DisplayStringLine(Line2,ucLcd);
}
if(setting==3)
{
LCD_SetBackColor(Green);
sprintf((char*)ucLcd,"Upper:LD%u",led_max);
LCD_DisplayStringLine(Line3,ucLcd);
}
else
{
LCD_SetBackColor(Red);
sprintf((char*)ucLcd,"Upper:LD%u",led_max);
LCD_DisplayStringLine(Line3,ucLcd);
}
if(setting==4)
{
LCD_SetBackColor(Green);
sprintf((char*)ucLcd,"Lower:LD%u",led_min);
LCD_DisplayStringLine(Line4,ucLcd);
}
else
{
LCD_SetBackColor(Red);
sprintf((char*)ucLcd,"Lower:LD%u",led_min);
LCD_DisplayStringLine(Line4,ucLcd);
}
随后就是使用内部定时器来500ms刷新,代码如下(注意任意xx_tick要是uint32_t类型,不然会出现刷新不了的现象,xx_tick使用全局变量或者静态变量都行):
void LCD_Proc(void) /* LCD处理 */
{
static uint32_t LCD_Tick; //
if((uwTick - LCD_Tick) > 100 )
{
LCD_Tick=uwTick;
if(state==0) lcd1();
else
{
lcd2();
}
}
}
紧随其后,我会去写按键部分的代码,代码如下(其中i2c内容后面会叙述,其他部分并不难理解,可自行观看,都是一些状态机知识。长按和双击并未涉及,想看的朋友可以看我前一篇文章):
void KEY_Proc(void)
{
if(uwTick-Key_Tick<20) //官方建议20ms
return;
Key_Tick=uwTick;
KEY_Read();
//按键的短按处理
if(key_up==1) {if(++state>1) state=0; LCD_Clear(Red);}
else if(key_up==2) {if(state==1){if(++setting>4)setting=1;}}
else if(key_up==3)
{
if(state==1)
{
if(setting==1) //电压最大值调试
{
(v_max>=33) ? (v_max=0) : (v_max+=3);
MEM_Write((uint8_t*)&v_max,0x00,1);
}
if(setting==2) //电压最小值调试
{
(v_min>=33) ? (v_min=0) : (v_min+=3);
MEM_Write((uint8_t*)&v_min,0x08,1);
}
if(setting==3) //超出最大阈值指示灯
{
(led_max>=8) ? (led_max=0) : (led_max++);
MEM_Write((uint8_t*)&led_max,0x16,1);
}
if(setting==4) //低于最小阈值指示灯
{
(led_min>=8) ? (led_min=0) : (led_min++);
MEM_Write((uint8_t*)&led_min,0x24,1);
}
}
}
else if(key_up==4)
{
if(state==1)
{
if(setting==1) //电压最大值调试
{
(v_max<=0) ? (v_max=33) : (v_max-=3);
MEM_Write((uint8_t*)&v_max,0x00,1);
}
if(setting==2) //电压最小值调试
{
(v_min<=0) ? (v_min=33) : (v_min-=3);
MEM_Write((uint8_t*)&v_min,0x08,1);
}
if(setting==3) //超出最大阈值指示灯
{
(led_max<=0) ? (led_max=8) : (led_max--);
MEM_Write((uint8_t*)&led_max,0x16,1);
}
if(setting==4) //低于最小阈值指示灯
{
(led_min<=0) ? (led_min=8) : (led_min--);
MEM_Write((uint8_t*)&led_min,0x24,1);
}
}
}
然后,我会写led部分,代码如下:
void LED_Proc(void) /* LED处理 */
{
if(uwTick-led_tick<200) //200ms
return;
led_tick=uwTick;
if((float)v_value/4096.0*3.3>(float)v_max/10.0) //超出最大阈值指示灯代码
{
if(led_max==1)
{ ss=!ss; if(ss==0)ucLed=0x01; else ucLed=0x00;}
else if(led_max==2)
{ss=!ss; if(ss==0)ucLed=0x02; else ucLed=0x00;}
else if(led_max==3)
{ss=!ss; if(ss==0)ucLed=0x04; else ucLed=0x00;}
else if(led_max==4)
{ss=!ss; if(ss==0)ucLed=0x08; else ucLed=0x00;}
else if(led_max==5)
{ss=!ss; if(ss==0)ucLed=0x10; else ucLed=0x00;}
else if(led_max==6)
{ss=!ss; if(ss==0)ucLed=0x20; else ucLed=0x00;}
else if(led_max==7)
{ss=!ss; if(ss==0)ucLed=0x40; else ucLed=0x00;}
else if(led_max==8)
{ss=!ss; if(ss==0)ucLed=0x80; else ucLed=0x00;}
else
ucLed=0x00;
}
else if((float)v_value/4096.0*3.3<(float)v_min/10.0) //超出最大阈值指示灯代码
{
if(led_min==1)
{ ss=!ss; if(ss==0)ucLed=0x01; else ucLed=0x00;}
else if(led_min==2)
{ss=!ss; if(ss==0)ucLed=0x02; else ucLed=0x00;}
else if(led_min==3)
{ss=!ss; if(ss==0)ucLed=0x04; else ucLed=0x00;}
else if(led_min==4)
{ss=!ss; if(ss==0)ucLed=0x08; else ucLed=0x00;}
else if(led_min==5)
{ss=!ss; if(ss==0)ucLed=0x10; else ucLed=0x00;}
else if(led_min==6)
{ss=!ss; if(ss==0)ucLed=0x20; else ucLed=0x00;}
else if(led_min==7)
{ss=!ss; if(ss==0)ucLed=0x40; else ucLed=0x00;}
else if(led_min==8)
{ss=!ss; if(ss==0)ucLed=0x80; else ucLed=0x00;}
else
ucLed=0x00;
}
else
ucLed=0x00;
LED_Disp(ucLed); /* LED显示 */
}
由于led和lcd引脚冲突,所以需要要有锁存内容 来实现两者的合理运行,锁存代码如下:
void LED_Disp(uint8_t ucLed) /* LED显示 */
{ /* LED输出 */
GPIOC->ODR = ~ucLed << 8; /* 没有相应HAL函数 */
/* LED锁存 */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
解释:
由于GPIOC的led部分为16位,则我们配置8位ucled向左移8位,并取反就得到了结果.
随后进入到我们的AD部分(使用R37来实现模拟电压输出,具体内容解析我会出问文章解释):代码如下:
uint16_t ADC2_Read(void) /* ADC2读取 */
{
HAL_ADC_Start(&hadc2);
if (HAL_ADC_PollForConversion(&hadc2, 10) == HAL_OK)
return HAL_ADC_GetValue(&hadc2);
else
return 0;
}
void ADC_Proc(void) /* ADC处理 */
{
static uint32_t ADC_Tick; //
if((uwTick - ADC_Tick) > 100 )
{
ADC_Tick = uwTick;
v_value=ADC2_Read();
}
注意v_value需要是uint16_t类型和ad读到的数据进行储存,然后进行模数转化 ,由于ad分辨率为12位,所以换算如下:

最后,我会写i2c内容(具体内容解析,我后面会出文章解释),代码如下:
/* 存储器读 */
void MEM_Read(unsigned char* ucBuf, unsigned char ucAddr,
unsigned char ucNum)
{
I2C_Start();
I2C_SendByte(0xa0);
I2C_WaitAck();
I2C_SendByte(ucAddr);
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0xa1);
I2C_WaitAck();
while (ucNum--) {
*ucBuf++ = I2C_ReceiveByte();
if (ucNum)
I2C_SendAck();
else
I2C_SendNotAck();
}
I2C_Stop();
}
/* 存储器写 */
void MEM_Write(unsigned char* ucBuf, unsigned char ucAddr,
unsigned char ucNum)
{
I2C_Start();
I2C_SendByte(0xa0);
I2C_WaitAck();
I2C_SendByte(ucAddr);
I2C_WaitAck();
while (ucNum--) {
I2C_SendByte(*ucBuf++);
I2C_WaitAck();
}
I2C_Stop();
delay1(500);
}
使用的是软件i2c。
结尾:文章全为本人手打,如果有朋友使用了部分代码出现问题,可以积极在评论区讨论,使我们都能改正错误,共同进步!
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)