After talking about the UART device, we are already familiar with the RT-Thread I/O device model, let's look back at the basic PIN device.

Table of contents

foreword
1. Analysis of PIN device model

1.1 First understanding of GPIO operation functions
1.2 PIN Device Framework
1.3 PIN device driver framework layer
Implemented functions
PIN device control block
register function
1.4 PIN device driver layer
Implemented functions
initialization function
☆Pin definition☆

2. PIN device operation function

2.1 How to get PIN index number
2.2 Operation function
2.2.1 Set GPIO mode
2.2.2 Set/read GPIO level
2.2.3 Binding/disengaging interrupt callback function
2.2.4 Enable interrupt

3. Example of PIN device
epilogue

foreword

We learn the most basic GPIO port of an MCU, which is the PIN device model. We have not talked about it yet. As for the reason, we have mentioned it before, because the operation function of the PIN device is different from the general function name of the I/O device model we introduced. Too corresponding, for novices, it may be difficult to understand the PIN device first.

So in the previous article, we first talked about the UART device model, analyzed the design ideas of the UART device from the source code, from the device driver layer, and the device driver framework layer to the I/O device management layer, and finally to the application layer, we all understand Go through it again.

With the previous experience, we will learn about RT-Thread PIN devices in this article.

❤️
The development environment recorded in this RT-Thread column:
RT-Thread record (1. RT-Thread version, RT-Thread Studio development environment and quick start with CubeMX development)
RT-Thread record (2. RT-Thread kernel startup process – startup file and source code analysis)
❤️
RT-Thread Equipment Series blog post link:
RT-Thread records (10. Comprehensive understanding of RT-Thread I/O device model)
RT-Thread record (11. UART device of I/O device model – source code analysis)
RT-Thread record (12. UART device of I/O device model – use test)

1. Analysis of PIN device model

It has been said that the PIN device is a bit special, and it feels a little different from the device when we talk about the I/O device model. So what is the special method? We still need to analyze in detail:

1.1 First understanding of GPIO operation functions

Let’s start from the upper I/O device management layer to see what interfaces the PIN device management layer provides to access GPIO:

We can find that the operation function of the above PIN device management interface is completely different from the general function we will use, as shown in the figure below:

This is why we did not talk about the PIN device first when we gave an example of the device. I am afraid that many friends will not understand it at the beginning, so why is this so?

1.2 PIN Device Framework

Through the analysis of the previous UART device, we already know the basic framework of the device. First, let’s take a look at the UART device framework mentioned in the previous article:

For PIN devices, the framework is summarized in the following diagram:

❤️ It has always been said that the PIN device is a bit special, it is just because the application in the official description does not call the interface function of the I/O device management layer, but directly calls the interface function of the PIN device driver framework layer:

Knowing this, in fact, we don’t need to do too much analysis. For the specific process analysis, you can refer to the previous blog posts. We only need to have a brief understanding of the interface between the PIN device driver framework layer and the device driver layer. After all The operation of GPIO is still very simple.

1.3 PIN device driver framework layer

Through the above description, we know that the use of the PIN device is the interface of the device driver framework layer called directly, so let’s take a look at the file of the PIN device driver framework layer (pin.c) which function interface:

Implemented functions

// Private 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)// callable 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);

Pick a function for a quick look:

/* 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);}

The function first asserts whether the _hw_pin.ops structure is valid, and if it is valid, sets the pin mode.

The first parameter is the index number of the pin (this will explain what the index number is when we explain the PIN device driver layer below),
The second parameter is the pin mode (the specific mode will be explained uniformly when we explain the GPIO settings below).

PIN device control block

In RT-Thread, the PIN device is an object, so there must be its object control block, which is the same as all the objects we have learned before.pin.hObject structure with PIN device in:

/* pin device and operations for RT-Thread */struct rt_device_pin{    struct rt_device parent;  // rt_device As we mentioned earlier, the parent class of all devices is 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); };

❤️ The access functions of the PIN device are all structure members in the PIN device control blockopsIt is implemented in and is also associated with the underlying driver layer through this structure member – defined in the device driver layerrt_pin_opsA variable of type that implements these operation functions.

register function

When the PIN device is initialized,rt_hw_pin_init()will callrt_device_pin_registerThe function initializes the PIN device.

PIN device registration function, using this registration function, can bind the functions of the underlying driver layer, and at the same time provide the device interface to the upper layer I/O device management layer:

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 device does not need _hw_pin.parent.open = RT_NULL; // _hw_pin.parent.close = RT_NULL; // _hw_pin.parent.read = _pin_read; / /* Bind the read operation of the device to the _pin_read function of pin.c */ _hw_pin.parent.write = _pin_write; // Same as above _hw_pin.parent.control = _pin_control; // Same as above #endif/* put drv_gpio.c The implemented _stm32_pin_ops is bound to _hw_pin.ops because the registration function used by the PIN device driver layer is: 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 /* register it into the device frame */ */ rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR); return 0;}

In the registration function:_hw_pin.ops = ops;This operation associates the hardware operation function implemented by the device driver layer with the device driver framework layer.

The officially described interface for PIN device access is the function interface provided at the device driver framework layer.

But we see:

_hw_pin.parent.read = _pin_read; // Bind the read operation of the device to the _pin_read function of pin.c _hw_pin.parent.write = _pin_write; _hw_pin.parent.control = _pin_control;

This means that we can not only usert_pin_readTo get the value of a PIN device, you can also usert_device_readGet the value of the PIN device! ! !

❤️ In RT-Thread’s PIN device model,rt_pin_readfunction andrt_device_readfunction has the same effect.

1.4 PIN device driver layer

The PIN device driver layer is the level that directly deals with the hardware. For the STM32 we use, we should be familiar with many operations in it. We also briefly understand the functions inside. The main purpose is to realize the PIN device control block.rt_pin_opsSeveral functions in the member:

Implemented functions

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)/* An important structure */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)... // A series of external interrupt functions... int rt_hw_pin_init(void)

Let’s take a brief look at a function that doesn’t need too much explanation at all:

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);}

initialization function

Although the initialization function is important, it is simple, and you can understand it at a glance. The first is the familiar GPIO clock initialization.

Then call the device registration function. The device name pin is also defined here. If it is changed to other names, other names will be displayed when using list_device in the shell tool.

The second parameter is to associate the hardware operation function implemented in the device driver layer with the PIN device driver framework layer for use by the application program.

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);}

☆Pin definition☆

In the driver file, we need to explain the definition of GPIO pins (STM32 as an example).

Different from UART, GPIO configuration is simple and can be more directly associated with hardware, so the HAL library does not provide a handle structure description for GPIO. Two parameters are used to describe GPIO in the HAL library: GPIO_TypeDef* GPIOx and GPIO_Pin, for example:

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);

In RT-Thread, it defines a structurepin_index, a GPIO can be described by a variable, as follows:

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

For this structure, some supplementary operations are provided in the driver:

/*It is equivalent to the structure pin_index being initialized in the form of macro definition, using C language string connection to define the pin information index, the index number of the pin, the pin number defined by the driver that can be defined by the user gpio is equivalent to the HAL library GPIO_TypeDef gpio_index is equivalent to GPIO_Pin in the HAL library. For example, the macro __STM32_PIN(0, A, 0) means that the structure content is {0, GPIOA, GPIO_PIN_0}*/#define __STM32_PIN(index, gpio, gpio_index) \ { \ index, GPIO##gpio, GPIO_PIN_##gpio_index \ }//Reserve unused macro definitions, some IO ports are not used, use this macro definition #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), //Omit a lot later...

First the macro definition#define __STM32_PIN(index, gpio, gpio_index)

in##A connector in C language, its function is to connect two substrings (tokens) in a macro definition with parameters to form a new substring, such as macro__STM32_PIN(0, A, 0)It means that the content of the structure is{0, GPIOA, GPIO_PIN_0}, which is equivalent to defining apin_indexstructure.

Then the macro definition__STM32_PIN_RESERVE

Reserved IO floor, some IO ports are not used, use this macro definition

The next array of structurespins

pinsforpin_indexAn array of structure types, used by RT-ThreadpinsThe array initializes and defines all GPIO pins.

In this way, the IO ports supported on the chip are all initialized and defined, and each GPIO has a corresponding index number index.
In the PIN device operation function provided by RT-Thread void rt_pin_mode(rt_base_t pin, rt_base_t mode);, its first parameter is not a data structure like the PIN device control block, but a pin index number, which is Corresponding to the above index.

The analysis of pin interrupt is similar to the pin definition, you can check the code by yourself, so I won’t explain too much here.

2. PIN device operation function

At the beginning of the article, although we have already known the operation function of the PIN device, we did not explain the possible values ​​​​of the function parameters. Learning the use of the API is still the same, directly put the function prototype and then read the comments.

2.1 How to get PIN index number

When we use a GPIO,The first step is to get the index number of GPIO, which is mentioned aboveindex. Because all access operations to the PIN device are performed through this index number.

In RT-Thread, there are 3 ways to get the PIN device index number:

Method 1: Use a functionrt_pin_get()

existpin.cThere is a function provided in the file:

rt_base_t rt_pin_get(const char *name)

The parameter inside is a name, so what is this name? There is a comment in the function declaration:

For STM32, the usage example is as follows:

// Get the index number pin_number = rt_pin_get("PA.9"); // pin_number is the index number // set the GPIO mode rt_pin_mode(pin_number , PIN_MODE_INPUT_PULLUP);

Method 2: Use macro definitionGET_PIN

existdrv_common.hThere are macro definitions in the file, you can directly get the index number of 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)

For STM32, the usage example is as follows:

//Get the index number #define LED0_PIN GET_PIN(B, 9)//LED0 is on or off #define LED0(n)(n ? rt_pin_write(LED0_PIN, PIN_HIGH) : rt_pin_write(LED0_PIN, PIN_LOW))

Method 3: Check the driver filedrv_gpio.c

As mentioned above when explaining the PIN device driver layer, the index numbers corresponding to all GPIOs will be defined in the driver file, and you can directly view the file and use the index number:

For STM32, the usage example is as follows:

//Corresponding to the driver file, the meaning of the following code is to set the mode of PA0 to PIN_MODE_INPUT_PULLUPrt_pin_mode(0, PIN_MODE_INPUT_PULLUP);

Note that the way to view driver files is not intuitive.

2.2 Operation function

The operation function description is the same

2.2.1 Set GPIO mode

/*parameter description pin number: index number mode pin working mode working mode optional: #define PIN_MODE_OUTPUT 0x00 output #define PIN_MODE_INPUT 0x01 input #define PIN_MODE_INPUT_PULLUP 0x02 pull-up input #define PIN_MODE_INPUT_PULLDOWN 0x03 pull-down input #define PIN_MODE_INPUT_PULLUP 0x03 pull-down input #define PIN_MODE_INPUT_PULLUP 0x03 pull-down input output */void rt_pin_mode(rt_base_t pin, rt_base_t mode);

2.2.2 Set/read GPIO level

Set pin level:

/*Parameter description pin pin number value level logic value, value value: PIN_LOW low level, PIN_HIGH high level*/void rt_pin_write(rt_base_t pin, rt_base_t value);

Read pin level:

/*parameter description pin number returns PIN_LOW low level PIN_HIGH high level*/int rt_pin_read(rt_base_t pin);

2.2.3 Binding/disengaging interrupt callback function

Binding interrupt callback function:

/* pin pin number mode interrupt trigger mode hdr interrupt callback function, users need to define the function args interrupt callback function parameters, if no need to set to RT_NULL return -- RT_EOK binding success error code binding failure mode is optional: #define PIN_IRQ_MODE_RISING 0x00 #define PIN_IRQ_MODE_FALLING 0x01 Falling edge #define PIN_IRQ_MODE_RISING_FALLING 0x02 Edge trigger (both rising and falling edge trigger) #define PIN_IRQ_MODE_HIGH_LEVEL 0x03 High Level trigger #define PIN_IRQ_MODE_LOW_LEVEL 0x04 Low level trigger */rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,                             void (*hdr)(void *args), void  *args);

Break away from the interrupt callback function:

/*parameter description pin pin number return——RT_EOK detachment success error code detachment failure*/rt_err_t rt_pin_detach_irq(rt_int32_t pin);

Note: After the pin is separated from the interrupt callback function, the interrupt is not closed, and the binding interrupt callback function can be called to bind other callback functions again.

2.2.4 Enable interrupt

After binding the pin interrupt callback function, you need to use the following function to enable the pin interrupt:

/*parameter description pin pin number enabled status return——RT_EOK enable success error code enable failure enabled can take one of two values: PIN_IRQ_ENABLE (on) PIN_IRQ_DISABLE (off)*/rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);

3. Example of PIN device

As long as you understand the principle of the PIN device model, it is still very simple to use. Let’s take a look at the schematic diagram first:

The program is as follows, the test is OK, too simple so there is nothing to say:

. // Add these header files #include #include "board.h"... static struct rt_thread led1_thread;    //led1 thread 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); // Dejitter 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();  // Use the device model without initializing this /* configuration LED pin for output */ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);    /* Configure the key as */ rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT); rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT);    /*LED default state */ 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 you get a thread control block, start the thread */ if (key1_thread! = RT_NULL)            rt_thread_startup(key1_thread);    . // The following is omitted

epilogue

In this article, we have analyzed the PIN device of the RT-Thread I/O device model in detail. In the end, it seems that the operation of using the PIN device model is very simple.

In fact, the key part is to understand the principle of the PIN device model, and it will be easier to use after understanding.

Although the GPIO device is simple, it takes more than 1W to write the article. Even if the PIN device was a bit vague before, as long as you read this article, I believe everyone will have the feeling of seeing the sun behind the clouds!

I hope you will support us a lot! That’s all for this article, thank you!

Reviewing editor: Tang Zihong

Leave a Reply

Your email address will not be published.