查看: 870|回复: 2

[原创] 【LPC845-BRK板卡试用申请】(四)GPIO适配管理

[复制链接]
  • TA的每日心情
    奋斗
    前天 09:28
  • 签到天数: 592 天

    [LV.9]以坛为家II

    51

    主题

    2216

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7033
    最后登录
    2024-4-24
    发表于 2023-4-8 08:03:02 | 显示全部楼层 |阅读模式
    本帖最后由 andeyqi 于 2023-4-8 12:54 编辑

    工作中我们经常会遇到跨平台适配的为问题,经常在代码中看到所谓的适配层的接口用于上层应用和底层驱动的解耦分离的设计,本贴的主题是将GPIO驱动抽象成通用的接口方式统一吸收底层的差异话的东西,对于GPIO的驱动rtthread 代码中的PIN设备驱动,是将GPIO 抽象成id 的方式,用户只需要知道操作的id 数值即可完成对GPIO的操作,本贴借鉴rtthread 的管理方式对LPC845平台的GPIO 进行统一的管理。

    1.GPIO接口描述

    Rtthread 的BSP 目录下已经支持了很多厂家的芯片,很多已经适配了PIN驱动框架,我们从中选取一个参考,我们选取的是\rt-thread\bsp\gd32\arm\libraries\gd32_drivers\drv_gpio.c 作为参考。
    Rtthread PIN 驱动框架将GPIO 接口ops 封装成如下的接口,定义如下:


    struct rt_pin_ops
    {
        void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);
        void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);
        rt_int8_t  (*pin_read)(struct rt_device *device, rt_base_t pin);
        rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_base_t pin,
                rt_uint8_t mode, void (*hdr)(void *args), void *args);
        rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin);
        rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled);
        rt_base_t (*pin_get)(const char *name);
    };
    1.1 pin_mode ops 接口用于设置gpio 工作模式。
    void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);
    device:表示PIN 设备句柄,此参数我们可以忽略理论上只要根据 pin 参数即可找到对应操作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

    1.2 pin_write: 设置引脚电平
        void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);
    1.2 pin_read: 读取引脚电平
        rt_int8_t  (*pin_read)(struct rt_device *device, rt_base_t pin);


    上面两个接口一个根据pin 引脚设置输出电平,一个是读取pin 引进的电平,其他中断相关配置引脚我们暂不关注,本次实验只要实现上述三个接口即可完成对gpio 输入输出控制。


    上述三个接口对应的GD32 下的实现如下:
    static void gd32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
    {
        const struct pin_index *index = RT_NULL;
        rt_uint32_t pin_mode = 0;

    #if defined SOC_SERIES_GD32F4xx
          rt_uint32_t pin_pupd = 0, pin_odpp = 0;
    #endif

        index = get_pin(pin);
        if (index == RT_NULL)
        {
            return;
        }

        /* GPIO Periph clock enable */
        rcu_periph_clock_enable(index->clk);
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_OUTPUT;
    #else
        pin_mode = GPIO_MODE_OUT_PP;
    #endif

        switch(mode)
        {
        case PIN_MODE_OUTPUT:
            /* output setting */
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_OUTPUT;
            pin_pupd = GPIO_PUPD_NONE;
            pin_odpp = GPIO_OTYPE_PP;
    #else
            pin_mode = GPIO_MODE_OUT_PP;
    #endif
            break;
        case PIN_MODE_OUTPUT_OD:
            /* output setting: od. */
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_OUTPUT;
            pin_pupd = GPIO_PUPD_NONE;
            pin_odpp = GPIO_OTYPE_OD;
    #else
            pin_mode = GPIO_MODE_OUT_OD;
    #endif
            break;
        case PIN_MODE_INPUT:
            /* input setting: not pull. */
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_INPUT;
            pin_pupd = GPIO_PUPD_PULLUP | GPIO_PUPD_PULLDOWN;
    #else
            pin_mode = GPIO_MODE_IN_FLOATING;
    #endif
            break;
        case PIN_MODE_INPUT_PULLUP:
            /* input setting: pull up. */
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_INPUT;
            pin_pupd = GPIO_PUPD_PULLUP;
    #else
            pin_mode = GPIO_MODE_IPU;
    #endif
            break;
        case PIN_MODE_INPUT_PULLDOWN:
            /* input setting: pull down. */
    #if defined SOC_SERIES_GD32F4xx
            pin_mode = GPIO_MODE_INPUT;
            pin_pupd = GPIO_PUPD_PULLDOWN;
    #else
            pin_mode = GPIO_MODE_IPD;
    #endif
            break;
        default:
                break;
        }

    #if defined SOC_SERIES_GD32F4xx
        gpio_mode_set(index->gpio_periph, pin_mode, pin_pupd, index->pin);
        if(pin_mode == GPIO_MODE_OUTPUT)
        {
            gpio_output_options_set(index->gpio_periph, pin_odpp, GPIO_OSPEED_50MHZ, index->pin);
        }
    #else
            gpio_init(index->gpio_periph, pin_mode, GPIO_OSPEED_50MHZ, index->pin);
    #endif
    }


    上述代码是根据传入的pin 值索引到对应GPIO 资源index ,然后调用GD32的库函数实现对应的功能从而完成应用层和底层的依赖解耦。

    struct pin_index
    {
        rt_int16_t index;
        rcu_periph_enum clk;
        rt_uint32_t gpio_periph;
        rt_uint32_t pin;
        rt_uint8_t port_src;
        rt_uint8_t pin_src;
    };


    对应的索引表如下:
    static const struct pin_index pins[] =
    {
    #ifdef GPIOA
        GD32_PIN(0,  A, 0),
        GD32_PIN(1,  A, 1),
        GD32_PIN(2,  A, 2),
        GD32_PIN(3,  A, 3),
        GD32_PIN(4,  A, 4),
        GD32_PIN(5,  A, 5),
        GD32_PIN(6,  A, 6),

    }
    GD32_PIN(0,  A, 0), 其中的第一个0 对应的用户操作的index, 后面的A0 对应的A0端口,同理gd32_pin_write/gd32_pin_read接口也是根据传入的pin id 索引到对应的资源配置后,根据对应的参数操作GPIO.

    2.lpc845 GPIO接口实现

    参照上面的封装device参数对我们是不需要的,我们之只需要根据pin 的数值索引到LPC845 驱动库依赖的资源即可,LPC485 主要通过GPIO_PinInit 接口初始化GPIO 输入输出属性,其中base 为固定参数GPIO,config 根据是输入输出设置对应的结构即可,port 和 pin 对应的端口和id,我们根据对应的抽象出来的index 能够索引到该配置即可完后GPIO 输入输出属性的配置,gpio 的读写接口也是类似。
    /*!
    * brief Initializes a GPIO pin used by the board.
    * param base   GPIO peripheral base pointer(Typically GPIO)
    * param port   GPIO port number
    * param pin    GPIO pin number
    * param config GPIO pin configuration pointer
    */
    void GPIO_PinInit(GPIO_Type *base, uint32_t port, uint32_t pin, const gpio_pin_config_t *config)  

    static inline void GPIO_PinWrite(GPIO_Type *base, uint32_t port, uint32_t pin, uint8_t output)
    {
        base->B[port][pin] = output;
    }

    static inline uint32_t GPIO_PinRead(GPIO_Type *base, uint32_t port, uint32_t pin)
    {
        return (uint32_t)base->B[port][pin];
    }
    GPIO 的输入输出上下拉模式的配置是通过如下接口,base 为固定值,ionumber 为GPIO number to mux 我们只要根据抽象出来的id 能索引到对应配置即可。
    /**
    * @brief   Sets I/O Control pin mux
    * @param   base        : The base of IOCON peripheral on the chip
    * @param   ionumber    : GPIO number to mux
    * @param   modefunc    : OR'ed values of type IOCON_*
    * @return  Nothing
    */

    IOCON_PinMuxSet(IOCON_Type *base, uint8_t ionumber, uint32_t modefunc)

    根据上述的接口的梳理,我们将抽象的index,port,pin,ionumber 构造成一个结构体,即可完成index 到资源的映射,定义如下结构体。

    struct pin_index
    {
        uint8_t index;
        uint8_t port;
        uint8_t pin;
        uint8_t iocon;
    };

    参考GD32的代码实现如下三个接口:
    void lpc84x_pin_mode(int32_t pin, int32_t mode);
    void lpc84x_pin_write(int32_t pin, int32_t value);
    int lpc84x_pin_read(int32_t pin);
    1. #include "drv_gpio.h"
    2. #include "fsl_device_registers.h"
    3. #include <stdio.h>
    4. #include <stdlib.h>
    5. #include "fsl_iocon.h"
    6. #include "fsl_gpio.h"

    7. #define SCON_INDEX(offset) ((offset)/0x04u)


    8. static const struct pin_index pins[] =
    9. {
    10.     LPC48X_PIN(0,0,0,SCON_INDEX(0x044u)), /* PIO0_0 */
    11.     LPC48X_PIN(1,0,1,SCON_INDEX(0x02cu)),
    12.     LPC48X_PIN(2,0,2,SCON_INDEX(0x018u)),
    13.     LPC48X_PIN(3,0,3,SCON_INDEX(0x014u)),
    14.     LPC48X_PIN(4,0,4,SCON_INDEX(0x010u)),
    15.     LPC48X_PIN(5,0,5,SCON_INDEX(0x00cu)),
    16.     LPC48X_PIN(6,0,6,SCON_INDEX(0x040u)),
    17.     LPC48X_PIN(7,0,7,SCON_INDEX(0x03cu)),
    18.     LPC48X_PIN(8,0,8,SCON_INDEX(0x038u)),
    19.     LPC48X_PIN(9,0,9,SCON_INDEX(0x034u)),
    20.     LPC48X_PIN(10,0,10,SCON_INDEX(0x020u)),
    21.     LPC48X_PIN(11,0,11,SCON_INDEX(0x01cu)),
    22.     LPC48X_PIN(12,0,12,SCON_INDEX(0x008u)),
    23.     LPC48X_PIN(13,0,13,SCON_INDEX(0x004u)),
    24.     LPC48X_PIN(14,0,14,SCON_INDEX(0x048u)),
    25.     LPC48X_PIN(15,0,15,SCON_INDEX(0x028u)),
    26.     LPC48X_PIN(16,0,16,SCON_INDEX(0x024u)),
    27.     LPC48X_PIN(17,0,17,SCON_INDEX(0x000u)),
    28.     LPC48X_PIN(18,0,18,SCON_INDEX(0x078u)),
    29.     LPC48X_PIN(19,0,19,SCON_INDEX(0x074u)),
    30.     LPC48X_PIN(20,0,20,SCON_INDEX(0x070u)),
    31.     LPC48X_PIN(21,0,21,SCON_INDEX(0x06cu)),
    32.     LPC48X_PIN(22,0,22,SCON_INDEX(0x068u)),
    33.     LPC48X_PIN(23,0,23,SCON_INDEX(0x064u)),
    34.     LPC48X_PIN(24,0,24,SCON_INDEX(0x060u)),
    35.     LPC48X_PIN(25,0,25,SCON_INDEX(0x05cu)),
    36.     LPC48X_PIN(26,0,26,SCON_INDEX(0x058u)),
    37.     LPC48X_PIN(27,0,27,SCON_INDEX(0x054u)),
    38.     LPC48X_PIN(28,0,28,SCON_INDEX(0x050u)),
    39.     LPC48X_PIN(29,0,29,SCON_INDEX(0x0c8u)),
    40.     LPC48X_PIN(30,0,30,SCON_INDEX(0x0ccu)),
    41.     LPC48X_PIN(31,0,31,SCON_INDEX(0x08cu)),
    42.     LPC48X_PIN(32,1,0,SCON_INDEX(0x090u)),
    43.     LPC48X_PIN(33,1,1,SCON_INDEX(0x094u)),
    44.     LPC48X_PIN(34,1,2,SCON_INDEX(0x098u)),
    45.     LPC48X_PIN(35,1,3,SCON_INDEX(0x0a4u)),
    46.     LPC48X_PIN(36,1,4,SCON_INDEX(0x0a8u)),
    47.     LPC48X_PIN(37,1,5,SCON_INDEX(0x0acu)),
    48.     LPC48X_PIN(38,1,6,SCON_INDEX(0x0b8u)),
    49.     LPC48X_PIN(39,1,7,SCON_INDEX(0x0c4u)),
    50.     LPC48X_PIN(40,1,8,SCON_INDEX(0x07cu)),
    51.     LPC48X_PIN(41,1,9,SCON_INDEX(0x080u))
    52. };

    53. #define ITEM_NUM(items) sizeof(items) / sizeof(items[0])

    54. /**
    55.   * @brief  get pin
    56.   * @param  pin
    57.   * @retval None
    58.   */
    59. const struct pin_index *get_pin(uint8_t pin)
    60. {
    61.     const struct pin_index *index;

    62.     if (pin < ITEM_NUM(pins))
    63.     {
    64.         index = &pins[pin];
    65.         if (index->index == -1)
    66.         index = NULL;
    67.     }
    68.     else
    69.     {
    70.         index = NULL;
    71.     }

    72.     return index;
    73. }


    74. /**
    75.   * @brief  set pin mode
    76.   * @param  pin, mode
    77.   * @retval None
    78.   */
    79. void lpc84x_pin_mode(int32_t pin, int32_t mode)
    80. {
    81.     const struct pin_index *index = NULL;
    82.     uint32_t pin_mode = 0;
    83.     uint8_t modefunc = 0;

    84.     index = get_pin(pin);
    85.     if (index == NULL)
    86.     {
    87.         return;
    88.     }

    89.     switch(mode)
    90.     {
    91.     case PIN_MODE_OUTPUT:
    92.         IOCON_PinMuxSet(IOCON,index->iocon,IOCON_MODE_PULLUP);
    93.         GPIO_PinInit(GPIO, index->port, index->pin, &(gpio_pin_config_t){kGPIO_DigitalOutput, (0)});
    94.         break;
    95.     case PIN_MODE_OUTPUT_OD:
    96.         IOCON_PinMuxSet(IOCON,index->iocon,IOCON_OPENDRAIN_EN);
    97.         GPIO_PinInit(GPIO, index->port, index->pin, &(gpio_pin_config_t){kGPIO_DigitalOutput, (0)});
    98.         break;
    99.     case PIN_MODE_INPUT:
    100.         GPIO_PinInit(GPIO, index->port, index->pin, &(gpio_pin_config_t){kGPIO_DigitalInput, (0)});
    101.         break;
    102.     case PIN_MODE_INPUT_PULLUP:
    103.         IOCON_PinMuxSet(IOCON,index->iocon,IOCON_MODE_PULLUP);
    104.         /* input setting: pull up. */
    105.         GPIO_PinInit(GPIO, index->port, index->pin, &(gpio_pin_config_t){kGPIO_DigitalInput, (0)});
    106.         break;
    107.     case PIN_MODE_INPUT_PULLDOWN:
    108.         IOCON_PinMuxSet(IOCON,index->iocon,IOCON_MODE_PULLDOWN);
    109.         /* input setting: pull down. */
    110.         GPIO_PinInit(GPIO, index->port, index->pin, &(gpio_pin_config_t){kGPIO_DigitalInput, (0)});
    111.         break;
    112.     default:
    113.         break;
    114.     }
    115. }


    116. /**
    117.   * @brief  pin write
    118.   * @param   pin, valuie
    119.   * @retval None
    120.   */
    121. void lpc84x_pin_write(int32_t pin, int32_t value)
    122. {
    123.     const struct pin_index *index = NULL;

    124.     index = get_pin(pin);
    125.     if (index == NULL)
    126.     {
    127.         return;
    128.     }

    129.     GPIO_PinWrite(GPIO,index->port,index->pin,(uint8_t)value);
    130. }



    131. /**
    132.   * @brief  pin read
    133.   * @param  dev, pin
    134.   * @retval None
    135.   */
    136. int lpc84x_pin_read(int32_t pin)
    137. {
    138.     int value = PIN_LOW;
    139.     const struct pin_index *index = NULL;

    140.     index = get_pin(pin);
    141.     if (index == NULL)
    142.     {
    143.         return value;
    144.     }

    145.     value = GPIO_PinRead(GPIO,index->port,index->pin);
    146.     return value;
    147. }

    复制代码


    3.GPIO接口验证

    上面我们已经实现了GPIO的驱动接口,我们编写测试代码针对板子上的GPIO LED进行控制同时对按键输入进行采集。原理图上的PIO1_0 对应LED 输出控制,PIO0_4 对应USER按键输入 分别对应的index 为 32 和 4,如下代码针对此index 操作即可完成GPIO的控制
    LPC48X_PIN(4,0,4,SCON_INDEX(0x010u)),
    LPC48X_PIN(32,1,0,SCON_INDEX(0x090u)),

    lpc845map.png


    void start_task(void *pvParameters)
    {
        uint8_t out = 0;
        lpc84x_pin_mode(32,PIN_MODE_OUTPUT);
        while(1)
        {
            lpc84x_pin_write(32,out);
            vTaskDelay(500);
            out = out ? 0 : 1;
        }
    }


    void start_task1(void *pvParameters)
    {
        uint8_t new = 1,old = 1;

        lpc84x_pin_mode(4,PIN_MODE_INPUT);

        while(1)
        {
           new = lpc84x_pin_read(4);
            if(new != old)
            {
                old = new;
                PRINTF("K3 %s\r\n",(new ? "UP":"DOWN"));
            }
            vTaskDelay(100);
        }
    }

    上述测试代码会500ms 控制LED闪烁及检测按键的状态,实际运行后发现led 会周期闪烁,按键的压下抬起也能打印输出。
    350646664c0cccbb1969274904bfa9a6[00-00-00--00-00-10].gif



    =================代码如下=================









    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    开心
    昨天 18:31
  • 签到天数: 1415 天

    [LV.10]以坛为家III

    17

    主题

    2096

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    6014
    最后登录
    2024-4-23
    发表于 2023-4-8 16:41:24 | 显示全部楼层
    感谢分享
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-2-21 10:06
  • 签到天数: 310 天

    [LV.8]以坛为家I

    2

    主题

    5670

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    11646
    最后登录
    2024-2-21
    发表于 2023-8-25 07:22:39 | 显示全部楼层
    活动第10天打卡
    活动第10天.png
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-24 09:37 , Processed in 0.126234 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表