一、vivado 工程搭建
因为十分简单,不在赘述。
生成平台文件,导出,建立vitis linux 设备树工程,不在赘述。
生成的设备树文件:
axi_iic_0: i2c@80000000 { #address-cells = <1>; #size-cells = <0>; clock-names = "s_axi_aclk"; clocks = <&zynqmp_clk 71>; compatible = "xlnx,axi-iic-2.1", "xlnx,xps-iic-2.00.a"; interrupt-names = "iic2intc_irpt"; interrupt-parent = <&gic>; interrupts = <0 89 4>; reg = <0x0 0x80000000 0x0 0x10000>; };将设备树文件放到linux下编译,生成映像文件:略。
二、AHT20读
在Linux操作系统下,你完全可以不编写内核驱动程序,直接通过系统提供的I2C用户空间接口(/dev/i2c-2)编写应用程序访问AHT20,这是嵌入式Linux中访问I2C外设最常用、最高效的方式。
核心原理
Linux内核已自带i2c-dev驱动(通用I2C字符设备驱动),会将硬件I2C控制器(如你裸机中用的IIC1)映射为/dev/i2c-x设备文件,用户空间程序通过操作该文件(open/read/write/ioctl)即可实现I2C通信,无需重复编写底层驱动。
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #include <errno.h> /************************** AHT20配置 *****************************/ #define AHT20_SLAVE_ADDR 0x38 // AHT20 7位I2C地址 #define AHT20_CMD_INIT 0xBE // 初始化指令 #define AHT20_CMD_MEASURE 0xAC // 测量指令 #define AHT20_DATA_LEN 6 // 读取数据长度 /************************** 函数声明 *****************************/ int aht20_init(int fd); int aht20_read_data(int fd, float *temp, float *humi); int i2c_write(int fd, unsigned char *buf, int len); int i2c_read(int fd, unsigned char *buf, int len); int main(int argc, char *argv[]) { int fd; float temperature, humidity; const char *i2c_dev = "/dev/i2c-2"; // 1. 打开I2C设备文件 fd = open(i2c_dev, O_RDWR); if (fd < 0) { perror("Failed to open i2c device"); return -1; } // 2. 设置I2C从机地址 if (ioctl(fd, I2C_SLAVE, AHT20_SLAVE_ADDR) < 0) { perror("Failed to set slave address"); close(fd); return -1; } // 3. 初始化AHT20 if (aht20_init(fd) != 0) { fprintf(stderr, "AHT20 init failed\n"); close(fd); return -1; } printf("AHT20 init success\n"); // 4. 循环读取温湿度 while (1) { if (aht20_read_data(fd, &temperature, &humidity) == 0) { // 格式化输出(与你裸机逻辑一致) int temp_int = (int)(temperature * 100 + 0.5); int humi_int = (int)(humidity * 100 + 0.5); printf("Temperature: %d.%02d ℃, Humidity: %d.%02d %%RH\n", temp_int/100, temp_int%100, humi_int/100, humi_int%100); } else { fprintf(stderr, "Read AHT20 data failed\n"); } sleep(1); // 1秒间隔 } close(fd); return 0; } /** * @brief 初始化AHT20 * @param fd: I2C设备文件描述符 * @return 0成功,-1失败 */ int aht20_init(int fd) { unsigned char init_cmd[3] = {AHT20_CMD_INIT, 0x08, 0x00}; // 发送初始化指令 if (i2c_write(fd, init_cmd, 3) != 3) { return -1; } usleep(10000); // 等待初始化完成(10ms) return 0; } /** * @brief 读取AHT20温湿度数据 * @param fd: I2C设备文件描述符 * @param temp: 输出温度值(℃) * @param humi: 输出湿度值(%RH) * @return 0成功,-1失败 */ int aht20_read_data(int fd, float *temp, float *humi) { unsigned char measure_cmd[3] = {AHT20_CMD_MEASURE, 0x33, 0x00}; unsigned char read_buf[AHT20_DATA_LEN] = {0}; // 发送测量指令 if (i2c_write(fd, measure_cmd, 3) != 3) { return -1; } usleep(80000); // 等待测量完成(80ms) // 读取6字节数据 if (i2c_read(fd, read_buf, AHT20_DATA_LEN) != AHT20_DATA_LEN) { return -1; } // 校验数据有效性(第1字节bit7=0表示有效) if (read_buf[0] & 0x80) { fprintf(stderr, "AHT20 data invalid\n"); return -1; } // 湿度计算:(read_buf[1]<<12 | read_buf[2]<<4 | read_buf[3]>>4) / 2^20 * 100 unsigned int humi_raw = (read_buf[1] << 12) | (read_buf[2] << 4) | (read_buf[3] >> 4); *humi = (float)humi_raw / (1 << 20) * 100.0; // 温度计算:((read_buf[3]&0x0F)<<16 | read_buf[4]<<8 | read_buf[5]) / 2^20 * 200 - 50 unsigned int temp_raw = ((read_buf[3] & 0x0F) << 16) | (read_buf[4] << 8) | read_buf[5]; *temp = (float)temp_raw / (1 << 20) * 200.0 - 50.0; return 0; } /** * @brief I2C写数据 * @param fd: I2C设备文件描述符 * @param buf: 待发送数据缓冲区 * @param len: 数据长度 * @return 实际发送字节数,失败返回-1 */ int i2c_write(int fd, unsigned char *buf, int len) { int ret = write(fd, buf, len); if (ret < 0) { perror("I2C write failed"); return -1; } return ret; } /** * @brief I2C读数据 * @param fd: I2C设备文件描述符 * @param buf: 接收数据缓冲区 * @param len: 期望读取长度 * @return 实际读取字节数,失败返回-1 */ int i2c_read(int fd, unsigned char *buf, int len) { int ret = read(fd, buf, len); if (ret < 0) { perror("I2C read failed"); return -1; } return ret; }编译与运行
- 编译命令(需交叉编译,替换为你的交叉编译器):
arm-linux-gnueabihf-gcc aht20_app.c -o aht20_app -O2- 拷贝到开发板:
scp aht20_app root@<开发板IP>:/root/- 运行程序(需root权限):
chmod +x aht20_app ./aht20_app关键注意事项
- 权限问题:
/dev/i2c-2默认可能只有root可访问,若普通用户运行需修改权限:
chmod 666 /dev/i2c-2- I2C地址确认:AHT20的7位地址是
0x38,i2c-dev驱动无需转换为8位(驱动会自动处理); - 时序保障:测量指令发送后需等待至少75ms再读取数据,否则数据无效;
- 错误处理:代码中增加了perror打印系统错误,便于调试(如I2C总线错误、设备无响应等)。
最终效果: