Linux下控制GPIO的三种方法,以及HI3559AV100 GPIO配置和操作(一)

本文介绍了海思HI3559AV100平台GPIO的配置和操作方法,包括GPIO基地址、控制寄存器设置、模式切换以及输出高低电平控制。通过控制台命令和寄存器操作,详细解析了如何控制GPIO1_0、GPIO1_1和GPIO2_0的输出和输入模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开头先吐槽下海思的gpio的寄存器,之前是玩三星的,比较容易理解,海思的gpio玩法,云里雾里的

一:海思的官方文档,关于gpio的介绍
在hisi文档,如下章节有介绍
在这里插入图片描述

1:gpio基地址:
在这里插入图片描述
2:gpio控制寄存器基地址
在这里插入图片描述
3:gpio控制寄存器控制方法;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二。实操举例
我的项目是控制两个led灯,和按键,分别对应原理图上的:GPIO1_0,GPIO1_1,GPIO2_0

根据手册资料,设置为输出模式是1: 设置为输入模式是0:输出高电平是1:输出低电平是0:
0x01 OUT //0x00 IN
0x01 HIGH //0x00 LOW

1:下面是我定义设置的:
//GPIO1_0
#define GPIO1_0_DIR 0x12141400
#define GPIO1_0_DATA 0x12141004

//GPIO1_1
#define GPIO1_1_DIR 0x12141400
#define GPIO1_1_DATA 0x12141008

//GPIO2_0
#define GPIO2_0_DIR 0x12142400
#define GPIO2_0_DATA 0x12142004

2:DIR的地址怎么来的和怎么控制呢:?
首先我的led要设置为输出模式,输出高或低电平

在这里插入图片描述
在这里插入图片描述
GPIO1_0_DIR 的地址是怎么来的?
(基地址+ 控制寄存器偏移地址)
所以是( 0x12141000 + 0x400)

GPIO1_1和GPIO1_0基地址是一样的,偏移地址也是一样的
所以也是( 0x12141000 + 0x400)

控制是通过掩码来控制的,bit[7:0],相应的位设置为1则为输出模式,设置为0则为输入模式,默认是输入模式
在这里插入图片描述

设置各个gpio不同模式,比如:
0000 0000 //第0个-7个gpio为输入模式
0000 0001 //第0个gpio为输出模式

0000 0000 //第0个-7个gpio为输入模式
0000 0010 //第1个gpio为输出模式

0000 0000 //第0个-7个gpio为输入模式
0000 0011 //第0个和1个gpio为输出模式

现在海思平台,控制台输入命令测试一下:
himm 0x12141400 0x01 //第0个gpio为输出模式
himm 0x12141400 0x02 //第0个gpio为输出模式
himm 0x12141400 0x03 //第0个和1个gpio为输出模式

3:DATA 怎么控制呢?这个可能不好理解
首先我的led要输出高或低电平,就要对对应的寄存器进行写入:
地址是怎么来的?
基地址 + dtat偏移地址:
#define GPIO1_0_DATA 0x12141004
#define GPIO1_1_DATA 0x12141008
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
[9:2] 对应 [7:0] ,对相应位设置为1,则可以操作,对相应位设置为0,则会被屏蔽:
第九位需要三个字节来表达,所以是 0x000 - 0x3fc
在这里插入图片描述
第0个gpio对应是第2bit
0000 0000 0100 就是0x004
GPIO1_0_DATA = 基地址 + dtat偏移地址:
0x12141000 + 004

第1个gpio对应是第3bit
0000 0000 1000 就是0x008
则GPIO1_0_DATA = 基地址 + dtat偏移地址:
0x1214100 + 008

控制GPIO1_0就是对第0位输出0或1
现在海思平台,控制台输入命令测试一下:
himm 0x12141400 0x01 / /第0个gpio为输出模式
himm 0x12141004 0x01 //第0个gpio为高
himm 0x12141004 0x00 //第0个gpio为低
、、、、
控制GPIO1_1就是对第1位输出0或1
现在海思平台,控制台输入命令测试一下:
himm 0x12141400 0x02 / /第1个gpio为输出模式
himm 0x12141008 0x02 //第1个gpio为高
himm 0x12141008 0x00 //第1个gpio为低

控制两个一起亮呢?
himm 0x12141400 0x03 / /gpio为输出模式
himm 0x1214100C 0x03 //gpio为高
himm 0x1214100C 0x00 //gpio为低

关于管脚控制,,如果有gpio有复用的,iic和普通gpio复用,需要先设置寄存器,在如下文档里
在这里插入图片描述

在这里插入图片描述
如下,设置管脚复用
i2c9_pin_mux()
{
himm 0x1F0010D0 0x1452; #I2C9_SCL
himm 0x1F0010D4 0x1452; #I2C9_SDA
}

gpio_pin_mux()
{
himm 0x1F000000 0x0450; #gpio1_0
himm 0x1F000004 0x0450; #gpio1_1
himm 0x1F000008 0x0450; #gpio1_2
himm 0x1F000030 0x1450; #gpio2_0
}

uart2_pin_mux()
{
himm 0x1F0000E4 0x1551; #UART2_RXD
himm 0x1F0000E8 0x1551; #UART2_TXD
}

HI3559AV100 GPIO配置和操作,有个坑,记录一下,
我使用的gpio1_6端口,接一个led灯,按前篇文档一介绍的方法,测试,始终没设置为输出模式,控制led。
gpio默认都是gpio,输入模式,查看此引脚,没有复用,所以没有复用它,直接操作,控制输入高,低。坑啊
最好上电使用时,对它复用配置,按文档一,配置它,在sdk里,代码里也设置了一下,坑,不知道这里也会
设置gpio属性,搜索这个复用寄存器0x1F000018,在自己的启动脚本load3559av100_multicore,这个不知道是hisi写的,还是
前面同事写的,这个脚本里,没有找到使用它,结果在sdk的 out/liteos/single/init/sdk_init.c找到了,复用为了gesonser的clk,靠,

//#VICAP default setting is VI
static void sensor_pin_mux(void)
{
//#CLK_A CLK_B
himm(0x1F000000, 0x000004f1);
himm(0x1F000004, 0x000004f1);

himm(0x1F000008, 0x000004f1);
himm(0x1F00000C, 0x000004f1);

himm(0x1F000010, 0x000004f1);
himm(0x1F000014, 0x000004f1);

himm(0x1F000018, 0x000004f1);
himm(0x1F00001C, 0x000004f1);

//#RST
himm(0x1F000020, 0x000004f1);
himm(0x1F000024, 0x000004f1);
himm(0x1F000028, 0x000004f1);
himm(0x1F00002C, 0x000004f1);
//echo "============vicap_pin_mux done============="

}

这个文件里,有很多复用的,使用时,最好检查一下。
在控制led程序时,himm 0x1F000018 0x00001400,复用为普通gpio模式

二、app调用system api,调用shell命令测试方法

设置4个管脚输出模式,输出高或低
//0x12151020 //0x12151040 //0x12151008 // 0x12151010
//gpio17_3 gpio17_4 gpio17_1 gpio17_2

system("himm 0x12151400  0x1e");// set out mode

system("himm 0x12151010  0x0");//gpio 2  //reset
delay_ms(100);
system("himm 0x12151010  0x04");//gpio 2  //reset
delay_ms(100);
system("himm 0x12151020  0x0");//gpio 3  
system("himm 0x12151040  0x0");//gpio 4 
system("himm 0x12151008  0xff");//gpio 1 

gpio17_1 = 17 * 8 + 1 //根据下面文档介绍,算得编号是137,使用命令查看管脚输出值

echo 137 > /sys/class/gpio/export
cat /sys/class/gpio/gpio137/value

可以看到输出是1,

启动内核后,可以查看GPIO全部信息

cat /sys/kernel/debug/pinctrl/pinctrl/pinmux-pins
 
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): wireless-wlan (GPIO UNCLAIMED) function wireless-wlan group wifi-wake-host
pin 1 (gpio0-1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 2 (gpio0-2): (MUX UNCLAIMED) gpio0:2
pin 3 (gpio0-3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 4 (gpio0-4): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 5 (gpio0-5): (MUX UNCLAIMED) gpio0:5
pin 6 (gpio0-6): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 7 (gpio0-7): (MUX UNCLAIMED) gpio0:7
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 10 (gpio0-10): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 11 (gpio0-11): ff050000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 12 (gpio0-12): ff050000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 13 (gpio0-13): (MUX UNCLAIMED) (GPIO UNCLAIMED)

三、hisi官方文档,控制台。shell命令控制gpio方法

参考hisi官方文档,关于内核态,用户态,怎么控制gpio的,在下面路径里有介绍说明
在这里插入图片描述

在这里插入图片描述
在控制台或应用层调用system控制这些命令的用法,设置为输出模式,可以直接这样写

此操作示例通过命令实现对GPIO的读写操作。

步骤 1 在控制台使用echo命令将要操作的GPIO编号export:

echo N > /sys/class/gpio/export
 
每组GPIO有8个GPIO管脚。
N为要操作的GPIO编号,该编号等于GPIO组号 * 8 + 组内偏移号,例如GPIO4_2的编号为4 * 8 + 2 = 34。
export之后就会生成/sys/class/gpio/gpioN目录
例如:exportGPIO4_2:
echo 34 > /sys/class/gpio/export

步骤 2 在控制台使用echo命令设置GPIO方向:

对于输入:echo in > /sys/class/gpio/gpioN/direction
对于输出:echo out > /sys/class/gpio/gpioN/direction
例如:设置GPIO4_2方向
对于输入:echo in > /sys/class/gpio/gpio34/direction
对于输出:echo out > /sys/class/gpio/gpio34/direction
GPIO方向只有out 和in 两种。
可使用cat命令查看GPIO方向:cat  /sys/class/gpio/gpioN/direction,例如查看GPIO4_2方向:cat /sys/class/gpio/gpio34/direction

步骤 3 在控制台使用cat或echo命令查看GPIO输入值或设置GPIO输出值:

查看输入值:cat /sys/class/gpio/gpioN/value
输出低:echo 0 > /sys/class/gpio/gpioN/value
输出高:echo 1 > /sys/class/gpio/gpioN/value

使用命令查看状态值
cat /sys/class/gpio/gpioN/value
GPIO的电平值只有 010为低电平;1为高电平。

步骤 4 在控制台使用echo命令将操作的GPIO编号unexport:

echo N > /sys/class/gpio/unexport

四、在应用层使用gpio方法

此操作示例在用户态下实现对GPIO的读写操作。
步骤 1 将要操作的GPIO编号export:
fp = fopen(“/sys/class/gpio/export”, “w”);
fprintf(fp, “%d”, gpio_num);
fclose(fp);

每组GPIO有8个GPIO管脚。
参数gpio_num为要操作的GPIO编号,该编号等于GPIO组号 * 8 + 组内偏移号,例如GPIO4_2的编号为4 * 8 + 2 = 34。

步骤 2 设置GPIO方向:
fp = fopen(“/sys/class/gpio/gpio%d/direction”, “rb+”);
对于输入:fprintf(fp, “in”);
对于输出:fprintf(fp, “out”);
fclose(fp);

步骤 3 查看GPIO输入值或设置GPIO输出值:
fp = fopen(“/sys/class/gpio/gpio%d/value”, “rb+”);
查看输入值:fread(buf, sizeof(char), sizeof(buf) - 1, fp);
输出低:
strcpy(buf,“0”);
fwrite(buf, sizeof(char), sizeof(buf) - 1, fp);
输出高:
strcpy(buf,“1”);
fwrite(buf, sizeof(char), sizeof(buf) - 1, fp);

步骤 4 将操作的GPIO编号unexport:
fp = fopen(“/sys/class/gpio/unexport”, “w”);
fprintf(fp, “%d”, gpio_num);
fclose(fp);

此代码为GPIO操作示例程序,仅为客户开发用户态的GPIO操作程序提供参考,不提供实际应用功能。

#include <stdio.h>
#include <string.h>

int gpio_test_in(unsigned int gpio_chip_num, unsigned int gpio_offset_num)
{
        FILE *fp;
        char file_name[50];
        unsigned char buf[10];
        unsigned int gpio_num;

        gpio_num = gpio_chip_num * 8 + gpio_offset_num;

        sprintf(file_name, "/sys/class/gpio/export");
        fp = fopen(file_name, "w");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "%d", gpio_num);
        fclose(fp);

        sprintf(file_name, "/sys/class/gpio/gpio%d/direction", gpio_num);
        fp = fopen(file_name, "rb+");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "in");
        fclose(fp);

        sprintf(file_name, "/sys/class/gpio/gpio%d/value", gpio_num);
        fp = fopen(file_name, "rb+");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        memset(buf, 0, 10);
        fread(buf, sizeof(char), sizeof(buf) - 1, fp);
        printf("%s: gpio%d_%d = %d\n", __func__,gpio_chip_num, gpio_offset_num, buf[0]-48);
        fclose(fp);
        sprintf(file_name, "/sys/class/gpio/unexport");
        fp = fopen(file_name, "w");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "%d", gpio_num);
        fclose(fp);

        return (int)(buf[0]-48);
}

int gpio_test_out(unsigned int gpio_chip_num, unsigned int gpio_offset_num,
                unsigned int gpio_out_val)
{
        FILE *fp;
        char file_name[50];
        unsigned char buf[10];
        unsigned int gpio_num;

        gpio_num = gpio_chip_num * 8 + gpio_offset_num;

        sprintf(file_name, "/sys/class/gpio/export");
        fp = fopen(file_name, "w");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "%d", gpio_num);
        fclose(fp);

        sprintf(file_name, "/sys/class/gpio/gpio%d/direction", gpio_num);
        fp = fopen(file_name, "rb+");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "out");
        fclose(fp);

        sprintf(file_name, "/sys/class/gpio/gpio%d/value", gpio_num);
        fp = fopen(file_name, "rb+");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        if (gpio_out_val)
                strcpy(buf,"1");
        else
                strcpy(buf,"0");

        fwrite(buf, sizeof(char), sizeof(buf) - 1, fp);
        printf("%s: gpio%d_%d = %s\n", __func__,
                        gpio_chip_num, gpio_offset_num, buf);
        fclose(fp);

        sprintf(file_name, "/sys/class/gpio/unexport");
        fp = fopen(file_name, "w");
        if (fp == NULL) {
                printf("Cannot open %s.\n", file_name);
                return -1;
        }
        fprintf(fp, "%d", gpio_num);
        fclose(fp);

        return 0;

}

五、内核态,驱动控制,内核态GPIO操作示例

此操作示例在内核态下实现对GPIO的读写操以及GPIO中断操作。
内核态GPIO读写操作示例
操作示例如下:
步骤 1 注册GPIO:
gpio_request(gpio_num, NULL);

 每组GPIO有8个GPIO管脚。
 参数gpio_num为要操作的GPIO编号,该编号等于GPIO组号 * 8 + 组内偏移号,例如GPIO4_2的编号为4 * 8 + 2 = 34

步骤 2 设置GPIO方向:
对于输入:gpio_direction_input(gpio_num)
对于输出:gpio_direction_output(gpio_num, gpio_out_val)

如果是输出,需要设置一个输出的初始值,参数gpio_out_val为输出时的初始值。

步骤 3 查看GPIO输入值或设置GPIO输出值:
查看输入值:gpio_get_value(gpio_num);
输出低:gpio_set_value(gpio_num, 0);
输出高:gpio_set_value(gpio_num, 1);

输入值为gpio_get_value(gpio_num)的返回值。

步骤 4 释放注册的GPIO编号:
gpio_free(gpio_num);

七、Linux下控制GPIO的三种方法

1. 应用空间控制gpio

在/sys/class/gpio/下有个export文件,向export文件写入要操作的GPIO号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO输入或者输出模式,而value可控制GPIO的状态或者读取状态。

/sys/class/gpio/目录下各个文件说明:
/sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号;
/sys/class/gpio/unexport 用于通知系统取消导出;
/sys/class/gpio/gpioX/direction文件,可以写入in(设置输入方向)或out(设置输出方向);
/sys/class/gpio/gpioX/value文件是可以读写GPIO状态;
/sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号,寄存器名称,引脚总数;其中X表示具体的引脚编号。

操作gpio,比如我要操作GPIO8_A6作为高电平输出有效, 那么有以下三个操作:
换算对应的gpio number,可以通过/sys/kernel/debug/gpio查询信息:

/sys/kernel/debug # cat gpio          
GPIOs 184-215, platform/ff770000.pinctrl, gpio6:
 gpio-193 (?                   ) in  hi
 gpio-194 (?                   ) in  hi
 
GPIOs 216-247, platform/ff770000.pinctrl, gpio7:
 gpio-218 (enable              ) out hi
 gpio-219 (lcd_en              ) in  hi
 gpio-220 (lcd_cs              ) in  hi
 gpio-221 (gslX680 wake pin    ) out hi
 gpio-222 (gslX680 irq pin     ) out lo
 gpio-223 (headset_gpio        ) in  hi
 gpio-233 (?                   ) in  hi
 gpio-234 (?                   ) in  hi
 GPIOs 248-279, platform/ff770000.pinctrl, gpio8:
 
 GPIOs 280-311, platform/ff770000.pinctrl, gpio15:

可以看到gpio8是以nubmer为248开始, 那么GPIO8_A6就是 248 + 6 = 254,接下来就可以导出gpio了。

/sys/class/gpio # echo 254 > export                          
root@rk3288:/sys/class/gpio # ls
export
gpio254

这样就有exportg,pio254

设置成输出

root@rk3288:/sys/class/gpio/gpio254 # echo out > direction                     
root@rk3288:/sys/class/gpio/gpio254 # cat direction                            
out

输出高电平

root@rk3288:/sys/class/gpio/gpio254 # echo 1 > value                           
root@rk3288:/sys/class/gpio/gpio254 # cat value 

这种方式一般不采用,为了gpio使用的安全性,一般是不将gpio的使用权暴露给应用层的,即sys/class/下没有gpio节点

2. 内核空间控制gpio
在内核空间控制gpio有两种方法,第一种是通过调用gpiolib的接口来控制gpio;第二种是通过ioremap来控制gpio。
gpiolib控制gpio
gpiolib简介

Linux Kernel 中对 GPIO 资源进行了抽象,抽象出一个叫做 Gpiolib 的东西。
中间层是 Gpiolib,用于管理系统中的 GPIO。Gpiolib 汇总了 GPIO 的通用操作,根据 GPIO 的特性,Gpiolib 对上(其他 Drivers)提供的一套统一通用的操作 GPIO 的软件接口,屏蔽了不同芯片的具体实现。对下,Gpiolib 提供了针对不同芯片操作的一套 framework,针对不同芯片,只需要实现 Specific Chip Driver ,然后使用 Gpiolib 提供的注册函数,将其挂接到 Gpiolib 上,这样就完成了这一套东西。

Gpiolib 为其他驱动提供的 APIs

int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_get_value(unsigned gpio);
int gpio_set_value(unsigned gpio);

3、ioremap控制gpio
这种方法会降低程序的可读性,不建议使用。
linux内核空间访问的地址为虚拟地址(3~4GB),故在内核空间操作某个寄存器时,需先通过ioremap函数将物理地址映射成虚拟地址。
用ioremap() 获取寄存器的地址:通过 readl() 或者 writel() 函数直接操作映射后的地址:

unsigned int __iomem  *base_addr1;  //__iomem可选择,告诉你为虚拟地址
#define  GPIO8_REGBASE  (0x20A0000)
#define  GPIO8_A6 (*(volatile unsigned int *)(base_addr1 + 6)) //指针unsigned int为4字节,指针加1,字节加4
base_addr1 = ioremap(GPIO8_REGBASE, 0x14)

GPIO8_A6 |= (1<<8);

int temp;
temp = readl(GPIO8_A6);
temp |= (1<<8);
writel(temp, GPIO8_A6);
    
iounmap(base_addr1)

网看到的一个人,app来操作gpio,很新奇,但是不建议用,可以测试
原文链接:https://blog.youkuaiyun.com/suifen_/article/details/138287050
在rk3588使能uart设备,并生成/dev/ttySx设备节点

1.1 dts中添加对应的节点
1.1.1 在Y:\rk3588_android\kernel-5.10\arch\arm64\boot\dts\rockchip\rk3588s.dtsi找到串口3肯串口9的名称

 uart3: serial@feb60000 {
        compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
        reg = <0x0 0xfeb60000 0x0 0x100>;
        interrupts = <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
        clock-names = "baudclk", "apb_pclk";
        reg-shift = <2>;
        reg-io-width = <4>;
        dmas = <&dmac0 12>, <&dmac0 13>;
        pinctrl-names = "default";
        pinctrl-0 = <&uart3m1_xfer>;
        status = "disabled";
    };
    uart9: serial@febc0000 {
        compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
        reg = <0x0 0xfebc0000 0x0 0x100>;
        interrupts = <GIC_SPI 340 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&cru SCLK_UART9>, <&cru PCLK_UART9>;
        clock-names = "baudclk", "apb_pclk";
        reg-shift = <2>;
        reg-io-width = <4>;
        dmas = <&dmac2 11>, <&dmac2 12>;
        pinctrl-names = "default";
        pinctrl-0 = <&uart9m1_xfer>;
        status = "disabled";
    };

由此可知名称为uart3、uart9.

1.1.2 在Y:\rk3588_android\kernel-5.10\arch\arm64\boot\dts\rockchip\rk3588s-pinctrl.dtsi查找RK_PA6、RK_PA5对应哪个节点,可知对应uart3m2_xfer,所以下面pinctrl的节点应该写uart3m2_xfer。uart9类似

uart3m2_xfer: uart3m2-xfer {
            rockchip,pins =
                /* uart3_rx_m2 */
                <4 RK_PA6 10 &pcfg_pull_up>,
                /* uart3_tx_m2 */
                <4 RK_PA5 10 &pcfg_pull_up>;
};
uart9m2_xfer: uart9m2-xfer {
            rockchip,pins =
                /* uart9_rx_m2 */
                <3 RK_PD4 10 &pcfg_pull_up>,
                /* uart9_tx_m2 */
                <3 RK_PD5 10 &pcfg_pull_up>;
 };

1.1.3 在Y:\rk3588_android\kernel-5.10\arch\arm64\boot\dts\rockchip\rk3588-nvr-demo-v10-android.dts重新写uart3、uart9节点,重新定义部分属性:

&uart9 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart9m2_xfer>;
    status = "okay";
};&uart3 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart3m2_xfer>;
    status = "okay";
};

1.2 rk3588 引脚标号怎么计算

//GPIO0_B7 --> 0*32 + (B-A)*8 + 7 = 15
//GPIO0_C6 --> 0*32 + (C-A)*8 + 6 = 22
//GPIO0_C5 --> 0*32 + (C-A)*8 + 5 = 21 
//GPIO0_C4 --> 0*32 + (C-A)*8 + 4 = 20

2 、串口3和串口9以及GPIO控制代码

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <stdlib.h>int gpio_export(int gpioNum)
{
    int fd = -1;
    char buf[4] = {0,};if(gpioNum == -1) {
        printf("gpio=-1\n");
        return -1;
    }sprintf(buf, "%d", gpioNum);
    fd = open("/sys/class/gpio/export", O_WRONLY);
    if (fd < 0) {
        printf("fail to export gpio[%d]\n", gpioNum);
        return -1;
    }write(fd, buf, sizeof(buf));
    close(fd);
    printf("export gpio[%d] ok\n", gpioNum);
    return 0;
}int gpio_unexport(int gpioNum)
{
    int fd = -1;
    char buf[4] = {0,};if(gpioNum == -1)
        return -1;sprintf(buf, "%d", gpioNum);
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if (fd < 0) {
        printf("no export gpio[%d]\n", gpioNum);
        return -1;
    }write(fd, buf, sizeof(buf));
    close(fd);
    return 0;
}int gpio_set_direction(int gpioNum, const char* direct)  //direct: "in", "out"
{
    int fd = -1;
    char path[48] = {0,};if(gpioNum == -1)
        return -1;sprintf(path, "/sys/class/gpio/gpio%d/direction", gpioNum);
    fd = open(path, O_WRONLY);
    if (fd < 0) {
        printf("fail direction[%s] gpio[%d]\n", direct, gpioNum);
        return -1;
    }write(fd, direct, sizeof(direct));
    close(fd);
    return 0;
}int gpio_get_value(int gpioNum)
{
    int gpio_fd = -1;
    char path[48] = {0,};
    char gpio_value = 0;
    
    if(gpioNum == -1)
        return -1;
    
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpioNum);
    
    gpio_fd = open(path, O_RDONLY);
    if (gpio_fd < 0) {
        perror("Failed to open GPIO sysfs file");
        return -1;
    }if (read(gpio_fd, &gpio_value, sizeof(gpio_value)) < 0) {
        perror("Failed to read GPIO value");
        close(gpio_fd);
        return -1;
    }
    
    close(gpio_fd);return atoi(&gpio_value);
}int gpio_set_value(int gpioNum, int value)
{
    int fd = -1;
    char buf[4] = {0,};
    char path[48] = {0,};if(gpioNum == -1)
        return -1;
    
    sprintf(buf, "%d", value);
    sprintf(path, "/sys/class/gpio/gpio%d/value", gpioNum);
​
    fd = open(path, O_WRONLY);
    if (fd < 0) {
        printf( "fail to open gpio[%d]\n", gpioNum);
        return -1;
    }write(fd, buf, 4);
    close(fd);
    return 0;
}int uart_open_dev(const char *dev_name)
{
    int fd = -1;
    if (!strcmp(dev_name, "RS232")) {
        fd = open("/dev/ttyS3", O_RDWR | O_NOCTTY);
        if (fd == -1) {
            perror("Failed to open the serial port");
            return -1;
        }
    } else if (!strcmp(dev_name, "RS485")) {
        fd = open("/dev/ttyS9", O_RDWR | O_NOCTTY);
        if (fd == -1) {
            perror("Failed to open the serial port");
            return -1;
        }
    }
    return fd;
}int uart_send_data(int fd, char *data)
{
   ssize_t bytesWritten = write(fd, data, strlen(data));
   if (bytesWritten == -1) {
       perror("Failed to write to serial port");
       return -1;
   }
   return 0;
}int uart_read_data(int fd, char *data)
{
    ssize_t bytesRead = 0;
    bytesRead = read(fd, data, sizeof(data));
    return bytesRead;
}int uart_set_baudrate(int fd, int baudrate)
{
    struct termios options;
    tcgetattr(fd, &options);
    
    // 设置串口波特率
    cfsetispeed(&options, baudrate);
    cfsetospeed(&options, baudrate);
    
    // 8个数据位,无奇偶校验,一个停止位
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    
    // 使能接收和本地模式
    options.c_cflag |= (CLOCAL | CREAD);
    
    // 设置为原始模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    options.c_oflag &= ~OPOST;
    
    tcsetattr(fd, TCSANOW, &options);
    
    return 0;
}void uart_close_dev(int fd)
{
    close(fd);
}
​
​
#if 1
//GPIO0_B7 --> 0*32 + (B-A)*8 + 7 = 15
//GPIO0_C6 --> 0*32 + (C-A)*8 + 6 = 22
//GPIO0_C5 --> 0*32 + (C-A)*8 + 5 = 21 
//GPIO0_C4 --> 0*32 + (C-A)*8 + 4 = 20#define GPIO0_C6    22
#define GPIO0_C5    21
#define GPIO0_C4    20  
void gpio_init(void)
{
    gpio_export(GPIO0_C6);
    gpio_export(GPIO0_C5);
    gpio_export(GPIO0_C4);
    
    gpio_set_direction(GPIO0_C6, "out");  //设置为输出
    gpio_set_direction(GPIO0_C5, "out");  //设置为输出
    gpio_set_direction(GPIO0_C4, "in");   //设置为输入
    
    gpio_set_value(GPIO0_C6, 1);//输出高电平
    gpio_set_value(GPIO0_C5, 0);//输出低电平
}void gpio_exit(void)
{
    gpio_unexport(GPIO0_C6);
    gpio_unexport(GPIO0_C5);
    gpio_unexport(GPIO0_C4);
}int main(int argc, char *argv[]) 
{
    printf("argc = %d, argv[1] = %s\n", argc, argv[1]);
    if (!strcmp(argv[1], "RS485") || !strcmp(argv[1], "RS232")) 
   {
        int fd = uart_open_dev(argv[1]);
        uart_set_baudrate(fd, B115200);
        
        char buffer[256];
        char sendData[256] = "Hello, serial port!\n";
        ssize_t bytesRead;
        
        while (1) {
            bytesRead = uart_read_data(fd, buffer);
            
            if (bytesRead > 0) {
                printf("Received %zd bytes: %.*s\n", bytesRead, (int)bytesRead, buffer);
                uart_send_data(fd, sendData);
            }
        }
        
        uart_close_dev(fd);
    } 
    else 
    {
        gpio_init();
        while (1) {
            if (gpio_get_value(GPIO0_C4)) {
                printf("GPIO0_C4 is high level\n");
                gpio_set_value(GPIO0_C6, 1);//输出高电平
                gpio_set_value(GPIO0_C5, 1);//输出高电平
            } else {
                printf("GPIO0_C4 is low level\n");
                gpio_set_value(GPIO0_C6, 0);//输出低电平
                gpio_set_value(GPIO0_C5, 0);//输出低电平
            }
            usleep(200 * 1000);
        }
        gpio_exit();
    }
    return 0;
}
#endif
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值