Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
PikaScript
pikascript GPIO 接口如何基于 rtt pin 设备实现
发布于 2023-05-31 21:37:17 浏览:272
订阅该版
[tocm] # 介绍 Pikascript 是 RT-Thread 软件包中心 - 编程语言 中的一个包,是一个对单片机友好的轻量级 python 脚本支持工具,类似 micropython。 在 Pikascript 中,架构如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230531/0cc34a61c3ac51e884e18bda6d921349.png.webp) 对于不同的平台,我们需要手动为平台适配 pika_hal 的设备抽象层接口。今天以 `packages/pikascript-latest/pikaRTDevice/pika_hal_RTT_GPIO.c` 为例,讲解 Pikascript GPIO 接口如何基于 RT-Thread Pin 设备 `rt-thread/components/misc/pin.c` 实现。 # 参考资料 [pikascript 文档](http://pikascript.com/doc/PikaStdDevice%20%E6%A0%87%E5%87%86%E8%AE%BE%E5%A4%87.html#pikastddevice) [RT-Thread Pin 设备文档](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin?id=%e8%ae%bf%e9%97%ae-pin-%e8%ae%be%e5%a4%87) # 讲解 模型如下: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230531/e309fd249c2ce860d5afeb88fbf92af2.png.webp) 所有设备均遵循类 linux 文件的编程模型,所有类型的设备均使用 pika_dev 结构体来作为设备句柄。 pika_dev 类型定义: ```c typedef struct { PIKA_HAL_DEV_TYPE type; PIKA_BOOL is_enabled; void* ioctl_config; void* platform_data; } pika_dev; ``` 在 RT-Thread 的文档中可以得知,应用程序通过 RT-Thread 提供的 PIN 设备管理接口来访问 GPIO,相关接口如下所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230531/854806d940f6033d9d27d4194cfdfea2.png.webp) ```c // 通过设备名,返回 pin num rt_base_t rt_pin_get(const char *name); // 通过 pin num,返回该 pin 的数据 int rt_pin_read(rt_base_t pin); // 把 value 电平信息写到对应 pin 上 void rt_pin_write(rt_base_t pin, rt_base_t value); // 把 pin 的模式设置为 mode void rt_pin_mode(rt_base_t pin, rt_base_t mode) ``` 所以经过分析,不难看出我们在 open 中要通过 rt_pin_get() 获取引脚编号,获取设备信息;在 read 中要通过 rt_pin_read() 读取引脚电平;在 write 中要通过 rt_pin_write() 设置引脚电平。 在 `pika_hal_RTT_GPIO.c` 中,我们一共有 7 个接口要实现: ``` int pika_hal_platform_GPIO_open(pika_dev* dev, char* name); int pika_hal_platform_GPIO_close(pika_dev* dev); int pika_hal_platform_GPIO_read(pika_dev* dev, void* buf, size_t count); int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count); int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev); int pika_hal_platform_GPIO_ioctl_disable(pika_dev* dev); int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg); ``` 下面我们依次进行讲解。 ## dev->platform_io 中存储的数据 首先定义一个结构体 platform_data_GPIO 用来存在 dev->platform_data 中,由上面对 rtt pin 接口的简单调用分析可知,只需要 pin 的数据: ```c typedef struct platform_data_GPIO { uint32_t pin_num; } platform_data_GPIO; ``` ## pika_hal_platform_GPIO_open 函数的原型为: `int pika_hal_platform_GPIO_open(pika_dev* dev, char* name);` 参数: - pika_dev* dev: 要操作的设备句柄 - char* name: GPIO 设备名 函数功能: 1. 根据 GPIO 设备名,找到对应的 GPIO 设备的 pin 2. 把设备 pin 数据存在 dev->platform_data 里面 实现: ```c int pika_hal_platform_GPIO_open(pika_dev* dev, char* name) { // 打印当前信息 rt_kprintf("\r\n=%s==%s=%d=name:%s==\r\n",__FILE__,__FUNCTION__,__LINE__,name); // 打印一下日志信息,当前正在打开哪个设备 __platform_printf("Open: %s \r\n", name); // 调用 pikaMalloc 分配内存,创建一个 platform_data_GPIO 结构体,用来存放这个 GPIO 设备的信息 platform_data_GPIO* data = pikaMalloc(sizeof(platform_data_GPIO)); // 在 RT_USING_PIN 这个宏定义存在时,通过 rt_pin_get 函数获取这个 GPIO 设备的引脚号,存放在 platform_data_GPIO 结构体的 pin_num 成员中。 #ifdef RT_USING_PIN data->pin_num = rt_pin_get(name) ; #endif // 将创建的 platform_data_GPIO 结构体赋值给 dev->platform_data dev->platform_data = data; return 0; } ``` ## pika_hal_platform_GPIO_close 函数的原型为:`int pika_hal_platform_GPIO_close(pika_dev* dev);` 参数: pika_dev* dev: 要操作的设备句柄 函数功能: 清除这个 GPIO 设备的信息,即清空 dev->platform_data 中的数据 实现: ```c int pika_hal_platform_GPIO_close(pika_dev* dev) { rt_kprintf("\r\n=%s==%s=%d===\r\n",__FILE__,__FUNCTION__,__LINE__); // 如果现在有 GPIO 设备数据,就清空 if (NULL != dev->platform_data) { pikaFree(dev->platform_data, sizeof(platform_data_GPIO)); dev->platform_data = NULL; } return 0; } ``` ## pika_hal_platform_GPIO_read 函数的原型为:`int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);` 参数: pika_dev* dev:要操作的设备句柄 void* buf:读取缓冲区 size_t count:读取数据长度,对于 GPIO、ADC 这样只能读取单个数据的设备,长度为 sizeof(uint32_t) 函数功能: 1. 根据之前存到 dev->platform_data 中的 pin num 数据,调用 rt_pin_read() 函数来获取该 pin 的数据 2. 把读取到的数据存到 buf 缓冲区中 实现: ```c int pika_hal_platform_GPIO_read(pika_dev* dev, void* buf, size_t count) { // 获取之前存放的platform_data_GPIO结构体指针 platform_data_GPIO* data = dev->platform_data; uint32_t level; rt_kprintf("\r\n=%s==%s=%d=gpio:%d==\r\n",__FILE__,__FUNCTION__,__LINE__,data->pin_num); #ifdef RT_USING_PIN // 根据 pin num 读取电平 level = rt_pin_read(data->pin_num); #endif // 只有可能是 0 或 1 if (level != 1 && level != 0) { return -1; } // 把 &level 处的 count 个(sizeof(uint32_t) 个)数据拷贝到 buf 缓存区,memcpy 函数不关心 buf 和 src 指向的内存是什么类型,它只根据 count 拷贝内存 memcpy(buf, &level, count); return 0; } ``` 注意: 在文档中指出,GPIO 设备 read 时读取的数据 count 应该为 sizeof(uin32_t),在 pika_hal_platform_GPIO_read 中,level 的类型设置为 uint32_t,这是和文档要求一致的。而给 level 赋值的 rt_pin_read() 函数的返回类型为 int,这里进行了一个隐式类型转换。 ## pika_hal_platform_GPIO_write 函数的原型为:`int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);` 参数: pika_dev* dev:要操作的设备句柄 oid* buf:写入缓冲区 size_t count:写入数据长度,对于 GPIO、ADC 这样只能读取单个数据的设备,长度为 sizeof(uint32_t) 函数功能: 1.根据之前存储的 dev->platform_data 信息获取 pin num 2.获取之前 buf 中存储的电平信息 3.把电平信息写到对应 pin 上 实现: ```c int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count) { // 获取之前 platform_data 数据 platform_data_GPIO* data = dev->platform_data; // 获取 buf 缓存区存储的高低电平信息 uint32_t level = 0; memcpy(&level, buf, count); // 把电平写到对应 pin 上 #ifdef RT_USING_PIN if (level == 0) { rt_pin_write(data->pin_num, PIN_LOW); return 0; } if (level == 1) { rt_pin_write(data->pin_num, PIN_HIGH); return 0; } #endif return 0; } ``` ## pika_hal_platform_GPIO_ioctl_enable 函数的原型为:`int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev);` 参数: pika_dev* dev:被操作的设备句柄 函数功能: 这个函数其实对应的是 `pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_ENABLE)` 这里的情况,使能了这个配置函数 所以要初始化一下 GPIO 的 pin num、输入输出模式、推挽模式、波特率等数据 实现: 目前只实现了打印日志 ```c int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev) { platform_data_GPIO* data = dev->platform_data; rt_kprintf("\r\n=%s==%s=%d=pin_num:%x==\r\n",__FILE__,__FUNCTION__,__LINE__,data->pin_num); /* TODO */ return 0; } ``` ## pika_hal_platform_GPIO_ioctl_disable 函数的原型为:`int pika_hal_platform_GPIO_ioctl_disable(pika_dev* dev);` 参数: pika_dev* dev:被操作的设备句柄 函数功能: 这个函数其实对应的是 `pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_DISABLE)` 这里的情况 实现: 同 enable 部分,disable 也只是打印了日志 ```c int pika_hal_platform_GPIO_ioctl_disable(pika_dev* dev) { rt_kprintf("\r\n=%s==%s=%d===\r\n",__FILE__,__FUNCTION__,__LINE__); platform_data_GPIO* data = dev->platform_data; return -1; } ``` ## pika_hal_platform_GPIO_ioctl_config 函数的原型为:`int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg);` 参数: pika_dev* dev:被操作的设备句柄 pika_hal_GPIO_config* cfg:GPIO 配置,具体定义如下: ```c typedef struct { PIKA_HAL_GPIO_DIR dir;//输入输出 PIKA_HAL_GPIO_PULL pull;//推挽模式 PIKA_HAL_GPIO_SPEED speed;//数据传输速率 void (*event_callback)(pika_dev* dev, PIKA_HAL_GPIO_EVENT_SIGNAL signal);//事件回调函数 PIKA_HAL_GPIO_EVENT_SIGNAL event_callback_filter;//上升沿还是下降沿 //事件回调是否使能 PIKA_HAL_EVENT_CALLBACK_ENA event_callback_ena; } pika_hal_GPIO_config; ``` 函数功能: 1.对输入输出进行讨论,分为 PIKA_HAL_GPIO_DIR_OUT、PIKA_HAL_GPIO_DIR_IN 两种 2.对推挽模式进行讨论,分为 PIKA_HAL_GPIO_PULL_NONE、PIKA_HAL_GPIO_PULL_UP、PIKA_HAL_GPIO_PULL_DOWN 三种 3.讨论事件回调是否使能、是否设置了回调函数 4.讨论回调函数是上升沿触发还是下降沿触发(PIKA_HAL_GPIO_EVENT_SIGNAL_RISING 以及 PIKA_HAL_GPIO_EVENT_SIGNAL_FALLING) 实现: 现有实现中并没有管回调函数的部分,所以相对简单 RT-Thread 文档中关于 `void rt_pin_mode(rt_base_t pin, rt_base_t mode);` 函数的 mode 可选项为: ``` #define PIN_MODE_OUTPUT 0x00 /* 输出 */ #define PIN_MODE_INPUT 0x01 /* 输入 */ #define PIN_MODE_INPUT_PULLUP 0x02 /* 上拉输入 */ #define PIN_MODE_INPUT_PULLDOWN 0x03 /* 下拉输入 */ #define PIN_MODE_OUTPUT_OD 0x04 /* 开漏输出 */ ``` 所以最外层讨论输入输出,内层讨论上拉下拉即可。 ```c int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg) { rt_kprintf("\r\n=%s==%s=%d=dir:%d==\r\n",__FILE__,__FUNCTION__,__LINE__,cfg->dir); platform_data_GPIO* data = dev->platform_data; uint8_t pinMode = 0; // 对 cfg 中各项分类讨论,从而确定 pinMode switch (cfg->dir) { case PIKA_HAL_GPIO_DIR_IN: switch(cfg->pull) { case PIKA_HAL_GPIO_PULL_UP: pinMode = PIN_MODE_INPUT_PULLUP; break; case PIKA_HAL_GPIO_PULL_DOWN: pinMode = PIN_MODE_INPUT_PULLDOWN; break; default: pinMode = PIN_MODE_INPUT; } break; case PIKA_HAL_GPIO_DIR_OUT: pinMode = PIN_MODE_OUTPUT; break; default: pinMode = PIN_MODE_OUTPUT; } // 将 pin 的模式设置为 pinMode #ifdef RT_USING_PIN rt_pin_mode(data->pin_num, pinMode); #endif return 0; } ``` # Todo 作者也在阅读源码的过程中发现了一些问题: 1.对失败的情况有时候没有做讨论,如 pika_hal_platform_GPIO_open 函数中,显然 rt_pin_get() 是可能获取不到的,此时应该返回 -1 表示出错并打印有关日志信息,但现有代码中没有这部分处理 2.pika_hal_platform_GPIO_ioctl_config 中没有配置为开漏模式的情况,这意味着无法使用 pikascript 脚本将 GPIO 模式设置为开漏模式
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
IcyFeather
这家伙很懒,什么也没写!
文章
10
回答
7
被采纳
1
关注TA
发私信
相关文章
1
利用qemu-vexpress-a9学习pikascript的问题
2
pikascript 移植报错
3
rt-thread下运行pikascript 无法打印数字
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
SFUD
msh
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1443
个答案
289
次被采纳
张世争
805
个答案
174
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部