2015年嵌入式第六届省赛真题解析

一、题目

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16

二、题目分析

题目要求做一个电压测量监控设备,各个模块的使用都比较简单,但是当要利用各个模块完成一个简单的小项目,就要理清楚整个程序的大致框架。这里将整个功能分为三大部分显示界面,设置界面,串口接发。

1.界面

显示界面由外设读取显示,设置界面设置自动上报时间的。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16 

 界面的控制用一个变量LCD_GUI来控制。 若变量为0x00表示显示界面;若变量为0x1_,则为设置界面,并且0x10设置时,0x11设置分,0x12设置秒。

if(unKey_Down == 2)  //界面控制
	{
		if(LCD_GUI == 0x00)  //若是显示界面切换为设置界面
		{
			LCD_Clear(White); // 清屏
			LCD_GUI = 0x10;
		}
		else    //返回显示界面
		{
			LCD_Clear(White);
			LCD_GUI=0x00;
			time_ctrl[0] = time_disp[0]; //再次返回时,设置更新上报时间
			time_ctrl[1] = time_disp[1];
			time_ctrl[2] = time_disp[2];
		}
	}

在设置界面时,选择时分秒,并且闪烁选中的。这里用一个变量set_ctrl,当为1是将要显示字符串对于的时分秒设置为空字符,为0就为原来的。并且该变量在一定时间类翻转来达到闪烁效果。

	if(LCD_GUI>>4==1 && unKey_Down ==3) //进入设置界面并且按下按键3
	{
		if(LCD_GUI==0x12)
		{
			LCD_GUI=0x10;
		}
		else
			LCD_GUI++;
	}

//设置界面
		sprintf((char *)Lcd_Disp_String, "Setting");
		LCD_DisplayStringLine(Line2, Lcd_Disp_String);	
		sprintf((char *)Lcd_Disp_String, "T:%02d-%02d-%02d",time_disp[0],time_disp[1],time_disp[2]);
		LCD_DisplayStringLine(Line6, Lcd_Disp_String);	
		if((uwTick -  uwTick_Set_time_Set_Point)>=500) //控制设置时分秒的闪烁时间
		{
			uwTick_Set_time_Set_Point = uwTick;
			set_ctrl ^= 0x01;
		}
		if(set_ctrl == 0x01)  // 将相应的时分秒设置为空字符
		{
			if(LCD_GUI == 0X10)
			{
				Lcd_Disp_String[2] = ' ';
				Lcd_Disp_String[3] = ' ';
			}
			else if(LCD_GUI == 0X11)
			{
				Lcd_Disp_String[5] = ' ';
				Lcd_Disp_String[6] = ' ';
			}
			else if(LCD_GUI == 0X12)
			{
				Lcd_Disp_String[8] = ' ';
				Lcd_Disp_String[9] = ' ';
			}
		}
		LCD_DisplayStringLine(Line6, Lcd_Disp_String);	

设置更新时间,用两个数值来存储(用一个其实就够了的),一个专门用来显示,一个用来控制上报时间的。当按键4按下时,修改显示变量即可,然后再界面返回时,将显示变量值赋给控制变量

uint8_t time_disp[3]={0}; 
uint8_t time_ctrl[3]={0}; 
if(unKey_Down == 4)  //值修改
	{
		if(LCD_GUI == 0X10) // hour
		{
			if(time_disp[0] >=23)time_disp[0] = 0;
			else
				time_disp[0]++;
		}
		else if(LCD_GUI == 0X11) // min
		{
			if(time_disp[1] >=59)time_disp[1] = 0;
			else 
				time_disp[1]++;
		}
		else if(LCD_GUI == 0X12) // sed
		{
			if(time_disp[2]>=59)time_disp[2] = 0;
			else
				time_disp[2]++;
		}

....


else    //返回显示界面
		{
			LCD_Clear(White);
			LCD_GUI=0x00;
			time_ctrl[0] = time_disp[0]; //再次返回时,设置更新上报时间
			time_ctrl[1] = time_disp[1];
			time_ctrl[2] = time_disp[2];
		}
....

界面的难点基本上就是上面的了,其他细节看后面的完整程序。

2.串口的接发

自动上报,直接在相等时刻发给串口即可,这里要控制在相等时刻只发一次,用一个变量来控制即可。(直接在发送字符后面适当的延时即可)

// 自动上报
	if(time_ctrl[0]==sTime.Hours && time_ctrl[1]==sTime.Minutes && time_ctrl[2]==sTime.Seconds)
	{
		if(Ctrl_Uart_Send_Time_Data_Times ==0)
		{
			Ctrl_Uart_Send_Time_Data_Times=1;
			sprintf(str, "%4.2f+%2.1f+%02d%02d%02d\n",v1, (k_int/10.),sTime.Hours,sTime.Minutes,sTime.Seconds);
			HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
		}
	}
	else
		Ctrl_Uart_Send_Time_Data_Times = 0;	//当时间变化或者控制值变化,两者不等的时候,恢复下一次数据发送允许。

本题的难点就是串口的接收,在发生错误的时候,设备不相应。发生的数据格式k0.x\n。转换为十六进制为:k0.1\n  ==>6B 30 2E 31 5C 6E 。这里在串口中"\n"会被当成两个字符处理

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA57yW56iL5bCP54aK,size_20,color_FFFFFF,t_70,g_se,x_16

 发生错误可以从两种角度考虑:数据内容不匹配和数据长度不为6。

因此,可以在中断接收函数里面以字节为单位接收数据,若是第一个字节并且为6b,于是才开始接收下一个字节。

//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(rx_buf == 0x6b && rx_buf_index ==0)//若数据为6b,并且是第一个数据
	{
		uwTick_Uart_TI_time_Set_Point= uwTick;//接收到第一个数据启动计时
		Start_Flag = 1;  //接收下一个字节
	}
	if(Start_Flag == 1) //满足则把数据接收完
	{
		rx_buffer[rx_buf_index++] = rx_buf;
	}
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&rx_buf, 1);
}

但是如果上一串数据是错的,但是下一串数据是对的,我们必须又恢复为原来还没开始接收数据前的样子,即需要在一个时间段内将数组的索引归0和开始接收标志归0,为一下接收做准备。所以用一个时间变量来控制串口接收处理时间,数据错误或者数据正确并处理完,时间到了就清0,为下一次接收做准备

// 设置k
	// k0.1\n ==>6B 30 2E 31 5C 6E 
	200ms~300ms之内处理数据
	if((uwTick - uwTick_Uart_TI_time_Set_Point) <=300 && (uwTick - uwTick_Uart_TI_time_Set_Point) >= 200) 
	{
		if(rx_buf_index==6)  //判断命令长度
		{
			// 比较内容处第三个
			if(rx_buffer[0] == 0x6b && rx_buffer[1] == 0x30 && rx_buffer[2] == 0x2e && rx_buffer[4] == 0x5c && rx_buffer[5] == 0x6e)
			{
				// 第三个字节范围 1-9
				if(rx_buffer[3]>=0x30 && rx_buffer[3]<=0x39)
				{
					k_int = rx_buffer[3] - 0x30;
					sprintf(str, "ok\n");
					HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
					iic_24c02_write(&k_int, 0, 1);
				}
			}
		}
		// 清0为一次接收做准备
		rx_buf_index =0; 
		Start_Flag =0;
	}
	

 

三、程序

#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
#include "UART\bsp_uart.h"
#include "I2C\bsp_i2c.h"
#include "ADC\bsp_adc.h"
//#include "TIM\bsp_tim.h"
#include "RTC\bsp_rtc.h"

//***全局变量声明区
//*减速变量
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
__IO uint32_t uwTick_Usart_Set_Point = 0;//控制Usart_Proc的执行速度
__IO uint32_t uwTick_Set_time_Set_Point = 0;// 时分秒闪烁
__IO uint32_t uwTick_Uart_TI_time_Set_Point = 0;// 串口中断
_Bool Start_Flag;//起始位判断

__IO uint32_t uwTick_Led_Point = 0;

//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;

//*LED专用变量
uint8_t ucLed=0;

//*LCD显示专用变量
uint8_t Lcd_Disp_String[21];//最多显示20个字符

//*串口专用变量
uint8_t str[40];
uint8_t rx_buffer[100];
uint8_t rx_buf;
uint8_t rx_buf_index = 0;//控制数据往buf里边存储的顺序。

//*EEPROM的相关变量
//uint8_t EEPROM_String_1[5] = {0x11,0x22,0x33,0x44,0x55};
//uint8_t EEPROM_String_2[5] = {0};
;



uint8_t k_int = 1;
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
uint8_t LCD_GUI = 0x00;  // 默认显示界面
uint8_t time_disp[3]={0}; 
uint8_t time_ctrl[3]={0}; 
uint8_t set_ctrl =0;
uint8_t led_ctrl =1; // 0:关闭 1:打开
uint8_t Ctrl_Uart_Send_Time_Data_Times = 0;// 控制只允许到闹钟时间后只上报一次


//***子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);
float v1;

//***系统主函数
int main(void)
{

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

	/*bsp资源的初始化*/
	KEY_LED_Init();
	
	LCD_Init();
	LCD_Clear(White);
  LCD_SetBackColor(White);
  LCD_SetTextColor(Blue);	
	
	UART1_Init();
	I2CInit();
//  ADC1_Init();
    ADC2_Init();

	RTC_Init();
	/*外设使用基本配置*/	
	//*EEPROM测试
	iic_24c02_read(&k_int,0,1);//去EEPOM中读取k的初值

	//*串口接收中断打开
	HAL_UART_Receive_IT(&huart1, (uint8_t *)(&rx_buffer), 4);	
  while (1)
  {
		Key_Proc();
		Led_Proc();
		Lcd_Proc();
		Usart_Proc();
		v1 = getADC2()/4096.0*3.3;
  }
}


//***按键扫描子函数
void Key_Proc(void)
{
	if((uwTick -  uwTick_Key_Set_Point)<50)	return;//减速函数
		uwTick_Key_Set_Point = uwTick;

	ucKey_Val = Key_Scan();
	unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val); 
	ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);	
	ucKey_Old = ucKey_Val;
	
	if(unKey_Down == 1)//LED控制
	{
		led_ctrl ^= 0x01;
		
	}
	
	if(unKey_Down == 2)  //界面控制
	{
		if(LCD_GUI == 0x00)  //若是显示界面切换为设置界面
		{
			LCD_Clear(White); // 清屏
			LCD_GUI = 0x10;
		}
		else    //返回显示界面
		{
			LCD_Clear(White);
			LCD_GUI=0x00;
			time_ctrl[0] = time_disp[0]; //再次返回时,设置更新上报时间
			time_ctrl[1] = time_disp[1];
			time_ctrl[2] = time_disp[2];
		}
	}
	if(LCD_GUI>>4==1 && unKey_Down ==3) //进入设置界面并且按下按键3
	{
		if(LCD_GUI==0x12)
		{
			LCD_GUI=0x10;
		}
		else
			LCD_GUI++;
	}
	
	if(unKey_Down == 4)  //值修改
	{
		if(LCD_GUI == 0X10) // hour
		{
			if(time_disp[0] >=23)time_disp[0] = 0;
			else
				time_disp[0]++;
		}
		else if(LCD_GUI == 0X11) // min
		{
			if(time_disp[1] >=59)time_disp[1] = 0;
			else 
				time_disp[1]++;
		}
		else if(LCD_GUI == 0X12) // sed
		{
			if(time_disp[2]>=59)time_disp[2] = 0;
			else
				time_disp[2]++;
		}
		
	}

}


//***LED扫描子函数
void Led_Proc(void)
{
	if((uwTick -  uwTick_Led_Set_Point)<200)	return;//减速函数
		uwTick_Led_Set_Point = uwTick;
	
	if(led_ctrl==1)// on
	{
		if(v1>3.3*k_int*0.1)  //亮
		{
			if((uwTick -  uwTick_Led_Point)>=200)
			{
				uwTick_Led_Point = uwTick;
				ucLed ^= 0x01;
			}		
		}
		else
			ucLed = 0;
	}
	LED_Disp(ucLed);
}


void Lcd_Proc(void)
{
	if((uwTick -  uwTick_Lcd_Set_Point)<100)	return;//减速函数
		uwTick_Lcd_Set_Point = uwTick;
	
	if(LCD_GUI == 0X00)  // 主界面
	{
		sprintf((char *)Lcd_Disp_String, "V1:%.2fV",v1);
		LCD_DisplayStringLine(Line2, Lcd_Disp_String);	
		
		sprintf((char *)Lcd_Disp_String, "k:%.1f", (k_int * 0.1));
		LCD_DisplayStringLine(Line4, Lcd_Disp_String);	
		
		if(led_ctrl ==0)
			sprintf((char *)Lcd_Disp_String, "LED:OFF");
		else
			sprintf((char *)Lcd_Disp_String, "LED:ON ");
		LCD_DisplayStringLine(Line6, Lcd_Disp_String);	
		
		//*RTC内容显示
		HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);//读取日期和时间必须同时使用
		HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
		sprintf((char *)Lcd_Disp_String, "T:%02d-%02d-%02d",(unsigned int)sTime.Hours,(unsigned int)sTime.Minutes,(unsigned int)sTime.Seconds);
		LCD_DisplayStringLine(Line8, Lcd_Disp_String);		

	}
	else  
	{	//设置界面
		sprintf((char *)Lcd_Disp_String, "Setting");
		LCD_DisplayStringLine(Line2, Lcd_Disp_String);	
		sprintf((char *)Lcd_Disp_String, "T:%02d-%02d-%02d",time_disp[0],time_disp[1],time_disp[2]);
		LCD_DisplayStringLine(Line6, Lcd_Disp_String);	
		if((uwTick -  uwTick_Set_time_Set_Point)>=500) //控制设置时分秒的闪烁时间
		{
			uwTick_Set_time_Set_Point = uwTick;
			set_ctrl ^= 0x01;
		}
		if(set_ctrl == 0x01)  // 将相应的时分秒设置为空字符
		{
			if(LCD_GUI == 0X10)
			{
				Lcd_Disp_String[2] = ' ';
				Lcd_Disp_String[3] = ' ';
			}
			else if(LCD_GUI == 0X11)
			{
				Lcd_Disp_String[5] = ' ';
				Lcd_Disp_String[6] = ' ';
			}
			else if(LCD_GUI == 0X12)
			{
				Lcd_Disp_String[8] = ' ';
				Lcd_Disp_String[9] = ' ';
			}
		}
		LCD_DisplayStringLine(Line6, Lcd_Disp_String);	
	}
}

void Usart_Proc(void)
{
	if((uwTick -  uwTick_Usart_Set_Point)<30)	return;//减速函数
	uwTick_Usart_Set_Point = uwTick;
	
	// 自动上报
	if(time_ctrl[0]==sTime.Hours && time_ctrl[1]==sTime.Minutes && time_ctrl[2]==sTime.Seconds)
	{
		if(Ctrl_Uart_Send_Time_Data_Times ==0)
		{
			Ctrl_Uart_Send_Time_Data_Times=1;
			sprintf(str, "%4.2f+%2.1f+%02d%02d%02d\n",v1, (k_int/10.),sTime.Hours,sTime.Minutes,sTime.Seconds);
			HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
		}
	}
	else
		Ctrl_Uart_Send_Time_Data_Times = 0;	//当时间变化或者控制值变化,两者不等的时候,恢复下一次数据发送允许。
		
	// 设置k
	// k0.1\n ==>6B 30 2E 31 5C 6E 
	200ms~300ms之内处理数据
	if((uwTick - uwTick_Uart_TI_time_Set_Point) <=300 && (uwTick - uwTick_Uart_TI_time_Set_Point) >= 200) 
	{
		if(rx_buf_index==6)  //判断命令长度
		{
			// 比较内容处第三个
			if(rx_buffer[0] == 0x6b && rx_buffer[1] == 0x30 && rx_buffer[2] == 0x2e && rx_buffer[4] == 0x5c && rx_buffer[5] == 0x6e)
			{
				// 第三个字节范围 1-9
				if(rx_buffer[3]>=0x30 && rx_buffer[3]<=0x39)
				{
					k_int = rx_buffer[3] - 0x30;
					sprintf(str, "ok\n");
					HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
					iic_24c02_write(&k_int, 0, 1);
				}
			}
		}
		// 清0为一次接收做准备
		rx_buf_index =0; 
		Start_Flag =0;
	}
	
}



//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(rx_buf == 0x6b && rx_buf_index ==0)//若数据为6b,并且是第一个数据
	{
		uwTick_Uart_TI_time_Set_Point= uwTick;//接收到第一个数据启动计时
		Start_Flag = 1;  //接收下一个字节
	}
	if(Start_Flag == 1) //满足则把数据接收完
	{
		rx_buffer[rx_buf_index++] = rx_buf;
	}
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&rx_buf, 1);
}






void Error_Handler(void)
{
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super.Bear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值