赛题内容如下:

 

【【等琳啊】最新视频来袭,快来看看吧!】 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。

结尾:文章全为本人手打,如果有朋友使用了部分代码出现问题,可以积极在评论区讨论,使我们都能改正错误,共同进步!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐