[OHOS - STM32MP157] 1 GPIO驱动分析 原创 精华

心往北方
发布于 2022-2-28 13:27
浏览
1收藏

[OHOS - STM32MP157] 1 GPIO驱动分析

1 . STM32MP157GPIO寄存器

  • 【参考资料】dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics.pdf

寄存器组地址

【P159 Memory map and register boundary addresses Table 9. Register boundary addresses】

PA0为例:
[OHOS - STM32MP157] 1 GPIO驱动分析-鸿蒙开发者社区
由上表可知,GPIOA基地址为0x50002000,记住备用。

【以输出模式为例】查看芯片GPIO使用方法:

【P1064 I/O pin alternate function multiplexer and mapping 】: 找到如下描述:

[OHOS - STM32MP157] 1 GPIO驱动分析-鸿蒙开发者社区

  • 【配置PA0为高速推挽上拉输出高电平模式】

​ 下面看GPIO寄存器组:

​ 【P1072 GPIO registers】

​ 【GPIO port mode register (GPIOx_MODER) 】端口模式配置寄存器

[OHOS - STM32MP157] 1 GPIO驱动分析-鸿蒙开发者社区

由上图可知,该寄存器每两位控制一个IO口,如PA0配置为输出模式,即将该寄存器MODER0[1:0]位配置为01即可,该寄存器地址为GPIOA基地址 + 寄存器偏移地址,由上面得到的GPIOA基地址为0x50002000,由上图得到端口模式配置寄存器偏移地址为0x00,故我们最后需要的操作就是将0x50002000+0x00地址的低两位设置为01(输出)即可。

【GPIO port output type register (GPIOx_OTYPER) 】输出类型配置寄存器

​ 配置详情同上,最后需要的操作就是将0x50002000+0x04地址的低一位设置为0(推挽输出)。

【GPIO port pull-up/pull-down register (GPIOx_PUPDR) 】端口上下拉配置寄存器

​ 配置详情同上,最后需要的操作就是将0x50002000+0x0C地址的低两位设置为01(上拉)。

【GPIO port output data register (GPIOx_ODR) 】端口输出数据配置寄存器

​ 配置详情同上,最后需要的操作就是将0x50002000+0x14地址的低一位设置为1(高电平)。

自此,芯片数据手册分析完毕,下面是openHarmony与具体soc平台GPIO的相关开发介绍。

2 .openHarmony驱动加载

驱动框架详情参考官方文档 | OpenHarmony

  • 驱动加载入口

    /* HdfDriverEntry definition */
    struct HdfDriverEntry g_GpioDriverEntry = {
        .moduleVersion = 1,
        .moduleName = "HDF_PLATFORM_GPIO",
        .Bind = GpioDriverBind,
        .Init = GpioDriverInit,
        .Release = GpioDriverRelease,
    };
    
    /* Init HdfDriverEntry */
    HDF_INIT(g_GpioDriverEntry);
    

    驱动加载时会执行Bind方法和Init方法,详情参考官方文档,本文只做GPIO分析,不做驱动框架层面分析。

    bind方法,GPIO驱动里面不做具体操作:

    static int32_t GpioDriverBind(struct HdfDeviceObject *device)
    {   
        (void)device;
        return HDF_SUCCESS;
    }
    

    Init方法,获取hcs文件具体配置信息,根据该信息初始化硬件驱动:

    device_gpio :: device {
                    device0 :: deviceNode {
                        policy = 0;
                        priority = 10;
                        permission = 0644;
                        moduleName = "HDF_PLATFORM_GPIO";
                        serviceName = "HDF_PLATFORM_GPIO";
                        deviceMatchAttr = "st_stm32mp157_gpio";
                    }
                }
                gpio_config {
                controller_0x50002000 {
                    match_attr = "st_stm32mp157_gpio";
                    groupNum = 11;
                    bitNum = 16;
                    gpioRegBase = 0x50002000;
                    gpioRegStep = 0x1000;
                    irqRegBase = 0x5000D000;
                    irqRegStep = 0x400;
                }
            }
    
    static int32_t GpioDriverInit(struct HdfDeviceObject *device)
    {
       
        int32_t ret;
        struct Stm32GpioCntlr *stm32gpio = &g_Stm32GpioCntlr;
    
        HDF_LOGD("%s: Enter", __func__);
        if (device == NULL || device->property == NULL) {
            HDF_LOGE("%s: device or property NULL!", __func__);
            return HDF_ERR_INVALID_OBJECT;
        }
        //获取属性数据
        ret = Stm32GpioReadDrs(stm32gpio, device->property); //读取hcs配置属性,
        /***************************分析时复制到此***************************************
        补充插入GPIO描述结构
        struct Stm32GpioCntlr {
            struct GpioCntlr cntlr; 	// HDF驱动框架父对象
            volatile unsigned char *regBase;	//GPIO寄存器基地址 : 为映射后的虚拟地址
            EXTI_TypeDef *exitBase;
            uint32_t gpioPhyBase;
            uint32_t gpioRegStep;
            uint32_t irqPhyBase;
            uint32_t iqrRegStep;
            uint16_t groupNum;
            uint16_t bitNum;
            struct GpioGroup *groups;
    	}
        ******函数调用传入设备描述对象结构体以及设备节点属性,该函数实现数据解析,类似设备树解析
            static int32_t Stm32GpioReadDrs(struct Stm32GpioCntlr *stm32gpio, const struct DeviceResourceNode *node)
        {
            int32_t ret;
            struct DeviceResourceIface *drsOps = NULL;
    
            drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
            if (drsOps == NULL || drsOps->GetUint32 == NULL) {
                HDF_LOGE("%s: invalid drs ops fail!", __func__);
                return HDF_FAILURE;
            }
    
            ret = drsOps->GetUint32(node, "gpioRegBase", &stm32gpio->gpioPhyBase, 0);
            //获取GPIO寄存器基地址0x50002000,该地址是基于上面数据手册得到的GPIOA的寄存器基地址(实际物理地址)
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read regBase fail!", __func__);
                return ret;
            }
    
            ret = drsOps->GetUint32(node, "gpioRegStep", &stm32gpio->gpioRegStep, 0);
            //由于STM32MP157GPIO组并非连续地址,查阅手册后的到GPIO端口之间地址间距为0x1000
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read gpioRegStep fail!", __func__);
                return ret;
            }
    
            ret = drsOps->GetUint16(node, "groupNum", &stm32gpio->groupNum, 0);
            //获取GPIO分组数,STM32MP157分组为A,B,C,D,E,F,G,H,I,J,Z共11组
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read groupNum fail!", __func__);
                return ret;
            }
    
            ret = drsOps->GetUint16(node, "bitNum", &stm32gpio->bitNum, 0);
            //获取每组GPIO数量,STM32MP157一组IO数量为16
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read bitNum fail!", __func__);
                return ret;
            }
    
            ret = drsOps->GetUint32(node, "irqRegBase", &stm32gpio->irqPhyBase, 0);
            //获取中断寄存器基地址
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read regBase fail!", __func__);
                return ret;
            }
    
            ret = drsOps->GetUint32(node, "irqRegStep", &stm32gpio->iqrRegStep, 0);
            //获取中断寄存器地址间距
            if (ret != HDF_SUCCESS) {
                HDF_LOGE("%s: read gpioRegStep fail!", __func__);
                return ret;
            }
            return HDF_SUCCESS;
        }
        ***********************************************************/
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: get gpio device resource fail:%d", __func__, ret);
            return ret;
        }
    
        if (stm32gpio->groupNum > GROUP_MAX || stm32gpio->groupNum <= 0 || stm32gpio->bitNum > BIT_MAX ||
            stm32gpio->bitNum <= 0) {
            HDF_LOGE("%s: invalid groupNum:%u or bitNum:%u", __func__, stm32gpio->groupNum,
                     stm32gpio->bitNum);
            return HDF_ERR_INVALID_PARAM;
        }
        //寄存器地址映射,MMU操作
        stm32gpio->regBase = OsalIoRemap(stm32gpio->gpioPhyBase, stm32gpio->groupNum * stm32gpio->gpioRegStep);
        if (stm32gpio->regBase == NULL) {
            HDF_LOGE("%s: err remap phy:0x%x", __func__, stm32gpio->gpioPhyBase);
            return HDF_ERR_IO;
        }
        /* OsalIoRemap: remap registers */
        stm32gpio->exitBase = OsalIoRemap(stm32gpio->irqPhyBase, stm32gpio->iqrRegStep);
        if (stm32gpio->exitBase == NULL) {
            dprintf("%s: OsalIoRemap fail!", __func__);
            return -1;
        }
    
        ret = InitGpioCntlrMem(stm32gpio);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: err init cntlr mem:%d", __func__, ret);
            OsalIoUnmap((void *)stm32gpio->regBase);
            stm32gpio->regBase = NULL;
            return ret;
        }
        stm32gpio->cntlr.count = stm32gpio->groupNum * stm32gpio->bitNum;
        stm32gpio->cntlr.priv = (void *)device->property;
        stm32gpio->cntlr.device = device;
        stm32gpio->cntlr.ops = &g_GpioMethod;
        ret = GpioCntlrAdd(&stm32gpio->cntlr);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: err add controller: %d", __func__, ret);
            return ret;
        }
        HDF_LOGE("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device));
        return ret;
    }
    
    

    上述为openHarmony初始化GPIO的方法简单介绍,详细情况请仔细阅读源码,自此,OpenHarmony已经得到STM32MP157芯片的GPIO外设基本信息,我们继续往下分析。

    3 .openHarmony驱动GPIO

    参考官方文档:GPIO | OpenHarmony

​ 【openHarmony对外提供的GPIO驱动函数】

struct GpioMethod {
  int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);// 【可选】
  int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);// 【可选】
  int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val);
  int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val);
  int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir);
  int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir);
  int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq);// 【可选】
  int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg);
  int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local);
  int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local);
  int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local);
}
  • STM32MP157驱动函数实例化:

    struct GpioMethod g_GpioMethod = {
        .request = NULL,
        .release = NULL,
        .write = Stm32Mp157GpioWrite,
        .read = Stm32Mp157GpioRead,
        .setDir = Stm32Mp157GpioSetDir,
        .getDir = Stm32Mp157GpioGetDir,
        .toIrq = NULL,
        .setIrq = Stm32Mp157GpioSetIrq,
        .unsetIrq = Stm32Mp157GpioUnsetIrq,
        .enableIrq = Stm32Mp157GpioEnableIrq,
        .disableIrq = Stm32Mp157GpioDisableIrq,
    };
    

【Stm32Mp157GpioSetDir】设置GPIO引脚反向

函数名 入参 出参 返回值 功能
Stm32Mp157GpioSetDir cntlr:结构体指针,核心层GPIO控制器;local:uint16_t,GPIO端口标识号 ;dir:uint16_t,管脚方向传入值; HDF_STATUS相关状态 设置GPIO引脚输入/输出方向
static int32_t Stm32Mp157GpioSetDir(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t dir)
{
    int32_t ret;
    uint32_t irqSave;
    unsigned int val;
    volatile unsigned char *addr = NULL;

    unsigned int bitNum = Stm32ToBitNum(gpio);  //将GPIO端口号转换为位号 如 0 = PA0 -> 0
    struct GpioGroup *group = NULL;

    ret = Stm32GetGroupByGpioNum(cntlr, gpio, &group); //将GPIO端口号转换为组号 如 1 = PA1 -> 0
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("Stm32GetGroupByGpioNum failed\n");
        return ret;
    }

    if (OsalSpinLockIrqSave(&group->lock, &irqSave) != HDF_SUCCESS) {
        HDF_LOGE("OsalSpinLockIrqSave failed\n");
        return HDF_ERR_DEVICE_BUSY;
    }
    /**
    ************************************补充宏定义说明**********************************
    #define STM32MP15X_GPIO_DIR(base)        ((base) + 0x00)  基地址+偏移地址
    #define STM32MP15X_GPIO_DATA(base)       ((base) + 0x18)
    #define STM32MP15X_GPIO_IDR(base)        ((base) + 0x10)
    *****/
    addr = STM32MP15X_GPIO_DIR(group->regBase);	 //获取GPIO寄存器地址,此处为虚拟地址
    val = OSAL_READL(addr);		//读取原来寄存器的值
    if (dir == GPIO_DIR_IN) {
        val &= ~(0X3 << (bitNum*2)); /* bit0:1 清零 */  // 位操作
    } else if (dir == GPIO_DIR_OUT) {
        val &= ~(0X3 << (bitNum*2)); /* bit0:1 清零 */
        val |= (0X1 << (bitNum*2)); /* bit0:1 设置 01 */
    }
    OSAL_WRITEL(val, addr); // 将运算后的值写入寄存器,配置完成
    (void)OsalSpinUnlockIrqRestore(&group->lock, &irqSave);
    return HDF_SUCCESS;
}

【Stm32Mp157GpioWrite】设置GPIO输出电平

函数名 入参 出参 返回值 功能
Stm32Mp157GpioWrite cntlr:结构体指针,核心层GPIO控制器;local:uint16_t,GPIO端口标识号 ;val:uint16_t,电平传入值; HDF_STATUS相关状态 GPIO引脚写入电平值
static int32_t Stm32Mp157GpioWrite(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t val)
{
    int32_t ret;
    uint32_t irqSave;
    unsigned int valCur;
    unsigned int bitNum = Stm32ToBitNum(gpio);
    volatile unsigned char *addr = NULL;
    struct GpioGroup *group = NULL;

    ret = Stm32GetGroupByGpioNum(cntlr, gpio, &group);
    if (ret != HDF_SUCCESS) {
        return ret;
    }
    if (OsalSpinLockIrqSave(&group->lock, &irqSave) != HDF_SUCCESS) {
        return HDF_ERR_DEVICE_BUSY;
    }
    /**
    ************************************补充宏定义说明**********************************
    #define STM32MP15X_GPIO_DIR(base)        ((base) + 0x00)  基地址+偏移地址
    #define STM32MP15X_GPIO_DATA(base)       ((base) + 0x18)  基地址+偏移地址
    #define STM32MP15X_GPIO_IDR(base)        ((base) + 0x10)
    *****/
    addr = STM32MP15X_GPIO_DATA(group->regBase);//获取输出数据寄存器地址
    valCur = OSAL_READL(addr);
    if (val == GPIO_VAL_LOW) {
        valCur &= ~(0x1 << bitNum);
        valCur |= (0x1 << (bitNum+16));
    } else {
        valCur |= (0x1 << bitNum);
    }
    OSAL_WRITEL(valCur, addr);
    (void)OsalSpinUnlockIrqRestore(&group->lock, &irqSave);

    return HDF_SUCCESS;
}

其他函数请自行阅读源码。

device/st/drivers/gpio · 小熊派开源社区/BearPi-HM_Micro_small - 码云 - 开源中国 (gitee.com)

自此,openHarmony操作硬件寄存器的基本流程已经介绍完毕,供阅读参考。

4 .openHarmony GPIO驱动使用

参阅官方GPIO | OpenHarmony

【补充】:STM32MP157 GPIO管脚号计算 PXY = = [X] * 16 + Y

其中 [X]: [A] = 0, [B] = 1,[C] = 2,[D] = 3,[E] = 4,[F] = 5,[G] = 6,[H] = 7,[I] = 8,[J] = 9,[K] = 10,[Z] = 11

如 : PA0 = 0 * 16 + 0 = 0 ; PC10 = 2 * 16 + 10 = 42

【Tips】:GPIO驱动使用时,仅能支持一个GPIO管脚设置中断,重复其他管脚会导致系统运行异常

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
2
收藏 1
回复
举报
回复
    相关推荐