七、系统使用说明
7.1 使用 GPIO
EA65xx 芯片支持三组 GPIO,分别命名为 SAP GPIO、CPU GPIO 和 RTC GPIO。在每个 GPIO 组内对信号从 0 开始编址,称为物理编号,此外,软件还对全部 GPIO 信号做了统一编址,称为逻辑编号,其编号规则是:
GPIO 逻辑编号 = GPIO 所属组 BASE 号 + 组内物理编号
其中,三个组的 BASE 号分别为 0、200、400。BASE 号的来源是根据 GPIO 组的命名和系统设计而定,以确保逻辑编号的唯一性和可识别性。于是,根据上述规则,GPIO 与 Linux 设备节点的对应关系如下表所示:
| GPIO 组别 | Linux 设备节点 | 物理编号范围 | 逻辑编号范围 |
|---|---|---|---|
| SAP GPIO | /sys/class/gpio/gpiochip0/ | 0 到 33 | 0 到 33 |
| CPU GPIO | /sys/class/gpio/gpiochip200/ | 0 到 142 | 200 到 342 |
| RTC GPIO | /sys/class/gpio/gpiochip400/ | 0 到 7 | 400 到 407 |
以 GPIO29 为例,它属于 SAP 组的原因是其逻辑编号为 29,根据逻辑编号计算规则,29 落在 SAP 组的逻辑编号范围内(0 到 33)。要正确判断 GPIO 的归属,可以根据其逻辑编号与上述表格中的逻辑编号范围进行匹配,可以通过以下命令进行操作:
- 导出 GPIO:将 GPIO 节点导出到
/sys/class/gpio/export。
echo 29 > /sys/class/gpio/export
- 设置 GPIO 方向:设置为输入或输出。
echo out > /sys/class/gpio/gpio29/direction
- 读写 GPIO 值
echo 1 > /sys/class/gpio/gpio29/value # 设置为高电平
cat /sys/class/gpio/gpio29/value # 读取当前值
7.2 使用 UART
EM20-DK 开发板提供了 12 组 UART,其中,EM20-DK 对外支持 UART0 已用作 bootloader 和 Linux 的 console 端口; UART5 用户可看情况配置使用。其他 UART 的可用性取决于具体的硬件设计和配置。用户可以通过以下命令测试 UART 通信:
root@taco-dk:~# echo "Hello World" > /dev/ttyPS0
Hello World
ttyPS0 为相应的 UART 设备文件,有 ttyPS0、ttyPS1、ttyPS2 等。如果 UART 不可用,可能是因为硬件设计或配置问题。
以下是一个示例代码,展示如何配置和使用 UART:
int set_interface_attribs(int fd, int speed, int hwfc) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
perror("tcgetattr");
return -1;
}
cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_iflag &= ~IGNBRK;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
if (hwfc) {
tty.c_cflag |= CRTSCTS; // Enable hardware flow control
} else {
tty.c_cflag &= ~CRTSCTS;
}
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("tcsetattr");
return -1;
}
return 0;
}
void set_blocking(int fd, int should_block) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
perror("tcgetattr");
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5;
if (tcsetattr(fd, TCSANOW, &tty) != 0)
perror("tcsetattr");
}
int rs485_send_test() {
int fd = open(rs485_port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
perror("open RS485 port failed");
return -1;
}
if (set_interface_attribs(fd, rs485_baudrate, 0) != 0) {
close(fd);
return -1;
}
set_blocking(fd, 0);
std::string full_data = rs485_test_data + "\r\n";
int ret = write(fd, full_data.c_str(), full_data.length());
if (ret < 0) {
perror("RS485 write failed");
close(fd);
return -1;
}
DEBUG_INFO("Sent %d bytes: %s", ret, full_data.c_str());
close(fd);
return 0;
}
功能说明:
set_interface_attrs函数用于配置 UART 接口属性,如波特率、数据位、停止位、校验位等。set_blocking函数用于设置 UART 接口的阻塞模式。rs485_send_test函数用于打开指定的 UART 端口,配置其属性,并通过该端口发送数据。
通过上述代码,用户可以配置 UART 接口并通过该接口发送数据。具体配置和功能取决于实际的硬件设计和需求。
7.3 使用 I2C
提供了 8 路 I2C,可以使用标准的 I2C tools 和 API 操作。
i2cdetect命令输出已安装 I2C 总线的列表:
root@taco-dk:~# i2cdetect -l
i2c-0 i2c Cadence I2C at d2400000 I2C adapter
i2c-1 i2c Cadence I2C at d2401000 I2C adapter
i2c-2 i2c Cadence I2C at c2401000 I2C adapter
i2cset配置 I2C 外设
i2cset -f -y $I2C_BUS $I2C_ADDR $reg $value
i2cget获取 I2C 外设的值
i2cget -f -y $I2C_BUS $I2C_ADDR $reg w
参数解释:
- $I2C_BUS:I2C 总线号。
- $I2C_ADDR:I2C 设备的地址。
- $reg:寄存器地址。
- $value:要写入的值。
以下为一个示例,展示如何使用 i2cset 和 i2cget 命令:
# 设置 I2C 设备寄存器值
i2cset -f -y 1 0x50 0x00 0x01
# 获取 I2C 设备寄存器值
i2cget -f -y 1 0x50 0x00
7.4 使用 PWM
PWM(脉冲宽度调制)是一种常用的信号生成技术,用于控制模拟信号的占空比。操作 PWM 的示例如下:
- 启用 PWM:
echo 0 > /sys/class/pwm/pwmchip0/pwm0/export
每个 PWM 通道下有以下文件:
period:设置 PWM 周期,单位为纳秒(ns)。duty_cycle:设置 PWM 占空比。enable:启用或禁用 PWM 通道。
- 配置 PWM:
echo 1000 > /sys/class/pwm/pwmchip0/pwm0/period # 设置 PWM 周期为 1ms
echo 500 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 设置 PWM 占空比
这表明设置 PWM0 的周期为 1ms,占空比为 50%。
- 使能 PWM:
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
在我们的参考底板上,这个 PWM 输出被用作风扇调速,Linux 的 thermal 框架会自动根据芯片温度调整风扇转速。所以用户会在第一步 export 时看到 Device or resource busy 错误,需要修改设备树把对应的 pwmfan 节点 disable 掉后才能自由使用:
fan0: fan {
compatible = "pwm-fan";
pwms = <&sappwm0 0 10000 0>;
cooling-levels = <255 192 128 64 1>;
#cooling-cells = <2>;
};
7.5 查询硬件温度
使用命令 cat /sys/class/thermal/thermal_zone0/temp 获取 SoC 芯片温度。该命令返回的温度数值单位为毫摄氏度(mC)。
root@taco-dk:~# cat /sys/class/thermal/thermal_zone0/temp
26816
上述命令返回的数值 26816 表示芯片温度为 26.816°C。
使用命令 cat /sys/class/thermal/thermal_zone1/temp 获取核心板温度。该命令返回的温度数值单位同样为毫摄氏度(mC)。
root@taco-dk:~# cat /sys/class/thermal/thermal_zone1/temp
30250
7.6 查询内存信息
EM20-DK 板载了 14GB DDR,可以分为三类:
- OS 管理的部分,即可以用
malloc、kmalloc等常规 API 分配出来使用。
root@taco-mes20:~# free -h
total used free shared buff/cache available
Mem: 9.6Gi 268Mi 9.3Gi 1.1Mi 121Mi 9.4Gi
Swap: 0B 0B 0B
- taco-sys 管理的部分,预留给 NPU、VENC、VDEC 使用,需要使用
libtacosys.so库接口使用,可通过启动介质中bootfs分区里config.txt配置(默认值是8G),可以按需修改:
########## Memory Configuration ##########
# Uncomment this to set tacosys memory address to 0x140000000,
# and size to 0x80000000 bytes (2GiB)
# tacosys_mem_addr=0x140000000
# tacosys_mem_size=0x80000000
下面是一些使用 libtacosys 接口的实例代码,调用 npu_usage_t 和 cpu_usage_t 结构体来获取使用情况:
taco_npu_usage_t npu_usage = {0};
if (taco_sys_get_npu_usage(&npu_usage) == TACO_SUCCESS) {
printf("NPU Usage: %d%%\n", npu_usage.npu_usage);
} else {
printf("Failed to get NPU usage\n");
}
taco_cpu_usage_t cpu_usage = {0};
if (taco_sys_get_cpu_usage(&cpu_usage) == TACO_SUCCESS) {
printf("Total CPU Usage: %d%%\n", cpu_usage.total_cpu_usage);
for (int i = 0; i < 8; i++) {
printf("CPU%d Usage: %d%%\n", i, cpu_usage.cpu_usage[i]);
}
} else {
printf("Failed to get CPU usage\n");
}
- NPU 管理的部分,专门预留给 NPU(下面所示是注释掉的默认值),可以按需修改:
# Uncomment this to set npu memory address to 0x1c0000000,
# and size to 0x280000000 bytes (10GiB)
# npu_mem_addr=0x1c0000000
# npu_mem_size=0x280000000
这些命令将显示 tacosys 和 NPU 内存的实际分配情况。要验证配置是否生效,可以通过重启设备并再次检查这些文件的内容。