news 2026/7/7 19:41:42

26.MPSOC FPGA linux读AHT20传感器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
26.MPSOC FPGA linux读AHT20传感器

一、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位地址是0x38i2c-dev驱动无需转换为8位(驱动会自动处理);
  • 时序保障:测量指令发送后需等待至少75ms再读取数据,否则数据无效;
  • 错误处理:代码中增加了perror打印系统错误,便于调试(如I2C总线错误、设备无响应等)。

最终效果:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 18:49:42

28.驱动程序,生成ko文件,需要放到特定文件夹去编译吗?

编写Linux内核驱动生成.ko&#xff08;内核模块&#xff09;文件&#xff0c;不一定需要把代码放到内核源码目录编译&#xff0c;但核心取决于你的编译方式——有两种主流方式&#xff0c;对应不同的文件存放逻辑&#xff0c;推荐第二种&#xff08;更灵活&#xff09;&#xf…

作者头像 李华
网站建设 2026/7/7 10:55:35

P1319 压缩技术

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h>int main() {// 动态读取所有压缩码数字int* codes NULL;int count 0;int capacity 10;int num;// 初始分配内存codes (int*)malloc(capacity * sizeof(int));// 读取所有数字while (s…

作者头像 李华
网站建设 2026/7/7 18:23:21

冒泡排序与选择排序详解

《冒泡排序和选择排序》冒泡排序&#xff1a;冒泡排序&#xff1a;两两相比&#xff0c;如果前面大于后面的值交换位置外循环要经过多少轮比较 一轮找出一个最值内循环比较多少次 例如5个数比较4次即可 下一轮的时候 不用和上传最值进行比较&#xff0c;所以写内循环次数可以 这…

作者头像 李华
网站建设 2026/7/6 23:38:37

告别AI痕迹:5款智能工具

现在AI“大行其道”&#xff0c;很多同学都学会了用chatgpt或者其他AI工具来辅助写论文&#xff0c;但是一些论文检测平台却管得更严了&#xff0c;不仅有查重检测&#xff0c;居然还有AI率检测&#xff01;很多高校也开始卡AI率&#xff0c;AI率不过关的很有可能论文不通过&am…

作者头像 李华
网站建设 2026/7/7 9:59:45

探索 SCN - Adaboost 的回归预测之旅

SCN-adaboost基于随机配置网络SCN的Adaboost回归预测&#xff0c;SCN-Adaboost回归预测&#xff0c;多输入单输出模型。 评价指标包括:R2、MAE、MSE、RMSE和MAPE等&#xff0c;代码质量极高&#xff0c;方便学习和替换数据。在机器学习的广袤世界里&#xff0c;回归预测一直是一…

作者头像 李华
网站建设 2026/7/7 6:16:38

探索对称双向半桥LLC谐振变换器双向运行仿真模型

对称双向半桥LLC谐振变换器双向运行仿真模型。 模型采用闭环PI控制&#xff0c;频率控制方法。 输出效果好&#xff0c;效率高。 如展示图所示。 matlab/simulink最近在研究电力电子相关内容时&#xff0c;接触到了对称双向半桥LLC谐振变换器双向运行仿真模型&#xff0c;感觉特…

作者头像 李华