APM32F427 上移植 PikaPython 实现 GPIO 点灯控制
本文介绍在 APM32F427 上运行 Python(PikaPython)的基础功能,重点介绍 GPIO 驱动移植,目标为点灯。
引入必要的包
在之前的基础上,我们在 pikapython/requirements.txt 加入了当时必须的两个包(或者称之为'模块')。在运行包管理器后,内容被追加了版本信息:
pikascript-core==v1.13.4PikaStdLib==v1.13.4
为了实现点灯,我们增加两个新的包:PikaStdDevice 和 time,内容变更为:
pikascript-core==v1.13.4PikaStdLib==v1.13.4PikaStdDevicetime
再次运行包管理器 pikaPackage.exe,完成后会追加合适的版本信息,内容为:
pikascript-core==v1.13.4PikaStdLib==v1.13.4PikaStdDevice==v2.4.6time==v0.2.2
这 4 个包是点灯必须的。下面将会增加针对 APM32F427 的自定义包。
加入针对 APM32F427 的模块
新建文本文件 pikapython/apm32f4_pika.pyi,内容如下:
// apm32f4_pika.pyi
fromPikaObj import *
import PikaStdDevice
class GPIO(PikaStdDevice.GPIO):
...
class UART(PikaStdDevice.UART):
...
这是声明了两个类:GPIO 和 UART。稍后编写 py 脚本的时候,是可以直接使用 apm32f4_pika.GPIO 实例化相应的类。
修改 pikapython/requirements.txt,把 apm32f4_pika 追加到文件末尾:
pikascript-core==v1.13.4
PikaStdLib==v1.13.4
PikaStdDevice==v2.4.6
time==v0.2.2
apm32f4_pika
运行 rust-msc-latest-win10.exe,生成必要的内容,可以看到程序的输出类似:程序输出显示有 5 个包,与 requirements.txt 的内容一一对应。
在 pikascript-api 文件夹下新增了 3 个文件:
apm32f4_pika.hapm32f4_pika_GPIO.hapm32f4_pika_UART.h
这几个文件的内容不用关心,它们是自动生成的,也不需要编辑修改。下面开始编写驱动。
编写驱动
在 pikascript-lib 文件夹下,创建 apm32f4_pika 文件夹,GPIO/UART 的驱动就放在这个文件夹下。简单起见,我们先把全部驱动的代码放在 pikapython/pikascript-lib/apm32f4_pika/apm32f4_pika.c 这个源文件里:
#include "BaseObj.h"
#include "dataStrs.h"
#include "pika_hal.h"
#include "board.h"
#include "apm32f427xx.h"
#include <stdint.h>
#include <string.h>
struct platform_data_GPIO {
int port;
int pin;
};
static GPIO_T *const gpios[] = {
GPIOA, GPIOB, GPIOC, GPIOD,
GPIOE, GPIOF, GPIOG, GPIOH,
};
// 把字符串类型的 Pin 脚名称解析为 port/pin 编号
static int get_io_port_and_pin(const char *name, int *port, int *pin) {
char ch = 0;
if (name[0] != 'P' && name[0] != 'p') {
return -1;
}
int res = sscanf(name + 1, "%c%d", &ch, pin);
if (res != 2) {
return -1;
}
if (ch >= 'A' && ch <= 'Z') {
*port = ch - 'A';
} else if (ch >= 'a' && ch <= 'z') {
*port = ch - 'a';
} else {
return -1;
}
if (*port < 0 || *port > 7 || *pin < 0 || *pin > 15) {
return -1;
}
return 0;
}
// 打开 GPIO 设备
int pika_hal_platform_GPIO_open(pika_dev *dev, char *name) {
int port, pin;
int res = get_io_port_and_pin(name, &port, &pin);
if (res != 0) {
return -1;
}
struct platform_data_GPIO *data = pikaMalloc(sizeof(struct platform_data_GPIO));
data->pin = pin;
data->port = port;
dev->platform_data = data;
return 0;
}
// 关闭 GPIO 设备
int pika_hal_platform_GPIO_close(pika_dev *dev) {
if (NULL != dev->platform_data) {
pikaFree(dev->platform_data, sizeof(struct platform_data_GPIO));
dev->platform_data = NULL;
}
return 0;
}
// 读取 GPIO
int pika_hal_platform_GPIO_read(pika_dev *dev, void *buf, size_t count) {
struct platform_data_GPIO *gpio_data = dev->platform_data;
uint32_t level = (gpios[gpio_data->port]->IDATA >> gpio_data->pin) & 1;
memcpy(buf, &level, count);
return 0;
}
// 设置 GPIO
int pika_hal_platform_GPIO_write(pika_dev *dev, void *buf, size_t count) {
struct platform_data_GPIO *gpio_data = dev->platform_data;
uint32_t level = 0;
memcpy(&level, buf, count);
if (level == 0) {
gpios[gpio_data->port]->BSCL |= (1 << gpio_data->pin);
} else {
gpios[gpio_data->port]->BSCH |= (1 << gpio_data->pin);
}
return 0;
}
// 使能 GPIO 设备,暂时保持空
int pika_hal_platform_GPIO_ioctl_enable(pika_dev *dev) {
(void)dev;
return 0;
}
// 禁用 GPIO 设备,暂时保持空
int pika_hal_platform_GPIO_ioctl_disable(pika_dev *dev) {
(void)dev;
return 0;
}
// 配置 GPIO 设备
int pika_hal_platform_GPIO_ioctl_config(pika_dev *dev, pika_hal_GPIO_config *cfg) {
struct platform_data_GPIO *gpio_data = dev->platform_data;
if (cfg->dir == PIKA_HAL_GPIO_DIR_OUT) {
// 配置为 输出
gpios[gpio_data->port]->MODE = (gpios[gpio_data->port]->MODE & (~(3 << (gpio_data->pin * 2)))) | (1 << (gpio_data->pin * 2));
} else {
// 否则为 输入
gpios[gpio_data->port]->MODE = gpios[gpio_data->port]->MODE & (~(3 << (gpio_data->pin * 2)));
}
gpios[gpio_data->port]->PUPD = gpios[gpio_data->port]->PUPD & (~(3 << (gpio_data->pin * 2)));
switch (cfg->pull) {
case PIKA_HAL_GPIO_PULL_UP: // 上拉
gpios[gpio_data->port]->PUPD |= (1 << (gpio_data->pin * 2));
break;
case PIKA_HAL_GPIO_PULL_DOWN: // 下拉
gpios[gpio_data->port]->PUPD |= (2 << (gpio_data->pin * 2));
break;
default:
break;
}
return 0;
}
// 提供 ms 延时
void pika_platform_sleep_ms(uint32_t ms) {
ms_delay(ms);
}
驱动写完了,把它加入 keil 工程。需要注意的是,pikascript-lib 目录下因为我们新增的两个包 PikaStdDevice 和 time 都自动生成了相应的源文件,也都需要加入到 keil 工程一起编译。
运行测试
编译成功,下载程序到开发板。
为了测试新增的包,编写一个测试程序,让 LED2 闪烁 50 次:
import PikaStdLib
import PikaStdDevice
import apm32f4_pika
import time
mem = PikaStdLib.MemChecker()
print('mem.max :')
mem.max()
print('mem.now :')
mem.now()
led2 = apm32f4_pika.GPIO()
led2.setPin('PF0')
led2.setMode('out')
led2.enable()
led2.low()
for i in range(0, 50):
print(i)
led2.low()
time.sleep_ms(500)
led2.high()
time.sleep_ms(500)
print('over')
因为目前的工程尚未集成文件系统,所以我们不能直接运行这个脚本。好在 PikaPython 项目提供了 pikaStudio 这个工具,可以通过串口发送并执行脚本。
下载并执行这个程序,在红色框处选择串口,波特率并打开。复位一次设备,就能在终端看到串口输出的内容。
勾选复选框,把刚才准备好的 py 脚本拖进去。从图可以看到执行期间串口输出的计数值。
小结
PikaPython 提供了统一的驱动模式,只需要在 .pyi 文件声明支持的类,运行 rust-msc-latest-win10.exe 后就可以生成相应的程序框架。开发工程师就只需要完成具体的驱动开发就可以达成目标。总体来说还是很方便的。
APM32F427 大存储、高主频的特性,也给 PikaPython 提供了一个非常合适的运行平台。


