單片機(jī)電路板一般專(zhuān)有的,如汽車(chē)的車(chē)燈控制電路板和EPS控制的電路板是完全不同的。專(zhuān)有的電路板,軟件就比較難通用,軟件編程比較強(qiáng)調(diào)的是單片機(jī)系統(tǒng)。用單片機(jī)來(lái)做工控板則有很強(qiáng)的通用性,如一個(gè)工控電路板既可以用來(lái)控制口罩設(shè)備,也可以用來(lái)控制電梯運(yùn)行,還可以控制自助發(fā)銀行的設(shè)備等等.如此說(shuō)來(lái),工控電路板不僅僅是電路板,而是一個(gè)工控平臺(tái).平臺(tái)的軟件是高度抽象的,如個(gè)人電腦,手機(jī), 瀏覽器都是平臺(tái),開(kāi)發(fā)者都是基于平臺(tái)接口編程的,不用知道底層CPU寄存器,時(shí)鐘等.工控平臺(tái)也一樣,也是希望業(yè)務(wù)程序代碼中不要有單片機(jī)的寄存器設(shè)置,固件庫(kù),時(shí)鐘相關(guān)的代碼.
要達(dá)到這個(gè)目的,就要把業(yè)務(wù)代碼和驅(qū)動(dòng)系統(tǒng)代碼實(shí)現(xiàn)工程級(jí)分離;或者業(yè)務(wù)代碼使用腳本實(shí)現(xiàn)(PLC的梯形圖就是一種圖形腳本),下面說(shuō)明一下前面的一種方式.
業(yè)務(wù)代碼和驅(qū)動(dòng)系統(tǒng)代碼實(shí)現(xiàn)工程級(jí)分離
工程分離有靜態(tài)分離和動(dòng)態(tài)分離兩種.靜態(tài)分離是把業(yè)務(wù)程序當(dāng)做一個(gè)靜態(tài)庫(kù)嵌入到驅(qū)動(dòng)系統(tǒng)工程. 這個(gè)有一個(gè)缺點(diǎn),業(yè)務(wù)程序修改,整個(gè)驅(qū)動(dòng)軟件系統(tǒng)都要重新編譯,驅(qū)動(dòng)系統(tǒng)和業(yè)務(wù)程序是一個(gè)Hex文件,業(yè)務(wù)程序也無(wú)法單獨(dú)升級(jí)。動(dòng)態(tài)分離是把業(yè)務(wù)程序當(dāng)做一個(gè)單獨(dú)的Hex放到單片機(jī)內(nèi)部flash某一個(gè)扇區(qū),業(yè)務(wù)程序代碼修改時(shí),驅(qū)動(dòng)系統(tǒng)不用重新編譯,工程代碼少,簡(jiǎn)潔,代碼復(fù)用最大化.只需編譯業(yè)務(wù)程序代碼的工程,這時(shí)驅(qū)動(dòng)系統(tǒng)不止是完成電機(jī)驅(qū)動(dòng),傳感器狀態(tài)獲取,還要完成業(yè)務(wù)程序的在線升級(jí),加載業(yè)務(wù)程序運(yùn)行.
內(nèi)存及Flash區(qū)域劃分
由于驅(qū)動(dòng)和業(yè)務(wù)是兩個(gè)不同的Hex,那么兩者的RAM就不能有重疊的空間.于是可以把整個(gè)單片機(jī)RAM前48K用作驅(qū)動(dòng)系統(tǒng)軟件,后面的12K RAM留給App.
keil驅(qū)動(dòng)工程設(shè)置:
keil App設(shè)置:
Flash區(qū)域也應(yīng)該劃分為兩個(gè)區(qū)域,前64K用作Boot驅(qū)動(dòng),從0x8000000開(kāi)始。中間的128K扇區(qū)用作App程序存儲(chǔ),其它扇區(qū)用作設(shè)備參數(shù),電機(jī)參數(shù)等.
App加載
App加載分三步:檢測(cè)App是否存在以及完整性檢測(cè),App的全局變量初始化,跳轉(zhuǎn)到App區(qū)域運(yùn)行.
App完整性檢測(cè)可以通過(guò)App的啟動(dòng)文件的DCD偽指令實(shí)現(xiàn),start.s:
AREA RESET, Code, READONLY,ALIGN=4
IMPORT InitApp
IMPORT |Load$$ER_IROM1$$Limit|
IMPORT |Load$$RW_IRAM1$$Limit|
DCD 365 ;標(biāo)志單片機(jī)中已經(jīng)存在App
DCD 111 ;CRC校驗(yàn)數(shù)值
DCD |Load$$ER_IROM1$$Limit| ;RO Code大小,用于確定程序大小,用于確定Hex二進(jìn)制代碼的RAM初始化位置
DCD |Load$$RW_IRAM1$$Limit| ;RW結(jié)束位置,用于確定Hex二進(jìn)制代碼的RAM結(jié)束位置
DCD InitApp
END
DCD 365是單片機(jī)的第一條指令,位于Flash區(qū)域的0x8020000處,這樣DCD是在0x8020000處放置數(shù)值365。驅(qū)動(dòng)Boot執(zhí)行讀取0x8020000處數(shù)值是否為365。
unsigned int IsAppExist = *(unsigned int*)0x8020000;
if(IsAppExist!=365)//App沒(méi)有燒錄
{
return ;
}
DCD 111是單片機(jī)的第二條指令,位于0x8020004,預(yù)留存放App的CRC數(shù)值,App在升級(jí)前,上位機(jī)先計(jì)算好Hex文件的CRC數(shù)值,下發(fā)到單片機(jī),單片機(jī)收到CRC,寫(xiě)到0x8020004處.
**DCD |Load$$
ER_IROM1
Limit|** 單片機(jī)第三條指令,位置0x8020008,Load
ER_IROM1
Limit是Keil的內(nèi)置宏,代表App編譯后的Code大小.
**DCD |Load
RW_IRAM1
Limit|** 是單片機(jī)的第四條指令,位置0x802000c,Load
RW_IRAM1
Limit也是Keil內(nèi)置宏,表示App編譯后的RW大小,也就是全局變量的大小.
uint16_t Crc_16(uint8_t *buf, int len) {
uint16_t crc = 0;
uint16_t i;
while (len--) {
crc ^= *buf++;
for (i = 0; i < 8; i++) {
crc = (crc >> 1) ^ ((crc & 1) ? 0xA001 : 0);
}
}
return crc;
}
//App CRC完整性檢測(cè)
uint8_t Check_AppCRC()
{
uint16_t SrcCRC= *(unsigned int*)0x8020004;
int CodeSize = *(unsigned int*)0x8020008;
int RWSize = *(unsigned int*)0x802000c;
uint16_t DstCRC = Crc_16((uint8_t*)0x8020000,CodeSize+RWSize);
if(SrcCRC==DstCRC)
{
return 1;
}
return 0;
}
Check_AppCrc校驗(yàn)App的CRC是否正確,正確才能跳轉(zhuǎn)到App運(yùn)行.
App全局變量初始化
全局變量的初始化一般是啟動(dòng)文件里邊調(diào)用keil內(nèi)置的__main函數(shù)實(shí)現(xiàn)的,初始化完成以后就跳到main函數(shù)去了.為了使App的啟動(dòng)文件簡(jiǎn)單,可以Boot跳轉(zhuǎn)App前,Boot實(shí)現(xiàn)App的全局變量的初始化,全局變量初始化即把全局變量的初始數(shù)值拷貝到RAM區(qū)域,初始值編譯時(shí)屬于RW數(shù)據(jù),存在hex文件里邊,對(duì)于單片機(jī)就是存在于Falsh區(qū)域.
要實(shí)現(xiàn)初始化就需要知道RW區(qū)域的起始地址,區(qū)域大小.Load
ER_IROM1
Limit即是RW區(qū)域的偏移地址,Load
RW_IRAM1
Limit是區(qū)域大小,再一次使用這兩個(gè)宏
/***************************************************************************************************************
* 函數(shù)名稱(chēng): InitAppVar
* 函數(shù)描述: 初始化業(yè)務(wù)程序的全局變量 靜態(tài)變量
* 其它說(shuō)明 :
****************************************************************************************************************/
void InitAppVar()
{
unsigned int HexVarStartAddr = *(unsigned int*)0x8020000;//Load$$ER_IROM1$$Limit
unsigned int HexVarEndAddr = *(unsigned int*)0x8020004;//Load$$RW_IRAM1$$Limit
unsigned char* pRamStartAddr = (unsigned char*)0x2000C000;
memset(pRamStartAddr,0,10*1024); //默認(rèn)App最多10K數(shù)據(jù)清零
memcpy(pRamStartAddr,(unsigned char*)HexVarStartAddr,(HexVarEndAddr-HexVarStartAddr));
}
Boot與App交互
不同于一般的Boot,App的代碼和Boot代碼有很大程度的復(fù)用的,這個(gè)復(fù)用就是Boot把自己的一些功能函數(shù)封裝到一個(gè)函數(shù)結(jié)構(gòu)體,也就是一個(gè)接口文件,Boot跳轉(zhuǎn)到App時(shí)把這個(gè)函數(shù)結(jié)構(gòu)體作為跳轉(zhuǎn)函數(shù)的參數(shù)傳遞給App,App就可以通過(guò)這個(gè)結(jié)構(gòu)體調(diào)用系統(tǒng)功能.
Boot區(qū)加載代碼:
typedef struct _ActLib
{
//步進(jìn)
void (*RunSM)(char sm_id,int nLen);
//輸入傳感器,輸出
char (*IsSensorOn)(char SensorNum);
void (*SetPrjName)(char* name);
}ActLib;
ActLib gLib;
PtrInitApp InitApp;
InitApp = (PtrInitApp)0x8020008;
InitApp(&gLib); //加載業(yè)務(wù)app初始化入口
App入口代碼
ActLib* l;
void InitApp(ActLib* pLib)
{
l = pLib;
l->SetPrjName("口罩設(shè)備項(xiàng)目");
if(l->IsSensorOn(X101))
{
l->RunSM(SM101,200);
}
}
上面簡(jiǎn)單介紹了工程分離及引導(dǎo)App的方法.
-
單片機(jī)
+關(guān)注
關(guān)注
6023文章
44376瀏覽量
628369 -
電路板
+關(guān)注
關(guān)注
140文章
4810瀏覽量
96096 -
EPS
+關(guān)注
關(guān)注
6文章
188瀏覽量
31084 -
軟件編程
+關(guān)注
關(guān)注
1文章
37瀏覽量
11167
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論