相关文章推荐
狂野的伤疤  ·  二战德军单兵装备 ...·  1 年前    · 
空虚的南瓜  ·  java - Split string ...·  1 年前    · 
飘逸的感冒药  ·  WebUtility.UrlEncode(S ...·  1 年前    · 

MicroPython是Python3的精简实现,包括Python标准库的一小部分,经过优化可在微控制器和受限的环境中运行,在官方提供了相应的开发板,但最低的配置都需要32KB的SRAM空间和4KB的STACK空间,对MCU的性能也有绝对的要求,基于STM32F103的MicroPython开发板在某宝上几乎没有,最少也得STM32F4及以上系列的才玩得动,那我想在资源有限的MCU上运行Python程序,难道就不配吗?

想要在一个MCU上运行Python程序,一般步骤如下:首先得需要一个Linux环境(大多数人选择通过虚拟机来安装Linux)、然后需要更新相应的命令工具并下载交叉编译工具和编译器(交叉编译器选择gcc-arm-none-eabi、编译器选择gcc)、接着需要下载MicroPython源代码,生成固件程序(建议选择官方已经支持的开发板,否则需要自行实现和移植)、最后通过烧录工具或者使用USB的DFU模式烧录程序到MCU、至此才可以开始使用Python编程来实现应用功能;期间一步都不能错哦……但对于做MCU嵌入式开发的工程师来说,我们常用的都是KEIL、IAR这些IDE集成开发环境,难道一定得按照上面步骤一步步来吗,就不能使用KEIL来开发、调试了?

PiKaScript 应运而生

PiKaScript可以为资源受限的MCU提供极易部署和拓展的Python脚本支持。PiKaScript不需要操作系统和文件系统,支持裸机运行,最低可运行在RAM≥4KB,FLASH≥32KB的MCU中,而且还支持KEIL、IAR等IDE集成开发环境。此外PiKaScript是完全开源的(https://github.com/pikasTech/pikascript),采样的是MIT协议,允许修改和商用,但是要注意保留原作者的署名即可。

部署 PikaScript MM32 平台

PikaScript可以在所有支持libc的裸机和操作系统上运行,只需要编译器能够支持C99标准即可。当前PikaScript仅支持32位和64位内核的MCU,暂不支持8位内核的MCU;考虑到拓展模块的资源占用情况,如果是ARM内核的MCU推荐最低应该配备64KB FLASH和8KB SRAM,如果是RISC内核的MCU推荐最低应该配备128KB FLASH和8KB SRAM。

  • 准备模板工程

我当前使用的IDE集成开发环境是KEIL MDK,在部署PikaScript到MM32之前,我们需要新建一个基于MM32 MCU的模板工程,这个模板工程只需要实现printf功能的串口初始化即可,重载fputc和fgetc这两个函数,为后面实现功能做准备,至此我们就完成了部署PikaScript的第一步。

  • 获取 PikaScript 源码和工具集

我在模板工程中的Source文件夹中新建立一个PikaScript文件夹作为PikaScript部署路径;然后我们需要到GIT上去下载PikaScript包管理器:pikaPackage.exe,将这个包管理器存放在PikaScript文件夹下,通过这个包管理器我们可以轻松地拉取指定版本的源码和模块;接下来我们在PikaScript文件夹下新建一个requestment.txt文件,然后写入如下内容:

pikascript-core==v1.8.6

PikaStdLib==v1.8.6

如上内容表示使用1.8.6版本的pikascript解释器内核和1.8.6版本的标准库,解释器内核和标准库是必选项,且这两个版本号需要保持一致,而其它的模块则是可以有选择性的添加;在初始部署时,尽可能的只添加解释器内核和标准库即可,这样可以遇到兼容性的问题;版本号可以通过http://pikascript.com/这个网址来查看,当然你也可以通过这个网址来自动生成工程……

现在PikaScript文件夹下就有了pikaPackage.exe和requestment.txt这两个文件,双击运行pikaPackage.exe就可以拉取requestment.txt文件中指定版本的源码和模块了。拉取过程如下所示:

在源码和模块拉取结果后,PikaScript文件夹就多了不少文件,如下图所示:

其中pikascript-api文件夹下存放的是模块API相关文件,在预编译前这个文件夹是空的,pikascript-core文件夹下存放的是内核相关文件,pikascript-lib文件夹下存放的是模块库,rust-msc-latest-win10.exe是预编译器。然后我们在PikaScript文件夹新建一个main.py文件,然后写入:

import PikaStdLib

print('Hello PikaScript!')

其中import PikaStdLib表示导入标准库,而且标准库是必需要导入的;而print('Hello PikaScript!')则是用来测试pikascript是否正常启动。

  • 预编译模块

pikascript预编译器可以把python模块预编译为.c和.h文件;接下来我们运行PikaScript文件夹下的rust-msc-latest-win10.exe预编译器,它会将main.py和导入的模块预编译为pikascript的API文件,预编译后的文件存放在pikascript-api文件夹下;我们打开pikascript-api文件夹会发现多了很多.c和.h的文件,这就说明预编译成功运行了。

我们使用KEIL软件模板工程,在模板工程中添加3个Group,分别命名为:pikascript-api、pikascript-core、pikascript-lib,这也是PikaScript文件夹下的3个文件夹名,如下图所示:

然后将这3个文件夹下的所有.c源码文件分别添加到上面的3个Group当中,如下图所示:

然后设置KEIL编译器的Include Path,如下图所示:

  • 调整堆栈大小

我们可以在启动文件中修改堆和栈的大小,也可以通过修改建议SCF文件来修改堆和栈的大小;对于PikaScript的部署建议分配4KB的栈空间和16KB的堆空间;如下图所示:

  • 启动 PikaScript

在main.c中添加PikaScript的头文件和启动代码,在代码中通过重载fgetc函数结合libc实现了getchar函数功能,再通过覆用pikascript中读取用户输入字节的底层接口函数__platform_getchar(),加上启动PickScript Shell后,即实现了程序代码的交互式运行;代码如下所示:

void MCU_InitClock ( void )

/* 使能内部高速时钟 HSI */

RCC->CR |= RCC_CR_HSION_MASK;

/* 等待内部高速时钟 HSI 稳定 */

while (RCC_CR_HSIRDY_MASK != (RCC->CR & RCC_CR_HSIRDY_MASK));

/* 选择 HSI 输出用作系统时钟 */

RCC->CFGR = RCC_CFGR_SW( 0u );

/* 等待系统时钟选择状态稳定 */

while (RCC_CFGR_SWS( 0u ) != (RCC->CFGR & RCC_CFGR_SWS_MASK));

/* 复位除 HSI 之外的所有时钟 */

RCC->CR = RCC_CR_HSION_MASK;

RCC->CIR = RCC->CIR; /* 清除中断标志位 */

RCC->CIR = 0u ; /* 禁卡相应的中断 */

/* PWR/DBG 时钟使能 */

RCC->APB1ENR |= ( 1u << 28u );

/* 如果系统时钟需要达到最大频率 120MHz , 需要将 VOS 设置为 1.7V */

PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS_MASK) | PWR_CR1_VOS( 3u );

/* 使能外部高速时钟 HSE */

RCC->CR |= RCC_CR_HSEON_MASK;

/* 等待外部高速时钟 HSE 稳定 */

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 时钟源 , 1:HSE 作为 PLL1 时钟源 */

RCC_PLL1CFGR_PLL1MUL( 19 )| /* PLL1 倍频系数 */

RCC_PLL1CFGR_PLL1DIV( 1 ) | /* PLL1 分频系数 */

RCC_PLL1CFGR_PLL1LDS( 1 ) | /* PLL1 锁定检测器精度选择 : 高精度 */

RCC_PLL1CFGR_PLL1ICTRL( 3 ); /* PLL1 输入时钟源大于等于 8MHz 时,推荐设置值为 2'b11

PLL1 输入时钟源小于 8MHz 时,推荐设置值为 2'b01 */

/* 使能 PLL1 */

RCC->CR |= RCC_CR_PLL1ON_MASK;

/* 等待 PLL1 稳定 */

while ((RCC->CR & RCC_CR_PLL1RDY_MASK) == 0 );

/* FLASH 时钟使能 */

RCC->AHB1ENR |= ( 1u << 13u );

FLASH->ACR    = FLASH_ACR_LATENCY( 4u ) | /* 0 : 零个等待状态 , 0MHz < SYSCLK <= 24MHz

1 : 一个等待状态 , 24MHz < SYSCLK <= 48MHz

2 : 二个等待状态 , 48MHz < SYSCLK <= 72MHz

3 : 三个等待状态 , 72MHz < SYSCLK <= 96MHz

4 : 四个等待状态 , 96MHz < SYSCLK <= 120MHz */

FLASH_ACR_PRFTBE_MASK; /* 预取缓冲区开启 */

/* 时钟配置 */

RCC->CFGR = RCC_CFGR_HPRE( 0 )    | /* AHB 预分频系数 , 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 预分频系数 , PCLK1

0xx : HCLK 不分频

100 : HCLK  2 分频

101 : HCLK  4 分频

110 : HCLK  8 分频

111 : HCLK 16 分频 */

RCC_CFGR_PPRE2( 0x4 ) | /* APB2 预分频系数 , PCLK2

0xx : HCLK 不分频

100 : HCLK  2 分频

101 : HCLK  4 分频

110 : HCLK  8 分频

111 : HCLK 16 分频 */

RCC_CFGR_MCO( 7 ); /* MCO 输出时钟源选择

000x : 没有时钟输出

0010 : LSI 时钟输出

0011 : LSE 时钟输出

0100 : SYSCLK 时钟输出

0101 : HSI 时钟输出

0110 : HSE 时钟输出

0111 : PLL1 时钟输出

1000 : PLL2 时钟输出 */

/* ADC1 预分频 ( 频率范围 15MHz - 48MHz)

= PCLK2 / (PRE + 2), 要求 PRE 为偶数 , 使占空比为 50%

= 60MHz / ( 2  + 2)

= 15MHz */

RCC_SetADCClockDiv(ADC1, 2 );

/* ADC1 calibration 时钟分频 ( 频率范围 187.5kHz - 1.5MHz)

= PCLK2 / (PRECAL + 2), 要求 PRECAL 为偶数 , 使占空比为 50%

= 60MHz / (58     + 2)

= 1MHz */

RCC_SetADCClockDiv(ADC1, 58 );

/* 选择 PLL 输出用作系统时钟 */

RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW_MASK) | RCC_CFGR_SW( 2 );

/* 等待系统时钟选择状态稳定 */

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 参数 , 否则 UART ENABLE 后会有一个 0xFF 的异常字节 */

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();

编译程序无误后,我们将代码下载到MM32芯片,将MM32的UART通过USB转TTL工具连接到电脑,打开MobaXterm终端软件进行调试,芯片上电启动后如下图所示:

  • 在线运行 Python 脚本程序

在MobaXterm终端软件中我们输入如下图所示代码后,敲入回车键后Python代码就自动解析执行了,并输出相对应的结果:

如果出现如下图所示的error提示,请检查一下MM32对于堆栈大小的配置情况,适当的调整一下就可以了:

后续将继续来实现和分享通过串口来下载Python脚本并运行Python程序的功能、以及基于一块开发板来实现对模块的开发、调用、应用的全方位实现;通过对Python的支持,让更多的精力投入到应用功能的开发中去,同时也让资源相对不富裕的MCU有了施展的平台。

模板工程: PikaScript.zip (665.18 KB)

PikaScript模板工程: PikaScript_Template.zip (8.2 MB)
---------------------
作者:xld0932
链接:https://bbs.21ic.com/icview-3232352-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

MicroPython是Python3的精简实现,包括Python标准库的一小部分,经过优化可在微控制器和受限的环境中运行,在官方提供了相应的开发板,但最低的配置都需要32KB的SRAM空间和4KB的STACK空间,对MCU的性能也有绝对的要求,基于STM32F103的MicroPython开发板在某宝上几乎没有,最少也得STM32F4及以上系列的才玩得动,那我想在资源有限的MCU上运行Python程序,难道就不配吗?...
mm 32的f003系列芯片是基于arm的cortex-m0芯片,flash16k,ram4k,本来这么小的容量可以直接用iar和keil的软件免费编译,不过为了加深对gnuarm的理解,还是作了这么一个用gnuarm的工程,这个工程用cmake建立,理论上稍微改改就可以用于其他cortem0,cortexm3的芯片。github地址https://github.com/niexuzhong/ mm 32gnuarm 首先安装arm gcc ,cmake,jlink,编辑器我用的vi...
工作中经常用到rabbitmq,而用的语言主要是 python ,所以也就经常会用到 python 中的pika模块,但是这个模块的使用,也给我带了很多问题,这里整理一下关于这个模块我在使用过程的改变历程已经中间碰到一些问题 的解决方法 刚开写代码的小菜鸟 在最开始使用这个rabbitmq的时候,因为本身业务需求,我的程序既需要从rabbitmq消费消息,也需要给rabbitmq发布消息,代码的逻辑图为如下: userx = pika.PlainCredentials(user, pwd) # hosh:rabbitmq所在的ip port: 端口号 parameters = pika.ConnectionParameters(host, int(port), '/', credentials=userx) conn = p
SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库 推荐查看官方文档:一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库主控: MM 32F3273G8P火龙果 开发 板 spi flash : W25Q32 使用SPI2。(PB12 PB13 PB14 PB15)在移植sfud之前,我们先初始化spi2,用读取函数,读取一下w25q32的manufacturer_id值,如果能正常读取,说明我们的设备没有问题,spi初始化也正常,然后
void iap_load_app(u32 appxaddr) if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法. JumpAddress = *(__IO uint32_t *)(appxaddr + 4);// jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区
灵动微 MM 32F103系列产品使用高性能内核M3的 32 位微控制器,典型工作频率可达144MHZ,内置高速存储器,丰富的增强型 I/O 端口和外设连接到外部总线。提供5种封装形式,包括 LQFP100、LQFP64、LQFP48、LQFP32 和 QFN32 共 5 种封装形式。根据不同的封装形式,器件中的外设配置不尽相同。该产品适合使用在电机驱动和应用控制,医疗和手持设备,工业应用以及警报系统等。下面为大家解答关于 MM 32F103产品中的一些常见问题。 1 、SPI 支持哪几种模式 按传输方向分
最近有部分刚接触 MM 32 MCU的用户朋友们碰到了MCU无法进行下载的情况,然后跟我们反馈芯片有问题,最后经过技术工程师跟进,其实都是用户程序使用错误或者操作不规范等原因造成的,并非芯片有问题。如果大家有碰到这种情况请不要着急,此篇文章灵动微总代理英尚微电子专门来讲解如何解决烧写失败的情况及有可能出现该类问题的原因。 MM 32无法进行烧写原因有多种情况,我们从硬件和软件两个方面分析: 硬件原因: 1、使用的调试器不支持调试下载 MM 32 MCU,IAR/KEIL上仿真器选择/配置不正确, MM 32 MCU已经获
§01 设计要求 在 制作测试 MM 32F3277-Micro Python 最小电路板 测试了基于 MM 32F3277的Micro Python 测试板。也可以看到它的时钟是不需要。下面设计一个适应于面包板进行测试实验的Micro Python 测试板。 一、资源设置 1、Micro Python 支持模块 下面使用灵动苏勇提供的模块支持类别: ▲ 图1.1.1 MM 32F3277中的模块 ▲ 图1.1.2 MM 32F3277七个UART对应的管脚