STM32 IAP固件升級(四)


章節說明

STM32 IAP固件升級實驗分為以下的章節(加粗的字體是本章節的內容):

一、Flash和RAM的區域划分、工程建立、程序分散加載、程序燒寫
二、Stm32 bootloader、application、firmware 程序的分析和編寫
三、使用DMA收發串口的不定長數據
四、通信協議的設計
五、STM32 IAP程序的設計
六、上位機的程序的編寫

一、前言

為了能使上位機和下位機能進行可靠的通信,所以需要設計一個相對可靠的協議。當然設計的協議也不會太復雜,但是該有的功能還是得有。數據頭部,控制指令,數據長度,數據,校驗碼。這種類型通訊協議用於串口相對還是比較簡單便捷的,當然也可以設計的很復雜例如:加入版本控制,物理設備號等等。在本文中就不涉及太復雜的東西了,就設計一個相對完整又相對簡單的協議。話不多說,接下來進入正題吧。

二、通訊格式

設計的通訊格式為問答式(即一問一答的方式),分為控制指令,和應答指令。

1、控制指令格式

  1. 首部 : 所有的數據包都要加包頭:0xA5A5
  2. 父指令: 根據需求來設計
  3. 子指令: 根據需求來設計
  4. 長度 : 數據長度字節,用來指定該幀中攜帶數據的長度(單位是長度)
  5. 數據 : 該幀中寫帶的數據內容
  6. 校驗碼: 校驗碼使用校驗和的方式;校驗和是子指令到校驗和之間的所有字節之和,超出 2 字節的進位忽略

2、應答指令格式

  1. 首部 : 所有的數據包都要加包頭:0xA5A5
  2. 父指令: 根據需求來設計
  3. 子指令: 根據需求來設計
  4. 長度 : 數據長度字節,用來指定該幀中攜帶數據的長度(單位是長度)
  5. 數據 : 該幀中寫帶的數據內容
  6. 校驗碼: 校驗碼使用校驗和的方式;校驗和是子指令到校驗和之間的所有字節之和,超出 2 字節的進位忽略

三、控制指令設計

1、查詢指令/復位指令




2、程序更新控制指令




3、數據傳送指令

四、應答指令設計

1、查詢應答指令



2、狀態指令



五、幀管理程序設計

1、幀管理數據結構

/* 回調函數的類型定義如下 */
/* typedef uint16_t (*FRAME_VALIDATE)(u8 *buf, u16 len); */
typedef struct
{
	u8 *buffer;//指向一塊buff,用於存儲一幀的數據
	u16 head;  //數據存儲在頭部的位置
	u16 count; //接收到的數據
	u16 BUFFERLENGTH; //buffer的最大長度
	u16 MIN_FRAME_LEN; //最小幀
	FRAME_VALIDATE ValidateFrame; //這個是一個回調函數的指針,主要是處理幀的回掉函數
} FrameBufferStr;

2、結構體初始化函數

注意修改堆(heap)的大小

void Frame_Buffer_Init(FrameBufferStr *frame, u16 len, FRAME_VALIDATE validate_cb)
{
	/* 但是要值得注意啟動文件的堆內存的大小(默認是 512Byte)。*/
	/* 如果使用大於等於512Byte的大小,則需要修改堆內存大小,否則會報 Fault 異常 */
	/* 由於本實驗需要的緩存需要3Kb,所以已經在啟動文件里面修改為4KB */
	frame->buffer = (u8*)malloc(len);  
	frame->BUFFERLENGTH = len;
	frame->ValidateFrame = validate_cb;
	Frame_Buffer_Clean(frame);
}

3、清buff函數

void Frame_Buffer_Clean(FrameBufferStr *frame)
{
	memset(frame->buffer, 0, frame->BUFFERLENGTH);
	frame->head = 0;
	frame->count = 0;

}

4、幀管理程序

void Append_Frame_Buffer(FrameBufferStr *frame, u8 *input, u16 length)
{ 
	u16 i = 0;
	//s16 head=0;
	u16 mlen=0;
	
	// 如果上傳數據長度比緩存總長度還要長
	if (length > frame->BUFFERLENGTH)
	{
		// 此處可添加錯誤代碼
		return;
	}
	/* 需要將后面的數據copy到前面 */
	if((u16)frame->count + length > frame->BUFFERLENGTH)
	{
		/* frame->head 記錄frame->buffer的頭部,frame->count記錄接收到數據的尾部*/
		memmove(frame->buffer, frame->buffer+frame->head, frame->count - frame->head);
		/* 重新計算接收到數據的尾部 */
		frame->count = frame->count - frame->head;
		/* 頭部指向為0 */
		frame->head = 0;	
		/* 將后面的數據清零 */
		memset(frame->buffer + frame->count, 0, frame->BUFFERLENGTH - frame->count);
		/* 后面需要添加的數據超出了fream的范圍 */
		if((frame->count + length) > frame->BUFFERLENGTH)
		{
			// index out of buffer range
			/* 將數據清零 */
			frame->count=0;
			frame->head=0;
			return;
		}
	}

	if(length != 0)
	{
		/* 復制后面添加進來的數據 */
		memcpy(frame->buffer+frame->count, input, length);
		frame->count+=length;
	}

	i = 0;
	
	while(frame->head + i < frame->count)
	{
		/* 調用回調函數,解析接收數據,一個一個往下迭代,直到找到需要校驗的起始頭部 */
		mlen = frame->ValidateFrame(frame->buffer + frame->head + i, frame->count - frame->head - i);
		
		if(mlen > 0)
		 {
			 /* 返回的長度比buffer的總長度長,也將frame初始化 */
			if(mlen > frame->BUFFERLENGTH)
			{
				frame->head = 0;
				frame->count = 0;	
				memset(frame->buffer, 0, frame->BUFFERLENGTH);
				break;
			}
			/* 處理完這幀數據了,將數據清零 */
			frame->head = frame->head + i + mlen;		
			/* 如果計算完的后的frame->head后比 frame->count大,說明接收的數據有偏差*/
			if(frame->head > frame->count)
			{
				frame->count = frame->head;
			}
			i = 0;
			
			if(frame->head == frame->count)
			{
				/* 有偏差則需要重新將frame->buffer初始化 */
				frame->head = 0;
				frame->count = 0;	
				memset(frame->buffer, 0, frame->BUFFERLENGTH);
				break;
			}
		}
		else
		{
			i++;	
		}		
	}

}

六、在串口使用

1、DMA的回調函數

IAP實驗三里面有講到,使用DMA接收串口數據時,首先需要給USART2_Service函數指針賦值讓DMA接收函數回調。下面的USART2_DMA_Callback就是賦值給USART2_Service的函數。而這個函數也比較簡單,就是將數據存放到幀管理程序中。

void USART2_DMA_Callback(u8 *buff, u16 len)
{
	/* 里面調用幀管理程序,用於接收一幀數據 */
	Append_Frame_Buffer(&USART2_Frame,buff,len);
}

2、USART2 的 Frame 管理程序初始化

/* 定義一個 FrameBufferStr 變量*/
FrameBufferStr USART2_Frame;

/* frame_cb 是幀管理程序的回調函數,由用戶定義,在IAP實驗中,主要用於處理協議的 */
void USART2_Frame_Init(FRAME_VALIDATE frame_cb)
{
	/* 初始化USART2_Frame */
	Frame_Buffer_Init(&USART2_Frame,USART2_FRAME_BUFFER_LEN,frame_cb);
	/* 給 USART2_Service 賦值*/
	USART2_Service = USART2_DMA_Callback;
}

3、幀管理程序的回調函數

/* 這個函數就是處理協議的回調函數了,具體怎么處理就留給用戶來設計了 */
/* 在本實驗中,這里只是做一個接收復位指令,然后執行軟件復位 */
uint16_t Protocol_Handle(u8 *buf, u16 len)
{
	/* 判斷頭是否正確 */
	if(buf[0] == 0xa5 && buf[1] == 0xa5){
		/* 等待接收夠一幀數據 */
		/* 這個 len 是包括頭部的,不是協議上的len*/
		if(len >= 8){
			
			/* 判斷校驗位 */
			uint16_t check = (uint16_t)(buf[len-2]<<8 | buf[len-1]);
			if(check == 1){
				/* 判斷是否是軟復位指令 */
				if(buf[2]  == 0 && buf[3] == 1){
					/* 執行軟復位 */
					SoftReset();
				}
			}
			return len;
		}
	}
	return 0;
}

七、總結&實驗現象

1、使用方法

通過上面步驟的層層封裝,只要使用下面的步驟就可以正常使用:

  1. 定義一個幀管理的回調函數
  2. 調用 USART2_Frame_Init 初始化
  3. 使能串口
  4. 在回調函數中處理協議

2、本實驗的現象

當通過 USART2 發送復位指令之后(下圖中左邊的調試助手)就可以看 STM32 復位,並且打印復位信息(右邊的調試住手)。
這個實驗是在 application 環境下執行的。

3、project

最后將快要完成IAP實驗的工程送給大家--->點我project!!!

說明:由於上面的通訊協議還沒實驗過,有可能還會更改,但是更改的一般不會很多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM