首頁(yè) > 評(píng)測(cè) > 基于PikaScript在MM32平臺(tái)上部署Python開(kāi)發(fā)環(huán)境
基于PikaScript在MM32平臺(tái)上部署Python開(kāi)發(fā)環(huán)境
- [導(dǎo)讀]
- MicroPython是Python3的精簡(jiǎn)實(shí)現(xiàn),包括Python標(biāo)準(zhǔn)庫(kù)的一小部分,經(jīng)過(guò)優(yōu)化可在微控制器和受限的環(huán)境中運(yùn)行,在官方提供了相應(yīng)的
痛點(diǎn)
MicroPython是Python3的精簡(jiǎn)實(shí)現(xiàn),包括Python標(biāo)準(zhǔn)庫(kù)的一小部分,經(jīng)過(guò)優(yōu)化可在微控制器和受限的環(huán)境中運(yùn)行,在官方提供了相應(yīng)的開(kāi)發(fā)板,但最低的配置都需要32KB的SRAM空間和4KB的STACK空間,對(duì)MCU的性能也有絕對(duì)的要求,基于STM32F103的MicroPython開(kāi)發(fā)板在某寶上幾乎沒(méi)有,最少也得STM32F4及以上系列的才玩得動(dòng),那我想在資源有限的MCU上運(yùn)行Python程序,難道就不配嗎?
想要在一個(gè)MCU上運(yùn)行Python程序,一般步驟如下:首先得需要一個(gè)Linux環(huán)境(大多數(shù)人選擇通過(guò)虛擬機(jī)來(lái)安裝Linux)、然后需要更新相應(yīng)的命令工具并下載交叉編譯工具和編譯器(交叉編譯器選擇gcc-arm-none-eabi、編譯器選擇gcc)、接著需要下載MicroPython源代碼,生成固件程序(建議選擇官方已經(jīng)支持的開(kāi)發(fā)板,否則需要自行實(shí)現(xiàn)和移植)、最后通過(guò)燒錄工具或者使用USB的DFU模式燒錄程序到MCU、至此才可以開(kāi)始使用Python編程來(lái)實(shí)現(xiàn)應(yīng)用功能;期間一步都不能錯(cuò)哦……但對(duì)于做MCU嵌入式開(kāi)發(fā)的工程師來(lái)說(shuō),我們常用的都是KEIL、IAR這些IDE集成開(kāi)發(fā)環(huán)境,難道一定得按照上面步驟一步步來(lái)嗎,就不能使用KEIL來(lái)開(kāi)發(fā)、調(diào)試了?
PiKaScript應(yīng)運(yùn)而生
PiKaScript可以為資源受限的MCU提供極易部署和拓展的Python腳本支持。PiKaScript不需要操作系統(tǒng)和文件系統(tǒng),支持裸機(jī)運(yùn)行,最低可運(yùn)行在RAM≥4KB,F(xiàn)LASH≥32KB的MCU中,而且還支持KEIL、IAR等IDE集成開(kāi)發(fā)環(huán)境。此外PiKaScript是完全開(kāi)源的(https://github.com/pikasTech/pikascript),采樣的是MIT協(xié)議,允許修改和商用,但是要注意保留原作者的署名即可。
部署PikaScript到MM32平臺(tái)
PikaScript可以在所有支持libc的裸機(jī)和操作系統(tǒng)上運(yùn)行,只需要編譯器能夠支持C99標(biāo)準(zhǔn)即可。當(dāng)前PikaScript僅支持32位和64位內(nèi)核的MCU,暫不支持8位內(nèi)核的MCU;考慮到拓展模塊的資源占用情況,如果是ARM內(nèi)核的MCU推薦最低應(yīng)該配備64KB FLASH和8KB SRAM,如果是RISC內(nèi)核的MCU推薦最低應(yīng)該配備128KB FLASH和8KB SRAM。
- 準(zhǔn)備模板工程
我當(dāng)前使用的IDE集成開(kāi)發(fā)環(huán)境是KEIL MDK,在部署PikaScript到MM32之前,我們需要新建一個(gè)基于MM32 MCU的模板工程,這個(gè)模板工程只需要實(shí)現(xiàn)printf功能的串口初始化即可,重載fputc和fgetc這兩個(gè)函數(shù),為后面實(shí)現(xiàn)功能做準(zhǔn)備,至此我們就完成了部署PikaScript的第一步。
- 獲取PikaScript源碼和工具集
我在模板工程中的Source文件夾中新建立一個(gè)PikaScript文件夾作為PikaScript部署路徑;然后我們需要到GIT上去下載PikaScript包管理器:pikaPackage.exe,將這個(gè)包管理器存放在PikaScript文件夾下,通過(guò)這個(gè)包管理器我們可以輕松地拉取指定版本的源碼和模塊;接下來(lái)我們?cè)赑ikaScript文件夾下新建一個(gè)requestment.txt文件,然后寫(xiě)入如下內(nèi)容:
pikascript-core==v1.8.6
PikaStdLib==v1.8.6
如上內(nèi)容表示使用1.8.6版本的pikascript解釋器內(nèi)核和1.8.6版本的標(biāo)準(zhǔn)庫(kù),解釋器內(nèi)核和標(biāo)準(zhǔn)庫(kù)是必選項(xiàng),且這兩個(gè)版本號(hào)需要保持一致,而其它的模塊則是可以有選擇性的添加;在初始部署時(shí),盡可能的只添加解釋器內(nèi)核和標(biāo)準(zhǔn)庫(kù)即可,這樣可以遇到兼容性的問(wèn)題;版本號(hào)可以通過(guò)http://pikascript.com/這個(gè)網(wǎng)址來(lái)查看,當(dāng)然你也可以通過(guò)這個(gè)網(wǎng)址來(lái)自動(dòng)生成工程……
現(xiàn)在PikaScript文件夾下就有了pikaPackage.exe和requestment.txt這兩個(gè)文件,雙擊運(yùn)行pikaPackage.exe就可以拉取requestment.txt文件中指定版本的源碼和模塊了。拉取過(guò)程如下所示:

在源碼和模塊拉取結(jié)果后,PikaScript文件夾就多了不少文件,如下圖所示:

其中pikascript-api文件夾下存放的是模塊API相關(guān)文件,在預(yù)編譯前這個(gè)文件夾是空的,pikascript-core文件夾下存放的是內(nèi)核相關(guān)文件,pikascript-lib文件夾下存放的是模塊庫(kù),rust-msc-latest-win10.exe是預(yù)編譯器。然后我們?cè)赑ikaScript文件夾新建一個(gè)main.py文件,然后寫(xiě)入:
import PikaStdLib
print('Hello PikaScript!')
其中import PikaStdLib表示導(dǎo)入標(biāo)準(zhǔn)庫(kù),而且標(biāo)準(zhǔn)庫(kù)是必需要導(dǎo)入的;而print('Hello PikaScript!')則是用來(lái)測(cè)試pikascript是否正常啟動(dòng)。
- 預(yù)編譯模塊
pikascript預(yù)編譯器可以把python模塊預(yù)編譯為.c和.h文件;接下來(lái)我們運(yùn)行PikaScript文件夾下的rust-msc-latest-win10.exe預(yù)編譯器,它會(huì)將main.py和導(dǎo)入的模塊預(yù)編譯為pikascript的API文件,預(yù)編譯后的文件存放在pikascript-api文件夾下;我們打開(kāi)pikascript-api文件夾會(huì)發(fā)現(xiàn)多了很多.c和.h的文件,這就說(shuō)明預(yù)編譯成功運(yùn)行了。
- 添加源碼
我們使用KEIL軟件模板工程,在模板工程中添加3個(gè)Group,分別命名為:pikascript-api、pikascript-core、pikascript-lib,這也是PikaScript文件夾下的3個(gè)文件夾名,如下圖所示:

然后將這3個(gè)文件夾下的所有.c源碼文件分別添加到上面的3個(gè)Group當(dāng)中,如下圖所示:



然后設(shè)置KEIL編譯器的Include Path,如下圖所示:

- 調(diào)整堆棧大小
我們可以在啟動(dòng)文件中修改堆和棧的大小,也可以通過(guò)修改建議SCF文件來(lái)修改堆和棧的大小;對(duì)于PikaScript的部署建議分配4KB的?臻g和16KB的堆空間;如下圖所示:

- 啟動(dòng)PikaScript
在main.c中添加PikaScript的頭文件和啟動(dòng)代碼,在代碼中通過(guò)重載fgetc函數(shù)結(jié)合libc實(shí)現(xiàn)了getchar函數(shù)功能,再通過(guò)覆用pikascript中讀取用戶輸入字節(jié)的底層接口函數(shù)__platform_getchar(),加上啟動(dòng)PickScript Shell后,即實(shí)現(xiàn)了程序代碼的交互式運(yùn)行;代碼如下所示:
- void MCU_InitClock(void)
- {
- /* 使能內(nèi)部高速時(shí)鐘HSI */
- RCC->CR |= RCC_CR_HSION_MASK;
- /* 等待內(nèi)部高速時(shí)鐘HSI穩(wěn)定 */
- while(RCC_CR_HSIRDY_MASK != (RCC->CR & RCC_CR_HSIRDY_MASK));
- /* 選擇HSI輸出用作系統(tǒng)時(shí)鐘 */
- RCC->CFGR = RCC_CFGR_SW(0u);
- /* 等待系統(tǒng)時(shí)鐘選擇狀態(tài)穩(wěn)定 */
- while(RCC_CFGR_SWS(0u) != (RCC->CFGR & RCC_CFGR_SWS_MASK));
- /* 復(fù)位除HSI之外的所有時(shí)鐘 */
- RCC->CR = RCC_CR_HSION_MASK;
- RCC->CIR = RCC->CIR; /* 清除中斷標(biāo)志位 */
- RCC->CIR = 0u; /* 禁卡相應(yīng)的中斷 */
- /* PWR/DBG時(shí)鐘使能 */
- RCC->APB1ENR |= (1u << 28u);
- /* 如果系統(tǒng)時(shí)鐘需要達(dá)到最大頻率120MHz時(shí), 需要將VOS設(shè)置為1.7V */
- PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS_MASK) | PWR_CR1_VOS(3u);
- /* 使能外部高速時(shí)鐘HSE */
- RCC->CR |= RCC_CR_HSEON_MASK;
- /* 等待外部高速時(shí)鐘HSE穩(wěn)定 */
- while(RCC_CR_HSERDY_MASK != (RCC->CR & RCC_CR_HSERDY_MASK));
- /* PLL1 = HSE * (MUL + 1) / (DIV + 1)
- = 12MHz * 20 / 2
- = 120MHz
- */
- RCC->PLL1CFGR = RCC_PLL1CFGR_PLL1SRC(1) | /* 0:HSI作為PLL1時(shí)鐘源, 1:HSE作為PLL1時(shí)鐘源 */
- RCC_PLL1CFGR_PLL1MUL(19)| /* PLL1倍頻系數(shù) */
- RCC_PLL1CFGR_PLL1DIV(1) | /* PLL1分頻系數(shù) */
- RCC_PLL1CFGR_PLL1LDS(1) | /* PLL1鎖定檢測(cè)器精度選擇: 高精度 */
- RCC_PLL1CFGR_PLL1ICTRL(3); /* PLL1輸入時(shí)鐘源大于等于8MHz時(shí),推薦設(shè)置值為2'b11
- PLL1輸入時(shí)鐘源小于 8MHz時(shí),推薦設(shè)置值為2'b01 */
- /* 使能PLL1 */
- RCC->CR |= RCC_CR_PLL1ON_MASK;
- /* 等待PLL1穩(wěn)定 */
- while((RCC->CR & RCC_CR_PLL1RDY_MASK) == 0);
- /* FLASH時(shí)鐘使能 */
- RCC->AHB1ENR |= (1u << 13u);
- FLASH->ACR = FLASH_ACR_LATENCY(4u) | /* 0 : 零個(gè)等待狀態(tài), 當(dāng) 0MHz < SYSCLK <= 24MHz
- 1 : 一個(gè)等待狀態(tài), 當(dāng)24MHz < SYSCLK <= 48MHz
- 2 : 二個(gè)等待狀態(tài), 當(dāng)48MHz < SYSCLK <= 72MHz
- 3 : 三個(gè)等待狀態(tài), 當(dāng)72MHz < SYSCLK <= 96MHz
- 4 : 四個(gè)等待狀態(tài), 當(dāng)96MHz < SYSCLK <= 120MHz */
- FLASH_ACR_PRFTBE_MASK; /* 預(yù)取緩沖區(qū)開(kāi)啟 */
- /* 時(shí)鐘配置 */
- RCC->CFGR = RCC_CFGR_HPRE(0) | /* AHB 預(yù)分頻系數(shù), HCLK
- 0xxx : SYSCLK 不分頻
- 1000 : SYSCLK 2分頻
- 1001 : SYSCLK 4分頻
- 1010 : SYSCLK 8分頻
- 1011 : SYSCLK 16分頻
- 1100 : SYSCLK 64分頻
- 1101 : SYSCLK 128分頻
- 1110 : SYSCLK 256分頻
- 1111 : SYSCLK 512分頻 */
- RCC_CFGR_PPRE1(0x4) | /* APB1預(yù)分頻系數(shù), PCLK1
- 0xx : HCLK 不分頻
- 100 : HCLK 2分頻
- 101 : HCLK 4分頻
- 110 : HCLK 8分頻
- 111 : HCLK 16分頻 */
- RCC_CFGR_PPRE2(0x4) | /* APB2預(yù)分頻系數(shù), PCLK2
- 0xx : HCLK 不分頻
- 100 : HCLK 2分頻
- 101 : HCLK 4分頻
- 110 : HCLK 8分頻
- 111 : HCLK 16分頻 */
- RCC_CFGR_MCO(7); /* MCO輸出時(shí)鐘源選擇
- 000x : 沒(méi)有時(shí)鐘輸出
- 0010 : LSI時(shí)鐘輸出
- 0011 : LSE時(shí)鐘輸出
- 0100 : SYSCLK時(shí)鐘輸出
- 0101 : HSI時(shí)鐘輸出
- 0110 : HSE時(shí)鐘輸出
- 0111 : PLL1時(shí)鐘輸出
- 1000 : PLL2時(shí)鐘輸出 */
- /* ADC1預(yù)分頻(頻率范圍15MHz - 48MHz)
- = PCLK2 / (PRE + 2),要求PRE為偶數(shù),使占空比為50%
- = 60MHz / ( 2 + 2)
- = 15MHz */
- RCC_SetADCClockDiv(ADC1, 2);
- /* ADC1 calibration時(shí)鐘分頻(頻率范圍187.5kHz - 1.5MHz)
- = PCLK2 / (PRECAL + 2),要求PRECAL為偶數(shù),使占空比為50%
- = 60MHz / (58 + 2)
- = 1MHz */
- RCC_SetADCClockDiv(ADC1, 58);
- /* 選擇PLL輸出用作系統(tǒng)時(shí)鐘 */
- RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW_MASK) | RCC_CFGR_SW(2);
- /* 等待系統(tǒng)時(shí)鐘選擇狀態(tài)穩(wěn)定 */
- while((RCC->CFGR & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS(2));
- }
- void MCU_InitUART1(void)
- {
- GPIO_Init_Type GPIO_InitStructure;
- UART_Init_Type UART_InitStructure;
- /* 先配置GPIO, 再配置UART參數(shù), 否則UART ENABLE后會(huì)有一個(gè)0xFF的異常字節(jié) */
- RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
- GPIO_PinAFConf(GPIOB, GPIO_PIN_6, GPIO_AF_7); /* PB6 <-> UART1_TX */
- GPIO_PinAFConf(GPIOB, GPIO_PIN_7, GPIO_AF_7); /* PB7 <-> UART1_RX */
- GPIO_InitStructure.Pins = GPIO_PIN_6;
- GPIO_InitStructure.PinMode = GPIO_PinMode_AF_PushPull;
- GPIO_InitStructure.Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_InitStructure.Pins = GPIO_PIN_7;
- GPIO_InitStructure.PinMode = GPIO_PinMode_In_Floating;
- GPIO_InitStructure.Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART1, true);
- UART_InitStructure.ClockFreqHz = CLOCK_APB2_FREQ;
- UART_InitStructure.BaudRate = 115200;
- UART_InitStructure.WordLength = UART_WordLength_8b;
- UART_InitStructure.StopBits = UART_StopBits_1;
- UART_InitStructure.Parity = UART_Parity_None;
- UART_InitStructure.XferMode = UART_XferMode_RxTx;
- UART_InitStructure.HwFlowControl = UART_HwFlowControl_None;
- UART_Init(UART1, &UART_InitStructure);
- UART_Enable(UART1, true);
- }
- int fputc(int ch, FILE *f)
- {
- UART_PutData(UART1, (uint8_t)ch);
- while((UART_GetStatus(UART1) & UART_STATUS_TX_DONE) == 0);
- return ch;
- }
- int fgetc(FILE *f)
- {
- while((UART_GetStatus(UART1) & UART_STATUS_RX_DONE) == 0u);
- return UART_GetData(UART1);
- }
- void InitSystem(void)
- {
- MCU_InitClock();
- MCU_InitUART1();
- }
- int main(void)
- {
- InitSystem();
- printf("\r\n");
- printf("\r\nPikaScript PLUS-F5270(MM32F5277E9P) %s %s", __DATE__, __TIME__);
- printf("\r\n");
- printf("\r\n------------------------------------------------------------------");
- printf("\r\n| |");
- printf("\r\n| ____ _ __ _____ _ __ |");
- printf("\r\n| / __ \\ (_) / /__ ____ _ / ___/ _____ _____ (_) ____ / /_ |");
- printf("\r\n| / /_/ // / / //_// __ `/ \\__ \\ / ___// ___// / / __ \\ / __/ |");
- printf("\r\n| / ____// / / ,< / /_/ / ___/ // /__ / / / / / /_/ // /_ |");
- printf("\r\n| /_/ /_/ /_/|_| \\__,_/ /____/ \\___//_/ /_/ / .___/ \\__/ |");
- printf("\r\n| /_/ |");
- printf("\r\n| PikaScript - An Ultra Lightweight Python Engine |");
- printf("\r\n| |");
- printf("\r\n| [ https://github.com/pikastech/pikascript ] |");
- printf("\r\n| [ https://gitee.com/lyon1998/pikascript ] |");
- printf("\r\n| |");
- printf("\r\n------------------------------------------------------------------");
- printf("\r\n");
- PikaObj *pikaMain = pikaScriptInit();
- goto main_loop;
- main_loop:
- pikaScriptShell(pikaMain);
- /* after exit() from pika shell */
- NVIC_SystemReset();
- }
- char __platform_getchar(void)
- {
- return getchar();
- }
編譯程序無(wú)誤后,我們將代碼下載到MM32芯片,將MM32的UART通過(guò)USB轉(zhuǎn)TTL工具連接到電腦,打開(kāi)MobaXterm終端軟件進(jìn)行調(diào)試,芯片上電啟動(dòng)后如下圖所示:

- 在線運(yùn)行Python腳本程序
在MobaXterm終端軟件中我們輸入如下圖所示代碼后,敲入回車鍵后Python代碼就自動(dòng)解析執(zhí)行了,并輸出相對(duì)應(yīng)的結(jié)果:

如果出現(xiàn)如下圖所示的error提示,請(qǐng)檢查一下MM32對(duì)于堆棧大小的配置情況,適當(dāng)?shù)恼{(diào)整一下就可以了:

后續(xù)
后續(xù)將繼續(xù)來(lái)實(shí)現(xiàn)和分享通過(guò)串口來(lái)下載Python腳本并運(yùn)行Python程序的功能、以及基于一塊開(kāi)發(fā)板來(lái)實(shí)現(xiàn)對(duì)模塊的開(kāi)發(fā)、調(diào)用、應(yīng)用的全方位實(shí)現(xiàn);通過(guò)對(duì)Python的支持,讓更多的精力投入到應(yīng)用功能的開(kāi)發(fā)中去,同時(shí)也讓資源相對(duì)不富裕的MCU有了施展的平臺(tái)。
附件
模板工程:
PikaScript.zip (665.18 KB)

PikaScript模板工程:
PikaScript_Template.zip (8.2 MB)

-
- 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!
網(wǎng)友評(píng)論
- 聯(lián)系人:巧克力娃娃
- 郵箱:board@21ic.com
- 我要投稿
-
歡迎入駐,開(kāi)放投稿
行業(yè)新聞
熱門(mén)標(biāo)簽
論壇活動(dòng)
more+
公開(kāi)課
more+
項(xiàng)目外包
more+
- NRF52810藍(lán)牙數(shù)字耳機(jī)找人定制
預(yù)算:¥30005天前
- 125KW模塊式PCS軟硬件外包開(kāi)發(fā)
預(yù)算:¥1100000015小時(shí)前
- 12V汽車啟動(dòng)電源項(xiàng)目BMS設(shè)計(jì)
預(yù)算:¥50000023小時(shí)前
- 數(shù)據(jù)可視化軟件 開(kāi)發(fā)
預(yù)算:¥5000023小時(shí)前
- PLC項(xiàng)目調(diào)試修改
預(yù)算:¥100001天前
- 起動(dòng)電機(jī)控制器開(kāi)發(fā)
預(yù)算:¥1100001天前