Hi3861的micropython移植之Pin 原创 精华

再见南丫岛
发布于 2022-3-13 22:03
浏览
1收藏

本文介绍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) },
Hi3861的micropython移植之Pin-鸿蒙开发者社区

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的一个过程。后续会持续的更新这方便的内容。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-3-13 22:13:36修改
2
收藏 1
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

干货,必须支持!

回复
2022-3-14 10:52:27
千云山庄
千云山庄

真牛,盼望出一个从0开始的教程,并对比C与py 的转化

回复
2022-3-15 14:56:43
回复
    相关推荐