•【参考资料】dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics.pdf
寄存器组地址
【P159 Memory map and register boundary addresses Table 9. Register boundary addresses】
以PA0
为例:由上表可知,GPIOA基地址为0x50002000,记住备用。
【以输出模式为例】查看芯片GPIO使用方法:
【P1064 I/O pin alternate function multiplexer and mapping 】: 找到如下描述:
•【配置PA0为高速推挽上拉输出高电平模式】
下面看GPIO寄存器组:
【P1072 GPIO registers】
【GPIO port mode register (GPIOx_MODER) 】端口模式配置寄存器
由上图可知,该寄存器每两位控制一个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[1]
•
驱动加载入口
/* 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[2]
【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)[3]
自此,openHarmony操作硬件寄存器的基本流程已经介绍完毕,供阅读参考。
4 .openHarmony GPIO驱动使用
参阅官方GPIO | OpenHarmony[4]
【补充】: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管脚设置中断,重复其他管脚会导致系统运行异常
References
[1]
驱动框架详情参考官方文档 | OpenHarmony: https://docs.openharmony.cn/pages/0004010000/#简介[2]
参考官方文档:GPIO | OpenHarmony: https://docs.openharmony.cn/pages/0004010101/#概述[3]
device/st/drivers/gpio · 小熊派开源社区/BearPi-HM_Micro_small - 码云 - 开源中国 (gitee.com): https://gitee.com/bearpi/bearpi-hm_micro_small/tree/master/device/st/drivers/gpio[4]
参阅官方GPIO | OpenHarmony: https://docs.openharmony.cn/pages/0004010200/#概述