> 文章列表 > [攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)

[攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)

[攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)

文章目录

  • [攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)
    • PIN简介
      • 🚗输入输出模式
        • 输出
        • 输入
      • 🚗中断模式
    • PIN 设备管理
    • 创建和注册PIN设备
      • 🚗创建
      • 🚗注册
    • 访问PIN设备
      • 🚗获取引脚编号
        • 使用 API
      • 🚗设置引脚模式
      • 🚗设置引脚电平
      • 🚗读取引脚电平
      • 🚗绑定引脚中断回调函数
      • 🚗使能引脚中断
      • 🚗脱离引脚中断回调函数
    • 代码

[攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)

🚀🚀开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯和 瑞萨MCU 联合发起的「 致敬未来的攻城狮计划 」的第2天,点击查看活动计划详情

🚀🚀首先非常感谢李老师能给我参加这个计划的机会,让我有机会接触到许多的开发板,同时也感谢瑞萨官方 为我们提供的开发板。在参加活动的期间,结识了许多的大佬,让我收获了许多的知识,我感觉短短的几天,收获的知识与开发经验比自己独自学习几个月的收获还要多。所以,希望各位大佬们也能加入李老师的这个活动,而且,李老师水平真的很高,而且非常耐心,谁来谁知道!!!!

🚀🚀好了,接下来我们开始进入今天的主题,如何在RA2E1上基于RT-Thread优雅地使用PIN设备!

🚀🚀本文将会详细介绍有关RT-Thread的PIN设备,但是考虑到可能有部分和我一样的初学者,所以有些具体细节并未介绍。其实本文中的内容并不仅仅适用于一款开发板,只要运行了RT-Thread操作系统的开发板,都是一样的。


PIN简介

🚀🚀PIN设备也叫做GPIO设备(General Purpose Input Output,通用输入 / 输出),在后文中可能会混着使用,这一点事先声明以下,避免造成读者的混乱。


🚗输入输出模式

🚀🚀GPIO,既然叫通用输入 / 输出设备,那么肯定是和输入输出有关的,接下来我们就来简单介绍一下PIN设备的输入输出模式。

输出

🚀🚀输出模式一般包括:推挽、开漏、上拉、下拉这四种模式。当引脚为输出模式时,我们可以通过配置引脚输出的电平状态来控制连接的设备,比如我们的LED灯。

输入

🚀🚀输入模式一般包括:浮空、上拉、下拉、模拟。当引脚为输入模式时,我们可以读取引脚的电平状态,特别是模拟,这部分在ADC的时候会用到。


🚗中断模式

🚀🚀本文也会简单介绍一下有关中断的知识,具体的中断模式如下图所示(官方文档):

[攻城狮计划]|RT-Thread—详解PIN设备(基于RA2E1)


PIN 设备管理

🚀🚀在RT-Thread中,应用程序可以通过PIN设备来操作GPIO,接下来我们来看看PIN设备的模型,他是从设备对象派生而来的,当然,这部分我们只会简答介绍一下,不会过多介绍。

/* pin device and operations for RT-Thread */
struct rt_device_pin
{struct rt_device parent;      /* 设备基类 */const struct rt_pin_ops *ops;  /* PIN设备驱动方法 */
};

🚀🚀我们主要的就是需要去实现这些驱动方法,我们在下面会介绍。


创建和注册PIN设备


🚗创建

🚀🚀接下来,我们需要来创建一个PIN设备,而主要需要做的就是实现PIN设备的操作方法,也就是将其实例化。

struct rt_pin_ops
{void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);int (*pin_read)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args);rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);rt_base_t (*pin_get)(const char *name);
};
方法名称 方法描述
pin_get 获取某个PIN的引脚编号
pin_mode 设置引脚模式,将某个引脚初始化成相应的模式
pin_write 设置某个引脚的输出电平
pin_read 读取某个引脚的电平
pin_attach_irq 中断操作,为某个绑定引脚中断回调函数
pin_detach_irq 脱离某个引脚的中断回调函数
pin_irq_enable 开启或关闭引脚中断

🚗注册

🚀🚀之后我们再使用以下的接口注册到PIN设备驱动框架中,其中参数和返回值我已给出。

int rt_device_pin_register	(	const char * 				name,const struct rt_pin_ops * 	ops,void * 						user_data 
)	
参数 描述
name PIN设备名称
ops PIN设备操作方法对象指针
user_data 用户数据,一般设为RT_NULL
返回 描述
RT_EOK 注册成功
-RT_ERROR 注册失败,已有其他驱动使用该name注册。

访问PIN设备

🚀🚀接下来我们就需要来访问我们的PIN设备了,主要就是通过PIN设备管理接口去调用我们刚刚实现的PIN设备的操作方法,但是在一般的开发中,PIN设备的操作方法一般都是官方帮我们写好了(只是可能名称有点不一样,比如瑞萨的就是ra_开头的,我一开始看成了rt)我们只需要调用函数即可。接下来我们就来详细介绍一下。


🚗获取引脚编号

🚀🚀RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。有3种方式可以获取引脚编号: API 接口获取、使用宏定义或者是查看PIN 驱动文件,接下来我们来介绍一下。

使用 API

🚀🚀这个就是使用 rt_pin_get() 获取引脚编号,如下获取 P502 的引脚编号:

rt_base_t led1_pin = rt_pin_get(LED1_PIN);  /* 获取 P502 的引脚编号 */

🚀🚀我们的开发板就是通过这种方式,所以我们只介绍这种,后面的是从官方文档来的。


🚗设置引脚模式

🚀🚀引脚在使用前需要先设置好输入或者输出模式,通过如下函数完成:

void rt_pin_mode(rt_base_t pin, rt_base_t mode);
参数 描述
pin 引脚编号
mode 引脚工作模式

🚀🚀目前 RT-Thread 支持的引脚工作模式可取如所示的 5 种宏定义值之一,每种模式对应的芯片实际支持的模式需参考 PIN 设备驱动程序的具体实现:

#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       	 /* 开漏输出 */

🚀🚀使用示例如下所示:

#define LED1_PIN    "P502" /* Onboard LED pins *//* 获取引脚编号 */
rt_base_t led1_pin = rt_pin_get(LED1_PIN);
/* 引脚为输出模式 */
rt_pin_mode(led1_pin, PIN_MODE_OUTPUT);

🚗设置引脚电平

🚀🚀设置引脚输出电平的函数如下所示:

void rt_pin_write(rt_base_t pin, rt_base_t value);
参数 描述
pin 引脚编号
value 电平逻辑值,可取 2 种宏定义值之一:PIN_LOW 低电平,PIN_HIGH 高电平

🚀🚀使用示例如下所示:

#define LED1_PIN    "P502" /* Onboard LED pins *//* 获取引脚编号 */
rt_base_t led1_pin = rt_pin_get(LED1_PIN);
/* 引脚为输出模式 */
rt_pin_mode(led1_pin, PIN_MODE_OUTPUT);
/* 设置低电平 */
rt_pin_write(led1_pin, PIN_HIGH);

🚗读取引脚电平

🚀🚀读取引脚电平的函数如下所示:

int rt_pin_read(rt_base_t pin);
参数 描述
pin 引脚编号
返回 描述
PIN_LOW 低电平
PIN_HIGH 高电平

🚀🚀使用示例如下所示:

#define LED1_PIN    "P502" /* Onboard LED pins */
int status;/* 获取引脚编号 */
rt_base_t led1_pin = rt_pin_get(LED1_PIN);
/* 引脚为输出模式 */
rt_pin_mode(led1_pin, PIN_MODE_OUTPUT);
/* 设置低电平 */
rt_pin_write(led1_pin, PIN_HIGH);status = rt_pin_read(led1_pin);

🚗绑定引脚中断回调函数

🚀🚀因为介绍中断较少,这边就简单介绍一下基本函数,具体实现在代码体现。

🚀🚀若要使用到引脚的中断功能,可以使用如下函数将某个引脚配置为某种中断触发模式并绑定一个中断回调函数到对应引脚,当引脚中断发生时,就会执行回调函数:

rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,void (*hdr)(void *args), void *args);
参数 描述
pin 引脚编号
mode 中断触发模式
hdr 中断回调函数,用户需要自行定义这个函数
args 中断回调函数的参数,不需要时设置为 RT_NULL
返回 描述
RT_EOK 绑定成功
错误码 绑定失败

🚀🚀中断触发模式 mode 可取如下 5 种宏定义值之一:

#define PIN_IRQ_MODE_RISING 		0x00         /* 上升沿触发 */
#define PIN_IRQ_MODE_FALLING 		0x01       	 /* 下降沿触发 */
#define PIN_IRQ_MODE_RISING_FALLING 0x02 		 /* 边沿触发(上升沿和下降沿都触发)*/
#define PIN_IRQ_MODE_HIGH_LEVEL 	0x03   	 	 /* 高电平触发 */
#define PIN_IRQ_MODE_LOW_LEVEL 		0x04     	 /* 低电平触发 */

🚗使能引脚中断

🚀🚀绑定好引脚中断回调函数后使用下面的函数使能引脚中断:

rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);
参数 描述
pin 引脚编号
enabled 状态,可取 2 种值之一:PIN_IRQ_ENABLE(开启),PIN_IRQ_DISABLE(关闭)
返回 描述
RT_EOK 使能成功
错误码 使能失败

🚗脱离引脚中断回调函数

🚀🚀可以使用如下函数脱离引脚中断回调函数:

rt_err_t rt_pin_detach_irq(rt_int32_t pin);
参数 描述
pin 引脚编号
返回 ——
RT_EOK 脱离成功
错误码 脱离失败

🚀🚀引脚脱离了中断回调函数以后,中断并没有关闭,还可以调用绑定中断回调函数再次绑定其他回调函数。


代码

🚀🚀接下来,我们就看一下我们的代码,因为没有板载蜂鸣器,我就自己接了一个在P105引脚上(J1-11),实验现象就是灯亮的时候蜂鸣器刚好会闪一下,然后在FinSH控制台可以调用我们自己写的msh命令,在此之后,按下板载按键,LED2就会翻转。

#include <rtthread.h>
#include "hal_data.h"
#include <rtdevice.h>#define LED1_PIN    "P502" /* Onboard LED1 pins */
#define LED2_PIN    "P501" /* Onboard LED2 pins */
#define BEEP_PIN    "P105" /* 蜂鸣器 */
#define KEY_INPUT   "P004"/* 按键 */void hal_entry(void)
{rt_kprintf("\\nHello RT-Thread!\\n");/* 获取引脚编号 */rt_base_t led1_pin = rt_pin_get(LED1_PIN);rt_base_t beep_pin = rt_pin_get(BEEP_PIN);while (1){			 /* 写高低电平,驱动灯和蜂鸣器 */rt_pin_write(led1_pin, PIN_HIGH);rt_pin_write(beep_pin, PIN_LOW);/* 延时 */rt_thread_mdelay(500);rt_pin_write(led1_pin, PIN_LOW);rt_pin_write(beep_pin, PIN_HIGH);rt_thread_mdelay(500);}
}
/* 回调函数,LED2翻转 */
void irq_callback_test(void *args){rt_base_t led2_pin = rt_pin_get(LED2_PIN);rt_pin_write(led2_pin, !rt_pin_read(led2_pin));  
}void icu_sample(void)
{/* init *//* 获取引脚编号 */rt_base_t pin = rt_pin_get(KEY_INPUT);rt_kprintf("\\n pin : 0x%04X \\n", pin);/* 绑定引脚中断回调函数 */rt_err_t err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, irq_callback_test, RT_NULL);if (RT_EOK != err){rt_kprintf("\\n attach irq failed. \\n");}/* 使能引脚中断 */err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE);if (RT_EOK != err){rt_kprintf("\\n enable irq failed. \\n");}
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(icu_sample, icu sample);

🚀🚀现象如下所示:

RT