OpenHarmony 5.1.0 南向开发实战:基于RK3568开发板移植3个关键驱动
RK3568作为当前主流的嵌入式处理器之一,在智能终端设备领域有着广泛应用。本文将深入探讨如何在OpenHarmony 5.1.0系统上为RK3568开发板移植三个关键硬件驱动:GPIO控制器驱动、I2C总线驱动和PWM控制器驱动。通过完整的代码示例和详细的移植步骤,帮助开发者快速掌握OpenHarmony南向开发的核心技术。
1. 开发环境准备与基础配置
在开始驱动移植前,需要搭建完整的开发环境。OpenHarmony 5.1.0对开发工具链和系统组件进行了多项优化,特别是对RK3568芯片的支持更加完善。
1.1 硬件准备清单
- RK3568开发板(建议使用官方标准开发板)
- Type-C数据线(用于烧录和调试)
- 5V/3A电源适配器
- 调试用串口转USB模块(如CH340)
- 可选:J-Link调试器(用于深度调试)
1.2 软件环境配置
# 安装基础工具链 sudo apt-get install -y git python3.8 python3-pip # 获取OpenHarmony 5.1.0代码 repo init -u https://gitee.com/openharmony/manifest.git -b OpenHarmony-5.1.0-Release --no-repo-verify repo sync -c repo forall -c 'git lfs pull' # 安装编译依赖 ./build/prebuilts_download.sh注意:建议使用Ubuntu 20.04 LTS作为开发主机系统,确保环境一致性。编译过程需要至少16GB内存和100GB磁盘空间。
1.3 内核配置调整
RK3568使用Linux 5.10内核,需要进行针对性配置:
# 进入内核配置界面 cd kernel/linux-5.10 make ARCH=arm64 menuconfig # 关键配置项 CONFIG_GPIO_ROCKCHIP=y CONFIG_I2C_ROCKCHIP=y CONFIG_PWM_ROCKCHIP=y保存配置后,需要更新到设备树文件。RK3568的设备树位于kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568.dtsi。
2. GPIO控制器驱动移植
GPIO驱动是硬件控制的基础,RK3568的GPIO控制器具有丰富的功能和灵活的配置方式。
2.1 驱动框架分析
OpenHarmony的GPIO子系统采用分层设计:
- 硬件抽象层(HAL):
drivers/peripheral/gpio/hal - 内核接口层:
drivers/peripheral/gpio/linux - 用户态接口:通过
/dev/gpio设备节点提供操作接口
2.2 关键代码实现
创建drivers/peripheral/gpio/chips/gpio_rockchip.c:
#include "gpio_core.h" #define RK3568_GPIO_BANKS 4 #define GPIO_PER_BANK 32 struct rockchip_gpio_chip { void __iomem *reg_base; struct GpioCntlr cntlr; int irq; }; static int RockchipGpioSetDir(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t dir) { struct rockchip_gpio_chip *chip = container_of(cntlr, struct rockchip_gpio_chip, cntlr); unsigned long flags; u32 val; spin_lock_irqsave(&chip->lock, flags); val = readl(chip->reg_base + GPIO_SWPORT_DDR); if (dir == GPIO_DIR_OUT) val |= BIT(gpio); else val &= ~BIT(gpio); writel(val, chip->reg_base + GPIO_SWPORT_DDR); spin_unlock_irqrestore(&chip->lock, flags); return 0; } static const struct GpioMethod g_method = { .request = NULL, .release = NULL, .write = RockchipGpioWrite, .read = RockchipGpioRead, .setDir = RockchipGpioSetDir, .getDir = RockchipGpioGetDir, .toIrq = NULL, }; int RockchipGpioInit(struct HdfDeviceObject *device) { struct rockchip_gpio_chip *chip; int ret; chip = (struct rockchip_gpio_chip *)OsalMemCalloc(sizeof(*chip)); chip->reg_base = OsalIoRemap(0xFE720000, 0x1000); chip->cntlr.device = device; chip->cntlr.ops = &g_method; chip->cntlr.count = RK3568_GPIO_BANKS * GPIO_PER_BANK; ret = GpioCntlrAdd(&chip->cntlr); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: add gpio controller failed", __func__); return ret; } return HDF_SUCCESS; }2.3 驱动配置与测试
在vendor/hihope/rk3568/hdf_config/device_info.hcs中添加配置:
gpio_config { controller_0x1200 :: gpio_controller { match_attr = "rockchip_gpio_driver"; gpioDir = 0xFE720000; gpioRegBase = 0xFE720000; gpioRegSize = 0x1000; irqNum = 100; irqShare = 1; } }测试命令:
cat /sys/kernel/debug/gpio # 查看GPIO状态 echo 123 > /sys/class/gpio/export # 导出GPIO123 echo out > /sys/class/gpio/gpio123/direction # 设置为输出 echo 1 > /sys/class/gpio/gpio123/value # 输出高电平3. I2C总线驱动移植
I2C总线是连接各类传感器的关键接口,RK3568内置了多个I2C控制器。
3.1 驱动框架分析
OpenHarmony的I2C架构包含:
- HDF驱动框架层:提供统一设备模型
- 平台驱动层:实现具体控制器操作
- 设备驱动层:对接具体I2C设备
3.2 关键代码实现
创建drivers/peripheral/i2c/chips/i2c_rockchip.c:
#include "i2c_core.h" #define RK3568_I2C_SPEED_STANDARD 100000 #define RK3568_I2C_SPEED_FAST 400000 struct rockchip_i2c { struct I2cCntlr cntlr; void __iomem *regs; unsigned int irq; unsigned int speed; }; static int RockchipI2cTransfer(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int num) { struct rockchip_i2c *i2c = container_of(cntlr, struct rockchip_i2c, cntlr); int ret = 0; /* 配置I2C控制器 */ writel(I2C_CON_EN | I2C_CON_MOD_TX | I2C_CON_START, i2c->regs + I2C_CON); for (int i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) { /* 读操作处理 */ writel(msgs[i].addr << 1 | 1, i2c->regs + I2C_TXD); writel(I2C_CON_EN | I2C_CON_MOD_TX | I2C_CON_START, i2c->regs + I2C_CON); /* 读取数据 */ for (int j = 0; j < msgs[i].len; j++) { if (j == msgs[i].len - 1) writel(I2C_CON_EN | I2C_CON_MOD_RX | I2C_CON_STOP, i2c->regs + I2C_CON); else writel(I2C_CON_EN | I2C_CON_MOD_RX, i2c->regs + I2C_CON); msgs[i].buf[j] = readl(i2c->regs + I2C_RXD); } } else { /* 写操作处理 */ writel(msgs[i].addr << 1, i2c->regs + I2C_TXD); writel(I2C_CON_EN | I2C_CON_MOD_TX | I2C_CON_START, i2c->regs + I2C_CON); for (int j = 0; j < msgs[i].len; j++) { writel(msgs[i].buf[j], i2c->regs + I2C_TXD); writel(I2C_CON_EN | I2C_CON_MOD_TX, i2c->regs + I2C_CON); } writel(I2C_CON_EN | I2C_CON_MOD_TX | I2C_CON_STOP, i2c->regs + I2C_CON); } } return ret; } static const struct I2cMethod g_rockchipI2cMethod = { .transfer = RockchipI2cTransfer, }; int RockchipI2cInit(struct HdfDeviceObject *device) { struct rockchip_i2c *i2c; int ret; i2c = (struct rockchip_i2c *)OsalMemCalloc(sizeof(*i2c)); i2c->regs = OsalIoRemap(0xFDD40000, 0x1000); i2c->cntlr.priv = i2c; i2c->cntlr.methods = &g_rockchipI2cMethod; i2c->cntlr.busId = 0; i2c->speed = RK3568_I2C_SPEED_STANDARD; ret = I2cCntlrAdd(&i2c->cntlr); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: add i2c controller failed", __func__); return ret; } return HDF_SUCCESS; }3.3 设备树配置
在kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568.dtsi中添加:
i2c0: i2c@fdd40000 { compatible = "rockchip,rk3568-i2c"; reg = <0x0 0xfdd40000 0x0 0x1000>; interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; clocks = <&pmucru CLK_I2C0>, <&pmucru PCLK_I2C0>; clock-names = "i2c", "pclk"; pinctrl-names = "default"; pinctrl-0 = <&i2c0_xfer>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; };3.4 测试验证
使用i2c-tools进行测试:
i2cdetect -y 0 # 扫描I2C0总线上的设备 i2cget -y 0 0x50 0x00 # 读取I2C设备0x50的0x00寄存器 i2cset -y 0 0x50 0x00 0x12 # 向I2C设备0x50的0x00寄存器写入0x124. PWM控制器驱动移植
PWM驱动在电机控制、背光调节等场景中至关重要,RK3568提供了多路PWM输出。
4.1 驱动框架分析
OpenHarmony的PWM驱动架构:
- 硬件抽象层:
drivers/peripheral/pwm/hal - 平台驱动层:实现具体PWM控制器操作
- 服务层:提供统一的用户态接口
4.2 关键代码实现
创建drivers/peripheral/pwm/chips/pwm_rockchip.c:
#include "pwm_core.h" #define RK3568_PWM_CHANNELS 4 #define PWM_REG_CNTR 0x00 #define PWM_REG_PERIOD_HPR 0x04 #define PWM_REG_DUTY_LPR 0x08 #define PWM_REG_CTRL 0x0c struct rockchip_pwm { struct PwmDev dev; void __iomem *base; struct clk *clk; }; static int RockchipPwmSetConfig(struct PwmDev *pwm, struct PwmConfig *config) { struct rockchip_pwm *chip = (struct rockchip_pwm *)pwm->priv; u64 div, val; u32 clk_rate; clk_rate = clk_get_rate(chip->clk); div = (u64)clk_rate * config->period; do_div(div, NSEC_PER_SEC); writel(div, chip->base + PWM_REG_PERIOD_HPR); val = (u64)div * config->dutyCycle; do_div(val, 100); writel(val, chip->base + PWM_REG_DUTY_LPR); return 0; } static int RockchipPwmEnable(struct PwmDev *pwm) { struct rockchip_pwm *chip = (struct rockchip_pwm *)pwm->priv; u32 val; val = readl(chip->base + PWM_REG_CTRL); val |= PWM_ENABLE; writel(val, chip->base + PWM_REG_CTRL); return 0; } static const struct PwmMethod g_rockchipPwmOps = { .setConfig = RockchipPwmSetConfig, .enable = RockchipPwmEnable, .disable = RockchipPwmDisable, }; int RockchipPwmInit(struct HdfDeviceObject *device) { struct rockchip_pwm *pwm; int ret; pwm = (struct rockchip_pwm *)OsalMemCalloc(sizeof(*pwm)); pwm->base = OsalIoRemap(0xFE6E0000, 0x10); pwm->dev.method = &g_rockchipPwmOps; pwm->dev.priv = pwm; pwm->dev.device = device; ret = PwmDeviceAdd(&pwm->dev); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: add pwm device failed", __func__); return ret; } return HDF_SUCCESS; }4.3 设备树配置
在rk3568.dtsi中添加PWM节点:
pwm0: pwm@fe6e0000 { compatible = "rockchip,rk3568-pwm"; reg = <0x0 0xfe6e0000 0x0 0x10>; clocks = <&cru CLK_PWM0>, <&cru PCLK_PWM0>; clock-names = "pwm", "pclk"; pinctrl-names = "default"; pinctrl-0 = <&pwm0_pin>; #pwm-cells = <3>; status = "disabled"; };4.4 测试验证
通过sysfs接口测试PWM:
echo 0 > /sys/class/pwm/pwmchip0/export # 导出PWM0 echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period # 设置周期为1ms echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 设置占空比为50% echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable # 启用PWM输出5. 驱动调试与性能优化
驱动移植完成后,需要进行系统级调试和性能优化,确保驱动稳定可靠。
5.1 调试技巧与工具
常用调试命令:
dmesg | grep rockchip # 查看内核日志 cat /proc/interrupts # 查看中断统计 cat /proc/iomem | grep -i pwm # 查看内存映射性能分析工具:
- perf:分析驱动性能热点
- ftrace:跟踪函数调用关系
- systrace:系统级性能分析
5.2 常见问题解决
问题1:GPIO无法正确设置方向
- 检查设备树中pinctrl配置
- 验证寄存器映射地址是否正确
- 确认GPIO是否被其他驱动占用
问题2:I2C通信失败
- 使用示波器检查SCL/SDA信号
- 确认从设备地址正确
- 检查时钟配置和上拉电阻
问题3:PWM输出不稳定
- 验证时钟源配置
- 检查负载是否匹配
- 调整死区时间配置
5.3 性能优化建议
中断优化:
- 使用线程化中断处理
- 实现中断共享
- 合理设置中断亲和性
DMA传输:
- 对大数据量传输启用DMA
- 合理设置DMA缓冲区对齐
电源管理:
- 实现runtime PM支持
- 合理使用延迟工作队列
// DMA配置示例 void SetupDmaTransfer(struct device *dev) { struct dma_chan *chan; struct dma_slave_config config = {0}; chan = dma_request_chan(dev, "tx"); config.direction = DMA_MEM_TO_DEV; config.dst_addr = phys_addr; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; dmaengine_slave_config(chan, &config); dma_async_issue_pending(chan); }通过以上完整的驱动移植过程和优化建议,开发者可以快速在RK3568开发板上构建稳定的OpenHarmony 5.1.0系统。这三个关键驱动的实现为后续外设开发奠定了坚实基础,开发者可以根据实际需求进一步扩展更多功能模块。