GPIO 驱动实现

更新时间:
2024-01-09
下载文档

GPIO 驱动实现

本节使用 imx6Q 的 SylixOS GPIO 为例。在编写 GPIO 驱动之前先设置好 GPIO 控制器,即填充IMX6Q_GPIO_CONTROLER 结构体中的成员变量。

typedef struct {
    addr_t              GPIOC_ulPhyAddrBase;           /*  物理地址基地址       */
    addr_t              GPIOC_stPhyAddrSize;           /*  物理地址空间大小     */
    ULONG               CPIOC_ulIrqNum1;                /*  0~15 引脚的中断号   */
    ULONG               CPIOC_ulIrqNum2;                /*  16~32 引脚的中断号  */
    UINT                GPIOC_uiPinNumber;
    addr_t              GPIOC_ulVirtAddrBase;          /*  虚拟地址基地址       */
} IMX6Q_GPIO_CONTROLER, *PIMX6Q_GPIO_CONTROLER;

因为 GPIO 一般都是成组的,这样便把每个 GPIO 的寄存器等信息抽象。一组 GPIO 控制器结构,例如 GPIO0 和 GPIO1 是一组(共 32 个 GPIO 口),共用一组寄存器,所以 GPIO0 和 GPIO1 可以一起用 IMX6Q_GPIO_CONTROLER 来控制。

i.MX6Q 有 7 组 GPIO 控制器。

static IMX6Q_GPIO_CONTROLER  _G_imx6qGpioControlers[] = {
    {
           GPIO1_BASE_ADDR,
         LW_CFG_VMM_PAGE_SIZE,
        IMX_INT_GPIO1_INT15_0,
        IMX_INT_GPIO1_INT31_16,
         32,
         GPIO1_BASE_ADDR,                                  /*  默认是物理地址        */
        },{
……
}, {
          GPIO7_BASE_ADDR,
         LW_CFG_VMM_PAGE_SIZE,
        IMX_INT_GPIO7_INT15_0,
        IMX_INT_GPIO7_INT31_16,
         14,
         GPIO7_BASE_ADDR,
    },
};
static LW_GPIO_CHIP  _G_imx6qGpioChip ;

申请 GPIO 管脚

调用 imx6qGpioRequest 函数可以申请一个 GPIO 管脚。首先检查管脚号是否有效,即是否在 GPIO 控制器控制的范围内,接着根据管脚号计算出该 GPIO 相关的寄存器地址,最后通过 writel 函数直接操作寄存器即可。

static INT  imx6qGpioRequest (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    INT    iRet;
……
iRet = GpioPinmuxSet(uiOffset / 32, uiOffset % 32, 0x10 | ALT5);
    return (iRet);
}
UINT32  GpioPinmuxSet (UINT32  uiPinGroup, UINT32 uiPinNum, UINT32 uiCfg)
{
    addr_t atAddr = GpioPinmuxReg[uiPinGroup][uiPinNum];
                                                           /*  计算对应GPIO管脚的寄存器地址*/
    ……
    writel(uiCfg, atAddr);                                 /*  GPIO 功能均为 ALT5       */
    return  ERROR_NONE;
}

释放 GPIO 管脚

调用 x6qGpioFree 函数释放一个正在被使用的 GPIO,如果当前是中断模式,则放弃中断输入功能。

static VOID  imx6qGpioFree (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    PIMX6Q_GPIO_CONTROLER      pGpioControler;
    addr_t                      atBase;
    UINT32                     uiRegVal;
    ……
    uiRegVal = readl(atBase + GPIO_IMR) &(~(1 << (uiOffset % 32)));
    writel(uiRegVal, atBase + GPIO_IMR);                 /*  禁止该 GPIO 中断            */
    uiRegVal = readl(atBase + GPIO_GDIR) &(~(1 << (uiOffset % 32)));
    writel(uiRegVal, atBase + GPIO_GDIR);                /*  设置该 GPIO 为输入          */
}

获得指定 GPIO 方向

调用 imx6qGpioGetDirection 函数获得指定 GPIO 的方向。

static INT  imx6qGpioGetDirection (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    ……
    uiRegVal = readl(atBase + GPIO_GDIR) & (1 << (uiOffset % 32));
    if(uiRegVal) {
        return  (1);
    } else {
        return  (0);
    }
}

设置指定 GPIO 为输入

调用 imx6qGpioDirectionInput 函数设置指定 GPIO 为输入模式。

static INT  imx6qGpioDirectionInput (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    ……
    uiRegVal = readl(atBase + GPIO_GDIR) &(~(1 << (uiOffset % 32)));
    writel(uiRegVal, atBase + GPIO_GDIR);          /*  设置该 GPIO 为输入          */
    return  (0);
}

获得指定 GPIO 电平

调用 imx6qGpioGet 函数获得指定 GPIO 电平。

static INT  imx6qGpioGet (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    ……
    uiRegVal = readl(atBase + GPIO_PSR) & (1 << (uiOffset % 32));
    if(uiRegVal) {
        return  (1);
    } else {
        return  (0);
    }
}

设置指定 GPIO 为输出

调用 imx6qGpioDirectionOutput 函数设置指定 GPIO 为输出模式。

static INT  imx6qGpioDirectionOutput (PLW_GPIO_CHIP      pGpioChip, 
                                      UINT               uiOffset,
                                      INT                iValue)
{
    ……
    uiRegVal = readl(atBase + GPIO_GDIR) | (1 << (uiOffset % 32));
    writel(uiRegVal, atBase + GPIO_GDIR);          /*  设置该 GPIO 为输出        */
    if(iValue) {                                      /*  设置该 GPIO 默认电平       */
        uiRegVal = readl(atBase + GPIO_DR) | (1 << (uiOffset % 32));
        writel(uiRegVal, atBase + GPIO_DR);      /*  设置该 GPIO 默认电平为 1    */
    } else {
        uiRegVal = readl(atBase + GPIO_DR) &(~(1 << (uiOffset % 32)));
        writel(uiRegVal, atBase + GPIO_DR);        /*  设置该 GPIO 默认电平为 0   */
    }
    return  (0);
}

设置指定 GPIO 电平

调用 imx6qGpioSet 函数设置指定 GPIO 电平。

static VOID  imx6qGpioSet (PLW_GPIO_CHIP    pGpioChip, 
                           UINT             uiOffset, 
                           INT              iValue)
{
    ……
    if(iValue) {                                     /*  设置该 GPIO 电平            */
        uiRegVal = readl(atBase + GPIO_DR) | (1 << (uiOffset % 32));
        writel(uiRegVal, atBase + GPIO_DR);      /*  设置该 GPIO 电平为 1        */
    } else {
        uiRegVal = readl(atBase + GPIO_DR) &(~(1 << (uiOffset % 32)));
        writel(uiRegVal, atBase + GPIO_DR);       /*  设置该 GPIO 电平为 0        */
    }
}

设置指定 GPIO 为外部中断输入

调用 imx6qGpioSetupIrq 函数设置指定 GPIO 为外部中断输入。

static INT  imx6qGpioSetupIrq (PLW_GPIO_CHIP    pGpioChip, 
                               UINT             uiOffset, 
                               BOOL             bIsLevel, 
                               UINT             uiType)
{
    ……
    if(uiPinOff > 15) {                              /*  计算需要设置的寄存器位      */
        atBase       = atBase + GPIO_ICR2;
        uiIrqNum     = pGpioControler->CPIOC_ulIrqNum2;
        uiPinOff     = (uiPinOff - 15) << 1;
    } else {
        atBase     = atBase + GPIO_ICR1;
        uiIrqNum     = pGpioControler->CPIOC_ulIrqNum1;
        uiPinOff    = uiPinOff << 1;
    }
uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL);

    writel(uiRegVal &(~(1 << (uiOffset % 32))), 
           pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL);                                                                                      /*  清除双边沿中断触发设置    */
    if (bIsLevel) {                                          /*  如果是电平触发中断       */
        if(uiType) {                                         /*  高电平触发              */
            uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
            uiRegVal = uiRegVal | 0x1 << uiPinOff;
            writel(uiRegVal, atBase);
        } else {                                              /*  低电平触发             */
            uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
            uiRegVal = uiRegVal | 0x0 << uiPinOff;
            writel(uiRegVal, atBase);
        }
    } else {
        if (uiType == 1) {                                    /*  上升沿触发             */
            uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
            uiRegVal = uiRegVal | 0x2 << uiPinOff;
            writel(uiRegVal, atBase);
        } else if (uiType == 0) {                             /*  下降沿触发             */
            uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
            uiRegVal = uiRegVal | 0x3 << uiPinOff;
            writel(uiRegVal, atBase);
        } else {                                              /*  双边沿触发             */
            uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL) |                                  (1<<(uiOffset%32));
            writel(uiRegVal, pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL);
        }
    }
    uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR);  

    writel(uiRegVal | (1 << (uiOffset % 32)), 
           pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR); /*  使能该 GPIO 中断      */   
    /*
     * 测试用代码
     */
    uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR);
    return  (uiIrqNum);
}

清除指定 GPIO 中断标志

调用 imx6qGpioClearIrq 函数清除指定 GPIO 中断标志。

static VOID  imx6qGpioClearIrq (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    ……
    pGpioControler     = &_G_imx6qGpioControlers[uiOffset / 32];
    atBase             = pGpioControler->GPIOC_ulVirtAddrBase;
    uiRegVal           = readl(atBase + GPIO_ISR) | (1 << (uiOffset % 32));
    writel(uiRegVal, atBase + GPIO_ISR);                      /*  清除该 GPIO 中断状态    */
}

判断 GPIO 中断标志

调用 imx6qGpioSvrIrq函数判断 GPIO 中断标志。

static irqreturn_t  imx6qGpioSvrIrq (PLW_GPIO_CHIP  pGpioChip, UINT  uiOffset)
{
    ……
    uiRegVal = readl(atBase + GPIO_ISR) & (1 << (uiOffset % 32));
    if (uiRegVal) {
        return  (LW_IRQ_HANDLED);
    } else {
        return  (LW_IRQ_NONE);
    }
}

将 GPIO 驱动函数设置到 GPIO 控制器结构体。

static LW_GPIO_CHIP  _G_imx6qGpioChip = {
        .GC_pcLabel                = "IMX6Q  GPIO",
        .GC_pfuncRequest           = imx6qGpioRequest,
        .GC_pfuncFree              = imx6qGpioFree,
        .GC_pfuncGetDirection      = imx6qGpioGetDirection,
        .GC_pfuncDirectionInput    = imx6qGpioDirectionInput,
        .GC_pfuncGet               = imx6qGpioGet,
        .GC_pfuncDirectionOutput   = imx6qGpioDirectionOutput,
        .GC_pfuncSetDebounce       = imx6qGpioSetDebounce,
        .GC_pfuncSetPull           = LW_NULL,
        .GC_pfuncSet               = imx6qGpioSet,
        .GC_pfuncSetupIrq          = imx6qGpioSetupIrq,
        .GC_pfuncClearIrq          = imx6qGpioClearIrq,
        .GC_pfuncSvrIrq            = imx6qGpioSvrIrq,
};

将 GPIO 驱动安装到操作系统。

INT  imx6qGpioDrv (VOID)
{
    PIMX6Q_GPIO_CONTROLER    pGpioControler;
    INT                          i;
    _G_imx6qGpioChip.GC_uiBase       = 0;
    _G_imx6qGpioChip.GC_uiNGpios     = 0;
    /*
     * GPIO 控制器时钟和电源使能,及地址映射,
     */
    for (i = 0; i < GPIO_CONTROLER_NR; i++) {
        pGpioControler = &_G_imx6qGpioControlers[i];
        _G_imx6qGpioChip.GC_uiNGpios += pGpioControler->GPIOC_uiPinNumber;
    }
    return  (API_GpioChipAdd(&_G_imx6qGpioChip));
}
文档内容是否对您有所帮助?
有帮助
没帮助