升級(jí)介紹
藍(lán)牙固件升級(jí)是使用手機(jī)給固件進(jìn)行版本升級(jí)吹由,以達(dá)到修復(fù)bug或者添加新功能的作用呛讲。升級(jí)的大概流程是:首先,當(dāng)藍(lán)牙固件需要升級(jí)時(shí)恩商,由嵌入式開(kāi)發(fā)人員提供新的固件变逃,由服務(wù)器管理人員將固件放到服務(wù)器上,此時(shí)怠堪,用戶打開(kāi)手機(jī)APP的時(shí)候會(huì)檢測(cè)到服務(wù)器有更新揽乱,請(qǐng)求最新的藍(lán)牙固件,確認(rèn)更新后粟矿,手機(jī)會(huì)從服務(wù)器下載固件凰棉。下載完畢后,APP會(huì)讀取固件內(nèi)容陌粹,并根據(jù)升級(jí)協(xié)議將內(nèi)容傳到藍(lán)牙固件里撒犀,完成升級(jí)。
DFU = Device Firmware Update (設(shè)備固件更新)
OTA = Over The Air (空中升級(jí))
升級(jí)流程
各個(gè)藍(lán)牙設(shè)備不盡相同掏秩,以下是我測(cè)試設(shè)備的升級(jí)流程:升級(jí)固件為.bin后綴的文件或舞,文件名會(huì)有一定的格式。
我司的硬件工程師一般發(fā)送給我們的升級(jí)軟件是hex文件蒙幻,然后升級(jí)的時(shí)候需要的是bin文件這就需要我們把hex文件轉(zhuǎn)換為bin文件嚷那。轉(zhuǎn)換bin文件有兩種方式:
1.通過(guò)軟件的方式,軟件的方式我是通過(guò)軟件J-Flash來(lái)轉(zhuǎn)換的杆煞。具體操作流程如下:
(1)打開(kāi)J-Flash選擇Create a new project魏宽。
(2)把hex文件拖入J-Flash 。
(3)找到hex文件對(duì)應(yīng)的結(jié)束的最后一位的位置决乎。
(4)選擇Save data file as 保存類型選bin類型队询,然后點(diǎn)擊保存之后彈出Enter address range框 start address 保持不變,End address 輸入你想要轉(zhuǎn)換文件的結(jié)束地址构诚,然后點(diǎn)擊OK hex轉(zhuǎn)bin文件轉(zhuǎn)換成功蚌斩。
2.通過(guò)到代碼的方式。轉(zhuǎn)換的代碼如下:
//
//? LBHexToBin.m
//
//? Created by lingbing on 2020/9/18.
//
#import
NS_ASSUME_NONNULL_BEGIN
@interface LBHexToBin : NSObject
+ (NSData*)convert:(NSData*)hex;
@end
NS_ASSUME_NONNULL_END
//
//? LBHexToBin.m
//
//? Created by lingbing on 2020/9/18.
//
#import "LBHexToBin.h"
@implementation LBHexToBin
+ (constByte)ascii2char:(constByte*)ascii
{
? ? if(*ascii >='A')
? ? ? ? return*ascii -0x37;
? ? if(*ascii >='0')
? ? ? ? return*ascii -'0';
? ? return-1;
}
+ (constByte)readByte:(constByte*)pointer
{
? ? Bytefirst = [LBHexToBinascii2char:pointer];
? ? Bytesecond = [LBHexToBinascii2char:pointer +1];
? ? return(first <<4) | second;
}
+ (constUInt16)readAddress:(constByte*)pointer
{
? ? Bytemsb = [LBHexToBinreadByte:pointer];
? ? Bytelsb = [LBHexToBinreadByte:pointer +2];
? ? return(msb <<8) | lsb;
}
+ (NSUInteger)calculateBinLength:(NSData*)hex
{
? ? if(hex ==nil|| hex.length==0)
? ? {
? ? ? ? return0;
? ? }
? ? NSUIntegerbinLength =0;
? ? constNSUIntegerhexLength = hex.length;
? ? constByte* pointer = (constByte*)hex.bytes;
? ? UInt32lastBaseAddress =0;
? ? do
? ? {
? ? ? ? constBytesemicollon = *pointer++;
? ? ? ? // Validate - each line of the file must have a semicollon as a firs char
? ? ? ? if(semicollon !=':')
? ? ? ? {
? ? ? ? ? ? return0;
? ? ? ? }
? ? ? ? constUInt8reclen = [LBHexToBinreadByte:pointer]; pointer +=2;
? ? ? ? constUInt16offset = [LBHexToBinreadAddress:pointer]; pointer +=4;
? ? ? ? constUInt8rectype = [LBHexToBinreadByte:pointer]; pointer +=2;
? ? ? ? switch(rectype) {
? ? ? ? ? ? case0x04: {
? ? ? ? ? ? ? ? // Only consistent hex files are supported. If there is a jump to non-following ULBA address skip the rest of the file
? ? ? ? ? ? ? ? constUInt32newULBA = [LBHexToBinreadAddress:pointer];
? ? ? ? ? ? ? ? if(binLength >0&& newULBA != (lastBaseAddress >>16) +1)
? ? ? ? ? ? ? ? ? ? returnbinLength;
? ? ? ? ? ? ? ? lastBaseAddress = newULBA <<16;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? case0x02: {
? ? ? ? ? ? ? ? // The same with Extended Segment Address. The calculated ULBA must not be greater than the last one + 1
? ? ? ? ? ? ? ? constUInt32newSBA = [LBHexToBinreadAddress:pointer] <<4;
? ? ? ? ? ? ? ? if(binLength >0&& (newSBA >>16) != (lastBaseAddress >>16) +1)
? ? ? ? ? ? ? ? ? ? returnbinLength;
? ? ? ? ? ? ? ? lastBaseAddress = newSBA;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? case0x00:
? ? ? ? ? ? ? ? // If record type is Data Record (rectype = 0), add it's length (only it the address is >= 0x1000, MBR is skipped)
? ? ? ? ? ? ? ? if(lastBaseAddress + offset >=0x1000)
? ? ? ? ? ? ? ? ? ? binLength += reclen;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? pointer += (reclen <<1);? // Skip the data when calculating length
? ? ? ? pointer +=2;? // Skip the checksum
? ? ? ? // Skip new line
? ? ? ? if(*pointer =='\r') pointer++;
? ? ? ? if(*pointer =='\n') pointer++;
? ? }while(pointer != hex.bytes+ hexLength);
? ? returnbinLength;
}
+ (NSData*)convert:(NSData*)hex
{
? ? constNSUIntegerbinLength = [LBHexToBincalculateBinLength:hex];
? ? constNSUIntegerhexLength = hex.length;
? ? constByte* pointer = (constByte*)hex.bytes;
? ? NSUIntegerbytesCopied =0;
? ? UInt32lastBaseAddress =0;
? ? Byte* bytes =malloc(sizeof(Byte) * binLength);
? ? Byte* output = bytes;
? ? do
? ? {
? ? ? ? constBytesemicollon = *pointer++;
? ? ? ? // Validate - each line of the file must have a semicollon as a firs char
? ? ? ? if(semicollon !=':')
? ? ? ? {
? ? ? ? ? ? free(bytes);
? ? ? ? ? ? returnnil;
? ? ? ? }
? ? ? ? constUInt8reclen = [LBHexToBinreadByte:pointer]; pointer +=2;
? ? ? ? constUInt16offset = [LBHexToBinreadAddress:pointer]; pointer +=4;
? ? ? ? constUInt8rectype = [LBHexToBinreadByte:pointer]; pointer +=2;
? ? ? ? switch(rectype) {
? ? ? ? ? ? case0x04: {
? ? ? ? ? ? ? ? constUInt32newULBA = [LBHexToBinreadAddress:pointer]; pointer +=4;
? ? ? ? ? ? ? ? if(bytesCopied >0&& newULBA != (lastBaseAddress >>16) +1)
? ? ? ? ? ? ? ? ? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];
? ? ? ? ? ? ? ? lastBaseAddress = newULBA <<16;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? case0x02: {
? ? ? ? ? ? ? ? constUInt32newSBA = [LBHexToBinreadAddress:pointer] <<4; pointer +=4;
? ? ? ? ? ? ? ? if(bytesCopied >0&& (newSBA >>16) != (lastBaseAddress >>16) +1)
? ? ? ? ? ? ? ? ? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];
? ? ? ? ? ? ? ? lastBaseAddress = newSBA;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? case0x00:
? ? ? ? ? ? ? ? // If record type is Data Record (rectype = 0), copy data to output buffer
? ? ? ? ? ? ? ? // Skip data below 0x1000 address (MBR)
? ? ? ? ? ? ? ? if(lastBaseAddress + offset >=0x1000)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? for(inti =0; i < reclen; i++)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? *output++ = [LBHexToBinreadByte:pointer]; pointer +=2;
? ? ? ? ? ? ? ? ? ? ? ? bytesCopied++;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? pointer += (reclen <<1);? // Skip the data
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? pointer += (reclen <<1);? // Skip the irrelevant data
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? pointer +=2;? // Skip the checksum
? ? ? ? // Skip new line
? ? ? ? if(*pointer =='\r') pointer++;
? ? ? ? if(*pointer =='\n') pointer++;
? ? }while(pointer != hex.bytes+ hexLength);
? ? return[NSDatadataWithBytesNoCopy:byteslength:bytesCopied];
}
@end
升級(jí)過(guò)程:
1.掃描并連接要升級(jí)的藍(lán)牙設(shè)備
2.發(fā)送給外設(shè)升級(jí)前的驗(yàn)證指令范嘱,驗(yàn)證是否是合法升級(jí)
3.外設(shè)驗(yàn)證返回成功之后送膳,發(fā)送授權(quán)升級(jí)指令员魏,指令返回成功之后開(kāi)始固件升級(jí)
3.判斷隨機(jī)數(shù)無(wú)誤,準(zhǔn)備發(fā)送打包好的數(shù)據(jù)
4.真正發(fā)送打包好的數(shù)據(jù)(每次發(fā)送10包叠聋,一包20個(gè)字節(jié))撕阎,這里會(huì)重復(fù)N多次,看你的原數(shù)據(jù)包有多大碌补;每次接到我發(fā)的包后虏束,外設(shè)都會(huì)給我會(huì)OK否,我收到OK后才會(huì)發(fā)一下個(gè)數(shù)據(jù)包
5.告訴外設(shè)我數(shù)據(jù)發(fā)送完畢厦章,并發(fā)送一段指令(包括本次空中升級(jí)數(shù)據(jù)包的大小镇匀,還有加密參數(shù)什么的)
6.外設(shè)給我回OK無(wú)誤后,才算真正升級(jí)完成