聚豐項(xiàng)目 > 自研Python解釋器
PikaScript是一個(gè)完全重寫的超輕量級(jí)python引擎,具有完整的解釋器,字節(jié)碼和虛擬機(jī)架構(gòu),可以在少于4KB的RAM下運(yùn)行,用于小資源嵌入式系統(tǒng)。相比同類產(chǎn)品,如MicroPython,LuaOS等,資源占用減少85%以上。 入選2021年度 Gitee最有價(jià)值開(kāi)源項(xiàng)目,加入RT-Thread嵌入式實(shí)時(shí)操作系統(tǒng)編程語(yǔ)言類軟件包。 在CH32V103 RISC-V開(kāi)發(fā)板上完成了PikaScript的部署,并為CH32V103提交了PikaSciprt標(biāo)準(zhǔn)BSP和驅(qū)動(dòng)模塊包,并完成了交互式運(yùn)行的驅(qū)動(dòng)。
lyon1998
lyon1998
團(tuán)隊(duì)成員
lyon1998 開(kāi)發(fā)者
RT-Thread使用情況概述:
整個(gè)方案涉及的技術(shù)棧有:RT-Thread線程和定時(shí)器, 編譯原理、字節(jié)碼設(shè)計(jì)、虛擬機(jī)設(shè)計(jì)、PikaScript部署技術(shù)和驅(qū)動(dòng)模塊開(kāi)發(fā)技術(shù)等等。通過(guò)這個(gè)作品,擴(kuò)充了PikaScript的BSP支持列表,驗(yàn)證了PikaScript和rt-thread的兼容性,驗(yàn)證了PikaScript在小容量(64Kb)RISC-V架構(gòu)的部署能力和兼容性。
內(nèi)核部分:使用了線程、定時(shí)器 。
軟件包:
PikaScript軟件包
硬件使用了RTT大賽提供的CH32V103開(kāi)發(fā)板,使用了板上的LED資源用于指示腳本運(yùn)行狀態(tài),為GPIO硬件開(kāi)發(fā)了Python腳本模塊,用于測(cè)試腳本驅(qū)動(dòng)拓展功能。
0.摘要
PikaScript是一個(gè)完全重寫的超輕量級(jí)python引擎,具有完整的解釋器,字節(jié)碼和虛擬機(jī)架構(gòu),可以在少于4KB的RAM下運(yùn)行,用于小資源嵌入式系統(tǒng)。相比同類產(chǎn)品,如MicroPython,LuaOS等,資源占用減少85%以上。
入選2021年度 Gitee最有價(jià)值開(kāi)源項(xiàng)目,加入RT-Thread嵌入式實(shí)時(shí)操作系統(tǒng)編程語(yǔ)言類軟件包。
本項(xiàng)目在CH32V103 RISC-V開(kāi)發(fā)板上完成了PikaScript的部署,為CH32V103提交了PikaSciprt標(biāo)準(zhǔn)BSP和驅(qū)動(dòng)模塊包,并完成了交互式運(yùn)行的驅(qū)動(dòng)。
1.方案選型——CH32V103運(yùn)行Python腳本,并不好辦
首先我們需要選擇一個(gè)能夠在CH32上運(yùn)行的嵌入式Python解釋器。
能夠在flash為64Kb的RISC-V MCU上部署Python解釋器,需要有極小的編譯體積,還不能依賴于ARM架構(gòu)的獨(dú)享技術(shù)。
首先排除通用Python解釋器CPython,不說(shuō)CPython需要依賴linux,單是體積就可以排除。
其次在嵌入式領(lǐng)域大火的MicroPython技術(shù)是有可能選用的備選項(xiàng),但是MicroPython在ARM平臺(tái)需要最少128Kb的體積,而RISC-V平臺(tái)的GCC編譯器優(yōu)化成熟度不如ARM平臺(tái),所以編譯體積只會(huì)更大不會(huì)更小,所以MicroPython不能在本次的CH32V103平臺(tái)部署。
好了,不賣關(guān)子了,能夠在CH32V103平臺(tái)部署的Python解釋器,只有我目前在開(kāi)發(fā)的PikaScript超輕量級(jí)Python解釋器,(如果還有其他方案,請(qǐng)批評(píng)指正,我麻溜修改)。雖然相對(duì)于MicroPython,PikaScript沒(méi)有那么完整的標(biāo)準(zhǔn)庫(kù)支持,但基本的運(yùn)行時(shí)對(duì)象、控制流、交互式運(yùn)行都是可以實(shí)現(xiàn)的,且PikaScript的跨平臺(tái)能力非常好,在極限的依賴管理策略下,PikaScript只依賴LibC,在任何平臺(tái)都幾乎沒(méi)有依賴缺失問(wèn)題,或許還能夠運(yùn)行在FPGA軟核中(理論上可行,未驗(yàn)證)。
另外感謝Gitee提供的開(kāi)源平臺(tái),PikaScript剛剛被Gitee評(píng)委大佬們選入GVP——最有價(jià)值開(kāi)源項(xiàng)目,所以如果你現(xiàn)在打開(kāi)Gitee首頁(yè),大概率可以看到PikaScript的金色牌牌。
PikaScript還入選了rt-thread軟件包,rt-thread真的是非常有活力的開(kāi)源社區(qū)
PikaScript嚴(yán)苛的依賴管理策略,使得部署非常輕松,這是跨平臺(tái),易部署的特點(diǎn)。但是單純的易部署并沒(méi)有什么用,如果難以拓展功能,就只是一個(gè)花瓶而已。我們知道在MCU開(kāi)發(fā)領(lǐng)域,一直是C語(yǔ)言的天下,C語(yǔ)言的生態(tài)占據(jù)MCU開(kāi)發(fā)的80%以上,大部分MCU都有廠家提供的C語(yǔ)言開(kāi)發(fā)套件,因此MCU平臺(tái)的Python解釋器,最重要的拓展手段,就是綁定C語(yǔ)言的原生庫(kù),將C語(yǔ)言庫(kù)綁定為Python模塊,這通常被稱為Python的C模塊。
為MicroPython綁定C語(yǔ)言模塊與通用的CPython類似,需要將C庫(kù)編譯為靜態(tài)庫(kù),再進(jìn)行鏈接,鏈接時(shí)需要手動(dòng)注冊(cè)許多全局表,且制作C模塊的過(guò)程中需要使用大量linux平臺(tái)獨(dú)有的工具,這對(duì)于以Windows平臺(tái)開(kāi)發(fā)為主的MCU工程師來(lái)說(shuō),門檻很高。
而PikaScript可以在MCU工程師熟悉的Windos平臺(tái)完成C模塊的開(kāi)發(fā),通過(guò)自研的模塊預(yù)編譯器,能夠自動(dòng)完成模塊的注冊(cè)工作,C模塊的開(kāi)發(fā)者需要提供的僅僅是一個(gè)用Python寫成的模塊的調(diào)用API而已,預(yù)編譯器會(huì)自動(dòng)將這個(gè)Python文件預(yù)編譯為C文件,完成模塊的鏈接和注冊(cè)。而只要使用正確的命名,原生的C的函數(shù)就能夠被自動(dòng)注冊(cè)進(jìn)模塊中,供解釋器調(diào)用,也不需要編譯靜態(tài)庫(kù)。
讓PikaScript在CH32V103跑起來(lái),意思也就是開(kāi)發(fā)一個(gè)能在CH32V103運(yùn)行的PikaScript固件。
我們先看一下一個(gè)PikaScript固件有哪些部分。
在圖中標(biāo)注黃色的部分是我們需要制作的,而綠色部分是跨平臺(tái)的,我們只需要拉取源碼進(jìn)行編譯即可,不需要修改。
從下往上看,首先是需要一份PikaScript的BSP,BSP也就是板級(jí)支持包,這通常只要將廠商提供的MCU的標(biāo)準(zhǔn)庫(kù)稍加整理即可獲得。然后是PikaScript的啟動(dòng)器,這包含了固件入口main.c,以及基本的設(shè)備初始化代碼,包括對(duì)printf的支持。
有了BSP和啟動(dòng)器,就已經(jīng)可以運(yùn)行PikaScript的固件了,只不過(guò)還只能使用PikaScript提供的標(biāo)準(zhǔn)庫(kù)功能和Python的基本語(yǔ)法,還不能使用MCU上搭載的外設(shè)資源。
為了使用CH32V103的外設(shè)資源,我們還需要開(kāi)發(fā)CH32V103的驅(qū)動(dòng)模塊,在這個(gè)項(xiàng)目中,我們開(kāi)發(fā)了GPIO的驅(qū)動(dòng)模塊和基于rt-thread tick定時(shí)器的延時(shí)模塊。
最上層的就是我們要運(yùn)行的Python腳本了,模塊預(yù)編譯器也可以處理Python腳本,根據(jù)腳本中導(dǎo)入的模塊來(lái)自動(dòng)裁剪固件,在腳本中沒(méi)有import的固件會(huì)被自動(dòng)裁剪掉,我們可以在main.py中選擇要加入固件的模塊,以及編寫系統(tǒng)初始化后最先運(yùn)行的Python腳本,將其燒錄進(jìn)固件中。
2.制作BSP和啟動(dòng)器——先跑起來(lái)再說(shuō)
BSP通常是用芯片的原廠提供的例程制作的,在這個(gè)項(xiàng)目中,我們就使用CH32V103的官方例程中的uart_printf和MounRiver River Studio生成的rt-thread模板來(lái)制作。完成了對(duì)rt-thread模板的一些剪裁之后,再加入printf的初始化函數(shù),對(duì)項(xiàng)目稍作整理,BSP部分就完成了。
PikaScript的啟動(dòng)器的制作也比較簡(jiǎn)單,在main.c中添加#include “pikaScript.h”并調(diào)用pikaScriptInit()函數(shù)即可啟動(dòng)PikaScript。pikaScript.h和pikaScriptInit()都是由預(yù)編譯器自動(dòng)生成的,在制作啟動(dòng)器之前,需要拉取PikaScript的源碼。
PikaScript官方(其實(shí)就是我自己)提供了一個(gè)包管理工具,只需要編寫requestment.txt,就可以從gitee中自動(dòng)拉取相應(yīng)版本的源碼和模塊。在拉取內(nèi)核源碼時(shí),預(yù)編譯器也會(huì)自動(dòng)被拉取下來(lái),我們?cè)趍ain.py中寫入import PikaStdLib,然后用我們使用拉取下來(lái)的預(yù)編譯器進(jìn)行預(yù)編譯,就能得到pikaScriptInit()函數(shù)了。
包管理工具不僅可以拉取內(nèi)核,還可以拉取模塊,也就是說(shuō)我們自己制作的CH32V103的驅(qū)動(dòng)模塊,也可以掛到PikaScript模塊庫(kù)中,進(jìn)行自動(dòng)拉取。
BSP和啟動(dòng)器的制作我錄制了一個(gè)視頻教程,想要了解細(xì)節(jié)或者想自己制作BSP的大佬可以看視頻了解。
https://www.bilibili.com/video/BV1Cq4y1G7Tj
3.制作CH32V103的驅(qū)動(dòng)模塊
接下來(lái)我們制作CH32V103的驅(qū)動(dòng)模塊,使得CH32V103上面的外設(shè)資源能夠被Python腳本調(diào)用到。
在這個(gè)項(xiàng)目中,我們制作了一個(gè)PikaScript的標(biāo)準(zhǔn)設(shè)備驅(qū)動(dòng),什么是標(biāo)準(zhǔn)設(shè)備驅(qū)動(dòng)呢?我們先從其他的腳本技術(shù)說(shuō)起,比如MicroPython,并沒(méi)有統(tǒng)一的外設(shè)調(diào)用API,這使得用戶在使用不同的平臺(tái)時(shí),都需要重新學(xué)習(xí)API,比如下面這個(gè)是MicroPython在STM32F4平臺(tái)驅(qū)動(dòng)GPIO的代碼。
這個(gè)是ESP8266的
可以明顯看到在選擇pin的管腳時(shí),一個(gè)用的是字符串,而另一個(gè)用的是整型數(shù),驅(qū)動(dòng)的API標(biāo)準(zhǔn)很混亂。
有沒(méi)有什么辦法,能夠統(tǒng)一外設(shè)的API,使得用戶只需要熟悉一套API,就能夠在任意平臺(tái)通用呢?
方法是有的,就是PikaStdDevice標(biāo)準(zhǔn)設(shè)備驅(qū)動(dòng)模塊!
PikaStdDevice是一個(gè)抽象的設(shè)備驅(qū)動(dòng)模塊,定義了所有的用戶API,而各個(gè)平臺(tái)的驅(qū)動(dòng)模塊只要從PikaStdDevice繼承,就能夠獲得一模一樣的用戶API,而PikaStdDevice內(nèi)部會(huì)間接調(diào)用平臺(tái)驅(qū)動(dòng),通過(guò)多態(tài)特性重寫底層的平臺(tái)驅(qū)動(dòng),就可以在不同的平臺(tái)工作了!
以GPIO模塊為例,以下是PikaStdDevice定義的用戶API
以下是PikaStdDevice需要重寫的平臺(tái)驅(qū)動(dòng)
而我們要制作的CH32V103的GPIO模塊,就從標(biāo)準(zhǔn)驅(qū)動(dòng)模塊中繼承。
通過(guò)這個(gè)方法,我們就可以讓STM32的驅(qū)動(dòng)模塊、CH32的驅(qū)動(dòng)模塊、ESP32的驅(qū)動(dòng)模塊有著一模一樣的用戶API!用戶只要熟悉了一套API,就可以輕松使用支持了PikaScript標(biāo)準(zhǔn)驅(qū)動(dòng)模塊的所有平臺(tái)!這才是真正的跨平臺(tái)!
下面是部分被注冊(cè)在驅(qū)動(dòng)模塊里面C原生驅(qū)動(dòng)函數(shù)
驅(qū)動(dòng)模塊的開(kāi)發(fā),我也制作了兩個(gè)視頻,供想要了解細(xì)節(jié)的大佬們參考。
https://www.bilibili.com/video/BV1aP4y1L7pi
https://www.bilibili.com/video/BV1Jr4y117Z8
4.支持交互式運(yùn)行
PikaScript不依賴文件系統(tǒng),只要傳入字符串就可以運(yùn)行,所以只要制作支持字符串讀取的串口驅(qū)動(dòng),就可以支持交互式運(yùn)行了!
下面是本項(xiàng)目中支持交互式運(yùn)行的驅(qū)動(dòng)代碼。
5.main.py初始化腳本
最后我們編寫一段用Python寫成的初始化腳本,在固件啟動(dòng)后運(yùn)行,初始化GPIO,并且獲得一個(gè)系統(tǒng)對(duì)象,用于提供延時(shí)功能。在初始化結(jié)束后,led閃爍10次,并打印hello pikascript!
編寫好初始化腳本后,用預(yù)編譯器就可以集成在固件中了。
下面是預(yù)編譯器生成的初始化函數(shù)
項(xiàng)目地址:
PikaScript-CH32V103參賽項(xiàng)目倉(cāng)庫(kù):
https://gitee.com/lyon1998/ch32v103-pika
PikaScript總倉(cāng)庫(kù):
在演示視頻中,演示了PikaScript的啟動(dòng)和交互式運(yùn)行,包括:
1.測(cè)試了PikaScript的Python腳本交互式運(yùn)行功能。
2.使用led對(duì)象的high()方法和low()方法,控制IO的電平,進(jìn)而控制LED燈。
3.測(cè)試了PikaScript解釋器對(duì)Python變量的動(dòng)態(tài)創(chuàng)建的支持。
4.測(cè)試了PikaScript對(duì)Python標(biāo)準(zhǔn)庫(kù)函數(shù)print的支持,包括打印整型數(shù)和字符串?dāng)?shù)。
5.測(cè)試了PikaScript解釋器對(duì)運(yùn)算符以及組合運(yùn)算的支持。
6.測(cè)試了PikaScript解釋器對(duì)條件運(yùn)算符的支持和對(duì)控制流的支持。
7.測(cè)試了PikaScript對(duì)多行Python腳本交互式運(yùn)行的支持。
8.測(cè)試了sys對(duì)象的delay()系統(tǒng)方法,該方法基于rt-thread延時(shí)函數(shù),驗(yàn)證了與rt-thread操作系統(tǒng)的兼容性。
9.循環(huán)打印1-1000整型數(shù),測(cè)試了腳本的運(yùn)行速度。