写点什么

RT-Thread 记录(十三、I/O 设备模型之 PIN 设备)

作者:矜辰所致
  • 2022 年 8 月 23 日
    江苏
  • 本文字数:9611 字

    阅读完需:约 32 分钟

RT-Thread记录(十三、I/O 设备模型之PIN设备)
讲完UART设备之后,我们已经熟悉RT-Thread I/O 设备模型了,回头看看基本的 PIN 设备。
复制代码


前言

我们学习一个 MCU 的最基本的 GPIO 口,也就是 PIN 设备模型,我们还没有讲过,至于原因之前也说了,因为 PIN 设备的操作函数与我们介绍的 I/O 设备模型的通用函数名称不太对应,对于新手来说先将 PIN 设备可能会让人难以理解。


所以前面的文章我们先讲了 UART 设备模型,从源码分析了一下 UART 设备的设计思路,从设备驱动层,和设备驱动框架层再到 I/O 设备管理层,最后到应用层,我们都理过一遍。


有了前面的经验,本文我们就来学习了解 RT-Thread PIN 设备 。


本 RT-Thread 专栏记录的开发环境:

RT-Thread 记录(一、RT-Thread 版本、RT-Thread Studio 开发环境 及 配合 CubeMX 开发快速上手)

https://xie.infoq.cn/article/44be1057caace7a6a2c4c4b59

RT-Thread 记录(二、RT-Thread 内核启动流程 — 启动文件和源码分析)

https://xie.infoq.cn/article/44be1057caace7a6a2c4c4b59

RT-Thread 设备篇系列博文链接:

RT-Thread 记录(十、全面认识 RT-Thread I/O 设备模型)

https://xie.infoq.cn/article/40536d29988d683c78b4ba5ff

RT-Thread 记录(十一、I/O 设备模型之 UART 设备 — 源码解析)

https://xie.infoq.cn/article/38bb4bf15cb81f1fdb060b29e

RT-Thread 记录(十二、I/O 设备模型之 UART 设备 — 使用测试)

https://xie.infoq.cn/article/1df47f3ae5174dfcb418b07b6

一、PIN 设备模型解析

一直说到 PIN 设备有点特殊,和我们讲 I/O 设备模型时候的设备感觉有一点区别的,那么到底怎么个特殊法?我们还是需要具体来分析一下:

1.1 初识 GPIO 操作函数

我们还是从上层的 I/O 设备管理层来开始,看看 PIN 设备管理层提供的访问 GPIO 的接口有哪些:



我们可以发现,上面的 PIN 设备管理接口的操作函数,与我们将的通用的函数完全不一样,如下图:



这也是为什么我们将设备示例的时候没有先讲 PIN 设备的原因,怕很多小伙伴刚开始不理解,那么为什么会这样呢?

1.2 PIN 设备框架

我们通过前面的 UART 设备的分析,已经知道了设备的基本的框架了,首先我们来看一下 上一篇文章讲到的 UART 设备框架:



对于 PIN 设备来说,框架总结如下图表:



前面一直说 PIN 设备有点特别,那只不过是因为官方说明中 应用程序调用的不是 I/O 设备管理层的接口函数,而是直接调用的 PIN 设备驱动框架层的接口函数:


(下图中的说明有点欠缺,实际上其实也可以直接使用 device.c 中的接口对 PIN 设备进行控制的,这个我们后面会说明)



知道了这一点的话,其实我们都不需要进行过多的分析,具体的过程分析可以查看前面几篇博文,我们这里只需要对 PIN 设备驱动框架层 和 设备驱动层的接口简单的了解一下,毕竟 GPIO 的操作还是很简单的。

1.3 PIN 设备驱动框架层

通过上面的说明,我们知道 PIN 设备的使用是直接调用的 设备驱动框架层的接口,所以我们来看看 PIN 设备驱动框架层的文件(pin.c)有哪些函数接口:

实现的函数

//私有的static struct rt_device_pin _hw_pin;static rt_size_t _pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)static rt_size_t _pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)static rt_err_t  _pin_control(rt_device_t dev, int cmd, void *args)
//可调用的int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data);
/* Get pin number by name,such as PA.0,P0.12 */rt_base_t rt_pin_get(const char *name);void rt_pin_mode(rt_base_t pin, rt_base_t mode);void rt_pin_write(rt_base_t pin, rt_base_t value);int rt_pin_read(rt_base_t pin);rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args);rt_err_t rt_pin_detach_irq(rt_int32_t pin);rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);
复制代码


挑一个函数简单看看:


/* RT-Thread Hardware PIN APIs */void rt_pin_mode(rt_base_t pin, rt_base_t mode){    RT_ASSERT(_hw_pin.ops != RT_NULL);    _hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);}
复制代码


函数先断言判断_hw_pin.ops 这个结构体是否有效,有效的情况下就设置引脚的模式。


第一个参数是引脚的索引号(这个在我们下面讲解 PIN 设备驱动层的时候会有说明什么是索引号),第二个参数是引脚模式(具体的模式我们也会再下面讲解 GPIO 设置时候统一说明)。

PIN 设备控制块

在 RT-Thread 中 PIN 设备作为一个对象,那么肯定有他的对象控制块,和我们前面学习的所有的对象一样,在pin.h中有 PIN 设备的对象结构体:


/* pin device and operations for RT-Thread */struct rt_device_pin{    struct rt_device parent;  // rt_device 我们前面讲过的,所有 device 的父类    const struct rt_pin_ops *ops;};
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);
/* TODO: add GPIO interrupt */ 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 设备的访问函数都是在 PIN 设备控制块中的结构体成员 ops 中实现的,也是通过这个结构体成员与 底层驱动层关联起来 —— 在设备驱动层定义rt_pin_ops类型的变量,实现这些操作函数。

注册函数

在 PIN 设备初始化的时候,rt_hw_pin_init()会调用 rt_device_pin_register 函数进行 PIN 设备的初始化。


PIN 设备注册函数,使用这个注册函数,可以绑定底层驱动层的函数,也同时将设备接口提供给上层 I/O 设备管理层:


int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data){    _hw_pin.parent.type         = RT_Device_Class_Miscellaneous;    _hw_pin.parent.rx_indicate  = RT_NULL;    _hw_pin.parent.tx_complete  = RT_NULL;
#ifdef RT_USING_DEVICE_OPS _hw_pin.parent.ops = &pin_ops;#else _hw_pin.parent.init = RT_NULL; //PIN 设备不需要 _hw_pin.parent.open = RT_NULL; // _hw_pin.parent.close = RT_NULL; // _hw_pin.parent.read = _pin_read; //* 把设备的read操作绑定在pin.c的_pin_read函数 */ _hw_pin.parent.write = _pin_write; //同上 _hw_pin.parent.control = _pin_control; //同上#endif /* 把drv_gpio.c所实现的_stm32_pin_ops绑定在_hw_pin.ops上 因为 PIN 设备驱动层使用的注册函数为: rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL); */ _hw_pin.ops = ops; _hw_pin.parent.user_data = user_data;
/* register a character device /* 将其注册进device设备框架中 */ */ rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);
return 0;}
复制代码


在注册函数中:_hw_pin.ops = ops; 这个操作就把设备驱动层实现的硬件操作函数给关联到了 设备驱动框架层。


官方说明的 PIN 设备访问的接口就是在 设备驱动框架层 提供的函数接口。


但是我们看到:


    _hw_pin.parent.read         = _pin_read; //把设备的read操作绑定在pin.c的_pin_read函数     _hw_pin.parent.write        = _pin_write;    _hw_pin.parent.control      = _pin_control;
复制代码


这说明我们不仅可以使用 rt_pin_read 获取 PIN 设备的值,还可以使用 rt_device_read 获取 PIN 设备的值!!!


在 RT-Thread 的 PIN 设备模型中, rt_pin_read 函数和 rt_device_read 函数效果一样。

1.4 PIN 设备驱动层

PIN 设备驱动层,直接与硬件打交道的层面,对于我们使用的 STM32 来说,里面的很多操作我们应该都不会陌生,我们也简单了解下里面的函数,主要的目的在于实现 PIN 设备控制块中 rt_pin_ops 成员中的几个函数:

实现的函数

static const struct pin_index *get_pin(uint8_t pin)static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)static int stm32_pin_read(rt_device_t dev, rt_base_t pin)static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
rt_inline rt_int32_t bit2bitno(rt_uint32_t bit)rt_inline const struct pin_irq_map *get_pin_irq_map(uint32_t pinbit)
static rt_err_t stm32_pin_attach_irq(struct rt_device *device, rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args)static rt_err_t stm32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled)
/*一个重要的结构体*/const static struct rt_pin_ops _stm32_pin_ops ={ stm32_pin_mode, stm32_pin_write, stm32_pin_read, stm32_pin_attach_irq, stm32_pin_dettach_irq, stm32_pin_irq_enable,};
rt_inline void pin_irq_hdr(int irqno)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)void EXTI0_IRQHandler(void)...//一系列的外部中断函数...int rt_hw_pin_init(void)
复制代码


我们简单来看一个函数,根本不需要过多的解释:


static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value){    const struct pin_index *index;
index = get_pin(pin); if (index == RT_NULL) { return; }
HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value);}
复制代码

初始化函数

初始化函数虽然重要,但是简单,看一下就能明白,首先就是熟悉的 GPIO 时钟初始化,


然后就是调用设备注册函数,设备名称 pin ,也是在这里定义的,如果改成其他的,在 shell 工具中使用 list_device 就会显示其他的名称了。


第二个参数,就是将设备驱动层中实现的对硬件的操作函数关联到 PIN 设备驱动框架层以供应用程序使用用。


int rt_hw_pin_init(void){#if defined(__HAL_RCC_GPIOA_CLK_ENABLE)    __HAL_RCC_GPIOA_CLK_ENABLE();#endif    #if defined(__HAL_RCC_GPIOB_CLK_ENABLE)    __HAL_RCC_GPIOB_CLK_ENABLE();#endif    #if defined(__HAL_RCC_GPIOC_CLK_ENABLE)    __HAL_RCC_GPIOC_CLK_ENABLE();#endif    #if defined(__HAL_RCC_GPIOD_CLK_ENABLE)    __HAL_RCC_GPIOD_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOE_CLK_ENABLE) __HAL_RCC_GPIOE_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOF_CLK_ENABLE) __HAL_RCC_GPIOF_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOG_CLK_ENABLE) #ifdef SOC_SERIES_STM32L4 HAL_PWREx_EnableVddIO2(); #endif __HAL_RCC_GPIOG_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOH_CLK_ENABLE) __HAL_RCC_GPIOH_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOI_CLK_ENABLE) __HAL_RCC_GPIOI_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOJ_CLK_ENABLE) __HAL_RCC_GPIOJ_CLK_ENABLE();#endif
#if defined(__HAL_RCC_GPIOK_CLK_ENABLE) __HAL_RCC_GPIOK_CLK_ENABLE();#endif
return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);}
复制代码

☆引脚定义☆

在驱动文件中,关于 GPIO 引脚的定义方式(STM32 为例),我们有必要说明一下。


与 UART 不同的是,GPIO 配置简单能够更直接关联硬件,所以 HAL 库并没有为 GPIO 提供句柄结构体描述,在 HAL 库中描述 GPIO 使用了两个参数:GPIO_TypeDef* GPIOxGPIO_Pin,比如:


GPIO_PinState     HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void              HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码


而在 RT-Thread 中,其定义了一个结构体 pin_index,通过一个变量即可描述一个 GPIO,如下:


/* STM32 GPIO driver */struct pin_index{    int index;    GPIO_TypeDef *gpio;    uint32_t pin;};
复制代码


针对这个结构体,驱动程序中给了一些补充的操作:


/*相当于结构体pin_index以宏定义的形式被初始化用C语言字符串连接定义引脚信息index     引脚的索引号,用户可自行定义的驱动程序定义的引脚编号gpio       相当于HAL库中的GPIO_TypeDef gpio_index   相当于HAL库中的GPIO_Pin
例如宏__STM32_PIN(0, A, 0) 就表示结构体内容为 {0, GPIOA, GPIO_PIN_0}*/
#define __STM32_PIN(index, gpio, gpio_index) \ { \ index, GPIO##gpio, GPIO_PIN_##gpio_index \ }
//保留未使用的宏定义,有些IO口未使用,使用这个宏定义#define __STM32_PIN_RESERVE \ { \ -1, 0, 0 \ }
static const struct pin_index pins[] = {#if defined(GPIOA) __STM32_PIN(0 , A, 0 ), __STM32_PIN(1 , A, 1 ), __STM32_PIN(2 , A, 2 ), __STM32_PIN(3 , A, 3 ), __STM32_PIN(4 , A, 4 ), __STM32_PIN(5 , A, 5 ), __STM32_PIN(6 , A, 6 ), __STM32_PIN(7 , A, 7 ), __STM32_PIN(8 , A, 8 ), __STM32_PIN(9 , A, 9 ), __STM32_PIN(10, A, 10), __STM32_PIN(11, A, 11), __STM32_PIN(12, A, 12), __STM32_PIN(13, A, 13), __STM32_PIN(14, A, 14), __STM32_PIN(15, A, 15),#if defined(GPIOB) __STM32_PIN(16, B, 0), __STM32_PIN(17, B, 1),
//后面省略很多......
复制代码


首先宏定义#define __STM32_PIN(index, gpio, gpio_index)


其中##为 C 语言连接符,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串,例如宏__STM32_PIN(0, A, 0) 就表示结构体内容为 {0, GPIOA, GPIO_PIN_0},就等于定义了一个pin_index结构体。


然后宏定义__STM32_PIN_RESERVE


预留的 IO 楼,有些 IO 口未使用,使用这个宏定义


接下来的结构体数组pins


pinspin_index结构体类型的数组,RT-Thread 使用 pins 数组对 所有的 GPIO 引脚进行初始化定义。


这样就相当于芯片上所支持的 IO 口都进行了初始化定义,每一个 GPIO 都有了一个对应的索引号index。在 RT-Thread 提供的 PIN 设备操作函数中void rt_pin_mode(rt_base_t pin, rt_base_t mode);, 他的第一个参数也不是类似 PIN 设备控制块之类的数据结构,而是一个引脚索引号,就是对应的上面这个index


引脚中断的分析和 引脚定义类似,可自行查看代码,这里就不过多说明。


.

二、PIN 设备操作函数

文章开头我们虽然已经认识过 PIN 设备的操作函数,但是我们没有对函数参数可取值做说明,学习 API 的使用还是老样子,直接放函数原型然后看注释。

2.1 获取 PIN 索引号方法

在我们使用某个 GPIO 的时候,<font color=#0033FF>第一步要做的就是获取 GPIO 的索引号,即上文说到的index。因为对 PIN 设备的访问操作都是通过这个索引号进行的。


在 RT-Thread 中,提供了 3 种方式获取 PIN 设备索引号:


方法一: 使用函数rt_pin_get()


pin.c 文件中提供了一个函数:


rt_base_t rt_pin_get(const char *name)
复制代码


里面的参数为一个名字,那么这个名字是什么呢?在函数申明有注释:



对于 STM32 而言,使用示例如下:


//获取索引号pin_number = rt_pin_get("PA.9"); // pin_number 就是索引号//设置GPIO模式rt_pin_mode(pin_number , PIN_MODE_INPUT_PULLUP);
复制代码


方法二: 使用宏定义GET_PIN


drv_common.h文件中有宏定义,可以直接获取 GPIO 的索引号:


#define __STM32_PORT(port)  GPIO##port##_BASE#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
复制代码



对于 STM32 而言,使用示例如下:


//获取索引号#define LED0_PIN        GET_PIN(B,  9)
//LED0 点亮或者熄灭#define LED0(n) (n ? rt_pin_write(LED0_PIN, PIN_HIGH) : rt_pin_write(LED0_PIN, PIN_LOW))
复制代码


方法三: 查看驱动文件drv_gpio.c


上面讲解 PIN 设备驱动层的时候说到过,所有的 GPIO 对应的索引号都会在驱动文件中定义,直接查看文件使用索引号就可以:



对于 STM32 而言,使用示例如下:


//对应驱动文件,下面的代码含义就是 设置 PA0 的模式为 PIN_MODE_INPUT_PULLUPrt_pin_mode(0, PIN_MODE_INPUT_PULLUP);
复制代码


说明,查看驱动文件的方式并不直观。

2.2 操作函数

操作函数说明老样子

2.2.1 设置 GPIO 模式

/*参数   描述pin   引脚编号:索引号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 开漏输出*/void rt_pin_mode(rt_base_t pin, rt_base_t mode);
复制代码

2.2.2 设置/ 读取 GPIO 电平

设置引脚电平:


/*参数   描述pin   引脚编号value   电平逻辑值,
value 取值:PIN_LOW 低电平,PIN_HIGH 高电平*/void rt_pin_write(rt_base_t pin, rt_base_t value);
复制代码


读取引脚电平:


/*参数   描述pin   引脚编号返回   PIN_LOW   低电平PIN_HIGH   高电平*/int  rt_pin_read(rt_base_t pin);
复制代码

2.2.3 绑定/脱离中断回调函数

绑定中断回调函数:


/*参数   描述pin   引脚编号mode   中断触发模式hdr   中断回调函数,用户需要自行定义这个函数args   中断回调函数的参数,不需要时设置为 RT_NULL返回   ——RT_EOK   绑定成功错误码   绑定失败
其中 mode 可选参数:#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_attach_irq(rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args);
复制代码


脱离中断回调函数:


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


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

2.2.4 使能中断

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


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

三、PIN 设备示例

只要明白了 PIN 设备模型原理,使用起来还是很简单的,我们先看一下原理图:



程序如下,测试 OK,太简单所以没有什么好说的:


...//添加这两个头文件#include <rtdevice.h>#include "board.h"...
static struct rt_thread led1_thread; //led1线程static char led1_thread_stack[256];
static rt_thread_t key1_thread = RT_NULL; //
#define LED1_PIN GET_PIN(D, 9)#define LED2_PIN GET_PIN(D, 8)#define KEY1_PIN GET_PIN(D, 11)#define KEY2_PIN GET_PIN(D, 10)
#define key1_read rt_pin_read(KEY1_PIN)#define LED1_ON rt_pin_write(LED1_PIN, PIN_LOW);#define LED1_OFF rt_pin_write(LED1_PIN, PIN_HIGH);#define LED2_ON rt_pin_write(LED2_PIN, PIN_LOW);#define LED2_OFF rt_pin_write(LED2_PIN, PIN_HIGH);//#define LED0(n) (n ? rt_pin_write(LED0_PIN, PIN_HIGH) : rt_pin_write(LED0_PIN, PIN_LOW))
static void led1_thread_entry(void *par){ while(1){ LED1_ON; rt_thread_mdelay(1000); LED1_OFF; rt_thread_mdelay(1000); }}
static void key1_thread_entry(void *par){ while(1){ if(key1_read == 0){ rt_thread_mdelay(10); //去抖动 if(key1_read == 0){ rt_kprintf("key1 kicked..\r\n"); } while(key1_read == 0){rt_thread_mdelay(10);//去抖动 } } rt_thread_mdelay(1); }}
int main(void){
MX_USART1_UART_Init(); // MX_GPIO_Init(); //使用设备模型不需要初始化这个 /*配置LED管脚为输出*/ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT); /*配置按键为输入*/ rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT); rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT); /*LED默认状态*/ rt_pin_write(LED1_PIN, 1); rt_pin_write(LED2_PIN, 0); rt_err_t rst2; rst2 = rt_thread_init(&led1_thread, "led1_blink ", led1_thread_entry, RT_NULL, &led1_thread_stack[0], sizeof(led1_thread_stack), RT_THREAD_PRIORITY_MAX -1, 50);
if(rst2 == RT_EOK){ rt_thread_startup(&led1_thread); } key1_thread = rt_thread_create("key1_control", key1_thread_entry, RT_NULL, 512, RT_THREAD_PRIORITY_MAX -2, 50);
/* 如果获得线程控制块,启动这个线程 */ if (key1_thread != RT_NULL) rt_thread_startup(key1_thread); ...//后面省略
复制代码

结语

本文我们详细的分析了 RT-Thread I/O 设备模型之 PIN 设备,最终看来,使用 PIN 设备模型操作还是特别的简单的。


其实关键的部分还是在于理解 PIN 设备模型的原理,理解了以后使用起来也更加的得心应手。


GPIO 设备虽然简单,但是文章写下来也 1W 多字了,即便以前对 PIN 设备有点模糊,只要看了本文,相信大家肯定有拨云见日的感觉!


希望大家多多支持!本文就到这里,谢谢!

发布于: 刚刚阅读数: 4
用户头像

矜辰所致

关注

不浮夸,不将就,认真对待学知识的我们! 2022.08.02 加入

不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开! 为了活下去的嵌入式工程师,画画板子,敲敲代码,玩玩RTOS,搞搞Linux ...

评论

发布
暂无评论
RT-Thread记录(十三、I/O 设备模型之PIN设备)_RT-Thread_矜辰所致_InfoQ写作社区