介绍
pikascript 是 rt-thread 软件包中心 - 编程语言 中的一个包,是一个对单片机友好的轻量级 python 脚本支持工具,类似 micropython。
在 pikascript 中,架构如下:
对于不同的平台,我们需要手动为平台适配 pika_hal 的设备抽象层接口。今天以 packages/pikascript-latest/pikartdevice/pika_hal_rtt_gpio.c 为例,讲解 pikascript gpio 接口如何基于 rt-thread pin 设备 rt-thread/components/misc/pin.c 实现。
讲解
模型如下:
所有设备均遵循类 linux 文件的编程模型,所有类型的设备均使用 pika_dev 结构体来作为设备句柄。
pika_dev 类型定义:
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,相关接口如下所示:
// 通过设备名,返回 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 的数据:
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 设备名
函数功能:
根据 gpio 设备名,找到对应的 gpio 设备的 pin
把设备 pin 数据存在 dev->platform_data 里面
实现:
int pika_hal_platform_gpio_open(pika_dev* dev, char* name) {
// 打印当前信息
rt_kprintf(rn=%s==%s=%d=name:%s==rn, file , function , line ,name);
// 打印一下日志信息,当前正在打开哪个设备
__platform_printf(open: %s rn, 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 中的数据
实现:
int pika_hal_platform_gpio_close(pika_dev* dev) {
rt_kprintf(rn=%s==%s=%d===rn, 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)
函数功能:
根据之前存到 dev->platform_data 中的 pin num 数据,调用 rt_pin_read() 函数来获取该 pin 的数据
把读取到的数据存到 buf 缓冲区中
实现:
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(rn=%s==%s=%d=gpio:%d==rn, 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 上
实现:
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、输入输出模式、推挽模式、波特率等数据
实现:
目前只实现了打印日志
int pika_hal_platform_gpio_ioctl_enable(pika_dev* dev) {
platform_data_gpio* data = dev->platform_data;
rt_kprintf(rn=%s==%s=%d=pin_num:%x==rn, 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 也只是打印了日志
int pika_hal_platform_gpio_ioctl_disable(pika_dev* dev) {
rt_kprintf(rn=%s==%s=%d===rn, 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 配置,具体定义如下:
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 / 开漏输出 */
所以最外层讨论输入输出,内层讨论上拉下拉即可。
int pika_hal_platform_gpio_ioctl_config(pika_dev* dev,
pika_hal_gpio_config* cfg) {
rt_kprintf(rn=%s==%s=%d=dir:%d==rn, 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 模式设置为开漏模式。
人工智能技术在安防行业的实际落地应用 可以从以下几个方面来看
海尔李华刚:创物联网时代全球引领的生态品牌
荣耀9评测:荣耀9与小米6对比评测,作为小米6的劲敌,荣耀9到底哪里比不上对手?
为迎小米6上市米5大降价,拥有十大黑科技的米5是否值得购买?
锂电升压48V恒压方案:FP5207B单节锂电升压48V带载20W资料
pikascript GPIO接口如何基于rtt pin设备实现呢?
特斯拉CEO马斯克_特斯拉未来将在全球建造10-12家工厂
MiZ702学习笔记一:奔跑吧Linux
智慧温室大棚方案实际应用
九天睿芯荣获2023 GAIE年度“最佳人工智能企业”等奖项
立磨磨辊磨损量过大原因及处理方法
安泰测试:DG535脉冲信号发生器好不好?
思亚诺推出ISDB-T数字电视接收芯片SMS2270
德国研制机器人手臂 可轻松破解肿瘤活检难题
如何用FPGA逻辑来驱动VGA显示器
惠普星14全能轻薄本绝了,牛配显卡+轻薄
我国OTT终端规模快速增长,2020年OTT总激活规模有望突破3亿台
浅谈Mini LED现有的技术难点
三星Galxy Fold国行版正式开售该机搭载骁龙855平台配备6颗摄像头
dropout带来的模型的变化