主頁 >  其他 > 基于涂鴉智能開發的墨水屏座位管理器——2.嵌入式功能實作篇

基于涂鴉智能開發的墨水屏座位管理器——2.嵌入式功能實作篇

2021-11-14 08:40:03 其他

隨著互聯網連接技術的日益普及,以及大眾環保意識增強,電子紙顯示市場不斷發展,墨水屏的應用場景也越來越多,墨水屏座位管理器方案具體功耗低,多節點管控,資訊實時同步等特點,可應用于智慧辦公,智慧零售,智慧商超等眾多場景,

上篇給大家介紹硬體方案搭建墨水屏座位管理器-電路設計篇本篇將會從方案設計及功能實作等維度帶大家了解墨水屏座位管理器嵌入式方案,

一、平臺產品創建

1.創建產品

硬體部分搭建完成后可登錄涂鴉IoT平臺創建產品,下面是創建墨水屏demo的流程步驟,

  • 登錄涂鴉IoT平臺,單擊創建產品

  • 在標準類目導航欄中,選擇電工 >智能開關 >開關

  • 選擇自定義方案輸入產品名稱如墨水屏demo,可選擇zigbee或藍牙通訊協議,本方案以zigbee方案,單擊創建產品

  • (可選)在功能定義選項中,可以先選擇一項標準功能開關,方便后續除錯,

  • (必選)在自定義功能區域單擊添加功能,
    例如本次方案需要添加五個 DP 點,按照如下類別依次新建5個自定義功能點,

  • 在設備面板頁面中選擇想要的面板,開始除錯時可選擇開發除錯面板,后面可根據自身需要自由配置面板,

  • 在硬體開發頁面中,開發者根據需求選擇對應zigbee模組或藍牙模組, 需注意的是要選擇低功耗韌體,否則會對設備功耗有很大影響,

  • 上述操作完成之后,單擊下載全部下載所有開發需要用到的資料,如MCU SDK、模組除錯助手和和功能點除錯檔案等,接下來就可以開始嵌入式程式的開發,

2.環境搭建

本次方案采用涂鴉MCU SDK+Zigbee模組的設計來進行開發,下面是搭建開發環境的流程步驟,

  • ST開發環境安裝

    官方下載并安裝keil,完成后開始搭建開發工程,

  • MCU SDK獲取

    在IoT平臺創建完產品后就可以獲取到MCU SDK軟體包了,將MCU SDK添加到開發工程中,單擊 Build 并根據軟體提示修改相關錯誤或者警告資訊,

    移植了MCU-SDK后,再搭配一塊燒錄并授權了通用對接低功耗韌體的zigbee或藍牙模組,此時 MCU 就具備了連接涂鴉云和上報下發DP點的功能,

    待程式編譯通過之后,就可以下載到開發板中進行除錯和測驗,開發者可以先使用開發包中的串口除錯助手分別模擬MCU和模擬模組來驗證二者是否正常作業或通信,除錯模組助手使用方法可以參考下面檔案,

    除錯模組助手

    STM32 支持 ST-Link ,J-Link 等工具下載,這里推薦 ST-Link,引腳連接方式如下:

二、韌體開發

完成上述步驟后正式開始墨水屏demo的應用功能開發,

1.方案設計

  • 功能需求

功能描述詳細說明
螢屏顯示1.座位號 2.座位狀態 3.提醒資訊 4.二維碼
復位按鍵長按3秒,設備重新配網
資訊下發正常:預定/無預定 例外:“暫不開放”
預約資訊記錄后臺記錄近30天座位占用資訊
低電量報警設備電池電量低于10%上報至管理端(云端)
  • 流程邏輯

  • 工程目錄結構

├── User
│   ├── main.c                                             /* 主程式入口檔案 */
│   └── MY_ST_config.h                                     /* 硬體組態檔 */
├── system                                                 /* 系統檔案目錄 */
│   ├── delay.c                                            /* 延時函式 */
│   ├── delay.h
│   ├── EPAPER.c                                           /* 電子螢屏初始化 */
│   ├── EPAPER.h
│   ├── GT5SLAD3BFA_stm32l431_keil5.lib                    /* 字庫芯片靜態庫檔案 */
│   ├── GT5SLAD3B-GTA6401.h                                /* 字庫芯片頭檔案 */
│   ├── IO.c                                               /* GPIO口初始化 */
│   ├── IO.h
│   ├── key.c                                              /* 按鍵初始化 */
│   ├── key.h
│   ├── picture.h                                          /* 圖片像素資料存盤 */
│   ├── RCC.c                                              /* 系統時鐘配置 */
│   ├── RCC.h
│   ├── SPI.c                                              /* SPI初始化 */
│   ├── SPI.h
│   ├── sys.c                                              /* 系統任務檔案 */                 
│   ├── sys.h
│   ├── qrcode_create.c                                    /* 二維碼組件 */
│   ├── qrcode_create.h
│   ├── tuya_qrcode_create.c                               
│   ├── tuya_qrcode_create.h
│   ├── USART.c                                            /* 串口初始化 */
│   ├── USART.h
│   ├── utf8ToUnicode.c                                    /* UTF8轉UNICODE */
│   └── utf8ToUnicode.h
├── CJSON
│   ├── cJSON.c                                            /* JSON組態檔 */
│   └── cJSON.h
├── mcu_sdk
│   ├── mcu_api.c                                          /* dp功能資料檔案 */
│   ├── mcu_api.h
│   ├── protocol.c                                         /* 協議分析和接收模塊發送訊息時的回應 */
│   ├── protocol.h
│   ├── system.c                                           /* 單片機與zigbee通信的框架分析 */
│   ├── system.h
│   └── zigbee.h                                           /* SDK中使用的宏定義 */


2.硬體外設

首先是硬體部分的配置,本次硬體部分主要在于墨水螢屏的顯示和字庫芯片的讀取,字庫芯片與屏使用同一塊SPI驅動,所以首先將SPI進行初始化配置,另外MCU與zigbee模組通過串口進行通信,所以也要將串口進行初始化;

//SPI
void SPI_Init(void)
{
	SPI_SCK_OUT;
	SPI_MOSI_OUT;
	SPI_MISO_IN;
	LIB_CS_OUT;
	EPD_CS_OUT;
	EPD_DC_OUT;
	EPD_RST_OUT;
	EPD_BUSY_IN;
}
//UART2  通信串口
void Configure_USART_ZIGBEE(uint32_t bound) //PA2 MTX , PA3 MRX
{
	RCC->APB1RSTR1 &=~(1<<17);
	RCC->AHB2ENR |= 1<<0;
	GPIOA->MODER &=~(3<<4|3<<6);        
	GPIOA->MODER |=2<<4|2<<6;  
	GPIOA->AFR[0] &=~(0xf<<8|0xf<<12);
    GPIOA->AFR[0] |=7<<8|7<<12;
	RCC->APB1ENR1 |=1<<17;
	USART_ZIGBEE->BRR = 16000000 / bound; 
	USART_ZIGBEE->CR1 |= 1<<0|1<<2|1<<3|1<<5;
	NVIC_SetPriority(USART2_IRQn, 1);
	NVIC_EnableIRQ(USART2_IRQn);	
}
//USART3   log串口
void Configure_USART_LOG(uint32_t bound)    //PB10 TXD   PB11 RXD   
{
	RCC->APB1RSTR1 &=~(1<<18);              //恢復串口3
	RCC->AHB2ENR |= 1<<1;                   //使能GPIOB時鐘
	GPIOB->MODER &=~(3<<20|3<<22);     
	GPIOB->MODER |=2<<20|2<<22;       
	GPIOB->AFR[1] &=~(0xf<<8|0xf<<12);
	GPIOB->AFR[1] |=7<<8|7<<12;
	RCC->APB1ENR1 |=1<<18;                     //使能串口3時鐘
	USART_LOG->BRR = 16000000 / bound; 
	USART_LOG->CR1 |= 1<<0|1<<2|1<<3|1<<5;  
	while((USART_LOG->ISR & 1<<6) != 1<<6)
	{ 
		break;/* add time out here for a robust application */
	}	
	NVIC_SetPriority(USART3_IRQn, 1);
	NVIC_EnableIRQ(USART3_IRQn);	
}

3.墨水屏部分

下面是電子屏的顯示部分,螢屏初始化完成后可以直接在主程式中呼叫顯示函式,另外如果SPI初始化有問題可以將模擬SPI改為硬體SPI再進行驅動 ;

螢屏顯示也可以顯示圖片,采用取模軟體將圖片里的像素點資料放進picture.h檔案中就可以顯示圖片了,要注意輸出圖片大小不能超過螢屏的尺寸大小,螢屏顯示完成后呼叫局刷與全刷函式可以實作螢屏的區域重繪與全域重繪;需要注意螢屏每次重繪完后需要進入休眠模式,否則會對設備的整機功耗有很大影響;

  • 螢屏重繪函式(全刷/局刷)

    全屏重繪:整個頁面全部重繪一次,整個螢屏要閃幾次, 優勢是沒有殘影,缺點是要多刷幾下屏,
    區域重繪:每一次重繪顯示內容時,不會整個螢屏都重繪,僅重繪那些有畫面和字的地方,優勢是螢屏不會閃爍,但會有殘影,(殘影問題多刷幾次白屏就能清除掉或者執行一次全刷也可以清掉)

    在實作墨水屏的全域重繪與區域重繪功能時, 從局刷轉到全刷時休眠后一定要先進入初始化再重繪,

// refresh_mode = Full      全屏重繪
// refresh_mode = Partial   區域重繪
void EPD_HW_Init(const unsigned char refresh_mode)
{
	EPD_W21_Init();						
	Epaper_READBUSY();
	Epaper_Write_Command(0x12); 
	Epaper_READBUSY();
	Epaper_Write_Command(0x01);   
	Epaper_Write_Data(0x2B);
	Epaper_Write_Data(0x01);
	Epaper_Write_Data(0x00);
	Epaper_Write_Command(0x11);    
	Epaper_Write_Data(0x03);	
	Epaper_Write_Command(0x44);
	Epaper_Write_Data(0x00);		
	Epaper_Write_Data(0x31);   
	Epaper_Write_Command(0x45);        
	Epaper_Write_Data(0x00);   	
	Epaper_Write_Data(0x00);
	Epaper_Write_Data(0x2B);	
	Epaper_Write_Data(0x01); 
	Epaper_Write_Command(0x3C); 
	Epaper_Write_Data(0x01);	
    Epaper_Write_Command(0x18);
	Epaper_Write_Data(0x80);	  
    Epaper_Write_Command(0x22); 
		if(refresh_mode==Full)
			Epaper_Write_Data(0xB1);  
		if(refresh_mode==Partial)
			Epaper_Write_Data(0xB9); 
    Epaper_Write_Command(0x20); 
    Epaper_READBUSY(); 	
	Epaper_Write_Command(0x4E);  
	Epaper_Write_Data(0x00);
	Epaper_Write_Command(0x4F);  
	Epaper_Write_Data(0x00);
	Epaper_Write_Data(0x00);
	Epaper_READBUSY();	
}
  • 螢屏顯示函式
//mode==POS , 正顯
//mode==NEG , 負顯
//mode==OFF , 清除
void EPD_Dis_Part(unsigned int xstart,unsigned int ystart,const unsigned char * datas,unsigned int PART_LINE,unsigned int PART_COLUMN,unsigned char mode)
{
	unsigned int i;  
	int xend,ystart_H,ystart_L,yend,yend_H,yend_L;
	xstart=xstart/8;                   //轉換為位元組
	xend=xstart+PART_LINE/8-1; 
	ystart_H=ystart/256;
	ystart_L=ystart%256;
	yend=ystart+PART_COLUMN-1;
		yend_H=yend/256;
		yend_L=yend%256;		
	Epaper_Write_Command(0x44);       // set RAM x address start/end
	Epaper_Write_Data(xstart);    	  // RAM x address start;
	Epaper_Write_Data(xend);    	  // RAM x address end
	Epaper_Write_Command(0x45);       // set RAM y address start/end
	Epaper_Write_Data(ystart_L);      // RAM y address start Low
	Epaper_Write_Data(ystart_H);      // RAM y address start High
	Epaper_Write_Data(yend_L);    	  // RAM y address end Low
	Epaper_Write_Data(yend_H);    	  // RAM y address end High
	Epaper_Write_Command(0x4E);   	  // set RAM x address count
	Epaper_Write_Data(xstart); 
	Epaper_Write_Command(0x4F);   	  // set RAM y address count
	Epaper_Write_Data(ystart_L);
	Epaper_Write_Data(ystart_H);
	Epaper_Write_Command(0x24);   //Write Black and White image to RAM
		for(i=0;i<PART_COLUMN*PART_LINE/8;i++)
			{                         
				if (mode==POS)
					{
						Epaper_Write_Data(*datas);
						datas++;
					}
				if (mode==NEG)
					{
						Epaper_Write_Data(~*datas);
						datas++;
					}	
			  if (mode==OFF)
				  {
						Epaper_Write_Data(0xFF);
					}		
			} 	
}
  • 螢屏刷白屏函式
void EPD_WhiteScreen_White(void)

{
   unsigned int k;
    Epaper_Write_Command(0x24);          //write RAM for black(0)/white (1)   36
		for(k=0;k<ALLSCREEN_GRAGHBYTES;k++)
		{
			Epaper_Write_Data(0xff);
		}
	
    Epaper_Write_Command(0x26);          //write RAM for black(0)/white (1)
		for(k=0;k<ALLSCREEN_GRAGHBYTES;k++)
		{
			Epaper_Write_Data(0xff);
		}	
		EPD_Update();
}
  • 硬體復位函式

當需要清掉區域資訊并重新顯示新的資訊時就可以用硬體復位函式來實作;

void EPD_W21_Init(void)
{
	EPD_W21_RST_0;     
	driver_delay_xms(100); 
	EPD_W21_RST_1; 			//hard reset  
	driver_delay_xms(100); 
}
  • 螢屏休眠函式
void EPD_DeepSleep(void)
{  	
  Epaper_Write_Command(0x10);  //enter deep sleep
  Epaper_Write_Data(0x01); 
  driver_delay_xms(100);
}

注意螢屏重繪完后必須進入休眠模式,

4.字庫芯片

在使用字庫芯片時,用戶只要知道字符的內碼,就可以計算出該字符點陣在芯片中的地址,然后就可從該地址連續讀出點陣資訊用于顯示,本次螢屏顯示資訊主要通過從字庫芯片中讀取資料從而顯示在螢屏上,從字庫中讀到的資料一個字符就是一個陣列資料,然后電子屏再將每一位陣列資料顯示在螢屏上,

  • 字庫芯片初始化
void zk_init(void)
{
   Rom_csH;
   MOSIH;
   Rom_sckH;
}
  • 字庫芯片休眠
void GT5S_DeepSleep(void)
{ 
    Rom_csL; 	
	Send_Byte(0xB9);
	Rom_csH;
	delay_us(40);
}
  • 字庫芯片喚醒

    以低電平作為起始位,高電平作為停止位;

void GT5S_Wakeup(void)
{ 
    Rom_csL; 	
	Send_Byte(0xAB);
	Rom_csH;
	delay_us(40);
}
  • 矢量文字讀取函式

    呼叫方式:通過指定引數進行呼叫,獲取點陣資料到pBits[]陣列中

/*
	 *函式名	get_font()
	 *功能		矢量文字讀取函式
	 *引數:pBits		資料存盤
	 *		sty		  文字字體選擇 @矢量公用部分
	 *		fontCode	字符編碼中文:GB18030, ASCII/外文: unicode
	 *		width		文字寬度
	 *		height		文字高度
	 *		thick		文字粗細
	 *回傳值:文字顯示高度
**/
unsigned int get_font(unsigned char *pBits,unsigned char sty,unsigned long fontCode,unsigned char width,unsigned char height, unsigned char thick);

如果有需要固定顯示的文字部分,可在程式中將固定文字資訊寫在陣列中從而直接顯示固定資訊,一個ASCII碼對應2位GBK內碼占一個位元組,一個中文字符對應4位GBK內碼占兩個位元組;

unsigned char jtwb[128]="共享空間,預定優先";	        
unsigned char state[128]="暫不開放";	
unsigned char pBits[512]; 

將固定資訊通過文字讀取函式get_font()與螢屏顯示函式EPD_Dis_Part()進一步顯示在墨水屏上;

void SEAT_SET(void)
{
    EPD_HW_Init(Partial);
	zk_init();
	GT5S_Wakeup(); 
    get_font(pBits,VEC_SONG_STY,(jtwb[0]<<8)+jtwb[1],24,24,24);  //共
    EPD_Dis_Part(196,55,pBits,24,24,NEG); 
    get_font(pBits,VEC_SONG_STY,(jtwb[2]<<8)+jtwb[3],24,24,24);  //享
    EPD_Dis_Part(220,55,pBits,24,24,NEG);
    get_font(pBits,VEC_SONG_STY,(jtwb[4]<<8)+jtwb[5],24,24,24);  //空
    EPD_Dis_Part(244,55,pBits,24,24,NEG);
    get_font(pBits,VEC_SONG_STY,(jtwb[6]<<8)+jtwb[7],24,24,24);  //間
    EPD_Dis_Part(268,55,pBits,24,24,NEG);
    EPD_Part_Update();
	EPD_DeepSleep();
	GT5S_DeepSleep();
}

5.設備配網

1)網關配網

由于墨水屏需要連接到涂鴉智能平臺,依靠平臺來實作自動化、App端操控、以及設備之間的相互聯動,所以需要有一個zigbee網關來幫助設備連接上智能平臺,zigbee網關的作用就是負責連接智能平臺,間接的把zigbee設備接入我們的智能平臺,確保手機和zigbee網關處于同一個Wi-Fi網路,以保證手機與智能網關之間的有效連接,

  • 將網關與電源連接,并通過網線與家庭2.4GHz頻段路由器相連;

  • 確認配網指示燈(綠燈)常亮(若指示燈處于其他狀態,長按"復位鍵",至綠燈常亮);

  • 確保手機連接家庭2.4GHz頻段路由器,此時手機、網關處于同一個局域網;

  • 打開涂鴉智能App首頁,點擊頁面右上角添加按鈕“+”;

  • 選擇網關中控/有線網關(zigbee),依照提示操作設備入網;

  • 添加成功后,即可在串列中找到網關設備;

2)zigbee模組配網

zigbee網關配網成功后,就可以在網關里添加子設備了,依據涂鴉智能App首頁配網提示操作子設備配網,配網成功后就可以在涂鴉智能App上進行除錯了;

除了在程式里寫入配網指令實作配網還可以通過涂鴉除錯助手發送配網指令來操作設備入網,詳細說明可以從zigbee串口協議檔案中深入了解;

zigbee串口協議

注意配網之前必須先在IoT平臺將dp添加完成,否則配網會一直失敗;

6.dp資料鏈路處理

定義一個結構體陣列來存放MCU接收到網關下發或上報的dp value,

TYPE_BUFFER_S FlashBuffer;
typedef struct {
  uint8_t seat_set[255];     //座位資訊
  uint8_t st_qrcode[255];    //座位二維碼鏈接地址
  uint8_t st_qrcode1[255];   //第一條座位二維碼鏈接地址
  uint8_t st_qrcode2[255];   //第二條座位二維碼鏈接地址
  uint8_t st_add[255];       //增量預約資訊起始時間
  uint8_t et_add[255];       //增量預約資訊終止時間
  uint8_t n_add[255];        //增量預約者姓名
  uint8_t st_all[255];       //全量預約資訊起始時間
  uint8_t et_all[255];       //全量預約資訊終止時間
  uint8_t n_all[255];        //全量預約者姓名
  uint8_t low_power;         //低電量數值
} TYPE_BUFFER_S;

1)座位資訊更新

除錯程序可使用除錯面板來下發dp,MCU收到指令后回復并執行對應的操作,

  • 座位資訊設定

首先下發dp101 ,dp資料格式為{“n”:“seat number”,“st”:“state”}

dpid內容備注
101“query”設備發送,同步座位號資訊
格式{“n”:“seat number”,“st”:“state”}n:座位編碼欄位; seat number:編碼內容 st:座位狀態欄位 ;state:座位真實狀態 enable disable repairing

下發dp后MCU接收并決議出座位編碼、座位狀態等欄位資訊,然后驅動螢屏將座位編號資訊顯示在墨水屏上;

定義一個結構體,用來表示墨水屏的三種作業狀態:

typedef enum
{
    enable,     //使用中
    disable,    //未開放
    reparing    //維修中
}state;

dp決議及處理函式:

/*****************************************************************************
函式名稱 : dp_download_seat_set_handle
功能描述 : 針對DPID_SEAT_SET的處理函式
輸入引數 : value:資料源資料
        : length:資料長度
回傳引數 : 成功回傳:SUCCESS/失敗回傳:ERROR
使用說明 : 可下發可上報型別,需要在處理完資料后上報處理結果至app
*****************************************************************************/
static unsigned char dp_download_seat_set_handle(const unsigned char value[], unsigned short length)
{
    //示例:當前DP型別為STRING
    unsigned char ret;
	unsigned char dp_download_seat_set_handle = NULL; 
	cJSON *root = NULL, *item = NULL, *item1 = NULL;
	char *number = NULL;
	char *state = NULL; 
	char *pstr = NULL;
	pstr = (char*)malloc(length+1);
    if(NULL == pstr){
		printf("malloc err\r\n");
		return ERROR;
	}
    my_memset(pstr, 0x00, length+1);
    my_memcpy(pstr, value, length);
	printf("value: %s\r\n", value);
	root = cJSON_Parse(pstr);
	free(pstr), pstr = NULL;
    if(NULL == root){							
		printf("root ERROR\r\n");
		return ERROR;
    }
    item = cJSON_GetObjectItem(root, "n");          //獲取座位編碼欄位
	if(NULL == item){			 				
        printf("item ERROR\r\n");
    }
	number = (char*)malloc(sizeof(item));
	if(NULL == number){
		printf("malloc number err\r\n");
		return ERROR;
	}
	free(number), number = NULL;
	number = item->valuestring;
	printf("number: %s\r\n", number);
    memcpy(FlashBuffer.seat_set,number,strlen(number));
	item1 = cJSON_GetObjectItem(root, "st");           //獲取座位實際狀態
    if(NULL == item1){						    	
		printf("item1 ERROR\r\n");
		return ERROR;
    }
	state = (char*)malloc(sizeof(item1));            
	if(NULL == state){
		printf("malloc state err\r\n");
		return ERROR;
	}
	free(state), state = NULL;
	 state = item1->valuestring;	
	 printf("state: %s\r\n", state);      
	if(0 == strcmp(state, "enable")){				    //使用中	
		SEAT_SET();                                     //顯示座位詳細資訊
	}	
	else if(0 == strcmp(state, "disable")){				 //未開放
        SEAT_CLOSE();                                    //顯示“暫不開放”
	}	
		else if(0 == strcmp(state, "repairing")){		 //維修中 
        SEAT_CLOSE();                                    //顯示“暫不開放”
	}	
    cJSON_Delete(root);
    return 0;
    //處理完DP資料后應有反饋
    ret = mcu_dp_string_update(DPID_SEAT_SET,value, length);
    if(ret == SUCCESS)
	{
		Enter_stop_mode();     //處理完dp后設備需進入休眠模式
        return SUCCESS;
	}
    else
        return ERROR;
}

以座位S-1234為例,當座位狀態為enable時才可以執行預約的動作,如果是disable 和 repairing 則顯示該座位暫不開放(可根據自己需求修改);

座位狀態dp格式顯示內容
enable{“n”:“S-1234”,“st”:"enable "}顯示座位編號,若有預約資訊則顯示預約資訊,無預約資訊顯示空白
disable{“n”:“S-1234”,“st”:"disable "}顯示座位編號以及“暫不開放”
repairing{“n”:“S-1234”,“st”:"repairing "}顯示座位編號以及“暫不開放”

MCU接收到value后將決議出的資訊存放在 FlashBuffer.seat_set[]中并通過修改顯示函式EPD_Dis_Part()中的橫縱坐標來調整在螢屏中的位置,注意顯示函式完成后螢屏和字庫芯片都需要進入休眠;

座位編號顯示函式:

void SEAT_SET(void)
{
    EPD_HW_Init(Partial);            //螢屏局刷初始化
	zk_init();                       //字庫芯片初始化
	GT5S_Wakeup();                   //字庫芯片喚醒
	get_font(pBits,VEC_FT_ASCII_STY, FlashBuffer.seat_set[0],48,48,48);     //下發資訊
	EPD_Dis_Part(8,45,pBits,48,48,NEG);  
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[1],48,48,48);    
	EPD_Dis_Part(40,45,pBits,48,48,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[2],48,48,48);  
	EPD_Dis_Part(64,45,pBits,48,48,NEG);		           
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[3],48,48,48);
	EPD_Dis_Part(88,45,pBits,48,48,NEG);	 
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[4],48,48,48); 
	EPD_Dis_Part(114,45,pBits,48,48,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[5],48,48,48);
	EPD_Dis_Part(136,45,pBits,48,48,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.seat_set[6],48,48,48);   
	EPD_Dis_Part(160,45,pBits,48,48,NEG);	            	
	get_font(pBits,VEC_SONG_STY,(jtwb[0]<<8)+jtwb[1],24,24,24);               //固定資訊
	EPD_Dis_Part(196,55,pBits,24,24,NEG); 
	get_font(pBits,VEC_SONG_STY,(jtwb[2]<<8)+jtwb[3],24,24,24);  
	EPD_Dis_Part(220,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[4]<<8)+jtwb[5],24,24,24);  
	EPD_Dis_Part(244,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[6]<<8)+jtwb[7],24,24,24);  
	EPD_Dis_Part(268,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[8]<<8)+jtwb[9],24,24,24);  
	EPD_Dis_Part(292,55,pBits,24,24,NEG); 
	get_font(pBits,VEC_SONG_STY,(jtwb[10]<<8)+jtwb[11],24,24,24);  
	EPD_Dis_Part(310,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[12]<<8)+jtwb[13],24,24,24);  
	EPD_Dis_Part(330,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[14]<<8)+jtwb[15],24,24,24);
	EPD_Dis_Part(354,55,pBits,24,24,NEG);
	get_font(pBits,VEC_SONG_STY,(jtwb[16]<<8)+jtwb[17],24,24,24);
	EPD_Dis_Part(380,55,pBits,24,24,NEG);
	EPD_Part_Update();
	EPD_DeepSleep();                           //螢屏休眠
	GT5S_DeepSleep();                          //字庫芯片睡眠
}
  • 座位二維碼鏈接

可通過除錯面板下發dp102生成對應座位二維碼資訊,dp存放位元組長度最大為62位元組,若url超過了最大限長,可將二維碼鏈接分為兩段dp來下發;這里以長度為80的url為例,單條dp可以不固定長度,但不能超過限長,開發者可以按照自身的長度自行分配分兩段還是三段,

dpid內容備注
102“query”設備發送,同步座位鏈接資訊
{“qr”:“url”,“d”:“th”,“a”:“all”}qr:二維碼鏈接欄位;url:座位鏈接長度不超過255 設備通過url本地生成二維碼 d:二維碼條目欄位 th:第幾段二維碼鏈接資訊 a:二維碼欄位總量 all:共有幾段二維碼

示例:座位1111 url地址為https://seat-reservation-mobile.tuyacn.com/weapp?a=1&seat_id=1435857125099298816

故dp格式為 {“qr”:“https://seat-reservation-mobile.tuyacn.com”,“d”:“1”,“a”:“2”}

? {“qr”:"/weapp?a=1&seat_id=1435857125099298816",“d”:“2”,“a”:“2”}

下發dp102后MCU接收并決議出url并存放在FlashBuffer.st_qrcode中,進一步呼叫二維碼生成函式將座位二維碼顯示在墨水屏上;

dp決議及處理函式:

/*****************************************************************************
函式名稱 : dp_download_st_qrcode_handle
功能描述 : 針對DPID_ST_QRCODE的處理函式
輸入引數 : value:資料源資料
        : length:資料長度
回傳引數 : 成功回傳:SUCCESS/失敗回傳:ERROR
使用說明 : 可下發可上報型別,需要在處理完資料后上報處理結果至app
*****************************************************************************/
static unsigned char dp_download_st_qrcode_handle(const unsigned char value[], unsigned short length)
{
    //示例:當前DP型別為STRING
    unsigned char ret;
	unsigned char dp_download_st_qrcode_handle = NULL;  
	cJSON *root = NULL, *item_1 = NULL, *item_2 = NULL, *item_3 = NULL;
	char *qrcode = NULL;
	char *d = NULL;
    char *a = NULL;
	char *pstr = NULL;
	pstr = (char*)malloc(length+1);
    if(NULL == pstr){
		printf("malloc error\r\n");
		return ERROR;
	}
	my_memset(pstr, 0x00, length+1);
    my_memcpy(pstr, value, length);
	printf("value: %s\r\n", value);
	root = cJSON_Parse(pstr);
	free(pstr), pstr = NULL;
    if(NULL == root){							
		printf("root ERROR!!!!!!!!!!!!!");
		return ERROR;
    }
	//qr      URL欄位
    item_1 = cJSON_GetObjectItem(root, "qr");          
	if(NULL == item_1){			 				
        printf("item_1 ERROR\r\n");
    }
    qrcode = (char*)malloc(sizeof(item_1));
	if(NULL == qrcode){
		printf("malloc qrcode err\r\n");
		return ERROR;
	}
	free(qrcode), qrcode = NULL;
	 qrcode = item_1->valuestring;
	 printf("qrcode: %s\r\n", qrcode);
	//d       url次序
	 item_2 = cJSON_GetObjectItem(root, "d");        
    if(NULL == item_2){						    	
		return ERROR;
    }
	d = (char*)malloc(sizeof(item_2));
	if(NULL == d){
		printf("malloc d err\r\n");
		return ERROR;
	}
	free(d), d = NULL;
	 d = item_2->valuestring;
	 printf("d: %s\r\n", d);
     if(0 == strcmp(d, "1")){		                  //第一段url				 
		 memcpy(FlashBuffer.st_qrcode1,qrcode,strlen(qrcode));
		 printf("FlashBuffer.st_qrcode1: %s\r\n", FlashBuffer.st_qrcode1);
	}	
	else if(0 == strcmp(d, "2")){				 	  //第二段url		 
         memcpy(FlashBuffer.st_qrcode2,qrcode,strlen(qrcode));
	     printf("FlashBuffer.st_qrcode2: %s\r\n", FlashBuffer.st_qrcode2);
		 strcat(FlashBuffer.st_qrcode1,FlashBuffer.st_qrcode2);
	     printf("FlashBuffer.st_qrcode1: %s\r\n", FlashBuffer.st_qrcode1);	  
	     memset(FlashBuffer.st_qrcode, 0x00, length+1);
 	     memcpy(FlashBuffer.st_qrcode,FlashBuffer.st_qrcode1,strlen(FlashBuffer.st_qrcode1));
         qrcod_test();		                          //二維碼顯示函式	   
	}
    //a
	item_3 = cJSON_GetObjectItem(root, "a");
	if(NULL == item_3) {
		return ERROR;
	}
	a = item_3->valuestring;
    cJSON_Delete(root);
	root = NULL;
    return 0;
    //處理完DP資料后應有反饋
    ret = mcu_dp_string_update(DPID_ST_QRCODE,value, length);
    if(ret == SUCCESS)
	{
		Enter_stop_mode();                       //處理完后設備進入低功耗模式   
        return SUCCESS;
	}
    else
        return ERROR;
}

二維碼顯示函式:

TY_CREATE_IN_T Qrcod_CREATE_IN;
TY_CREATE_OUT_T *Qrcod_CREATE_OUT;
void qrcod_test(void)
{
	Qrcod_CREATE_IN.ecc_level=QRCODE_ECC_MEDIUM;
	Qrcod_CREATE_IN.version=9;                            //版本為9   53*53
	Qrcod_CREATE_IN.magnifications=2;                     //放大倍數2
	Qrcod_CREATE_IN.mode=STORAGE_MODE_BIT;
	Qrcod_CREATE_IN.information=FlashBuffer.st_qrcode;                        //url存放位置
	tuya_svc_image_generate_qrcode_create(&Qrcod_CREATE_IN,&Qrcod_CREATE_OUT);
	EPD_HW_Init(Partial);
	EPD_Dis_Part2(288,150,Qrcod_CREATE_OUT->dst_data,112,106);                //顯示坐標位置
	EPD_Part_Update();
	EPD_DeepSleep();
	tuya_svc_image_generate_qrcode_create_free(Qrcod_CREATE_OUT);
}

開發者可以根據自己需求修改url資訊,另外如果url前半部分固定不變的話可以將其寫在程式中,通過dp只下發后半部分url,可以簡化很多步驟;

2)預約資訊更新

dpid內容備注
103{“st”:time,“et”:time,“n”:“name”,“t”:“type”,“d”:“th”}st:預約起始時間欄位 et:預約結束時間欄位 time:時間 n:預約者名字欄位 name:名字內容 t:型別欄位 type:add:增加 del:洗掉 d:第幾條預約資訊

dp103是預約資訊的更新,更新型別包括新增與洗掉,更新的前提是設備已經從服務端同步過才有更新;

例如當前顯示兩條預約資訊,當需要更新第一條預約資訊時先下發更新dp,并且第一條更新完成的同時需將第二條資訊洗掉,下面為更新與洗掉的舉例;

型別舉例
新增{“st”:“10:00”,“et”:“12:00”,“n”:“無施”,“t”:“add”,“d”:“1”}
洗掉{“st”:“10:00”,“et”:“12:00”,“n”:“無施”,“t”:“del”,“d”:“2”}

dp決議及處理函式:

/*****************************************************************************
函式名稱 : dp_download_st_add_handle
功能描述 : 針對DPID_ST_ADD的處理函式
輸入引數 : value:資料源資料
        : length:資料長度
回傳引數 : 成功回傳:SUCCESS/失敗回傳:ERROR
使用說明 : 只下發型別,需要在處理完資料后上報處理結果至app
*****************************************************************************/
static unsigned char dp_download_st_add_handle(const unsigned char value[], unsigned short length)
{
    //示例:當前DP型別為STRING
    unsigned char ret;
    unsigned char dp_download_st_add_handle = NULL;  
	my_memset(FlashBuffer.st_add, 0x00, length+1);
	memcpy(FlashBuffer.st_add, value,length*sizeof(unsigned char));
	cJSON *root = NULL, *item = NULL, *item1 = NULL, *item2 = NULL, *item3 = NULL,*item4 = NULL;
	char* st = NULL;            //預約起始時間
	char* et = NULL;            //預約終止時間
	char* n = NULL;             //預約者姓名
    char* t = NULL;             //預約型別 : add/del
	char* d = NULL;             //預約資訊次序
	char *pstr = NULL;
	pstr = (char*)malloc(length+1);
	printf("pstr: %s\r\n", pstr);
    if(NULL == pstr){
		printf("malloc err\r\n");
		return ERROR;
	}
    my_memset(pstr, 0x00, length+1);
    my_memcpy(pstr, value, length);
	root = cJSON_Parse(pstr);
	free(pstr), pstr = NULL;
    if(NULL == root){								
		printf("ERROR!!!!!!!!!!!!!");
		return ERROR;
    }
	//st  預約起始時間
    item = cJSON_GetObjectItem(root, "st");
	if(NULL == item){			 				
        printf("item ERROR\r\n");
    }
	 st = item->valuestring;
	 printf("st: %s\r\n", st);
     memcpy(FlashBuffer.st_add,st,strlen(st));
     //et  預約終止時間
    item1 = cJSON_GetObjectItem(root, "et");
	if(NULL == item1){			 			
        printf("item1 ERROR\r\n");
    }
	 et = item1->valuestring;
	 printf("et: %s\r\n", et);
	 memcpy(FlashBuffer.et_add,et,strlen(et));		
	//n    預約者姓名
	 item2 = cJSON_GetObjectItem(root, "n");
	 if(NULL == item2){			 				
    }
	 n = item2->valuestring;
	 printf("n: %s\r\n", n);
     memcpy(FlashBuffer.n_add,n,strlen(n));	
	//t   預約型別 : add/del
	 item3 = cJSON_GetObjectItem(root, "t");
    if(NULL == item3){						    
		return ERROR;
    }
	 t = item3->valuestring;	
	 printf("t: %s\r\n", t);	
	//d   預約資訊次序
	 item4 = cJSON_GetObjectItem(root, "d");
    if(NULL == item4){						    	
		return ERROR;
    }
	 d = item4->valuestring;
	 printf("d: %s\r\n", d);
			
	 if(0 == strcmp(t, "add"))         //新增
    {  
        if(0 == strcmp(d, "1"))        //新增第一條資訊
            ST_ADD1();  
        else if(0 == strcmp(d, "2"))   //新增第二條資訊
            ST_ADD2();  
        else  
            ST_ADD3();                //新增第三條資訊
    }  
    else  
    {   
        if(0 == strcmp(t, "del"))       //洗掉
	       {
			   if(0 == strcmp(d, "1"))  
                  ST_DEL1();                   //洗掉第一條資訊
               else if(0 == strcmp(d, "2"))  
                  ST_DEL2();                  //洗掉第二條資訊
               else  
                  ST_DEL3();                  //洗掉第三條資訊
          }  
    } 
    cJSON_Delete(root);
    return 0;	
    //處理完DP資料后應有反饋
    ret = mcu_dp_string_update(DPID_ST_ADD,value, length);
        if(ret == SUCCESS)
	{
		Enter_stop_mode();                  //處理完后設備進入低功耗模式
        return SUCCESS;
	}
    else
        return ERROR;;
}

MCU接收到value后將決議出的起始時間存放在 FlashBuffer.st_add,將終止時間存放在 FlashBuffer.et_add,姓名欄位存放在gbk_n中,并通過修改顯示函式EPD_Dis_Part()中的橫縱坐標來調整在螢屏中的位置,

  • 更新函式:
void ST_ADD1(void)
{
	EPD_SetRAMValue_BaseMap(pBits);                     //保留背景資訊
	EPD_HW_Init(Partial);
	zk_init();
	GT5S_Wakeup();
    memset(pBits,0,sizeof(pBits));
    memcpy(pBits, pBits, sizeof(pBits));
	unsigned long gbk_n[30];
	utf8ToGBK(FlashBuffer.n_add, gbk_n, 30);             //GBK轉換
	//time    
	get_font(pBits,VEC_FT_ASCII_STY, FlashBuffer.st_add[0],24,24,24);     //st  
	EPD_Dis_Part(0,150,pBits,24,24,NEG);  
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[1],24,24,24);    
	EPD_Dis_Part(16,150,pBits,24,24,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[2],24,24,24);    
	EPD_Dis_Part(32,150,pBits,24,24,NEG);		           
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[3],24,24,24);   
	EPD_Dis_Part(44,150,pBits,24,24,NEG);	 
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[4],24,24,24);     
	EPD_Dis_Part(60,150,pBits,24,24,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,0X2D,24,24,24);  
	EPD_Dis_Part(80,150,pBits,24,24,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[0],24,24,24);      //et  
	EPD_Dis_Part(96,150,pBits,24,24,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[1],24,24,24);    
	EPD_Dis_Part(112,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[2],24,24,24);   
	EPD_Dis_Part(130,150,pBits,24,24,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[3],24,24,24);    
	EPD_Dis_Part(140,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[4],24,24,24);   
	EPD_Dis_Part(153,150,pBits,24,24,NEG);	
   //name
	get_font(pBits,VEC_SONG_STY,gbk_n[0],24,24,24);                   
	EPD_Dis_Part(176,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_SONG_STY,gbk_n[1],24,24,24);    
	EPD_Dis_Part(200,150,pBits,24,24,NEG);	
	EPD_Part_Update();
	EPD_DeepSleep();
	GT5S_DeepSleep();
}
  • 洗掉函式:
void ST_DEL1(void)
{
    EPD_SetRAMValue_BaseMap(pBits);  
    EPD_HW_Init(Partial); 	         
	zk_init();
	GT5S_Wakeup();
	memcpy(pBits, pBits, sizeof(pBits));
	unsigned long gbk_n[30];
    utf8ToGBK(FlashBuffer.n_add, gbk_n, 30);
	get_font(pBits,VEC_FT_ASCII_STY, FlashBuffer.st_add[0],24,24,24);     //st
	EPD_Dis_Part(0,150,pBits,24,24,OFF);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[1],24,24,24);    
	EPD_Dis_Part(16,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[2],24,24,24);     
	EPD_Dis_Part(32,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[3],24,24,24);    
	EPD_Dis_Part(44,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_add[4],24,24,24); 
	EPD_Dis_Part(60,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,0x2D,24,24,24);                     //-
	EPD_Dis_Part(80,150,pBits,24,24,OFF);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[0],24,24,24);     //et
	EPD_Dis_Part(96,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[1],24,24,24);     
	EPD_Dis_Part(112,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[2],24,24,24); 
	EPD_Dis_Part(130,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[3],24,24,24);     
	EPD_Dis_Part(140,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_add[4],24,24,24);
	EPD_Dis_Part(153,150,pBits,24,24,OFF);	
	get_font(pBits,VEC_SONG_STY,gbk_n[0],24,24,24); 
	EPD_Dis_Part(176,150,pBits,24,24,OFF); 
	get_font(pBits,VEC_SONG_STY,gbk_n[1],24,24,24);  
	EPD_Dis_Part(200,150,pBits,24,24,OFF);
	EPD_Part_Update();
	EPD_DeepSleep();
	GT5S_DeepSleep();
}

3)預約資訊同步

dpid內容備注
104“query”設備發送,同步預約資訊(全量)
{“st”:time,“et”:time,“n”:“name”,“d”:“th”,“a”:“all”}st:預約起始時間欄位 et:預約結束時間欄位 time:時間 n:預約者姓名欄位 name:姓名 d:單條預約資訊 a:預約資訊總量

dp104是預約資訊的同步,設備上電后同步所有資料,若查詢到幾條預約資訊就對應顯示幾條預約資訊,如查詢到有三條資訊則依次三條預約資訊,下發格式

預約資訊全量舉例
第一條{“st”:“10:00”,“et”:“12:30”,“n”:“一一”,”d”:”1”,”a”:”3”}
第二條{“st”:“14:10”,“et”:“16:30”,“n”:“一二”,”d”:”2”,”a”:”3”}
第三條{“st”:“18:30”,“et”:“20:00”,“n”:“一三”,”d”:”3”,”a”:”3”}
  • dp決議及處理函式:
/*****************************************************************************
函式名稱 : dp_download_st_all_handle
功能描述 : 針對DPID_ST_ALL的處理函式
輸入引數 : value:資料源資料
        : length:資料長度
回傳引數 : 成功回傳:SUCCESS/失敗回傳:ERROR
使用說明 : 可下發可上報型別,需要在處理完資料后上報處理結果至app
*****************************************************************************/
static unsigned char dp_download_st_all_handle(const unsigned char value[], unsigned short length)
{
    //示例:當前DP型別為STRING
    unsigned char ret;
	unsigned char dp_download_st_all_handle = NULL;
	memset(FlashBuffer.st_all, 0x00, length+1);
	memcpy(FlashBuffer.st_all, value,length*sizeof(unsigned char));
	cJSON *root = NULL, *item_1 = NULL, *item_2 = NULL, *item_3 = NULL, *item_4 = NULL, *item_5 = NULL;
	char *st = NULL;
	char *et = NULL; 
	char *n = NULL;
    char *d = NULL;
	char *a = NULL;
	char *pstr = NULL;
	pstr = (char*)malloc(length+1);
	printf("pstr: %s\r\n", pstr);
    if(NULL == pstr){
		printf("malloc error\r\n");
		return ERROR;
	}
	my_memset(pstr, 0x00, length+1);
    my_memcpy(pstr, value, length);
	printf("pstr: %s\r\n", pstr);
	my_memset(FlashBuffer.st_all, 0x00, length+1);
    my_memcpy(FlashBuffer.st_all, value, length);
	root = cJSON_Parse(pstr);
	free(pstr), pstr = NULL;
    if(NULL == root){								
		printf("root ERROR!!!!!!!!!!!!!");
		return ERROR;
    }
	//st
    item_1 = cJSON_GetObjectItem(root, "st");
	if(NULL == item_1){			 				
        printf("item_1 ERROR\r\n");
    }
	 st = item_1->valuestring;
	 printf("st: %s\r\n", st);
     memcpy(FlashBuffer.st_all,st,strlen(st));	
     //et
    item_2 = cJSON_GetObjectItem(root, "et");
	if(NULL == item_2){			 				
        printf("item_2 ERROR\r\n");
    }
	    et = item_2->valuestring;
	    printf("et: %s\r\n", et);
	memcpy(FlashBuffer.et_all,et,strlen(et));
	//n
	 item_3 = cJSON_GetObjectItem(root, "n");
	 if(NULL == item_3){			 				
       return ERROR;
    }
	 n = item_3->valuestring;
	 printf("n: %s\r\n", n);
    memcpy(FlashBuffer.n_all,n,strlen(n));
	//d
	 item_4 = cJSON_GetObjectItem(root, "d");
    if(NULL == item_4){						    	
		return ERROR;
    }
	 d = item_4->valuestring;
	 printf("d: %s\r\n", d);	
     if(0 == strcmp(d, "1")){		      //第一條預約資訊全量				 
		ST_ALL1();                   
	}	
	else if(0 == strcmp(d, "2")){		  //第二條預約資訊全量			 	 
        ST_ALL2();
	}	
	else if(0 == strcmp(d, "3")){         //第三條預約資訊全量	
         ST_ALL3();			
	}	
	//a
	item_5 = cJSON_GetObjectItem(root, "a");
	if(NULL == item_5) {
		return ERROR;
	}
	a = item_5->valuestring;
	printf("a: %s\r\n", a);
    cJSON_Delete(root), root = NULL;
    return 0;
    //處理完DP資料后應有反饋
    ret = mcu_dp_string_update(DPID_ST_ALL,value, length);
    if(ret == SUCCESS)
	{
		Enter_stop_mode();             //進入低功耗模式
        return SUCCESS;
	}
    else
        return ERROR;
}

MCU接收到value后將決議出的起始時間存放在 FlashBuffer.st_all,將終止時間存放在 FlashBuffer.et_all,姓名欄位存放在gbk_1中,并通過修改顯示函式EPD_Dis_Part()中的橫縱坐標來調整在螢屏中的位置,

  • 顯示函式:
void ST_ALL1(void)
{
    EPD_HW_Init(Partial);
	zk_init();
	GT5S_Wakeup();
	unsigned long gbk_1[30];
	utf8ToGBK(FlashBuffer.n_all, gbk_1, 30);
	//time
	get_font(pBits,VEC_FT_ASCII_STY, FlashBuffer.st_all[0],24,24,24);  
	EPD_Dis_Part(0,150,pBits,24,24,NEG);  
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_all[1],24,24,24);    
	EPD_Dis_Part(16,150,pBits,24,24,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_all[2],24,24,24);  
	EPD_Dis_Part(32,150,pBits,24,24,NEG);		      
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_all[3],24,24,24);   
	EPD_Dis_Part(44,150,pBits,24,24,NEG);	 
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.st_all[4],24,24,24);   
	EPD_Dis_Part(60,150,pBits,24,24,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,0X2D,24,24,24);   
	EPD_Dis_Part(80,150,pBits,24,24,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_all[0],24,24,24);  
	EPD_Dis_Part(96,150,pBits,24,24,NEG);	            
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_all[1],24,24,24);      
	EPD_Dis_Part(112,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_all[2],24,24,24);    
	EPD_Dis_Part(130,150,pBits,24,24,NEG);
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_all[3],24,24,24);    
	EPD_Dis_Part(140,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_FT_ASCII_STY,FlashBuffer.et_all[4],24,24,24);   
	EPD_Dis_Part(153,150,pBits,24,24,NEG);	
	//name
	get_font(pBits,VEC_SONG_STY,gbk_1[0],24,24,24);   
	EPD_Dis_Part(176,150,pBits,24,24,NEG);	
	get_font(pBits,VEC_SONG_STY,gbk_1[1],24,24,24); 
	EPD_Dis_Part(200,150,pBits,24,24,NEG);	
	EPD_Part_Update();
	EPD_DeepSleep();
	GT5S_DeepSleep();
}

4)電量報警

當設備電池電量低于10%時將低電量狀態上報至后臺端,由于電池的電量和電壓不是線性關系,若要通過電壓來做電量指示的話需要多做幾組充電放電的曲線測驗,程式中也需要分三種情況處理:空載時、負載時、以及充電時將三種情況分別表示出來,用電壓做電量指示才會更準確一些,

本次設計在計量電池電量時采用設備功耗來計算電池電量,如設備休眠狀態(80ms)電流10uA,接收狀態(600us)5mA,則可以算出平均電流,已知電池總電量,就可以計算出設備可作業時間,當電池電量低于10%,上報至后臺端,

7.功耗除錯

MCU與模組喚醒邏輯如下:

TX/RX:通訊串口

IO1:zigbee喚醒MCU引腳,拉低喚醒,電平持續時間10ms以上,

IO2:MCU喚醒Zigbee引腳,拉低喚醒,電平持續時間10ms以上,

MCU與模組喚醒的引腳為PB4 PB5,對應MCU的PB0,PC13

1)模組功耗除錯

zigebee模組燒錄的韌體為低功耗韌體,可通過配置zigbee網路策略引數來調節模組的功耗,以下為zigbee模組的常用引數

zigbee網路策略引數默認值
心跳時間17000
配網時間180
poll時間5000
上電持續poll30
poll失敗次數4
應用觸發rejion1
rejion間隔180
rejion次數1
發送功率11

下面是對各個引數的詳細描述,

  • 心跳時間:是用來維護設備和網關之間的資料鏈路是否正常的手段,強電設備的心跳時間默認為150+random(30)秒,低功耗設備的心跳時間默認為 4 小時,設定范圍為10~5*3600秒,且網關判定 12 小時內沒有收到心跳則認為設備離線,

  • 配網時間:當MCU發送配網指令之后,模組會執行一段時間的配網操作,并發送當前網路狀態為配網狀態,在一段時間內由于某些原因(例如附近沒有開啟配網的網路或者距離較遠)導致模組沒有加入到合適網路,則配網超時,配網超時之后,模組將處于未配網狀態,同時也會將此狀態發送給 MCU,配網超時時間默認為180秒,設定范圍為30~600秒,

  • poll時間(喚醒周期):poll 時間是指已經加入到網路的低功耗模組會在周期內喚醒,喚醒之后低功耗模組會發送資料請求(Data request)至其父節點,用于告知父節點,其當前處于喚醒狀態,父節點是否為其快取資料,如果存在快取資料,則父節點可以將資料發送給低功耗模組,

    注:Poll 值主要是影響功耗,喚醒周期越短,功耗越大,Poll 最小值為 200ms,小于最小值按照最小值處理,建議取值小于等于 8s,這里poll時間默認為5000ms,設定范圍為200~10000ms,

  • 上電持續poll:通常上電之后,設定一段時間的快速 Poll,可以在這個時間窗內將網關的配置命令下發,上電之后的快速 Poll 的時間默認為 30 秒,設定范圍為30~600s,

  • poll失敗次數:低功耗模塊發送Data request之后,父節點首先是需要回復 ack,該ack是對Data request的應答,如果有快取資料則將資料發送給模塊,如果沒有資料發送,則僅需回復ack,如果模塊發送了Data request,但由于環境、距離、父節點斷電等因素導致模塊沒有收到ack,則模塊的poll失敗次數會加1,如果在累加的程序中重新收到父節點的ack,則累加清零,當累加到的一定的值時(Poll失敗次數),認為模塊丟失父節點,需要觸發rejion,默認值為4次,設定范圍為3~40次,

  • rejoin(重連):即重新加入到網路,但無須網關開啟配網模式,是一種專門用于低功耗設備在父節點丟失時,重新加入網路的一種機制,

  • rejoin間隔: 周期觸發rejion的間隔時間,低功耗模塊觸發rejion之后,發送beacon request的周期間隔,默認值為180s,即當設備丟失父節點時,會間隔180秒嘗試rejion,

  • rejoin次數:低功耗模塊觸發rejion之后,發送beacon request的次數,默認值為1,可設定范圍為1~10次,

  • 發送功率:發送功率越大,功耗越高,默認值為11dB,可設定范圍為3~19dB,

    以上幾個引數為模組較常用的幾個引數,模組網路引數調節完之后測出模組休眠狀態功耗約為2.4uA,以上引數滿足低功耗要求故開發者無須再次修改調節,

2)MCU功耗除錯

MCU有多種低功耗模式:睡眠模式、低功耗運行模式、低功耗睡眠模式、停止模式、待機模式,

待機模式電流最低,但是待機模式時的MCU處于不受控制的狀態,所有的IO口都作業在高阻抗的狀態下,只有專門的幾個引腳能將MCU喚醒,而每次喚醒后相當于系統復位,RAM中的資料全部丟失,在外部器件連接的情況下,器件的引腳可能會吸收大量的電流,反而達不到低功耗的要求,

停止模式的功耗僅次于待機模式,在STOP模式下,PLL,HSL,HSE都被停止,RAM和暫存器的值保留,

本次方案采用STOP2模式為功耗最低的睡眠模式,且STOP2模式只能從運行模式進入,開發者可根據自身應用場景選擇相應的模式,下面是MCU在幾種模式下的功耗大小,

低功耗模式電流消耗喚醒時間
STOP0100 μA0.7 μs
STOP14.6 μA4 μs
STOP22.7μA5 μs

下面為MCU進入低功耗模式的流程步驟

  • 進入低功耗前的配置:

1)把所有開啟的外設先失能,再把引腳設為模擬模式,最后關閉外設時鐘;

2)進入STOP2模式,呼叫低功耗函式;可配置為中斷喚醒或事件喚醒,通過__WFI()或__**WFE()**選擇喚醒模式;

void Enter_stop_mode(void)
{
	RCC->APB1ENR1|=1<<28;     //PWR電源介面使能
	RCC->APB1RSTR1|=1<<17;    //復位所以IO口
	RCC->APB1RSTR1|=1<<18; 
	RCC->AHB2ENR|=0<<1;       //禁止GPIO B
    RCC->AHB2ENR|=0<<2;       //禁止GPIO C
	RCC->APB1ENR1|= 0<<14; 
	SPI_MISO_SLEEP;
	EPD_BUSY_SLEEP;
    //PB0作為外部中斷
    Exit_IO4();	
	//進入STOP2模式
	__WFI();	             //Wait For Interrupt
}
  • 出低功耗后的配置:

1)先恢復時鐘配置

2)恢復外設狀態,如GPIO、串口、SPI等

3)在需要進入STOP模式的地方直接呼叫函式

//退出STOP2后設定
void Exit_stop_mode(void)
{	
	SystemClock_Config();
	IO_Init();
	RCC->AHB2ENR|=1<<1;      
    RCC->AHB2ENR|=1<<2; 
	printf("exit stop\r\n");
}

  • 中斷喚醒
void Exit_IO4(void)
{
	PA4_IN;               //PA4喚醒引腳
	EXTI->IMR1|=1<<4;     //中斷請求未屏蔽
	EXTI->RTSR1|=1<<4;    //line4 上升沿 
	NVIC_SetPriority(EXTI4_IRQn, 1);
	NVIC_EnableIRQ(EXTI4_IRQn);	
	EXTI->PR1 = 1<<4;    //清除中斷標志位
	
}

注意在除錯低功耗時芯片有時會進入休眠自鎖狀態出現無法下載的情況,可按住開發板的復位鍵不放開再點下載程式,過1~2秒后放開按鍵即可燒寫成功;

三、整機演示

設備整機搭建如下圖:

OP模式下,PLL,HSL,HSE都被停止,RAM和暫存器的值保留,

本次方案采用STOP2模式為功耗最低的睡眠模式,且STOP2模式只能從運行模式進入,開發者可根據自身應用場景選擇相應的模式,下面是MCU在幾種模式下的功耗大小,

低功耗模式電流消耗喚醒時間
STOP0100 μA0.7 μs
STOP14.6 μA4 μs
STOP22.7μA5 μs

下面為MCU進入低功耗模式的流程步驟

  • 進入低功耗前的配置:

1)把所有開啟的外設先失能,再把引腳設為模擬模式,最后關閉外設時鐘;

2)進入STOP2模式,呼叫低功耗函式;可配置為中斷喚醒或事件喚醒,通過__WFI()或__**WFE()**選擇喚醒模式;

void Enter_stop_mode(void)
{
	RCC->APB1ENR1|=1<<28;     //PWR電源介面使能
	RCC->APB1RSTR1|=1<<17;    //復位所以IO口
	RCC->APB1RSTR1|=1<<18; 
	RCC->AHB2ENR|=0<<1;       //禁止GPIO B
    RCC->AHB2ENR|=0<<2;       //禁止GPIO C
	RCC->APB1ENR1|= 0<<14; 
	SPI_MISO_SLEEP;
	EPD_BUSY_SLEEP;
    //PB0作為外部中斷
    Exit_IO4();	
	//進入STOP2模式
	__WFI();	             //Wait For Interrupt
}
  • 出低功耗后的配置:

1)先恢復時鐘配置

2)恢復外設狀態,如GPIO、串口、SPI等

3)在需要進入STOP模式的地方直接呼叫函式

//退出STOP2后設定
void Exit_stop_mode(void)
{	
	SystemClock_Config();
	IO_Init();
	RCC->AHB2ENR|=1<<1;      
    RCC->AHB2ENR|=1<<2; 
	printf("exit stop\r\n");
}

  • 中斷喚醒
void Exit_IO4(void)
{
	PA4_IN;               //PA4喚醒引腳
	EXTI->IMR1|=1<<4;     //中斷請求未屏蔽
	EXTI->RTSR1|=1<<4;    //line4 上升沿 
	NVIC_SetPriority(EXTI4_IRQn, 1);
	NVIC_EnableIRQ(EXTI4_IRQn);	
	EXTI->PR1 = 1<<4;    //清除中斷標志位
	
}

注意在除錯低功耗時芯片有時會進入休眠自鎖狀態出現無法下載的情況,可按住開發板的復位鍵不放開再點下載程式,過1~2秒后放開按鍵即可燒寫成功,

以上就是墨水屏座位管理器嵌入式功能實作的所有內容,如果你這對墨水屏相關應用場景實踐感興趣,或者在開發程序中遇到任何問題,歡迎留言交流,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/356830.html

標籤:其他

上一篇:推薦一款基于nodejs+koa+vue開發的開源智慧物業系統

下一篇:物聯網產品中服務器的重要性

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more