4.3. 支持交互式运行
PikaPython 支持直接读取字符串运行 Python 脚本,因此支持交互式运行只需要制作一个串口接收驱动。
4.3.1. 方案一:按字节读取运行(推荐)
4.3.1.1. 实现一个阻塞的字节读取函数
交互式运行需要一个读取用户输入字节的底层接口 __platform_getchar()
,这个接口是一个 weak 函数,用户需要在自己的代码里面也实现一个 __platform_getchar()
来覆盖这个 weak 函数。
weak 函数原型在 PikaPlatform.c 里面,如果用户没有覆盖,就会在使用交互式运行时报错。
/* PikaPlatform.c */
PIKA_WEAK char __platform_getchar(void) {
__platform_printf("[error]: __platform_getchar need an implement!\r\n");
while(1){
}
}
用户可以直接在工程的 main.c 里面实现一个 __platform_getchar()
。
如果平台本身支持 getchar()
,那么可以直接接入平台的 getchar()
。
/* main.c */
char __platform_getchar(){
return getchar();
}
如果平台不支持,就需要自己实现,注意要实现一个阻塞的 getchar()
,也就是说,当没有串口输入字符时,要在 __platform_getchar()
中等待,有输入时返回一个字符。
例如在STM32上实现一个阻塞的 getchar()
函数,以接收中断并将接收到的字符赋值给 g_rx_char
的例子如下:
/* main.c */
#include "stm32f4xx.h"
#include <stdio.h>
volatile char g_rx_char = 0; // 全局变量用于存储接收到的字符
void USART2_IRQHandler(void) {
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
// 串口接收中断处理
g_rx_char = USART_ReceiveData(USART2); // 将接收到的字符赋值给g_rx_char
}
}
char __platform_getchar() {
char res = 0;
while (g_rx_char == 0) {
// 等待串口接收中断,直到有输入时返回字符
}
res = g_rx_char;
g_rx_char = 0;
return res;
}
int main(void) {
// 初始化USART2和相应的GPIO引脚等
// ...
// 启用USART2的接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 启动USART2
USART_Cmd(USART2, ENABLE);
// ...
}
这个例子中,我们使用了 STM32的 USART2
作为示例串口,并通过 USART2
的接收中断来实现阻塞的 getchar()
函数。在 USART2_IRQHandler
中,我们检查是否接收到了字符,并将接收到的字符赋值给全局变量 g_rx_char
。
__platform_getchar()
函数会一直等待,直到g_rx_char
中有字符可用,然后返回接收到的字符。
注意:这个 STM32 的例子只是一个原理示意,以便于你的理解,请根据您的实际硬件和应用需求对示例代码进行修改和适应,以确保它在您的项目中正确运行。
4.3.1.2. 启动 PikaPython Shell,直接运行 pikaScriptShell()
即可启动交互运行。
pikaScriptShell()
入口参数是 pika 的根对象,你可以通过运行 pikaScriptInit()
获得一个根对象。
pikaScriptShell(pikaScriptInit());
4.3.1.3. 示例代码
stm32g070cb: https://gitee.com/Lyon1998/pikapython/blob/master/bsp/stm32g070cb/Booter/main.c
rt-thread: https://gitee.com/Lyon1998/pikapython/blob/master/package/pikaRTThread/rt_pika.c
4.3.1.4. 注意事项:
内核版本需要不低于
v1.3.0
强烈建议使用 pikaStudio 作为串口终端。
4.3.2. 方案二:按字节输入运行
obj_runChar
内核 API 可以指定一个对象执行脚本,且输入为一个字节。
示例代码:
PikaObj* pikaMain = pikaScriptInit();
while(1){
char ch = my_get_char();
obj_runChar(pikaMain, ch);
}
4.3.2.1. 注意事项:
内核版本需要不低于 v1.11.6
4.3.3. 方案三:整行输入运行
obj_run
内核 API 可以指定一个对象执行脚本,使用这个 API 可以执行单行或者多行的脚本。
下面以 CH32 的交互式运行驱动为例,这个交互式运行的支持写在固件的主循环内,在 pikaScriptInit()
初始化脚本执行完毕之后,开始执行。
4.3.3.1. 驱动的内容
轮询接收字符,存入缓冲区。
当超过10ms没有新的字符接收时,认为一次接收完成。
使用空闲时间来判断字符串的传输完成就可以支持交互式运行多行脚本了。如果只需要运行单号脚本,则可以使用换行符 '\n'
来判断字符串接收结束。在运行单行脚本时,可以不带 '\n'
换行符,多行脚本需要带 '\n'
换行符。"\r\n"
形式的换行符也是支持的。
接收完成后回显接收到的字符串。
使用
obj_run
内核API执行脚本。
指定的对象是 pikaScriptInit()
初始化脚本创建的根对象,执行的内容是接收到的字符串。
清理接收缓冲区。
4.3.3.2. 注意事项:
内核版本需要不低于
v1.2.6
在执行多行脚本时,需要传入完整的代码块
例如:下面的脚本是完整的代码块,尤其需要注意的是第 4 行,需要有一个缩进为 0 的行,用来标志代码块的结束。以及最后一行需要有一个空行,这意味着 print('the end')
这行脚本末尾带有换行符。
while a < 10:
a = a + 1
print(a)
print('the end')
(空行)
以下的例子也是可以的
while a < 10:
a = a + 1
print(a)
(空行)
以下的例子是不行的
# 缺少最后的换行符
while a < 10:
a = a + 1
print(a)
# 缺少while代码块的内容
while a < 10:
4.3.4. 退出交互运行
输入 exit()
即可退出交互运行。
4.3.5. 运行临时文件
4.3.5.1. 运行 Python 文件
使用 pikaStudio (推荐):
拖拽 Python 文件即可运行
使用其他串口工具 (不推荐):
在要运行的文件的第一行和最后一行加上 #!pika
,然后直接将文件通过串口发送即可运行。
这个文件会被发送到 RAM 里然后直接运行,重启后会失效。
如:
#!pika
print('hello pikapython in file')
#!pika
输出:
>>> #
=============== [code] ===============
print('hello pikapython in file')
=============== [code] ===============
hello pikapython in file
>>>
注意:
需要内核版本
>= v1.11.4
。临时文件的第一行和最后一行必须是
#!pika
,否则会被当成普通的字符串处理。需要
__platform_getchar()
速度不要太慢,否则会导致文件发送失败,也可以尝试调慢串口波特率。
4.3.5.2. 运行字节码文件
将 xxx.py.o 字节码文件发送即可运行 输出:
=============== [Code] ===============
[ Info] Bytecode size: 305
=============== [ RUN] ===============
hello pikapython in file
注意:
需要内核版本
>= v1.11.6
。