【23物联网国赛样题第一套】nb-iot屏幕显示,串口接收,按键控制,物联网竞赛今年历年国赛样题真题代码

任务要求:

  • 初始运行图显示当前日期(年、月、日)。
  • 按压KEY4键,显示设置图,黑色三角表示当前设置项。KEY2键上移黑色三角设置项,KEY3键下移黑色三角设置项。
  • 在设置图页面按压KEY4,在当前设置项开启*符号,板上的LED2灯点亮,表示当前设置项可修改,KEY2键设置项加1,KEY3键设置项减1。
  • 修改设置项后,按压KEY4,关闭*符号,板上的LED2灯熄灭,保存当前值,返回设置图。
  • 通过USB数据线,将NB-IOT智慧盒连接到开发机串口上,从开发机串口上发送以下16进制命令帧,NB-IOT接收后自动修改年、月、日参数,并统一返回成功:0xFB 0x00 0xFE或失败:0xFB 0x01 0xFE。
  • 注:2021年拆分为20(年1),21(年2)两部分。
  • 在设置图状态下,按压KEY1复位键返回初始运行图,此时显示新设置的日期。

 

 任务分析:

        不难看出,这道题比起上一道ZigBee的题难度翻了N倍,所以隐约可以看出以后比赛中任务量与难度是NB、Lora>ZigBee的,所以平常可以针对性训练和了解一些相关知识。

        在这道题中,我们要使用一块Nb-iot版,要开发屏幕,按键,串口三个部分,在比赛中,竞赛资料中已经有了很充足的代码工程给我们使用,所以在接下来的stm32开发任务中,我将完全使用比赛时的竞赛资料中的库文件完成开发。

        这道题其实很简单,我们只要初始化相应模块,做好串口接收逻辑就可以了。

代码实现:

※这里使用library版通用工程LoRa_新库_发布。

首先,我们使用竞赛资料自带取模工具取出“当前日期年月日”的子模。工具设置如下图:

 

//汉字,顺序为当前日期年月日0123456
char zi[][32]={/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x40,0x42,0x44,0x58,0x40,0x40,0x7F,0x40,0x40,0x50,0x48,0xC6,0x00,0x00,0x00,
0x00,0x40,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0xFF,0x00,0x00,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x08,0x08,0xE8,0x29,0x2E,0x28,0xE8,0x08,0x08,0xC8,0x0C,0x0B,0xE8,0x08,0x08,0x00,
0x00,0x00,0xFF,0x09,0x49,0x89,0x7F,0x00,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x00,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0x00,0x00,0x00,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x04,0xFF,0x24,0x24,0x24,0xFF,0x04,0x00,0xFE,0x22,0x22,0x22,0xFE,0x00,0x00,
0x88,0x48,0x2F,0x09,0x09,0x19,0xAF,0x48,0x30,0x0F,0x02,0x42,0x82,0x7F,0x00,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x20,0x18,0xC7,0x44,0x44,0x44,0x44,0xFC,0x44,0x44,0x44,0x44,0x04,0x00,0x00,
0x04,0x04,0x04,0x07,0x04,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x00,0x00,0xFE,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0xFE,0x00,0x00,0x00,
0x80,0x40,0x30,0x0F,0x02,0x02,0x02,0x02,0x02,0x02,0x42,0x82,0x7F,0x00,0x00,0x00,

/*--  ??:  ?  --*/
/*--  ??12;  ??????????:?x?=16x16   --*/
0x00,0x00,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0x00,0x00,0x00,0x00,

}

汉字取模完毕后,按题目所说,我们要有年月日三个变量,其中年分为两部分,所以我们初步定义变量为

#define index 0 //主页
#define opt1 //设置页
unsigned int mode = 0;//显示页面
unsigned int year= 2021,mon = 10, day = 12;//年月日

然后对屏幕初始化,先做出基础默认界面。注意,在比赛设备中OLED 屏幕有以下应记:

        //屏幕x为横轴,y为纵轴,y有8行,每个正常显示占2行,8px模式占1行
        //每个汉字占用横轴15个点,为了清洗显示应加入多余的点占空分隔,
        //x=18为第一个汉字,后面每个字的起始点都在上一个字的基础上+18
        //调用Screen_ShowChineseChar函数显示汉字,Screen_ShowString函数显示英文字符

我们使用mode来确定当前是哪个页面,以下为主页代码

		//年月日:
        Screen_ShowChineseChar(0,0,zi[0]);
		Screen_ShowChineseChar(0+18,0,zi[1]);
		Screen_ShowChineseChar(0+18*2,0,zi[2]);
		Screen_ShowChineseChar(0+18*3,0,zi[3]);
		Screen_ShowString(18*4,0,": ");
		
		//在当前版本的竞赛库中,并没有直接显示数字的函数,所以要创建一个临时temp,
		//使用sprintf函数将type改变
		char temp[16];
		
        //xxx年
		sprintf(temp,"%d",year);
		Screen_ShowString(0,3,temp);
		Screen_ShowChineseChar(18*2,3,zi[4]);
		
        //xx月xx日
		sprintf(temp,"%d",mon);
		Screen_ShowString(0,6,temp);
		Screen_ShowChineseChar(18,6,zi[5]);
		sprintf(temp,"%d",day);
		Screen_ShowString(18*2,6,temp);
		Screen_ShowChineseChar(18*3,6,zi[6]);

目前为止,我们已经完成了第一步:初始运行图显示当前日期(年、月、日)

接下来我们将进行安检功能控制的开发,根据题目分析得知,我们任然要进行一个界面的设计,

由于汉字我们已经取模过,所以要取模‘◀’,打不出这个符号的可以直接复制:

/*--  文字:  ▲  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0xE0,0xE0,0xF0,0xF0,0xF8,0xF8,0xFC,0x00,0x00,
0x00,0x01,0x01,0x03,0x03,0x07,0x07,0x0F,0x0F,0x1F,0x1F,0x3F,0x3F,0x7F,0x00,0x00,

符号取模完后,我们来编写界面布局代码
 

    	//年
        Screen_ShowChineseChar(0,0,zi[4]);
		Screen_ShowString(18,0,": ");
		char temp[16];
		sprintf(temp,"%d",year);
   		Screen_ShowString(18*2,0,temp);
        
        //月
        Screen_ShowChineseChar(0,3,zi[5]);
		Screen_ShowString(18,3,": ");
		sprintf(temp,"%d",mon);
		Screen_ShowString(18*2,3,temp);

        //日
        Screen_ShowChineseChar(0,6,zi[6]);
		Screen_ShowString(18,6,": ");
		sprintf(temp,"%d",day);
		Screen_ShowString(18*2,6,temp);

界面布局完成后,我们要做出相应的按键功能,这里我们使用两个变量,opt_cursor与is_ok,opt_cursor表示opt页面下三角符号对应哪一行,is_ok表示‘*’所在的行数,

//年
        if(opt_cursor==0)
			Screen_ShowChineseChar(18*4,0,zi[7]);
		if(is_ok==0)
			Screen_ShowString(18*6,0,"*");
//月
        if(opt_cursor==1)
			Screen_ShowChineseChar(18*4,3,zi[7]);
		if(is_ok==1)
			Screen_ShowString(18*6,3,"*");
//日
        if(opt_cursor==1)
			Screen_ShowChineseChar(18*4,3,zi[7]);
		if(is_ok==1)
			Screen_ShowString(18*6,6,"*");

在按键中,key4用来确定页面的切换及选择,但是因为我们各设了变量,所以具体代码逻辑非常简单

void key4_E()
{
	if(mode == index) //如果是在主页就切换到设置页
		mode = opt;
	if(mode == opt)//如果是在设置页就确定选择了哪一项
	{
		is_ok = !is_ok;
		if(is_ok)
			Led_On(2);
		else
			Led_Off(2);
	}
	//更新屏幕
	Screen_Clear();
	LED_Disp();
}

在key2与key3中分别处理上移下移增加减少的选择

void key2_E()
{
	//设置项选择
	if(is_ok==0 && opt_cursor!=0)
		opt_cursor-=1;
	if(is_ok==1 && opt_cursor==0) //当前设置,年+1
		year+=1;
	if(is_ok==1 && opt_cursor==1) //当前设置,月+1
		mon+=1;
	if(is_ok==1 && opt_cursor==2) //当前设置,日+1
		day+=1;
	
	Screen_Clear();
	LED_Disp();
}
void key3_E()
{
		//设置项选择
	if(is_ok==0 && opt_cursor!=2)
		opt_cursor+=1;
	if(is_ok==1 && opt_cursor==0) //当前设置,年-1
		year-=1;
	if(is_ok==1 && opt_cursor==1) //当前设置,月-1
		mon-=1;
	if(is_ok==1 && opt_cursor==2) //当前设置,日-1
		day-=1;
	Screen_Clear();
	LED_Disp();
}

至此,我们已经完成了以下任务点,恭喜你,已经拿到了一半的任务分数。

  • 初始运行图显示当前日期(年、月、日)。
  • 按压KEY4键,显示设置图,黑色三角表示当前设置项。KEY2键上移黑色三角设置项,KEY3键下移黑色三角设置项。
  • 在设置图页面按压KEY4,在当前设置项开启*符号,板上的LED2灯点亮,表示当前设置项可修改,KEY2键设置项加1,KEY3键设置项减1。
  • 修改设置项后,按压KEY4,关闭*符号,板上的LED2灯熄灭,保存当前值,返回设置图。

按照题目要求,我们下一步编写串口收发相关代码,我们初始化串口并定义成功与失败的返回值,波特率按照题目要求来,没有就随意,建议使用115200。

Uart_Init(115200);//串口初始化,波特率115200
unsigned char fail = {0xfb,0x01,0xfe};//失败
unsigned char success = {0xfb,0x00,0xfe}; //成功

在初始化完成后,我们编写相应的逻辑代码,代码逻辑比较简单,就不一一写注释了,看不懂去补C基础去

1

然后到了相对来说比较重要的一点,flash闪存,因此,我们也需要对它进行相应的初始化,复位键不可编程,我这里使用数组的方式写入,但是在复位读取后数据任然是空,希望路过的各位大佬给予指点。

#include "lib_flash.h" //flash库
#define flash_add 0x0800f100//flash闪存地址
unsigned char flash_arr[3]={0x00,0x00,0x00};


Flash_WriteBuffer(flash_add,flash_arr,3);//储存数据
DelayMs(20);//5ms写入时间

Flash_ReadBuffer(flash_add,flash_arr,3);
year = flash_arr[0];
mon = flash_arr[1];
day = flash_arr[2];

“在设置图状态下,按压KEY1复位键返回初始运行图,此时显示新设置的日期。”

至此,完成了所有的任务代码,最后一个功能不能用,由于是新换的资料我第一次见所以有些问题,希望路过的各位大佬指指点点。

以下main.c源文件

/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  */
#include <string.h>
#include "board.h"
//#include "hal_oled.h"
#include "lib_screen.h"
#include "lib_screen_font.h"
#include "lib_key.h"
#include "lib_uart.h"
#include "lib_timertask.h"
#include "lib_timer.h"
#include "lib_led.h"
#include "lib_flash.h"
#include "lib_relay.h"


#define flash_add 0x0800f100

#define index 0
#define opt 1

//当前日期年月日
char zi[][32]={
0x00,0x40,0x42,0x44,0x58,0x40,0x40,0x7F,0x40,0x40,0x50,0x48,0xC6,0x00,0x00,0x00,
0x00,0x40,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0xFF,0x00,0x00,0x00,


0x08,0x08,0xE8,0x29,0x2E,0x28,0xE8,0x08,0x08,0xC8,0x0C,0x0B,0xE8,0x08,0x08,0x00,
0x00,0x00,0xFF,0x09,0x49,0x89,0x7F,0x00,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00,0x00,

0x00,0x00,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0x00,0x00,0x00,0x00,

0x00,0x04,0xFF,0x24,0x24,0x24,0xFF,0x04,0x00,0xFE,0x22,0x22,0x22,0xFE,0x00,0x00,
0x88,0x48,0x2F,0x09,0x09,0x19,0xAF,0x48,0x30,0x0F,0x02,0x42,0x82,0x7F,0x00,0x00,


0x00,0x20,0x18,0xC7,0x44,0x44,0x44,0x44,0xFC,0x44,0x44,0x44,0x44,0x04,0x00,0x00,
0x04,0x04,0x04,0x07,0x04,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x00,


0x00,0x00,0x00,0xFE,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0xFE,0x00,0x00,0x00,
0x80,0x40,0x30,0x0F,0x02,0x02,0x02,0x02,0x02,0x02,0x42,0x82,0x7F,0x00,0x00,0x00,


0x00,0x00,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xFF,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0x00,0x00,0x00,0x00,

/*--  文字:  ▲  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0xE0,0xE0,0xF0,0xF0,0xF8,0xF8,0xFC,0x00,0x00,
0x00,0x01,0x01,0x03,0x03,0x07,0x07,0x0F,0x0F,0x1F,0x1F,0x3F,0x3F,0x7F,0x00,0x00,


};

unsigned int mode = index,opt_cursor = 0,is_ok = 0;//显示页面,设置页光标
unsigned char fail[] = {0xfb,0x01,0xfe};//失败
unsigned char success[] = {0xfb,0x00,0xfe}; //成功
unsigned int year=2021,mon = 10, day = 12,uart_rx_len = 0;//年月日串口接收数
unsigned char uRxBuf[255],flash_arr[3]={0x00,0x00,0x00};
void LED_Disp()
{
	if(mode==index)//默认显示
	{
		//屏幕x为横轴,y为纵轴,y有8行,每个正常显示占2行,8px模式占1行
		//每个汉字占用横轴15个点,为了清洗显示应加入多余的点占空分隔,
		//x=18为第一个汉字,后面每个字的起始点都在上一个字的基础上+18
		//调用Screen_ShowChineseChar函数显示汉字,Screen_ShowString函数显示英文字符
		Screen_ShowChineseChar(0,0,zi[0]);
		Screen_ShowChineseChar(0+18,0,zi[1]);
		Screen_ShowChineseChar(0+18*2,0,zi[2]);
		Screen_ShowChineseChar(0+18*3,0,zi[3]);
		Screen_ShowString(18*4,0,": ");
		
		//在当前版本的竞赛库中,并没有直接显示数字的函数,所以要创建一个临时temp,
		//使用sprintf函数将type改变
		char temp[16];
		
		sprintf(temp,"%d",year);
		Screen_ShowString(0,3,temp);
		Screen_ShowChineseChar(18*2,3,zi[4]);
		
		sprintf(temp,"%d",mon);
		Screen_ShowString(0,6,temp);
		Screen_ShowChineseChar(18,6,zi[5]);
		sprintf(temp,"%d",day);
		Screen_ShowString(18*2,6,temp);
		Screen_ShowChineseChar(18*3,6,zi[6]);
	}
	if(mode==opt)
	{
		Screen_ShowChineseChar(0,0,zi[4]);
		Screen_ShowString(18,0,": ");
		char temp[16];
		sprintf(temp,"%d",year);
		Screen_ShowString(18*2,0,temp);
		if(opt_cursor==0)
			Screen_ShowChineseChar(18*4,0,zi[7]);
		if(is_ok==1 && opt_cursor==0)
			Screen_ShowString(18*6,0,"*");
		
		Screen_ShowChineseChar(0,3,zi[5]);
		Screen_ShowString(18,3,": ");
		sprintf(temp,"%d",mon);
		Screen_ShowString(18*2,3,temp);
		if(opt_cursor==1)
			Screen_ShowChineseChar(18*4,3,zi[7]);
		if(is_ok==1 && opt_cursor==1)
			Screen_ShowString(18*6,3,"*");
		
		Screen_ShowChineseChar(0,6,zi[6]);
		Screen_ShowString(18,6,": ");
		sprintf(temp,"%d",day);
		Screen_ShowString(18*2,6,temp);
		if(opt_cursor==2)
			Screen_ShowChineseChar(18*4,6,zi[7]);
		if(is_ok==1 && opt_cursor==2)
			Screen_ShowString(18*6,6,"*");
	}
}
void key2_E()
{
	//设置项选择
	if(is_ok==0 && opt_cursor!=0)
		opt_cursor-=1;
	if(is_ok==1 && opt_cursor==0) //当前设置,年+1
		year+=1;
	if(is_ok==1 && opt_cursor==1) //当前设置,月+1
		mon+=1;
	if(is_ok==1 && opt_cursor==2) //当前设置,日+1
		day+=1;
	
	Screen_Clear();
	LED_Disp();
}
void key3_E()
{
		//设置项选择
	if(is_ok==0 && opt_cursor!=2)
		opt_cursor+=1;
	if(is_ok==1 && opt_cursor==0) //当前设置,年-1
		year-=1;
	if(is_ok==1 && opt_cursor==1) //当前设置,月-1
		mon-=1;
	if(is_ok==1 && opt_cursor==2) //当前设置,日-1
		day-=1;
	Screen_Clear();
	LED_Disp();
}
void key4_E()
{
	if(mode == opt)//如果是在设置页就确定选择了哪一项
	{
		is_ok = !is_ok;
		if(is_ok)
			Led_On(2);
		else
			Led_Off(2);
	}
	if(mode == index) //如果是在主页就切换到设置页
		mode = opt;
	
	//更新屏幕
	Screen_Clear();
	LED_Disp();
}

void Init()
{
	BoardInitMcu();//初始化班板子
	Screen_Init();//led屏初始化
	Screen_Display_On();//开启显示
	Key_Init();//按键初始化
	Timer2_Init(1,Key_EventScanner);//定时器时间与回调
	Timer2_Start();//开始
	
	Timer3_Init(1,Uart_DataReceiveScanner);//定时器时间与回调
	Timer3_Start();//开始
	
	Led_Init();//led初始化
	Uart_Init(115200);//串口初始化,波特率115200
	
	//获取串口发来的数据
	Flash_ReadBuffer(flash_add,flash_arr,3);
	year = flash_arr[0];
	mon = flash_arr[1];
	day = flash_arr[2];
	LED_Disp();//自定义屏幕函数

}



/**
 * Main application entry point.
 */
int main( void )
{
	Init();
	while(1)
	{
		//使用Uart_DataRead函数接收串口数据并存储在uRxBuf中,返回接收到的数据长度
		uart_rx_len = Uart_DataRead(uRxBuf,255);
		if(uart_rx_len > 0)
		{
			//进行帧头帧尾判断,使用else返回报错
			if(uRxBuf[0] == 0xfb && uRxBuf[uart_rx_len-1] == 0xfe)
			{
				//数据处理,数据长度不对同样返回报错
				if(uart_rx_len==4 || uart_rx_len==5)
				{
					if(uRxBuf[1] == 0x01)
					{
						int a ,b;//局部变量,用于年的高低位
						a = uRxBuf[2];
						b = uRxBuf[3];
						year = a*100+b;
					}
					else if(uRxBuf[1] == 0x02)
						mon = uRxBuf[2];
					
					else if(uRxBuf[1] == 0x03)	
						day = uRxBuf[2];
					else
					{
						Uart_Write(fail,3);
						continue;
					}
					
					flash_arr[0] = year;
					flash_arr[1] = mon;
					flash_arr[2] = day;
					Flash_WriteBuffer(flash_add,flash_arr,3);
					DelayMs(20);//5ms写入时间
					Uart_Write(success,3);
					Screen_Clear();
					LED_Disp();
				}
				else
				{
					Uart_Write(fail,3);
				}
			}
			else
			{
				Uart_Write(fail,3);
			}
		}
		
		Key2_OnClickHandler(key2_E);
		Key3_OnClickHandler(key3_E);
		Key4_OnClickHandler(key4_E);
	}
	
}

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Azhuo9527

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

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

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

打赏作者

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

抵扣说明:

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

余额充值