首頁(yè) > 評(píng)測(cè) > 基于PikaScript在MM32平臺(tái)上部署Python開(kāi)發(fā)環(huán)境

基于PikaScript在MM32平臺(tái)上部署Python開(kāi)發(fā)環(huán)境

  
  • 作者:xld0932
  • 來(lái)源:21ic BBS
  • [導(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)行;代碼如下所示:
  1. void MCU_InitClock(void)
  2. {
  3.     /* 使能內(nèi)部高速時(shí)鐘HSI */
  4.     RCC->CR |= RCC_CR_HSION_MASK;
  5.     /* 等待內(nèi)部高速時(shí)鐘HSI穩(wěn)定 */
  6.     while(RCC_CR_HSIRDY_MASK != (RCC->CR & RCC_CR_HSIRDY_MASK));
  7.  
  8.  
  9.     /* 選擇HSI輸出用作系統(tǒng)時(shí)鐘 */
  10.     RCC->CFGR = RCC_CFGR_SW(0u);
  11.     /* 等待系統(tǒng)時(shí)鐘選擇狀態(tài)穩(wěn)定 */
  12.     while(RCC_CFGR_SWS(0u) != (RCC->CFGR & RCC_CFGR_SWS_MASK));
  13.  
  14.  
  15.     /* 復(fù)位除HSI之外的所有時(shí)鐘 */
  16.     RCC->CR = RCC_CR_HSION_MASK;
  17.  
  18.     RCC->CIR = RCC->CIR;    /* 清除中斷標(biāo)志位 */
  19.     RCC->CIR = 0u;          /* 禁卡相應(yīng)的中斷 */
  20.  
  21.  
  22.     /* PWR/DBG時(shí)鐘使能 */
  23.     RCC->APB1ENR |= (1u << 28u);
  24.  
  25.     /* 如果系統(tǒng)時(shí)鐘需要達(dá)到最大頻率120MHz時(shí), 需要將VOS設(shè)置為1.7V */
  26.     PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS_MASK) | PWR_CR1_VOS(3u);
  27.  
  28.  
  29.     /* 使能外部高速時(shí)鐘HSE */
  30.     RCC->CR |= RCC_CR_HSEON_MASK;
  31.     /* 等待外部高速時(shí)鐘HSE穩(wěn)定 */
  32.     while(RCC_CR_HSERDY_MASK != (RCC->CR & RCC_CR_HSERDY_MASK));
  33.  
  34.  
  35.     /* PLL1 = HSE * (MUL + 1) / (DIV + 1)
  36.             = 12MHz * 20 / 2
  37.             = 120MHz
  38.     */
  39.     RCC->PLL1CFGR = RCC_PLL1CFGR_PLL1SRC(1) |   /* 0:HSI作為PLL1時(shí)鐘源, 1:HSE作為PLL1時(shí)鐘源 */
  40.                     RCC_PLL1CFGR_PLL1MUL(19)|   /* PLL1倍頻系數(shù) */
  41.                     RCC_PLL1CFGR_PLL1DIV(1) |   /* PLL1分頻系數(shù) */
  42.                     RCC_PLL1CFGR_PLL1LDS(1) |   /* PLL1鎖定檢測(cè)器精度選擇: 高精度 */
  43.                     RCC_PLL1CFGR_PLL1ICTRL(3);  /* PLL1輸入時(shí)鐘源大于等于8MHz時(shí),推薦設(shè)置值為2'b11
  44.                                                    PLL1輸入時(shí)鐘源小于    8MHz時(shí),推薦設(shè)置值為2'b01 */
  45.  
  46.  
  47.     /* 使能PLL1 */
  48.     RCC->CR |= RCC_CR_PLL1ON_MASK;
  49.     /* 等待PLL1穩(wěn)定 */
  50.     while((RCC->CR & RCC_CR_PLL1RDY_MASK) == 0);
  51.  
  52.  
  53.     /* FLASH時(shí)鐘使能 */
  54.     RCC->AHB1ENR |= (1u << 13u);
  55.     FLASH->ACR    = FLASH_ACR_LATENCY(4u) |     /* 0 : 零個(gè)等待狀態(tài), 當(dāng) 0MHz < SYSCLK <= 24MHz
  56.                                                    1 : 一個(gè)等待狀態(tài), 當(dāng)24MHz < SYSCLK <= 48MHz
  57.                                                    2 : 二個(gè)等待狀態(tài), 當(dāng)48MHz < SYSCLK <= 72MHz
  58.                                                    3 : 三個(gè)等待狀態(tài), 當(dāng)72MHz < SYSCLK <= 96MHz
  59.                                                    4 : 四個(gè)等待狀態(tài), 當(dāng)96MHz < SYSCLK <= 120MHz */
  60.                     FLASH_ACR_PRFTBE_MASK;      /* 預(yù)取緩沖區(qū)開(kāi)啟 */
  61.  
  62.  
  63.     /* 時(shí)鐘配置 */
  64.     RCC->CFGR = RCC_CFGR_HPRE(0)    |           /* AHB 預(yù)分頻系數(shù), HCLK
  65.                                                    0xxx : SYSCLK  不分頻
  66.                                                    1000 : SYSCLK   2分頻
  67.                                                    1001 : SYSCLK   4分頻
  68.                                                    1010 : SYSCLK   8分頻
  69.                                                    1011 : SYSCLK  16分頻
  70.                                                    1100 : SYSCLK  64分頻
  71.                                                    1101 : SYSCLK 128分頻
  72.                                                    1110 : SYSCLK 256分頻
  73.                                                    1111 : SYSCLK 512分頻 */
  74.                 RCC_CFGR_PPRE1(0x4) |           /* APB1預(yù)分頻系數(shù), PCLK1
  75.                                                    0xx : HCLK 不分頻
  76.                                                    100 : HCLK  2分頻
  77.                                                    101 : HCLK  4分頻
  78.                                                    110 : HCLK  8分頻
  79.                                                    111 : HCLK 16分頻 */
  80.                 RCC_CFGR_PPRE2(0x4) |           /* APB2預(yù)分頻系數(shù), PCLK2
  81.                                                    0xx : HCLK 不分頻
  82.                                                    100 : HCLK  2分頻
  83.                                                    101 : HCLK  4分頻
  84.                                                    110 : HCLK  8分頻
  85.                                                    111 : HCLK 16分頻 */
  86.                 RCC_CFGR_MCO(7);                /* MCO輸出時(shí)鐘源選擇
  87.                                                    000x : 沒(méi)有時(shí)鐘輸出
  88.                                                    0010 : LSI時(shí)鐘輸出
  89.                                                    0011 : LSE時(shí)鐘輸出
  90.                                                    0100 : SYSCLK時(shí)鐘輸出
  91.                                                    0101 : HSI時(shí)鐘輸出
  92.                                                    0110 : HSE時(shí)鐘輸出
  93.                                                    0111 : PLL1時(shí)鐘輸出
  94.                                                    1000 : PLL2時(shí)鐘輸出 */
  95.  
  96.  
  97.     /* ADC1預(yù)分頻(頻率范圍15MHz - 48MHz)
  98.                  = PCLK2 / (PRE + 2),要求PRE為偶數(shù),使占空比為50%
  99.                  = 60MHz / ( 2  + 2)
  100.                  = 15MHz */
  101.     RCC_SetADCClockDiv(ADC1, 2);
  102.  
  103.     /* ADC1 calibration時(shí)鐘分頻(頻率范圍187.5kHz - 1.5MHz)
  104.                                = PCLK2 / (PRECAL + 2),要求PRECAL為偶數(shù),使占空比為50%
  105.                                = 60MHz / (58     + 2)
  106.                                = 1MHz */
  107.     RCC_SetADCClockDiv(ADC1, 58);
  108.  
  109.  
  110.     /* 選擇PLL輸出用作系統(tǒng)時(shí)鐘 */
  111.     RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW_MASK) | RCC_CFGR_SW(2);
  112.     /* 等待系統(tǒng)時(shí)鐘選擇狀態(tài)穩(wěn)定 */
  113.     while((RCC->CFGR & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS(2));
  114. }
  115.  
  116. void MCU_InitUART1(void)
  117. {
  118.     GPIO_Init_Type GPIO_InitStructure;
  119.     UART_Init_Type UART_InitStructure;
  120.  
  121.     /* 先配置GPIO, 再配置UART參數(shù), 否則UART ENABLE后會(huì)有一個(gè)0xFF的異常字節(jié) */
  122.     RCC_EnableAHB1Periphs(RCC_AHB1_PERIPH_GPIOB, true);
  123.  
  124.     GPIO_PinAFConf(GPIOB, GPIO_PIN_6, GPIO_AF_7);   /* PB6 <-> UART1_TX */
  125.     GPIO_PinAFConf(GPIOB, GPIO_PIN_7, GPIO_AF_7);   /* PB7 <-> UART1_RX */
  126.  
  127.     GPIO_InitStructure.Pins     = GPIO_PIN_6;
  128.     GPIO_InitStructure.PinMode  = GPIO_PinMode_AF_PushPull;
  129.     GPIO_InitStructure.Speed    = GPIO_Speed_50MHz;
  130.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  131.  
  132.     GPIO_InitStructure.Pins     = GPIO_PIN_7;
  133.     GPIO_InitStructure.PinMode  = GPIO_PinMode_In_Floating;
  134.     GPIO_InitStructure.Speed    = GPIO_Speed_50MHz;
  135.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  136.  
  137.     RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART1, true);
  138.  
  139.     UART_InitStructure.ClockFreqHz   = CLOCK_APB2_FREQ;
  140.     UART_InitStructure.BaudRate      = 115200;
  141.     UART_InitStructure.WordLength    = UART_WordLength_8b;
  142.     UART_InitStructure.StopBits      = UART_StopBits_1;
  143.     UART_InitStructure.Parity        = UART_Parity_None;
  144.     UART_InitStructure.XferMode      = UART_XferMode_RxTx;
  145.     UART_InitStructure.HwFlowControl = UART_HwFlowControl_None;
  146.     UART_Init(UART1, &UART_InitStructure);
  147.  
  148.     UART_Enable(UART1,   true);
  149. }
  150.  
  151. int fputc(int ch, FILE *f)
  152. {
  153.     UART_PutData(UART1, (uint8_t)ch);
  154.     while((UART_GetStatus(UART1) & UART_STATUS_TX_DONE) == 0);
  155.  
  156.     return ch;
  157. }
  158.  
  159. int fgetc(FILE *f)
  160. {
  161.     while((UART_GetStatus(UART1) & UART_STATUS_RX_DONE) == 0u);
  162.     return UART_GetData(UART1);
  163. }
  164.  
  165. void InitSystem(void)
  166. {
  167.     MCU_InitClock();
  168.  
  169.     MCU_InitUART1();
  170. }
  171.  
  172. int main(void)
  173. {
  174.     InitSystem();
  175.  
  176.     printf("\r\n");
  177.     printf("\r\nPikaScript PLUS-F5270(MM32F5277E9P) %s %s", __DATE__, __TIME__);
  178.     printf("\r\n");
  179.     printf("\r\n------------------------------------------------------------------");
  180.     printf("\r\n|                                                                |");
  181.     printf("\r\n|     ____   _   __            _____              _          __  |");
  182.     printf("\r\n|    / __ \\ (_) / /__ ____ _  / ___/ _____ _____ (_) ____   / /_ |");
  183.     printf("\r\n|   / /_/ // / / //_// __ `/  \\__ \\ / ___// ___// / / __ \\ / __/ |");
  184.     printf("\r\n|  / ____// / / ,<  / /_/ /  ___/ // /__ / /   / / / /_/ // /_   |");
  185.     printf("\r\n| /_/    /_/ /_/|_| \\__,_/  /____/ \\___//_/   /_/ / .___/ \\__/   |");
  186.     printf("\r\n|                                                /_/             |");
  187.     printf("\r\n|          PikaScript - An Ultra Lightweight Python Engine       |");
  188.     printf("\r\n|                                                                |");
  189.     printf("\r\n|           [ https://github.com/pikastech/pikascript ]          |");
  190.     printf("\r\n|           [  https://gitee.com/lyon1998/pikascript  ]          |");
  191.     printf("\r\n|                                                                |");
  192.     printf("\r\n------------------------------------------------------------------");
  193.     printf("\r\n");
  194.  
  195.     PikaObj *pikaMain = pikaScriptInit();
  196.     goto main_loop;
  197.  
  198. main_loop:
  199.     pikaScriptShell(pikaMain);
  200.  
  201.     /* after exit() from pika shell */
  202.     NVIC_SystemReset();
  203. }
  204.  
  205. char __platform_getchar(void)
  206. {
  207.     return getchar();
  208. }
復(fù)制代碼

 

編譯程序無(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)放投稿

熱門(mén)標(biāo)簽
項(xiàng)目外包 more+