張 建,凌興宏,,王宜懷
1(蘇州大學(xué) 文正學(xué)院,蘇州 215104)
2(蘇州大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,蘇州 215006)
ARM(Advanced RISC Machine)是高級(jí)精簡(jiǎn)指令機(jī)器的簡(jiǎn)稱.ARM架構(gòu)是一個(gè)32位精簡(jiǎn)指令集處理器架構(gòu),基于ARM架構(gòu)的處理器在性能、功耗和開(kāi)放性等方面的優(yōu)勢(shì)決定了其在全球芯片市場(chǎng)上舉足輕重的地位,其中Cortex M系列(Cortex-M0/M0+/M1/M3/M4/M7/M23/M33/M35P/M55)內(nèi)核的微控制器(Microcontroller Unit,MCU)因具有低功耗、高性能、低成本的特性,十分適用于嵌入式系統(tǒng)領(lǐng)域[1].匯編語(yǔ)言是一種將機(jī)器語(yǔ)言的操作用助記符進(jìn)行表示和替換的低級(jí)語(yǔ)言.它與計(jì)算機(jī)硬件緊密聯(lián)系,是電子類、計(jì)算機(jī)類、自動(dòng)化類等專業(yè)的一門主干課程,學(xué)好匯編語(yǔ)言能夠加深對(duì)微機(jī)原理以及操作系統(tǒng)的理解[2].匯編語(yǔ)言與機(jī)器指令一一對(duì)應(yīng)的特性使得其實(shí)現(xiàn)簡(jiǎn)單功能時(shí)需要進(jìn)行繁多的操作,完成系統(tǒng)的開(kāi)發(fā)需要較長(zhǎng)的周期;匯編語(yǔ)言在不同內(nèi)核的設(shè)備中對(duì)應(yīng)著不同的機(jī)器語(yǔ)言指令集,難以在不同內(nèi)核的設(shè)備中移植.當(dāng)前在ARM MCU上的匯編工程已經(jīng)有了一些研究,如蔡伯峰[3]等基于Cortex-M0+ KL系列MCU研究設(shè)計(jì)了純匯編底層驅(qū)動(dòng)構(gòu)件;蔣婷[4]基于Cortex-M0+ KL系列MCU研究了構(gòu)件化的匯編框架,但是他們的研究只是針對(duì)特定內(nèi)核的MCU,復(fù)用性和移植性不高,設(shè)計(jì)的匯編工程框架不具有普適性.因此,本文以ARM Cortex-M系列內(nèi)核為平臺(tái),提出一種可移植、可復(fù)用的匯編工程通用框架設(shè)計(jì)方案.該方案針對(duì)Cortex-M4內(nèi)核的STM32L431RCTx[5](簡(jiǎn)稱STM32L431)匯編工程的研究,設(shè)計(jì)出一種普適的匯編工程通用框架.并在Cortex-M0+內(nèi)核的MKL36Z64XXX[6](簡(jiǎn)稱KL36Z64)和Cortex-M4F內(nèi)核的MSP432P401R[7](簡(jiǎn)稱MSP432P401)兩款MCU上驗(yàn)證了該框架的通用性.
針對(duì)意法半導(dǎo)體公司的STM32L431微控制器,在其上實(shí)現(xiàn)匯編工程.
MCU的最小硬件系統(tǒng)是指包括電源、晶振、復(fù)位、寫(xiě)入調(diào)試器接口等可使內(nèi)部程序得以運(yùn)行的、規(guī)范的、可復(fù)用的核心構(gòu)件系統(tǒng)[8].依據(jù)硬件最小系統(tǒng)的定義,提出采用匯編語(yǔ)言實(shí)現(xiàn)點(diǎn)亮板載小燈及串口輸出調(diào)試功能的軟件系統(tǒng).在此軟件系統(tǒng)基礎(chǔ)上,抽象提取出基于STM32L431的匯編工程框架.
首先在STM32CubeIDE中新建一個(gè)STM32L431工程,該工程使用的驅(qū)動(dòng)有GPIO(General Purpose Input/Output,通用型輸入輸出)和UART(Universal Asynchronous Receiver/ Transmitter,通用異步收發(fā)傳輸器).在保持其它文件不變情況下,用匯編語(yǔ)言改寫(xiě)GPIO驅(qū)動(dòng),編譯測(cè)試GPIO匯編驅(qū)動(dòng)的正確性,待GPIO測(cè)試正確后;再用匯編語(yǔ)言對(duì)UART進(jìn)行構(gòu)件化修改;并逐步將中斷服務(wù)函數(shù)和主函數(shù)修改為純匯編并調(diào)整工程目錄.工程的詳細(xì)代碼與說(shuō)明可到蘇州大學(xué)嵌入式學(xué)習(xí)社區(qū)網(wǎng)站(sumcu.suda.edu.cn)下載“STM32L431_ASM_Framework_20201018.rar”查看.
通過(guò)串口輸出的方式,將運(yùn)行中板載小燈亮暗狀態(tài)和串口接收中斷的運(yùn)行情況打印出來(lái),圖1為運(yùn)行串口接收到的結(jié)果,從中可看出匯編工程同樣可完好的解決問(wèn)題.采用逐個(gè)構(gòu)件化、模塊化,進(jìn)而完成全部工程轉(zhuǎn)變?yōu)閰R編工程是切實(shí)高效可行的方案.
圖1 STM32L431匯編工程運(yùn)行結(jié)果
ARM Cortex-M系列不同內(nèi)核支持的指令集存在較大差異,如Cortex-M3以下的內(nèi)核不支持Thumb1指令集中的CBNZ、CBZ和IT3個(gè)指令,Cortex M23不支持IT這一指令,并且對(duì)于Thumb2和DSP等指令集的支持亦不相同[9],所以設(shè)計(jì)匯編工程時(shí)應(yīng)考慮到指令集的差異性所帶來(lái)的問(wèn)題.考慮到匯編工程的移植性和復(fù)用性,需要屏蔽Cortex-M系列不同內(nèi)核支持的指令集不同所帶來(lái)的差異性,因此需要抽取Cortex-M系列不同內(nèi)核之間支持的指令集共性,即Cortex-M系列內(nèi)核全部支持的指令.表1為Cortex-M系列內(nèi)核部分通用匯編指令集.
表1 Cortex M系列內(nèi)核部分通用指令集
不同匯編器的匯編風(fēng)格不相同,如MASM(Microsoft Macro Assembler)匯編器采用Inter風(fēng)格、GAS(GNU Assembler)匯編器采用的是AT&T風(fēng)格(也支持Intel風(fēng)格).基于工程實(shí)際應(yīng)用的一致性考量,選用GAS匯編器并使用Intel匯編風(fēng)格,編譯器采用支持GAS匯編器的GNU v7.2.1.匯編偽指令的格式統(tǒng)一遵循GAS手冊(cè)[10]標(biāo)準(zhǔn),在每個(gè)匯編源程序中使用匯編偽指令規(guī)定匯編指令集為Thumb指令集,規(guī)定指令對(duì)齊方式為2字節(jié)對(duì)齊,表2為規(guī)則約定.GNU編譯器支持匯編代碼中使用“//”和“/* */”符號(hào)注釋,為了在學(xué)習(xí)和使用過(guò)程中良好的在匯編和C語(yǔ)言中過(guò)渡,工程中統(tǒng)一使用“//”作為單邊注釋、以“/* */”作為行注釋.
表2 GAS匯編器規(guī)則約定
匯編函數(shù)是通過(guò)偽指令和標(biāo)號(hào)形式定義,入口參數(shù)通過(guò)寄存器傳遞,調(diào)用方式通過(guò)跳轉(zhuǎn)到定義好的標(biāo)號(hào)來(lái)實(shí)現(xiàn),調(diào)用時(shí)不能看到傳入?yún)?shù)數(shù)量和格式,因此需要在定義函數(shù)時(shí)對(duì)函數(shù)和傳入?yún)?shù)、傳出參數(shù)以及函數(shù)功能進(jìn)行詳細(xì)說(shuō)明,以提高函數(shù)的移植性與復(fù)用性,同時(shí)便于后續(xù)的調(diào)用.圖2為匯編語(yǔ)言定義函數(shù)的標(biāo)準(zhǔn)格式.
圖2 匯編函數(shù)定義格式
構(gòu)件是指軟件系統(tǒng)中具有明確構(gòu)成成分的、具有相對(duì)獨(dú)立功能的、可替換的組成部分,它封裝了實(shí)現(xiàn)體并提供了一組接口的實(shí)現(xiàn)方法,能夠獨(dú)立工作或同其它構(gòu)件組合起來(lái)協(xié)同工作的對(duì)象[11].軟件復(fù)用是指將已有的軟件及其有效成分用于構(gòu)造新的軟件或系統(tǒng),可復(fù)用軟件是指為了復(fù)用目的而設(shè)計(jì)的軟件[12].可復(fù)用的構(gòu)件技術(shù)是匯編工程能實(shí)現(xiàn)軟件復(fù)用的關(guān)鍵.軟、硬件緊密聯(lián)系是嵌入式系統(tǒng)領(lǐng)域的顯著特征,這使得基于硬件開(kāi)發(fā)的硬件抽象層,軟件對(duì)其實(shí)現(xiàn)的質(zhì)量決定了上層軟件的優(yōu)劣,亦即整個(gè)系統(tǒng)的穩(wěn)定性與可靠性[13].因此,以構(gòu)件化技術(shù)為設(shè)計(jì)原則、以可復(fù)用軟件技術(shù)為導(dǎo)向設(shè)計(jì)硬件抽象層的軟件和其上層應(yīng)用軟件,是保持系統(tǒng)穩(wěn)定行和可靠性、以及提高工程的復(fù)用性和移植性的有效方法.為了實(shí)現(xiàn)匯編工程的層次清晰、構(gòu)件的分門別類,根據(jù)“各有所歸、層層遞進(jìn)”的原則把匯編工程的構(gòu)件分為基礎(chǔ)構(gòu)件、應(yīng)用構(gòu)件與軟件構(gòu)件3種類型[14].
3.2.1 基礎(chǔ)構(gòu)件
基礎(chǔ)構(gòu)件是總結(jié)MCU內(nèi)部功能模塊的基本知識(shí)要素,對(duì)于特定MCU的引腳功能或內(nèi)部功能,操作MCU內(nèi)部寄存器,所編寫(xiě)的直接干預(yù)硬件的構(gòu)件.常用的基礎(chǔ)構(gòu)件主要有:GPIO、UART、FLASH等,以GPIO構(gòu)件為例闡述基礎(chǔ)構(gòu)件的設(shè)計(jì)封裝方法.
1)GPIO構(gòu)件
GPIO構(gòu)件是MCU的一個(gè)基礎(chǔ)構(gòu)件,是MCU感知外部消息和控制外圍器件的重要通道.GPIO引腳能夠被定義為輸入和輸出兩種情況:當(dāng)定義為輸入時(shí),程序要能夠獲得引腳的高低電平狀態(tài);當(dāng)被定義為輸出時(shí),程序要能夠設(shè)置引腳高低電平狀態(tài).部分引腳還具有中斷中能,可根據(jù)需要開(kāi)啟和關(guān)閉中斷.表3為根據(jù)構(gòu)件化原則設(shè)計(jì)的GPIO對(duì)外函數(shù)接口列表.
表3 GPIO構(gòu)件函數(shù)
gpio.inc匯編頭文件說(shuō)明了GPIO構(gòu)件的功能,需要宏定義端口號(hào)、引腳方向、中斷類型和寄存器基地址,做到只查看頭文件不查源文件的情況下能夠使用GPIO構(gòu)件.
gpio.s源文件在做到清晰的對(duì)外接口和簡(jiǎn)潔的注釋下,用匯編語(yǔ)言實(shí)現(xiàn)表3中GPIO匯編構(gòu)件函數(shù)的具體功能.
3.2.2 應(yīng)用構(gòu)件
應(yīng)用構(gòu)件是采用面向?qū)ο蟮木幊谭椒▽?duì)基礎(chǔ)構(gòu)件進(jìn)行應(yīng)用化的再次封裝,將其封裝為具體應(yīng)用的構(gòu)件.如light構(gòu)件通過(guò)調(diào)用GPIO基礎(chǔ)構(gòu)件完成對(duì)小燈的控制.根據(jù)匯編工程的軟件系統(tǒng)最小定義,以控制小燈的light構(gòu)件為例闡述應(yīng)用構(gòu)件的編程設(shè)計(jì)方法.
light應(yīng)用構(gòu)件頭文件light.inc部分內(nèi)容如下:
/*****************************
* 文件名稱:light.inc
* 功能概要:控制小燈的應(yīng)用構(gòu)件程序頭文件
* 版本更新:2020-07-28 V1.6
*****************************/
.include "gpio.inc"
//指示燈端口及引腳定義
.equ LIGHT_RED,(PTB_NUM|7)//紅色RUN燈使用的端口/引腳
.equ LIGHT_GREEN,(PTB_NUM|8) //綠色RUN燈使用的端口/引腳
.equ LIGHT_BLUE,(PTB_NUM|9)//藍(lán)色RUN燈使用的端口/引腳
//燈狀態(tài)宏定義(燈亮、燈暗對(duì)應(yīng)的物理電平由硬件接法決定)
.equ LIGHT_ON,0 //燈亮
.equ LIGHT_OFF,1 //燈暗
light應(yīng)用構(gòu)件的源文件部分內(nèi)容如下:
/*****************************
* 文件名稱:light.inc
* 功能概要:控制小燈的應(yīng)用構(gòu)件程序源文件
* 版本更新:2020-07-28 V1.6
*****************************/
.include"light.inc"
.section.text//定義代碼存儲(chǔ)text段開(kāi)始,實(shí)際代碼存儲(chǔ)在Flash中
.syntax unified //指示下方指令為ARM和thumb通用格式
.thumb //使用Thumb指令集
.align2 //指令和數(shù)據(jù)采用Thumb格式的2字節(jié)對(duì)齊
/*****************************
* 函數(shù)名稱:light_init
* 函數(shù)返回:無(wú)
* 參數(shù)說(shuō)明:r0:(端口號(hào))|(引腳號(hào)),例:(PTB_NUM|(7))表示B口7腳
* r1:設(shè)定小燈狀態(tài).由light.inc中宏定義.
* 功能概要:小燈初始化
*****************************/
.typelight_init function //聲明light_init為函數(shù)類型
.globallight_init //將light_init定義成全局函數(shù)
light_init:
push {r0-r7,lr} //保存現(xiàn)場(chǎng),將下一條指令地址入棧
str r2,[r1] //將傳入?yún)?shù)r1中存放的小燈狀態(tài)值存放至r2,
//騰出r1存儲(chǔ)引腳方向
ldr r1,=GPIO_OUTPUT //設(shè)置小燈的引腳方向?yàn)檩敵?/p>
bl gpio_init //調(diào)用gpio初始化函數(shù)
pop {r0-r7,pc} //恢復(fù)現(xiàn)場(chǎng),返回主程序處繼續(xù)執(zhí)行
/*****************************
* 函數(shù)名稱:light_change
* 函數(shù)返回:無(wú)
* 參數(shù)說(shuō)明:r0:(端口號(hào))|(引腳號(hào))
* 例:(PTB_NUM|(7))表示B口7腳
* 功能概要:切換小燈亮暗.
*****************************/
.typelight_change function //聲明light_change為函數(shù)類型
.globallight_change //將light_change定義成全局函數(shù)
//便于芯片初始化之后調(diào)用
light_change:
push {r0-r7,lr} //保存現(xiàn)場(chǎng),將下一條指令地址入棧
bl gpio_reverse //調(diào)用后gpio引腳反轉(zhuǎn)函數(shù)
pop {r0-r7,pc} //恢復(fù)現(xiàn)場(chǎng),返回主程序處繼續(xù)執(zhí)行
3.2.3 軟件構(gòu)件
軟件構(gòu)件是一個(gè)面向?qū)ο蟮木哂幸?guī)范接口和確定的上下文依賴的組裝單元,它不依賴于底層的硬件驅(qū)動(dòng)構(gòu)件,能夠被獨(dú)立使用或被其他構(gòu)件調(diào)用,可以在不同內(nèi)核的MCU間無(wú)縫切換,如數(shù)字進(jìn)制轉(zhuǎn)換構(gòu)件BaseConvert等.
軟件構(gòu)件BaseConvert的源文件部分內(nèi)容如下:
/*****************************
* 函數(shù)名稱:convert_to_binary
* 函數(shù)返回:無(wú)
* 參數(shù)說(shuō)明:r5:通過(guò)將所要操作的數(shù)一次移動(dòng)1位,
* 再與“1”進(jìn)行邏輯”與“運(yùn)算后,再存放到r5中
* 功能概要:將十進(jìn)制數(shù)轉(zhuǎn)換成二進(jìn)制表示并輸出.
*****************************/
.typeconvert_to_binary function //聲明convert_to_binary為函數(shù)類型
.globalconvert_to_binary //將convert_to_binary定義成全局函數(shù)
convert_to_binary:
//(1.1)保存現(xiàn)場(chǎng)
push {r0-r7,lr} //保存現(xiàn)場(chǎng),pc(lr)入棧
//(1.2)循環(huán)得到寄存器每次移位后的數(shù)據(jù),并用串口輸出
mov r2,#8
mov r4,#1
mov r7,r0
mov r3,r1
bin_loop:
mov r5,r7 //r5=r7=用于保存邏輯“與”之后的數(shù)
sub r2,r2,#1 //r2=r2-1
lsr r5,r5,r2
and r5,r5,r4 //r5移位后將最后一位保存起來(lái)
mov r6,r2
strb r5,[r3] //以字節(jié)形式存儲(chǔ)
add r3,r3,#1 //地址+1
mov r2,r6
cmp r2,#0
bne bin_loop //r2不等于0則跳轉(zhuǎn)到loop
//(1.3)恢復(fù)現(xiàn)場(chǎng)
pop {r0-r7,pc} //恢復(fù)現(xiàn)場(chǎng),lr出棧到pc
匯編工程包含眾多文件,如工程說(shuō)明文件、內(nèi)核文件、編譯器版本文件、MCU頭文件、啟動(dòng)文件、鏈接文件、驅(qū)動(dòng)文件、應(yīng)用文件、中斷服務(wù)程序文件和主程序文件等,有效合理地組織這些文件、規(guī)范匯編工程,可以提高匯編工程的閱讀清晰度、加快開(kāi)發(fā)效率、提升可維護(hù)性并降低維護(hù)難度.針對(duì)不同的MCU,其對(duì)應(yīng)的匯編工程必然存在不同的文件,從不同的文件中抽取出共性特點(diǎn)對(duì)文件進(jìn)行歸納整理并根據(jù)層次遞進(jìn)的原則建立文件夾存放頭文件和源程序,符合可移植和可復(fù)用的標(biāo)準(zhǔn)并滿足匯編工程的最小軟件系統(tǒng),表4為普適性匯編工程目錄.
表4 匯編工程目錄結(jié)構(gòu)
匯編工程通用框架是基于匯編工程之間的共性設(shè)計(jì)的,以軟件共性為骨、以工程目錄共性為血肉,二者缺一不可.通用指令集和匯編格式是構(gòu)件封裝的基石,同時(shí)歸納總結(jié)出基礎(chǔ)構(gòu)件的對(duì)外函數(shù)接口表,應(yīng)用構(gòu)件直接調(diào)用基礎(chǔ)構(gòu)件的對(duì)外接口、可以脫離具體寄存器的操作,而軟件構(gòu)件則是完全獨(dú)立于硬件的構(gòu)件.框架中的工程目錄是抽取嵌入式匯編工程的目錄共性而來(lái),具有工程普適性.圖3為匯編工程的通用框架設(shè)計(jì).
圖3 匯編工程通用框架
在已有Cortex-M4內(nèi)核STM32L431匯編工程的基礎(chǔ)上,基于本文設(shè)計(jì)的通用框架實(shí)現(xiàn)其它內(nèi)核MCU的匯編工程的方法有2種.第1種方法是在MCU對(duì)應(yīng)的集成開(kāi)發(fā)環(huán)境(Integrated Development Environment,IDE)中新建一個(gè)匯編工程,將STM32L431中的匯編共性文件復(fù)制到新建的工程中,然后進(jìn)行編譯和修改錯(cuò)誤,這種方法具有一定的難度;第2種方法是在MCU對(duì)應(yīng)的IDE中打開(kāi)STM32L431的工程,將原有的芯片固有文件替換為目標(biāo)芯片的固有文件,根據(jù)具體情況在做出少量修改即可,這種方法較為簡(jiǎn)單.下面介紹第2種方法.
本文基于對(duì)Cortex-M4內(nèi)核STM32L431的匯編工程的研究,提出面向ARM Cortex-M系列內(nèi)核的匯編工程通用框架設(shè)計(jì),并在Cortex-M0+內(nèi)核的KL36Z64和Cortex-M4F內(nèi)核的MSP432P401兩款MCU上對(duì)該框架的通用性進(jìn)行驗(yàn)證.表5為驗(yàn)證平臺(tái)的對(duì)比.
表5 框架通用性驗(yàn)證平臺(tái)參數(shù)
在不同內(nèi)核不同MCU上移植匯編工程的步驟分析:
1)建立工程:在目標(biāo)MCU對(duì)應(yīng)的IDE中打開(kāi)既有的匯編工程.
2)替換文件:將既有的芯片固有文件替換為目標(biāo)芯片固有文件,其中包含芯片頭文件、內(nèi)核文件、啟動(dòng)文件、鏈接文件.
3)設(shè)置工程屬性:修改鏈接文件、啟動(dòng)文件等的具體文件路徑.
4)修改基礎(chǔ)構(gòu)件:不同內(nèi)核的基礎(chǔ)構(gòu)件的寄存器基地址和實(shí)現(xiàn)具體功能的寄存器操作步驟不一樣,需要根據(jù)具體情況進(jìn)行修改.
5)編譯工程:在IDE中對(duì)工程進(jìn)行編譯,修改錯(cuò)誤,編譯成功后下載到開(kāi)發(fā)板上運(yùn)行,觀察運(yùn)行結(jié)果.
表6給出了將STM32L431匯編工程移植到Cortex-M0+內(nèi)核的KL36Z64和Cortex-M4F內(nèi)核的MSP432P401上的情況.
表6 不同內(nèi)核MCU間移植
從表6可以看出匯編工程在不同Cortex-M內(nèi)核的MCU間移植時(shí)只需替換內(nèi)核文件、啟動(dòng)文件和鏈接文件,修改說(shuō)明文件、基礎(chǔ)構(gòu)件中的寄存器基地址和user.inc中的宏定義,而應(yīng)用構(gòu)件、軟件構(gòu)件、中斷服務(wù)函數(shù)和主函數(shù)均無(wú)需修改.
在Cortex-M0+內(nèi)核和Cortex-M4F內(nèi)核的MCU上驗(yàn)證匯編工程框架的通用性的運(yùn)行結(jié)果如圖4所示.從圖4中可以看出,使用匯編工程通用框架設(shè)計(jì)方法實(shí)現(xiàn)的匯編工程在不同內(nèi)核的MCU上運(yùn)行良好.
圖4 不同內(nèi)核MCU移植驗(yàn)證
規(guī)范的匯編編程方法、構(gòu)件化的設(shè)計(jì)思想和結(jié)構(gòu)清晰的工程結(jié)構(gòu)可以有效降低匯編編程的學(xué)習(xí)和開(kāi)發(fā)難度,并且能夠有效增加匯編工程的可移植性.本文遵循“從個(gè)別到一般,再?gòu)囊话愕絺€(gè)別”的科學(xué)研究方法,基于Cortex-M4內(nèi)核的STM32L431匯編工程的研究,以ARM Cortex-M系列內(nèi)核MCU為平臺(tái),提出匯編工程通用框架的設(shè)計(jì)方法,并且分別在Cortex-M0+內(nèi)核的KL36Z64和Cortex-M4F內(nèi)核的MSP432P401上進(jìn)行了實(shí)驗(yàn)驗(yàn)證,證明了該工程框架具有可行性和通用性,降低了新內(nèi)核下開(kāi)發(fā)匯編工程的難度,通過(guò)較少的改動(dòng)即可實(shí)現(xiàn)新的匯編工程,增強(qiáng)了ARM匯編的移植性,并且在匯編語(yǔ)言和微機(jī)原理的課程學(xué)習(xí)中具有較高的實(shí)用價(jià)值.