Hi3861的micropython移植之Pin 原创 精华
本文介绍Micropython中的Pin接口使用,也就是GPIO接口的使用。
1、打开Pin的编译
需要将MICROPY_PY_PIN配置成1,参加编译。在modmachine.c文件中的machine_module_globals_table数据中会包含
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
2、移植Pin的接口函数
需要包含openharmony中的gpio的头文件,其中包含了GPIO的操作函数
#include "hi_gpio.h"
定义可以被引用的GPIO的名字
typedef struct _machine_pin_obj_t {
mp_obj_base_t base;
int pin;
int ble_ex;
mp_obj_t pin_isr_cb;
} machine_pin_obj_t;
STATIC machine_pin_obj_t machine_pin_obj[] = {
{{&machine_pin_type},1,1,MP_OBJ_NULL},
{{&machine_pin_type},2,1,MP_OBJ_NULL},
{{&machine_pin_type},3,1,MP_OBJ_NULL},
{{&machine_pin_type},4,1,MP_OBJ_NULL},
{{&machine_pin_type},101,1,MP_OBJ_NULL},
{{&machine_pin_type},102,1,MP_OBJ_NULL},
};
然后移植初始化定义函数mp_pin_make_new
mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
// get the wanted pin object
int wanted_pin = mp_obj_get_int(args[0]);
const machine_pin_obj_t *self = NULL;
/*
if (0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj)) {
self = (machine_pin_obj_t *)&machine_pin_obj[wanted_pin];
}*/
for(int i=0;i< MP_ARRAY_SIZE(machine_pin_obj);i++)
{
if(wanted_pin == machine_pin_obj[i].pin)
{
self = (machine_pin_obj_t *)&machine_pin_obj[i];
}
}
if (self == NULL || self->base.type == NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
}
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
}
return MP_OBJ_FROM_PTR(self);
}
// pin.init(mode, pull=None, *, value)
STATIC mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_mode, ARG_pull, ARG_value };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT },
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// get io mode
uint mode = args[ARG_mode].u_int;
switch(mode) {
case GPIO_MODE_IN: {
mode = IOT_GPIO_DIR_IN;
break;
}
case GPIO_MODE_OUT: {
mode = IOT_GPIO_DIR_OUT;
break;
}
default:{
mode = IOT_GPIO_DIR_OUT;
break;
}
}
if(self->ble_ex == 0)
{
IoTGpioInit(self->pin);
IoTGpioSetDir(self->pin, mode);
hi_io_set_pull(self->pin,1);
hi_io_set_func(self->pin,0);//设置IO口模式
}
else
{
set_io_attribute(self->pin,mode);
}
return mp_const_none;
}
定义完Pin之后,需要获取IO的状态或者设置IO的状态。
// pin.value([value])
STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
return machine_pin_call(args[0], n_args - 1, 0, args + 1);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
在micropython的函数定义中,MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN关键词用于定义函数可以接受的参数的范围。根据定义,如果该函数中参数为空,则为获取参数,参数有数值,则为设置IO的高低。
// fast method for getting/setting pin value
STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 1, false);
machine_pin_obj_t *self = self_in;
//printf("machine_pin_call n_args = %d\r\n",n_args);
if (n_args == 0) {
if(self->ble_ex == 0)
{
hi_gpio_value gpio_val = HI_GPIO_VALUE1;
IoTGpioGetInputVal(self->pin,&gpio_val);
return mp_obj_new_bool(gpio_val);
}
else
{
int val = 0;
get_io_value(self->pin,&val);
return mp_obj_new_bool(val);
}
} else {
if(self->ble_ex == 0)
{
IoTGpioSetOutputVal(self->pin, mp_obj_is_true(args[0]));
}
else
{
set_io_value(self->pin, mp_obj_is_true(args[0]));
}
return mp_const_none;
}
}
3、中断模式
想要获取IO的高低电平,轮训模式是其中一种,但是必然离不开IO口的中断模式。micropython也是支持中断模式的。
STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_handler, ARG_trigger};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = PIN_IRQ_MODE_RISING} },
};
machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
printf("machine_pin_irq self int = %d\r\n",self->pin);
if (n_args > 1 || kw_args->used != 0) {
// configure irq
mp_obj_t handler = args[ARG_handler].u_obj;
if (handler != mp_const_none)
{
self->pin_isr_cb = handler;
}
else
{
printf("%s\r\n","handler = mp_const_none.");
}
uint32_t trigger = args[ARG_trigger].u_int;
IoTGpioRegisterIsrFunc(self->pin,1,trigger,machine_pin_isr_handler,(void*)self);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
machine_pin_irq用于设置中断回调函数以及中断触发的条件,当IO中触发中断之后,会调用注册的函数machine_pin_isr_handler。
STATIC void machine_pin_isr_handler(void *arg) {
machine_pin_obj_t *self = arg;
printf("machine_pin_isr_handler = %d\r\n",self->pin);
mp_sched_schedule(self->pin_isr_cb, MP_OBJ_FROM_PTR(self));
}
然后调用mp_sched_schedule(self->pin_isr_cb, MP_OBJ_FROM_PTR(self));来实现回调函数的引用。后续会专门介绍mp_sched_schedule是如何工作的。
4、python编程驱动IO
1)实现LED灯闪烁
import time
from machine import Pin
led = Pin(12, Pin.OUT)
while True:
led.value(0) # turn off
time.sleep(0.5)
led.value(1) # turn on
time.sleep(0.5)
2)IO的电平获取
from machine import Pin
p_in = Pin(1,Pin.IN)
print(p_in.value()) # get value, 0 or 1
3)IO的中断
from machine import Pin
key_0 = Pin(12, Pin.IN)
def func(v):
print("Hello Haoqixing\r\n")
key_0.irq(handler=func,trigger=Pin.IRQ_RISING)
5、总结
IO口的使用是我们学习一款单片机的开始,通过python来驱动IO,使得我们学习的成本进一步降低。移植micropython到hi3861的过程,也是学习C和Python的一个过程。后续会持续的更新这方便的内容。
干货,必须支持!
真牛,盼望出一个从0开始的教程,并对比C与py 的转化