Rockchip平台之RK3566/3568 GPIO控制

前言

        笔者作为一名入职快一年的嵌入式软件工程师,对于嵌入式的很多知识还不是很熟悉,希望通过不断学习提升自己的能力,决定通过文章记录自己的学习成长过程。最近接触到的项目用到了瑞芯微的RK3566,主要基于该主控提供的SDK在linux系统上开发红外夜视功能,实现日夜场景的自动切换。其中,GPIO涉及红外补光灯的开关和红外滤光片的开关,本文主要介绍这两部分GPIO的控制。

RK3566 概述

        RK3566 采用四核 ARM Cortex-A55 CPU,主频高达 1.8GHz,具备较强的计算能力。同时,芯片集成了 ARM Mali-G52 GPU,支持 OpenGL ES 1.1/2.0/3.2、OpenCL 2.0 和 Vulkan 1.1,能够处理复杂的图形渲染任务。芯片支持多种视频编解码格式,包括 H.264、H.265、VP9 等,最高支持 4K@60fps 的视频解码和 1080p@60fps 的视频编码。

        此外,RK3566 还集成了 NPU(神经网络处理单元),支持 AI 计算,适用于人脸识别、图像处理等 AI 应用。RK3566 提供了丰富的接口,包括 USB 3.0、PCIe 2.1、SATA 3.0、千兆以太网等,支持多种外设连接。芯片还支持 DDR4/LPDDR4/LPDDR4X 内存,最大支持 8GB,能够满足高性能应用的需求。

GPIO 概述

        GPIO(General Purpose Input/Output)是一种通用的输入输出接口,广泛应用于嵌入式系统和微控制器中。它允许开发者通过软件配置来控制硬件设备的输入和输出状态。GPIO 引脚可以设置为输入模式,用于读取外部信号;也可以设置为输出模式,用于驱动外部设备。

GPIO 控制

        本次项目所使用到的GPIO有三根,包括IR CUT A-B脚和IR_LED_EN。硬件原理图如下所示,主要的控制逻辑为开启夜视,A低 B高 100ms,关闭夜视,A高 B低 100ms,开启或关闭之后,A 、B脚均需要保持拉低状态,IR_LED_EN拉低,开启红外补光灯,拉高,关闭红外补光灯。

图1.ircut引脚

图2.irled引脚

GPIO 命名

Rockchip Pin的ID按照 控制器(bank)+端口(port)+索引序号(pin) 组成。

  • 控制器和GPIO控制器数量⼀致

  • 端口固定 A、B、C和D,每个端口仅有8个索引号,(a=0,b=1,c=2,d=3)

  • 索引序号固定 0、1、2、3、4、5、6、7

RK3562/RK3566/RK3568具有5个GPIO控制器,每个控制器可以控制32个IO,作为GPIO功能时, 端口⾏为由GPIO控制器寄存器配置。

GPIO1_A4表达的意思为第1组控制器,端口号为A,索引号为4。该引脚号的计算公式为32 x 1 + 0 x 8 + 4 = 36,更多的计算例子如图3所示。本次项目所用到的三个GPIO经过计算分别为RK_IRC_BIN = 122、RK_IRC_AIN = 123和RK_IR_ENABLE = 145。

图3.计算举例

通过sysfs 接口控制GPIO

在Linux中,最常见的读写GPIO方式就是用GPIO sysfs interface, 是通过操作 /sys/class/gpio 目录下的 exportunexportgpio{N}/direction, gpio{N} /value (用实际引脚号替代{N})等文件实现的,经常出现shell脚本里面。本次的项目采用sysfs接口来实现对GPIO的控制,在开始编码之前可以提前使用脚本检测硬件和GPIO接口是否正常,下图是笔者控制台编写的测试用例,终端设备通过串口接入控制台实现串口控制,通过不断切换ircut引脚的高低电平实现红外滤光片的开关控制。

图4.测试用例

使用libgpiod控制IO

在Linux中,还要另外一种控制GPIO的方法,就是是用libgpiod实现对GPIO的控制,这需要提前在终端设备上通过以下命令安装相应的库后才能使用,本次项目笔者未采用此种方法,这里做个简单介绍。

sudo apt install gpiod

此外,gpiod工具的使用方法与sysfs接口的不同,gpiod是以控制器为单位,然后再详细到端口号和索引号,即gpiod使用两个数据确定引脚。具体的计算例子与常用的命令如下图所示,有兴趣的读者可以自行验证该方法。

图5.计算例子

图6.常用命令

代码实现

        本次项目主要是通过sysfs接口实现GPIO控制,其中最为重要的三个函数就是设置GPIO的输入输出方向direction,GPIO出口export和GPIO引脚的值value,以及通过GPIO实现对IRCUT的控制,以下是实现GPIO控制的主要源文件和头文件。

ircut_control.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h> 
#include <fcntl.h> 
#include <inttypes.h>
#include <unistd.h>
#include <errno.h>
#include <log/log.h>
#include <sys/ioctl.h>

#include "ircut_control.h"

#define SYSFS_GPIO_DIR  "/sys/class/gpio"
#define SYSFS_GPIO_NAME "/gpio"
#define MAX_BUF 64

const int RK_IRC_BIN = 122;
const int RK_IRC_AIN = 123;
const int RK_IR_ENABLE = 145;

// set gpio export
int prt_gpio_export(unsigned int gpio)
{
    int fd, len;
    char buf[MAX_BUF];

    fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);

    if (fd < 0)
    {
        perror("gpio/export");
        return fd;
    }

    len = snprintf(buf, sizeof(buf), "%d", gpio);

    write(fd, buf, len);

    close(fd);

    return 0;
}

// set gpio direction
int prt_gpio_set_direction(unsigned int gpio, const char *direction)
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR SYSFS_GPIO_NAME "%d/direction", gpio);

    fd = open(buf, O_WRONLY);
    if (fd < 0)
    {
        perror("gpio/direction");
        return fd;
    }

    write(fd, direction, strlen(direction)+1);

    close(fd);

    return 0;
}

// set gpio value
int prt_gpio_set_value(unsigned int gpio, unsigned int value)
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR SYSFS_GPIO_NAME "%d/value", gpio);

    fd = open(buf, O_WRONLY);

    if (fd < 0)
    {
        perror("gpio/set-value");
        return fd;
    }

    if (value)
        write(fd, "1", 2);
    else
        write(fd, "0", 2);

    close(fd);

    return 0;
}

// set gpio
int prt_gpio_set(int gpio_num,int value)
{
    char buf_name[50] = {0};

    if (value != 1 && value != 0)
    {
        perror("invalid_argument value.\n");
        return -1;
    }

	snprintf(buf_name,sizeof(buf_name),"/sys/class/gpio/gpio%d",gpio_num);

    if (access (buf_name,F_OK) != 0 )
    {
        // set gpio export
        prt_gpio_export(gpio_num);
    }

    // set gpio direction
    prt_gpio_set_direction(gpio_num,"out");

    // set gpio value
    prt_gpio_set_value(gpio_num,value);

    return 0;
}

void delay_ms(unsigned int ms) 
{
    usleep(ms * 1000); 
}

// control the ir_filter sheet
void ir_cutter_ctrl(bool on)
{
    if (on) {
        // open the ir_filter sheet  RC_AIN low, IRC_BIN high

        prt_gpio_set(RK_IRC_AIN, 0);
        prt_gpio_set(RK_IRC_BIN, 1);
    } else {
        // close the ir_filter sheet  RC_AIN high, IRC_BIN low

		prt_gpio_set(RK_IRC_AIN, 1);
        prt_gpio_set(RK_IRC_BIN, 0);
    }

    delay_ms(200);
    prt_gpio_set(RK_IRC_AIN, 0);
    prt_gpio_set(RK_IRC_BIN, 0);
}

ircut_control.h

#ifndef __IRCUT_CONTROL_H
#define __IRCUT_CONTROL_H

#ifdef __cplusplus
extern "C" {
#endif

extern const int RK_IRC_BIN;
extern const int RK_IRC_AIN;
extern const int RK_IR_ENABLE;

int prt_gpio_export(unsigned int gpio);
int prt_gpio_set_direction(unsigned int gpio, const char *direction);
int prt_gpio_set_value(unsigned int gpio, unsigned int value);
int prt_gpio_set(int gpio_num,int value);
void ir_cutter_ctrl(bool on);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif //__IRCUT_CONTROL_H

总结

        这是笔者发布的第一篇的博客,主要用于记录自己的学习和成长过程,对于其中理解不到位的地方还请各位大佬评论指正,大家共同学习,一起进步。文末附上笔者在该SDK下的功能实现位置,以便再次学习查看。

/external/camera_engine_rkaiq/hal_interface2.0/interface/ircut_control.c

/external/camera_engine_rkaiq/hal_interface2.0/interface/ircut_control.h

### RK3566 U-Boot GPIO 初始化配置教程 对于RK3566平台,在U-Boot阶段初始化GPIO涉及多个方面的工作,包括但不限于设置引脚的功能、方向以及初始电平等。基于不同版本的U-Boot和具体的硬件设计需求,实现细节可能有所差异。 #### 修改`board_f.c`文件以支持特定于RK3566GPIO操作 为了自定义GPIO的行为,通常需要编辑位于源码中的`/u-boot/common/board_f.c`这样的文件来加入针对目标芯片特性的处理逻辑。例如: ```c #include <linux/io.h> #define GPIO_BASE_ADDR 0xFF7C_0000 // 假设这是RK3566GPIO寄存器基地址[^1] // 定义具体要控制GPIO端口及其偏移量 #define GPIOX_DR_OFFSET 0x0000 #define GPIOX_DDR_OFFSET 0x0004 unsigned int *gpio_data_register; unsigned int *gpio_direction_register; void init_gpio(void){ gpio_data_register = (unsigned int*)(GPIO_BASE_ADDR + GPIOX_DR_OFFSET); gpio_direction_register = (unsigned int*)(GPIO_BASE_ADDR + GPIOX_DDR_OUTPUT); // 设置某些位为输出模式 writel((readl(gpio_direction_register)|OUTPUT_PIN_MASK), gpio_direction_register); // 将指定管脚拉高或拉低 if(HIGH_LEVEL_REQUIRED){ setbits_le32(gpio_data_register, OUTPUT_PIN_MASK); }else{ clrbits_le32(gpio_data_register, OUTPUT_PIN_MASK); } } ``` 这段代码展示了如何通过访问内存映射I/O区域内的专用寄存器来进行基本的GPIO配置。需要注意的是实际开发过程中应当参照最新的官方文档确认确切的寄存器位置和其他参数值[^4]。 #### 设备树中描述GPIO属性 除了直接编程外,现代嵌入式Linux系统往往依赖设备树(Device Tree)来传递有关外围接口的信息给内核及引导加载程序。因此,在适当的位置添加关于所需GPIO资源的相关节点也是必要的步骤之一。这可以通过调整`.dts`(device tree source) 文件完成: ```diff &iomux { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart0_default>; + pinctrl-gpios: gpios@0 { + compatible = "rockchip,rk3566-pinctrl"; + rockchip,pins = < + /* Pin number */ + 0x0A /* Function code for output */ + ROCKCHIP_PULL_NONE + ROCKCHIP_DRIVE_STRENGTH_DEFAULT + ROCKCHIP_SCHMITT_NORMAL>; + }; }; ``` 上述片段说明了怎样向现有的设备树结构里引入新的子节点用于表达一组预设好的GPIO特性集合[^2]。 #### 编译并测试更改后的固件 最后一步就是编译整个项目并将生成的目标镜像烧录到目标板卡上去验证效果。确保遵循标准流程执行这些任务可以减少潜在的风险因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值