当前位置:网站首页>2、GPIO相关操作

2、GPIO相关操作

2022-07-06 03:20:00 rou252051452

1、概述

        STM32的GPIO操作归类到RTT的pin设备中,本文参考RTT关于PIN设备的相关说明(RT-Thread 文档中心)进行编写及测试。共涉及到如下几个方面

  •         GPIO引脚的获取
  •         GPIO输出控制
  •         GPIO输入获取
  •         GPIO的外部中断实现

2、引脚编号的获取

       RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。官方提供了3种方式可以获取引脚编号: 

  • 使用API
  • 使用宏定义
  • 查看驱动文件

2.1驱动文件更新

        API获取方式通过函数rt_pin_get实现,程序如下

rt_base_t rt_pin_get(const char *name)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    RT_ASSERT(name[0] == 'P');

    if(_hw_pin.ops->pin_get == RT_NULL)
    {
        return -RT_ENOSYS;
    }

    return _hw_pin.ops->pin_get(name);
}

         查看初始化阶段对_hw_pin->ops的设置情况如下

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

          如上ops中并没有pin_get函数的实现方式,所以我们需要更新drv_gpio驱动文件。使用C:\RT-ThreadStudio\repo\Extract\Board_Support_Packages\RealThread\STM32H750-RT-ART-Pi\1.2.1\libraries\drivers的drv_gpio.c替换目录下得驱动文件,同步更新.h文件。查看更新后内容如下

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,
    stm32_pin_get,
};

2.2实现方式

2.2.1使用API


static rt_uint32_t reset_pin = 0;
reset_pin = rt_pin_get("PF.9");
rt_base_t rt_pin_get(const char *name)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    RT_ASSERT(name[0] == 'P');

    if(_hw_pin.ops->pin_get == RT_NULL)
    {
        return -RT_ENOSYS;
    }

    return _hw_pin.ops->pin_get(name);
}

如上所示,rt_pin_get首先对输入的字符串进行了严重是否为字母P开头,最终调用的函数为_hw_pin.ops->pin_get(name);内容如下:

static rt_base_t stm32_pin_get(const char *name)
{
    rt_base_t pin = 0;
    int hw_port_num, hw_pin_num = 0;
    int i, name_len;

    name_len = rt_strlen(name);    

    if ((name_len < 4) || (name_len >= 6))          //进行字符串长度验证PA.0   PA.15  最短4,最长5
    {
        return -RT_EINVAL;
    }
    if ((name[0] != 'P') || (name[2] != '.'))      //字符串第一个必须为P   第三个必须为.
    {
        return -RT_EINVAL;
    }

    if ((name[1] >= 'A') && (name[1] <= 'Z'))      //端口范围在A-Z
    {
        hw_port_num = (int)(name[1] - 'A');        //端口编号计算,A-Z转换为0-25
    }
    else
    {
        return -RT_EINVAL;
    }

    for (i = 3; i < name_len; i++)                //根据字符串的第4个进行编号转换PA.0=0  PA.15 = 15
    {
        hw_pin_num *= 10;
        hw_pin_num += name[i] - '0';
    }

    pin = PIN_NUM(hw_port_num, hw_pin_num);        //进行最后引脚编号的处理

    return pin;
}

 最终调用宏定义如下来进行引脚编号的获取

#define PIN_NUM(port, no) (((((port) & 0xFu) << 4) | ((no) & 0xFu)))

 测算结果如下表,后续IO以此类推

nameportnoresultnameportnoresult
PA.0000PC.02032
PA.1011PC.12133
PA.2022PC.22234
PA.3033PC.32335
PA.4044PC.42436
PA.5055PC.52537
PA.6066PC.62638
PA.7077PC.72739
PA.8088PC.82840
PA.9099PC.92941
PA.1001010PC.1021042
PA.1101111PC.1121143
PA.1201212PC.1221244
PA.1301313PC.1321345
PA.1401414PC.1421446
PA.1501515PC.1521547
PB.01016PD.03048
PB.11117PD.13149
PB.21218PD.23250
PB.31319PD.33351
PB.41420PD.43452
PB.51521PD.53553
PB.61622PD.63654
PB.71723PD.73755
PB.81824PD.83856
PB.91925PD.93957
PB.1011026PD.1031058
PB.1111127PD.1131159
PB.1211228PD.1231260
PB.1311329PD.1331361
PB.1411430PD.1431462
PB.1511531PD.1531563

2.2.2使用宏定义

#define LED0_PIN        GET_PIN(F,  9)
#define __STM32_PORT(port)  GPIO##port##_BASE

#if defined(SOC_SERIES_STM32MP1)
#define GET_PIN(PORTx,PIN) (GPIO##PORTx == GPIOZ) ? (176 + PIN) : ((rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x1000UL) )) + PIN))
#else
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
#endif
宏定义PORTx16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )PINresult
GET_PIN(A, 0)GPIOA_BASED3_AHB1PERIPH_BASE + 0x0000UL000
GET_PIN(A, 1)GPIOA_BASED3_AHB1PERIPH_BASE + 0x0000UL000
GET_PIN(A, 2)GPIOA_BASED3_AHB1PERIPH_BASE + 0x0000UL000
GET_PIN(A, 3)GPIOA_BASED3_AHB1PERIPH_BASE + 0x0000UL000
GET_PIN(B, 0)GPIOB_BASED3_AHB1PERIPH_BASE + 0x0400UL1016
GET_PIN(B, 1)GPIOB_BASED3_AHB1PERIPH_BASE + 0x0400UL1117
GET_PIN(B, 2)GPIOB_BASED3_AHB1PERIPH_BASE + 0x0400UL1218

        结果与API实现方式结果相同。

2.2.3查看驱动文件

        该方式在更新驱动文件drv_gpio后没有提供官方文档说明的查看驱动方式,这里不进行详细说明。

3、GPIO输出控制

        我们通过控制主控板上的LED灯来验证GPIO的输出功能。

#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"

#define LED0    GET_PIN(B, 0)

void example_gpio()
{
    rt_pin_mode(LED0,PIN_MODE_OUTPUT);      //设置管脚为输出
    rt_pin_write(LED0,PIN_LOW);
}

MSH_CMD_EXPORT(example_gpio, example_gpio)

4、GPIO输入控制

        通过板载按键来控制核心板的LED灯亮灭,按下后灯亮,松开后灯灭

#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"

#define LED0    GET_PIN(B, 0)

void example_gpio()
{
    static rt_uint32_t key = 0;

    rt_pin_mode(LED0,PIN_MODE_OUTPUT);          //设置管脚为输出
    rt_pin_write(LED0,PIN_LOW);

    key = rt_pin_get("PA.0");                   //获取引脚编号
    rt_pin_mode(key,PIN_MODE_INPUT);            //设置管脚为输入
    while(1)
    {
        if(rt_pin_read(key) == PIN_HIGH)        //读取输入状态
        {
            rt_pin_write(LED0,PIN_LOW);
        }
        else
        {
            rt_pin_write(LED0,PIN_HIGH);
        }
        rt_thread_mdelay(1000);
    }
}
MSH_CMD_EXPORT(example_gpio, example_gpio)

 5、GPIO中断

        通过板载按键来控制核心板的LED灯亮灭,按下后为上升沿中断,灯亮。松开后为下降沿中断,灯灭

6、驱动过程详解

6.1模式设置

        模式设置调用的函数为rt_pin_mode,最终调用的函数为ops->pin_mode,为drv_gpio.c中的stm32_pin_mode函数,具体内容如下

static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    //识别端口号是否合法
    if (PIN_PORT(pin) >= PIN_STPORT_MAX)        
    {
        return;
    }

    /*
        进行GPIO初始化结构体的相关赋值
        默认配置为推挽输出,高速,无上下拉
    */
    GPIO_InitStruct.Pin = PIN_STPIN(pin);   
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    /*
        根据输入的模式参数进行相关内容修改。
    */
    if (mode == PIN_MODE_OUTPUT)
    {
        /* output setting */
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    }
    else if (mode == PIN_MODE_INPUT)
    {
        /* input setting: not pull. */
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    }
    else if (mode == PIN_MODE_INPUT_PULLUP)
    {
        /* input setting: pull up. */
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
    }
    else if (mode == PIN_MODE_INPUT_PULLDOWN)
    {
        /* input setting: pull down. */
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    }
    else if (mode == PIN_MODE_OUTPUT_OD)
    {
        /* output setting: od. */
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
    }

    /*
        进行GPIO初始化,根据宏定义PIN_STPORT识别出GPIOx的地址
    */
    HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
}

6.2输出电平控制

        输出电平调用的函数为rt_pin_write,最终调用的函数为ops->pin_write,为drv_gpio.c中的stm32_pin_write函数,具体内容如下

static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
    GPIO_TypeDef *gpio_port;
    uint16_t gpio_pin;

    //根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输出
    if (PIN_PORT(pin) < PIN_STPORT_MAX)
    {
        gpio_port = PIN_STPORT(pin);
        gpio_pin = PIN_STPIN(pin);

        HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value);
    }
}

6.3输入电平获取

        输入电平获取调用的函数为rt_pin_read,最终调用的函数为ops->pin_read,为drv_gpio.c中的stm32_pin_read函数,具体内容如下

static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
    GPIO_TypeDef *gpio_port;
    uint16_t gpio_pin;
    int value = PIN_LOW;
     //根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输入信息的获取
    if (PIN_PORT(pin) < PIN_STPORT_MAX)
    {
        gpio_port = PIN_STPORT(pin);
        gpio_pin = PIN_STPIN(pin);
        value = HAL_GPIO_ReadPin(gpio_port, gpio_pin);
    }

    return value;
}

6.4绑定引脚中断回调函数

        绑定中断函数调用的是rt_pin_attach_irq,最终调用的函数为ops->pin_attach_irq,为drv_gpio.c中的stm32_pin_attach_irq函数,具体内容如下

        只进行了rtt下pin设备的中断管脚模式和中断回调函数的赋值,未进行实质性的STM32的底层HAL库配置。

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)
{
    rt_base_t level;
    rt_int32_t irqindex = -1;

    //识别端口是否正确
    if (PIN_PORT(pin) >= PIN_STPORT_MAX)
    {
        return -RT_ENOSYS;
    }

    //识别中断源为几  0-15
    irqindex = bit2bitno(PIN_STPIN(pin));
    if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
    {
        return RT_ENOSYS;
    }

    level = rt_hw_interrupt_disable();
    if (pin_irq_hdr_tab[irqindex].pin == pin &&
        pin_irq_hdr_tab[irqindex].hdr == hdr &&
        pin_irq_hdr_tab[irqindex].mode == mode &&
        pin_irq_hdr_tab[irqindex].args == args)
    {
        rt_hw_interrupt_enable(level);
        return RT_EOK;
    }
    if (pin_irq_hdr_tab[irqindex].pin != -1)
    {
        rt_hw_interrupt_enable(level);
        return RT_EBUSY;
    }
    pin_irq_hdr_tab[irqindex].pin = pin;
    pin_irq_hdr_tab[irqindex].hdr = hdr;
    pin_irq_hdr_tab[irqindex].mode = mode;
    pin_irq_hdr_tab[irqindex].args = args;
    rt_hw_interrupt_enable(level);

    return RT_EOK;
}

具体的HAL库底层驱动配置在rt_pin_irq_enable函数中实现,该函数最终调用函数为stm32_pin_irq_enable内容如下:

static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
                                     rt_uint32_t enabled)
{
    const struct pin_irq_map *irqmap;
    rt_base_t level;
    rt_int32_t irqindex = -1;
    GPIO_InitTypeDef GPIO_InitStruct;

    //识别IO是否合法
    if (PIN_PORT(pin) >= PIN_STPORT_MAX)
    {
        return -RT_ENOSYS;
    }

    //使能中断,进行是能的配置。
    if (enabled == PIN_IRQ_ENABLE)
    {
        //获取管脚序号
        irqindex = bit2bitno(PIN_STPIN(pin));
        if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
        {
            return RT_ENOSYS;
        }

        level = rt_hw_interrupt_disable();
        
        //pin_irq_hdr_tab的内容在stm32_pin_attach_irq时进行了赋值。
        if (pin_irq_hdr_tab[irqindex].pin == -1)
        {
            rt_hw_interrupt_enable(level);
            return RT_ENOSYS;
        }

        //根据管脚获取中断序号   EXTI0_IRQn-EXTI4_IRQn或EXTI9_5_IRQn
        irqmap = &pin_irq_map[irqindex];

        //配置GPIO初始化结构体
        /* Configure GPIO_InitStructure */
        GPIO_InitStruct.Pin = PIN_STPIN(pin);
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        switch (pin_irq_hdr_tab[irqindex].mode)
        {
        case PIN_IRQ_MODE_RISING:
            GPIO_InitStruct.Pull = GPIO_PULLDOWN;
            GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
            break;
        case PIN_IRQ_MODE_FALLING:
            GPIO_InitStruct.Pull = GPIO_PULLUP;
            GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
            break;
        case PIN_IRQ_MODE_RISING_FALLING:
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
            break;
        }
        HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);

        //设置中断优先级,使能中断
        HAL_NVIC_SetPriority(irqmap->irqno, 5, 0);
        HAL_NVIC_EnableIRQ(irqmap->irqno);
        pin_irq_enable_mask |= irqmap->pinbit;

        rt_hw_interrupt_enable(level);
    }
    //禁止中断
    else if (enabled == PIN_IRQ_DISABLE)
    {
        irqmap = get_pin_irq_map(PIN_STPIN(pin));
        if (irqmap == RT_NULL)
        {
            return RT_ENOSYS;
        }

        level = rt_hw_interrupt_disable();
        //复位GPIO
        HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin));

        pin_irq_enable_mask &= ~irqmap->pinbit;

        if ((irqmap->pinbit >= GPIO_PIN_5) && (irqmap->pinbit <= GPIO_PIN_9))
        {
            if (!(pin_irq_enable_mask & (GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9)))
            {
                HAL_NVIC_DisableIRQ(irqmap->irqno);
            }
        }
        else if ((irqmap->pinbit >= GPIO_PIN_10) && (irqmap->pinbit <= GPIO_PIN_15))
        {
            if (!(pin_irq_enable_mask & (GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15)))
            {
                HAL_NVIC_DisableIRQ(irqmap->irqno);
            }
        }
        else
        {
            HAL_NVIC_DisableIRQ(irqmap->irqno);
        }

        rt_hw_interrupt_enable(level);
    }
    else
    {
        return -RT_ENOSYS;
    }

    return RT_EOK;
}

原网站

版权声明
本文为[rou252051452]所创,转载请带上原文链接,感谢
https://blog.csdn.net/rou252051452/article/details/125443641