当前位置:网站首页>2.1 rtthread pin设备详解

2.1 rtthread pin设备详解

2022-07-06 03:20:00 rou252051452

目录

1、PIN设备说明        

2、PIN设备的初始化及注册

3、PIN设备的操作

3.1 获取管脚编号的实现

3.1.1使用API

3.1.2使用宏定义

3.1.3查看驱动文件

3.2 设置引脚模式

3.3 输出控制

3.4 输入获取

3.5 中断回调的绑定

3.6 中断回调的解绑

3.7 中断的使能和禁用

3.8 中断回调函数的实现


1、PIN设备说明        

        rtthread通过pin.c和pin.h两个文件进行pin设备的管理。通过pin.h中的结构体rt_device_pin进行pin设备的定义,pin设备继承自设备基类rt_device,rt_device继承自rt_object基类,继承关系如下

        PIN设备通过结构体的定义实现了对rt_device设备基类的继承,结构体中的成员rt_pin_ops来实现pin设备的具体操作实现。

struct rt_device_pin
{
    struct rt_device parent;
    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);
    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);
};

2、PIN设备的初始化及注册

        

         启动阶段rtthread会根据是否进行了RT_USING_PIN定义,在hw_board_init函数中进行pin设备的初始化,在rt_hw-pin_init函数中首先进行了时钟的初始化,最终调用函数rt_device_pin_register来实现STM32的IO和pin设备的关联及设备的挂载。

        

/*
进行PIN设备的结构体定义
*/
static struct rt_device_pin _hw_pin;    

int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
{
    /*
        PIN设备的父设备,设备基类的类型进行定义,定义为RT_Device_Class_Miscellaneous,杂类
    */
    _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;
    _hw_pin.parent.open         = RT_NULL;
    _hw_pin.parent.close        = RT_NULL;
    _hw_pin.parent.read         = _pin_read;
    _hw_pin.parent.write        = _pin_write;
    _hw_pin.parent.control      = _pin_control;
#endif

    /*
        PIN设备ops,即STM32的具体实现方式进行赋值。
    */
    _hw_pin.ops                 = ops;
    _hw_pin.parent.user_data    = user_data;


    /*
        设备注册
    */
    /* register a character device */
    rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);

    return 0;
}

         rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL)中参数_stm32_pin_ops为rt_pin_ops,具体定义内容如下,实现了GPIO的模式配置,输入输出控制,中断控制和管脚查找等功能在STM32下得具体实现方式函数指针的定义。

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

3、PIN设备的操作

        PIN设备对外提供如下接口函数

函数描述
rt_pin_get()获取引脚编号
rt_pin_mode()设置引脚模式
rt_pin_write()设置引脚电平
rt_pin_read()读取引脚电平
rt_pin_attach_irq()绑定引脚中断回调函数
rt_pin_irq_enable()使能引脚中断
rt_pin_detach_irq()脱离引脚中断回调函数

3.1 获取管脚编号的实现

         RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。管脚序号是后续其他输入输出中断函数实现的一个重要参数。STM32的驱动中对GPIOA(0-15)到GPIOx(0-15)进行了按顺序的编号操作。

        RTT官方文档描述针对STM32的GPIO驱动drv_gpio.c中针对管脚编号提供了三种方式进行实现:

3.1.1使用API

        此处需要注意

        旧版本的drv_gpio.c文件在_stm32_pin_ops 中并没有定义函数stm32_pin_get,的实现,所以旧版本的驱动无法使用API获取到管脚编号。新建工程时,系统使用的驱动任为旧版本的

        gpio驱动所以需要更新,可以从gitee进行文件下载bsp/stm32/libraries/HAL_Drivers/drv_gpio.c · RT-Thread/rt-thread - Gitee.com。具体实现方式函数如下

/*
    根据输入字符串的端口号A-Z,转换为数值0-25
    根据输入字符串的管脚号0-15,转换为数值0-15
    将两者结合端口号为高位,管脚号为地位
*/
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.0000PB.01016
PA.1011PB.11117
PA.2022PB.21218
PA.3033PB.31319
PA.4044PB.41420
PA.5055PB.51521
PA.6066PB.61622
PA.7077PB.71723
PA.8088PB.81824
PA.9099PB.91925
PA.1001010PB.1011026
PA.1101111PB.1111127
PA.1201212PB.1211228
PA.1301313PB.1311329
PA.1401414PB.1411430
PA.1501515PB.1511531

3.1.2使用宏定义

        针对STM32,RTT提供了宏定义GET_PIN来进行管脚编号的获取,再未更新drv_gpio驱动前,该定义再drv_common.h中进行了定义,更新驱动后在drv_gpio.h中也进行了定义,两者定义相同。内容如下。


//drv_common.h中的宏定义
#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)

//drv_gpio.h中的宏定义
#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

        如上图,对GET_PIN宏定义进行了重复定义,可以看出drv_gpio.h中对宏定义进行了预编译,包含了STM32MP1的支持。

        全局搜索GET_PIN的使用情况如下

         board.h、drv_gpio.h和drv_usart.c中均进行了drv_common.h的包含。仅在drv_gpio.c文件中进行了drv_gpio.h的包含。所以我们可以将drv_gpio.h中关于GET_PIN的相关宏定义进行删除(不涉及STM32MP1的使用)。来保证宏定义的唯一性。

        宏定义根据参数portx进行管脚端口的地址获取,将A-Z分别转换为0-25。参数PIN为端口下得具体IO。最终转换结果为端口序号*16+IO编号。与API转换结果相同。

宏定义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

3.1.3查看驱动文件

        旧版本的drv_gpio驱动提供了管脚与编号的对应定义,可以通过查看直接进行管脚序号的定义。

         新版本驱动更新了管脚编号识别逻辑,不在提供该驱动文件查看的方式。

3.2 设置引脚模式

        rtt通过rt_pin_mode进行管脚模式的配置

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

        最终调用的函数为pin设备类的ops下的pin_mode函数,在初始阶段该函数指针设置为了stm32_pin_mode,具体实现如下

/*
    1、管脚序号的识别
        通过宏定义
        #define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin)))    转换为位信息
        #define PIN_NO(pin) ((uint8_t)((pin) & 0xFu))             转换为0-15
        来进行序号识别   
    2、端口识别
        #define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
        来进行引脚编号到端口结构体地址的转换。
    3、最终调用库函数进行GPIO的配置。
*/
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;
    }

    //配置端口IO为默认推挽输出,不上下拉,输出速度HIGH
    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;
    }
    //调用库函数进行初始化
    HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
}

3.3 输出控制

        rtt通过rt_pin_write进行管脚模式的配置。

void rt_pin_write(rt_base_t pin, rt_base_t value)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    _hw_pin.ops->pin_write(&_hw_pin.parent, pin, value);
}

        最终调用的函数为pin设备类的ops下的pin_write函数,在初始阶段该函数指针设置为了stm32_pin_write,具体实现如下

/*
    1、管脚序号的识别
        通过宏定义
        #define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin)))    转换为位信息
        #define PIN_NO(pin) ((uint8_t)((pin) & 0xFu))             转换为0-15
        来进行序号识别   
    2、端口识别
        #define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
        来进行引脚编号到端口结构体地址的转换。
    3、最终调用库函数进行GPIO的配置。
*/
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;

    if (PIN_PORT(pin) < PIN_STPORT_MAX)
    {
        //获取端口
        gpio_port = PIN_STPORT(pin);
        //获取IO编号
        gpio_pin = PIN_STPIN(pin);
        //库函数输出
        HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value);
    }
}

3.4 输入获取

        rtt通过rt_pin_read进行管脚模式的配置。

int rt_pin_read(rt_base_t pin)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    return _hw_pin.ops->pin_read(&_hw_pin.parent, pin);
}

        最终调用的函数为pin设备类的ops下的pin_read函数,在初始阶段该函数指针设置为了stm32_pin_read,具体实现如下

/*
    1、管脚序号的识别
        通过宏定义
        #define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin)))    转换为位信息
        #define PIN_NO(pin) ((uint8_t)((pin) & 0xFu))             转换为0-15
        来进行序号识别   
    2、端口识别
        #define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin))))
        来进行引脚编号到端口结构体地址的转换。
    3、最终调用库函数进行GPIO的配置。
*/
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;

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

3.5 中断回调的绑定

          rtt通过rt_pin_attach_irq进行中断回调的绑定。

rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,void (*hdr)(void *args), void  *args)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    if(_hw_pin.ops->pin_attach_irq)
    {
        return _hw_pin.ops->pin_attach_irq(&_hw_pin.parent, pin, mode, hdr, args);
    }
    return -RT_ENOSYS;
}

        最终调用的函数为pin设备类的ops下的pin_attach_irq函数,在初始阶段该函数指针设置为了stm32_pin_attach_irq,具体实现如下

/*
    该函数不进行底层STM32的外部中断的实际操作。
    进行了pin设备的中断相关结构体赋值。
*/
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;
    }
    /*
        进行中断序号的识别
        1、通过管脚序号进行了中断序号的识别,首先通过PIN_STDPIN进行了位的转换
        2、通过函数bit2bitno实现了位序号的识别。
        3、实际可以通过PIN_NO来替代上述流程来进行识别
    */
    irqindex = bit2bitno(PIN_STPIN(pin));
    /*
        判断中断序号是否合法
    */
    if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
    {
        return RT_ENOSYS;
    }
    /*
        进行pin设备的中断模式和回调函数的赋值
    */
    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;
}

3.6 中断回调的解绑

          rtt通过rt_pin_detach_irq进行管脚模式的配置。

rt_err_t rt_pin_detach_irq(rt_int32_t pin)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    if(_hw_pin.ops->pin_detach_irq)
    {
        return _hw_pin.ops->pin_detach_irq(&_hw_pin.parent, pin);
    }
    return -RT_ENOSYS;
}

        最终调用的函数为pin设备类的ops下的pin_detach_irq函数,在初始阶段该函数指针设置为了stm32_pin_dettach_irq,具体实现如下

static rt_err_t stm32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
{
    rt_base_t level;
    rt_int32_t irqindex = -1;

    //识别输入端口是否合法
    if (PIN_PORT(pin) >= PIN_STPORT_MAX)
    {
        return -RT_ENOSYS;
    }
    /*
        进行中断序号的识别
        1、通过管脚序号进行了中断序号的识别,首先通过PIN_STDPIN进行了位的转换
        2、通过函数bit2bitno实现了位序号的识别。
        3、实际可以通过PIN_NO来替代上述流程来进行识别
    */
    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 == -1)
    {
        rt_hw_interrupt_enable(level);
        return RT_EOK;
    }
    pin_irq_hdr_tab[irqindex].pin = -1;
    pin_irq_hdr_tab[irqindex].hdr = RT_NULL;
    pin_irq_hdr_tab[irqindex].mode = 0;
    pin_irq_hdr_tab[irqindex].args = RT_NULL;
    rt_hw_interrupt_enable(level);

    return RT_EOK;
}

3.7 中断的使能和禁用

          rtt通过rt_pin_irq_enable进行管脚模式的配置。

rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    if(_hw_pin.ops->pin_irq_enable)
    {
        return _hw_pin.ops->pin_irq_enable(&_hw_pin.parent, pin, enabled);
    }
    return -RT_ENOSYS;
}

        最终调用的函数为pin设备类的ops下的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();

        if (pin_irq_hdr_tab[irqindex].pin == -1)
        {
            rt_hw_interrupt_enable(level);
            return RT_ENOSYS;
        }

        //查表获取中断序号对应的内容
        irqmap = &pin_irq_map[irqindex];

        /*中断具体配置*/
        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();
        //复位管脚
        HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin));

        pin_irq_enable_mask &= ~irqmap->pinbit;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
        if ((irqmap->pinbit >= GPIO_PIN_0) && (irqmap->pinbit <= GPIO_PIN_1))
        {
            if (!(pin_irq_enable_mask & (GPIO_PIN_0 | GPIO_PIN_1)))
            {
                HAL_NVIC_DisableIRQ(irqmap->irqno);
            }
        }
        else if ((irqmap->pinbit >= GPIO_PIN_2) && (irqmap->pinbit <= GPIO_PIN_3))
        {
            if (!(pin_irq_enable_mask & (GPIO_PIN_2 | GPIO_PIN_3)))
            {
                HAL_NVIC_DisableIRQ(irqmap->irqno);
            }
        }
        else if ((irqmap->pinbit >= GPIO_PIN_4) && (irqmap->pinbit <= GPIO_PIN_15))
        {
            if (!(pin_irq_enable_mask & (GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 |
                                         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);
        }
#else
        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);
        }
#endif
        rt_hw_interrupt_enable(level);
    }
    else
    {
        return -RT_ENOSYS;
    }

    return RT_EOK;
}

3.8 中断回调函数的实现

        在使能了外部中断后,STM32的底层在中断触发后会进行中断函数的调用如下函数。

  • EXTI0_IRQHandler~EXTI4_IRQHandler
  • EXTI9_5_IRQHandler
  • EXTI15_10_IRQHandler

        函数内部调用为HAL_GPIO_EXTI_IRQHandler。在该函数内部最终调用了回调函数HAL_GPIO_EXTI_Callback来进行回调函数的实现。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    pin_irq_hdr(bit2bitno(GPIO_Pin));
}

        如上回调函数调用了pin_irq_hdr进行了与pin设备所绑定的回调函数的关联。

rt_inline void pin_irq_hdr(int irqno)
{
    if (pin_irq_hdr_tab[irqno].hdr)
    {
        pin_irq_hdr_tab[irqno].hdr(pin_irq_hdr_tab[irqno].args);
    }
}

原网站

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