【RK3568 平台I2C协议与AGS10驱动开发】

RK3568 平台I2C协议与AGS10驱动开发

  • 一、I2C 总线协议基础
  • 二、I2C 通信过程详解
  • 三、AGS10 传感器概述
  • 四、AGS10驱动开发
    • 1. 硬件连接
    • 2. 设备树(DTS)配置
    • 3. 内核驱动开发
  • 五、调试与验证
  • 六、总结

引言

在嵌入式系统开发中,传感器数据采集是常见需求。本文将详细介绍如何在 RK3568 平台上开发 AGS10 空气质量传感器的 Linux 驱动,同时深入解析 I2C 总线协议的工作原理。通过本文,你将掌握 I2C 通信的核心概念,并学会如何为特定传感器开发 Linux 内核驱动。

一、I2C 总线协议基础

I2C(Inter-Integrated Circuit)是由飞利浦公司开发的一种串行通信协议,广泛应用于短距离、低速的设备间通信。它具有以下特点:

  1. 双线制:仅需两根信号线
    SDA(Serial Data Line):数据传输线
    SCL(Serial Clock Line):时钟线
  2. 主从架构:
    主设备(Master):控制总线,发起通信
    从设备(Slave):被动响应主设备请求
  3. 寻址机制:
    每个从设备有唯一的 7 位或 10 位地址
    地址在通信开始时由主设备发送
  4. 传输速率:
    标准模式:100kHz
    快速模式:400kHz
    高速模式:3.4MHz
  5. 信号特征:
    开漏输出,需外接上拉电阻
    逻辑 0:低电平;逻辑 1:高阻态(由上拉电阻拉至高电平)

二、I2C 通信过程详解

I2C 通信的基本流程如下:

  1. 起始条件(Start):
    主设备在 SCL 为高电平时,将 SDA 从高电平拉至低电平
    标志一次通信的开始
  2. 地址帧:
    主设备发送从设备地址(7 位或 10 位)
    第 8 位为 R/W 位(0 表示写,1 表示读)
  3. 应答位(ACK/NACK):
    每传输 8 位数据后,接收方需发送一个 ACK(低电平)或 NACK(高电平)
    表示是否成功接收数据
  4. 数据传输:
    根据 R/W 位决定数据方向
    写操作:主设备→从设备
    读操作:从设备→主设备
  5. 停止条件(Stop):
    主设备在 SCL 为高电平时,将 SDA 从低电平拉至高电平
    标志一次通信的结束
  6. 重复起始条件(Repeated Start):
    在不发送 Stop 条件的情况下,再次发送 Start 条件
    用于连续传输不同地址的数据

三、AGS10 传感器概述

AGS10 是奥松电子推出的一款高精度空气质量传感器,用于检测空气中的挥发性有机化合物(VOCs)。其主要特性包括:
在这里插入图片描述
传感器采用标准I2C通信协议,适应多种设备。I2C的物理接口包含串行数据信号(SDA)与串行
时钟信号(SCL)两个接口。两个接口需通过1kΩ~10kΩ电阻上拉至VDD。SDA用于读、写传感器数
据。SCL上电必须保持高电平直到进行I2C通信开始,否则会引起I2C通讯不良。当I2C通信时SCL用于主机与传感器之间的通讯同步。多个I2C设备可以共享总线,但是只能允许一个主机设备出现在总线上。传感器I2C器件地址为0x1A(7-bit),写指令为0x34,读指令为0x35。通讯速率不高于15kHz。
在这里插入图片描述
在这里插入图片描述
命令集合:
在这里插入图片描述

四、AGS10驱动开发

1. 硬件连接

AGS10 SCL ---> RK3568 I2C3_SCL_MO
AGS10 SDA ---> RK3568 I2C3_SDA_MO
AGS10 GND ---> RK3568 GND
AGS10 VCC ---> RK3568 VCC3V3_SYS

在这里插入图片描述

2. 设备树(DTS)配置

在 RK3568 的设备树文件kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi中添加 ags10节点:
在这里插入图片描述

&i2c3 {  clock-frequency = <15000>;status = "okay";ags10: ags10@1a {compatible = "aosong,ags10";reg = <0x1A>;status = "okay";};
};

i2c3定义如下:
kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi
在这里插入图片描述
i2c3引脚复用如下:
kernel/arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
在这里插入图片描述

3. 内核驱动开发

i2c函数介绍:

函数原型

int i2c_master_send(struct i2c_client *client, const char *buf, int count);

功能

  • 向指定 I2C 从设备发送数据,适用于简单的写操作(如配置寄存器)。

参数

  • client:指向目标 I2C 设备的客户端结构体指针
  • buf:指向要发送的数据缓冲区
  • count:要发送的字节数

返回值

  • 成功:返回实际发送的字节数(通常等于count
  • 失败:返回负值错误码(如-ENODEV-EIO等)

函数原型

int i2c_master_recv(struct i2c_client *client, char *buf, int count);

功能

  • 从指定 I2C 从设备接收数据,适用于简单的读操作(如读取传感器数据)。

参数

  • client:指向目标 I2C 设备的客户端结构体指针
  • buf:指向接收数据的缓冲区
  • count:期望接收的字节数

返回值

  • 成功:返回实际发送的字节数(通常等于count
  • 失败:返回负值错误码(如-ENODEV-EIO等)

函数原型

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

功能

  • 发送一个或多个 I2C 消息(struct i2c_msg数组),支持复杂的通信序列(如带重复 START 的复合操作)。

参数

  • adap:指向 I2C 适配器的指针
  • msgs:指向struct i2c_msg数组的指针
  • num:消息数组的长度(即消息数量)

返回值

  • 成功:返回实际成功传输的消息数(等于num
  • 失败:返回负值错误码,或已成功传输的消息数(小于num

struct i2c_msg结构

struct i2c_msg {__u16 addr;     /* 从设备地址 */__u16 flags;    /* 标志位(如I2C_M_RD表示读操作) */__u16 len;      /* 消息长度 */__u8 *buf;      /* 数据缓冲区 */
};
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/fs.h>       
#include <linux/miscdevice.h> // 包含miscdevice相关定义
#include <linux/uaccess.h>/* 寄存器命令 */
#define AGS10_CMD_READ_TVOC    0x00    // 读取TVOC值
#define AGS10_CMD_CALIBRATE    0x01    // 校准命令
#define AGS10_CMD_READ_VERSION 0x11    // 读取固件版本
#define AGS10_CMD_RESISTANCE  0x20    // 读取阻值#define AGS10_IOC_MAGIC  'a'/* 定义命令 */
#define AGS10_CMD_GET_TVOC    _IOR(AGS10_IOC_MAGIC, 1, u16)  // 读取TVOC值
#define AGS10_CMD_CALIBRATE   _IO(AGS10_IOC_MAGIC, 2)         // 触发校准struct ags10_data {struct i2c_client *client;struct mutex lock;struct miscdevice miscdev;u8 firmware_version;bool initialized;
};static int ags10_i2c_write(struct i2c_client *client, u8 cmd, u8 *data, int len)
{int ret;u8 buf[len + 1];memcpy(buf + 1, data, len);/* 发送命令 */buf[0] = cmd;ret = i2c_master_send(client, buf, len + 1);if (ret != 1) {dev_err(&client->dev, "Failed to send command: %d\n", ret);return ret;}return 0;
}/* 发送命令并读取响应 */
static int ags10_send_command(struct i2c_client *client, u8 cmd, u8 *data, int len)
{int ret;u8 buf[16];u8 addr = client->addr;/* 发送命令 */struct i2c_msg msgs[] = {[0] = {.addr = addr,.flags = 0,.len = sizeof(u8),.buf = &addr,},[1] = {.addr = addr,.flags = 0,.len = sizeof(u8),.buf = &cmd,},};ret = i2c_transfer(client->adapter, msgs, 2);printk(KERN_INFO "ags10_send_command ret = %d, addr = %x, cmd = %x", ret, addr, cmd);/*buf[0] = cmd;ret = i2c_master_send(client, buf, 1);if (ret != 1) {dev_err(&client->dev, "Failed to send command: %d\n", ret);return ret;}*//* 等待传感器响应 */msleep(10);/* 读取响应 */if (data && len > 0) {ret = i2c_master_recv(client, data, len);if (ret != len) {dev_err(&client->dev, "Failed to read response: %d\n", ret);return ret;}}return 0;
}/* 计算CRC校验 */
static u8 ags10_calculate_crc(u8 *data, int len)
{u8 crc = 0xFF;int i, j;for (i = 0; i < len; i++) {crc ^= data[i];for (j = 0; j < 8; j++) {if (crc & 0x80) {crc = (crc << 1) ^ 0x31;} else {crc <<= 1;}}}return crc;
}/* 读取TVOC值 (单位: ppb) */
static int ags10_read_tvoc(struct ags10_data *data, u16 *tvoc)
{int ret;u8 buf[5];  // 5字节缓冲区: [STATUS][DATA_H][DATA_M][DATA_L][CRC]u32 raw_value;mutex_lock(&data->lock);/* 发送读取TVOC命令并接收4字节数据 */ret = ags10_send_command(data->client, AGS10_CMD_READ_TVOC, buf, 5);if (ret) {mutex_unlock(&data->lock);return ret;}int i;for(i = 0; i < 5; i++){printk(KERN_INFO "ags10_read_tvoc[%d] = 0x%x", i, buf[i]);}/* 验证CRC (校验前3个数据字节) */if (buf[4] != ags10_calculate_crc(&buf[0], 4)) {dev_err(&data->client->dev, "TVOC CRC check failed: 0x%02X vs 0x%02X\n",buf[4], ags10_calculate_crc(&buf[0], 4));mutex_unlock(&data->lock);return -EIO;}/* 计算TVOC值 (24位原始值转换为ppb) */raw_value = ((u32)buf[1] << 16) | ((u32)buf[2] << 8) | buf[3];printk(KERN_INFO "ags10_read_tvoc raw_value = 0x%x", raw_value);*tvoc = (raw_value);mutex_unlock(&data->lock);return 0;
}//零点恢复校准
static int ags10_reset_calibration(struct ags10_data *data)
{	int ret;u8 buf[5] = {0x00, 0x0C, 0xFF, 0xFF, 0x81};mutex_lock(&data->lock);ret = ags10_i2c_write(data->client, AGS10_CMD_CALIBRATE, buf, 5);if (ret) {dev_err(&data->client->dev, "ags10_reset_calibration failed\n");return ret;}mutex_unlock(&data->lock);msleep(30);  // 校准需要约30msreturn 0;}/*以当前阻值为零点校准*/
static int ags10_current_resistance_calibration(struct ags10_data *data)
{	int ret;u8 buf[5] = {0x00, 0x0C, 0x00, 0x00, 0xAC};mutex_lock(&data->lock);ret = ags10_i2c_write(data->client, AGS10_CMD_CALIBRATE, buf, 5);if (ret) {dev_err(&data->client->dev, "ags10_current_resistance_calibration failed\n");return ret;}mutex_unlock(&data->lock);msleep(30);  // 校准需要约30msreturn 0;}/*以raw为零点校准*/
static int ags10_calibration(struct ags10_data *data, uint16_t raw)
{	int ret;u8 buf[5] = {0x00, 0x0C, (raw >> 8) & 0xFF, (raw >> 0) & 0xFF, };buf[4] = ags10_calculate_crc(buf, 4);mutex_lock(&data->lock);ret = ags10_i2c_write(data->client, AGS10_CMD_CALIBRATE, buf, 5);if (ret) {dev_err(&data->client->dev, "ags10_calibration failed\n");return ret;}mutex_unlock(&data->lock);msleep(30);  // 校准需要约30msreturn 0;}/* 读取固件版本 */
static int ags10_read_version(struct ags10_data *data)
{int ret;u8 buf[5]; //0-2:[Reserved][Version][CRC]mutex_lock(&data->lock);/* 发送读取版本命令 */ret = ags10_send_command(data->client, AGS10_CMD_READ_VERSION, buf, 5);if (ret) {mutex_unlock(&data->lock);return ret;}int i;for(i = 0; i < 5; i++){printk(KERN_INFO "ags10_read_version[%d] = 0x%x", i, buf[i]);}/* 验证CRC */if (buf[4] != ags10_calculate_crc(&buf[0], 4)) {dev_err(&data->client->dev, "CRC check failed\n");mutex_unlock(&data->lock);return -EIO;}data->firmware_version = buf[3];dev_info(&data->client->dev, "Firmware version: %d\n", data->firmware_version);mutex_unlock(&data->lock);return 0;
}/* 文件操作: 读取 */
static ssize_t ags10_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct ags10_data *data = filp->private_data;u16 tvoc;int ret;size_t len;if (count < 4)return -EINVAL;/* 读取TVOC值 */ret = ags10_read_tvoc(data, &tvoc);if (ret)return ret;/* 复制到用户空间 */if (copy_to_user(buf, &tvoc, sizeof(u16)))return -EFAULT;return len;
}/* 文件操作: ioctl */
static long ags10_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct ags10_data *data = filp->private_data;int ret;u16 tvoc;switch (cmd) {case AGS10_CMD_READ_TVOC:/* 读取TVOC值 */ret = ags10_read_tvoc(data, &tvoc);if (ret)return ret;/* 复制到用户空间 */if (copy_to_user((u16*)arg, &tvoc, sizeof(u16)))return -EFAULT;return 0;case AGS10_CMD_CALIBRATE:/* 发送校准命令 */ret = ags10_send_command(data->client, AGS10_CMD_CALIBRATE, NULL, 0);if (ret)return ret;return 0;default:return -ENOTTY;}
}/* 文件操作表 */
static const struct file_operations ags10_fops = {.read = ags10_read,.unlocked_ioctl = ags10_ioctl,.llseek = no_llseek,
};/* 探测函数 */
static int ags10_probe(struct i2c_client *client, const struct i2c_device_id *id)
{struct ags10_data *data;int ret;/* 检查设备是否支持 */if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {dev_err(&client->dev, "I2C functionality not supported\n");return -ENODEV;}dev_err(&client->dev, "ags10 I2C functionality supported\n");/* 分配并初始化驱动数据结构 */data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);if (!data) {dev_err(&client->dev, "Failed to allocate memory\n");return -ENOMEM;}data->client = client;mutex_init(&data->lock);i2c_set_clientdata(client, data);/* 初始化misc设备 */data->miscdev.minor = MISC_DYNAMIC_MINOR;data->miscdev.name = "ags10";data->miscdev.fops = &ags10_fops;data->miscdev.parent = &client->dev;ret = misc_register(&data->miscdev);if (ret) {dev_err(&client->dev, "Failed to register misc device\n");return ret;}/* 读取固件版本 */ret = ags10_read_version(data);if (ret) {dev_err(&client->dev, "Failed to read firmware version\n");misc_deregister(&data->miscdev);return ret;}u16 tvoc;ags10_read_tvoc(data, &tvoc);printk(KERN_INFO "ags10_read_tvoc tvoc = %d", tvoc);data->initialized = true;dev_info(&client->dev, "AGS10 TVOC sensor initialized\n");return 0;
}/* 移除函数 */
static int ags10_remove(struct i2c_client *client)
{printk(KERN_INFO "AGS10 TVOC sensor remove");struct ags10_data *data = i2c_get_clientdata(client);if (data->initialized) {misc_deregister(&data->miscdev);data->initialized = false;}return 0;
}/* 设备ID表 */
static const struct i2c_device_id ags10_id_table[] = {{ "ags10", 0 },{},
};
MODULE_DEVICE_TABLE(i2c, ags10_id_table);/* OF匹配表 */
static const struct of_device_id ags10_of_match[] = {{ .compatible = "aosong,ags10" },{},
};
MODULE_DEVICE_TABLE(of, ags10_of_match);/* I2C驱动结构体 */
static struct i2c_driver ags10_driver = {.driver = {.name = "ags10",.owner = THIS_MODULE,.of_match_table = ags10_of_match,},.probe = ags10_probe,.remove = ags10_remove,.id_table = ags10_id_table,
};module_i2c_driver(ags10_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("cmy");
MODULE_DESCRIPTION("AGS10 TVOC Sensor Driver");
MODULE_VERSION("1.0");

五、调试与验证

将编译好的驱动文件拷贝到开发板进行测试:
在这里插入图片描述
也可以通过i2cdetect 、i2cget、i2cset等命令进行调试。

六、总结

本文详细介绍了 I2C 总线协议的工作原理,并展示了如何在 RK3568 平台上开发 AGS10 空气质量传感器的 Linux 驱动。通过深入理解 I2C 协议和 Linux 内核 I2C 子系统,我们实现了一个完整的驱动程序,包括设备初始化、数据读取和用户接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914802.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914802.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arm版本的ubuntu安装git或者vim等方法

看起来你在基于 ARM 的 Ubuntu 系统上安装 Vim 时遇到了问题&#xff0c;错误提示为“E: 无法定位软件包 vim”。这通常是因为系统的软件包列表未更新&#xff0c;或者该软件包在你检查的标准软件源中不可用。以下是通常可以解决这个问题的方法&#xff1a;1. 更新软件包列表“…

MFC扩展库BCGControlBar Pro v36.2新版亮点:可视化设计器升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v36.2已全新发布了&#xff0c;在这个版本中添加了一个新的扩展器控件、改进了网格和报表…

小杰学C(eleven day)——莫道浮云终蔽日,总有云开雾散时。

1.结构体&#xff08;1&#xff09;内容定义&#xff1a;1.用户自定义的数据类型2.可以包含若干不同数据类型&#xff08;可相同&#xff09;的成员变量3.这些数据项组合起来反应某一信息格式&#xff1a;struct 结构体名 (用户自定义的数据类型){数据类型 成员变量1;数据类型 …

海豚远程控制APP:随时随地,轻松掌控手机

在快节奏的现代生活中&#xff0c;我们常常需要在不同设备之间切换&#xff0c;管理手机也变得越来越重要。无论是远程办公、远程学习还是日常生活中对手机的管理&#xff0c;一款高效、便捷的远程控制软件都能极大地提升我们的效率。海豚远程控制APP正是这样一款功能强大的手机…

Linux/Ubuntu安装go

Linux/Ubuntu安装go1. 首先移除旧版本&#xff08;如有&#xff09;&#xff1a;2. 下载Go 1.23.9安装包&#xff1a;3. 解压到系统目录&#xff1a;4. 设置环境变量&#xff08;添加到~/.profile或~/.bashrc文件末尾&#xff09;&#xff1a;5.使环境变量生效&#xff1a;6. 验…

教程:如何快速查询 A 股实时 K线和5档盘口

实时行情数据是量化交易策略、看板系统和交易决策系统的重要输入。本文将以 Infoway API 提供的 WebSocket API 为例&#xff0c;教你如何使用 Python 快速接入并获取 A 股的实时 K线数据 和 盘口数据。一、准备工作安装 WebSocket 库&#xff1a;pip install websockets二、查…

施易德门店管理系统应用案例分析:零售女装品牌伊芙丽的全球化布局

在零售品牌的全球化进程中&#xff0c;如何应对不同市场的合规要求、实现本地化精细化运营&#xff0c;是企业面临的重要课题。施易德&#xff08;Cegid&#xff09;门店管理系统凭借40年的全球零售数字化服务经验&#xff0c;为多个品牌的海外拓展提供了支持。其中&#xff0c…

安全初级作业2

一、作业要求 1、xss-labs 1~8关 2、python实现自动化sql布尔育注代码优化(二分查找) 二、操作过程 &#xff08;一&#xff09;xss-labs 1~8关 1、前期准备 &#xff08;1&#xff09;打开小皮面板&#xff0c;并启动Apache和MySQL &#xff08;2&#xff09;将 xss-labs…

多模态大语言模型arxiv论文略读(157)

Automatic Evaluation for Text-to-image Generation: Task-decomposed Framework, Distilled Training, and Meta-evaluation Benchmark ➡️ 论文标题&#xff1a;Automatic Evaluation for Text-to-image Generation: Task-decomposed Framework, Distilled Training, and M…

面试150——数组字符串

88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。倒序比较&#xff0c;避免覆…

深入理解设计模式:命令模式详解

在软件开发中&#xff0c;我们经常遇到需要将"请求"或"操作"封装成对象的情况。比如&#xff0c;GUI中的按钮点击、遥控器控制家电、事务系统中的操作回滚等场景。命令模式&#xff08;Command Pattern&#xff09;正是为解决这类问题而生的设计模式。本文…

自己写的 MyHttpServlet 和直接继承 HttpServlet 的区别

继承你自己写的 MyHttpServlet 和直接继承 HttpServlet 的区别如下&#xff1a;1. 继承 HttpServlet&#xff08;官方推荐用法&#xff09;HttpServlet 是 Java EE 官方提供的 Servlet 基类&#xff0c;已经实现了 Servlet 接口的大部分方法。它内部已经实现了 service() 方法&…

python库 maya 库的各种案例的使用详解(人性化的日期时间处理)

文章目录 一、Maya库概述 1.1 maya介绍 1.2 安装 maya 1.3 注意事项 二、基本使用 2.1 创建 MayaDT 对象 2.2 格式化输出 2.3 时间运算 三、高级使用 3.1 时区处理 3.2 时间间隔 3.3 网络时间获取 四、实际应用示例 4.1 日志时间处理 4.2 会议时间提醒 4.3 国际化时间显示 5. M…

企业选择大带宽服务器租用的原因有哪些?

大带宽服务器作为各个行业使用较多的服务器类型&#xff0c;可以为企业提供更高的数据传输速率&#xff0c;极大缩短文件上传与下载时间&#xff0c;对于大型文件&#xff0c;大带宽服务器能够将时间大幅缩减至数分钟或数小时&#xff0c;提高企业整体的工作效率。大带宽服务器…

使用canal同步分库分表数据,到 Elasticsearch

作者&#xff1a;小凯 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01; 本文的宗旨在于通过简单干净实践的方式教会读者&#xff0c;配置出一套 Canal 工具服务&#xff0c;来同步分库分表的数据到 Elasticsearch 文件夹系统中。同时在 SpringBoot 工程中&a…

气候为何愈演愈“炙” — 未来五年高温趋势与 AI 气象大模型的突破性价值

早、更准 代表性模型 主要特征 应用进展 GraphCast(DeepMind) 10 天全球预报;0.25 分辨率;< 1 min 推理 90 % 指标超 ECMWF HRES,已用于极端风暴提前锁定Google DeepMind MetNet-3(Google Research) 1–4 km 分辨率;2 min 时序;24 h 区域精细预报 美东、欧洲已在 G…

LVS四种模式及部署NAT、DR模式集群

1、lvs简介LVS:Linux Virtual Server&#xff0c;负载调度器&#xff0c;内核集成&#xff0c;章文嵩&#xff0c;阿里四层SLB(ServerLoadBalance)是基于LVSkeepalived实现LVS 官网: http://www.linuxvirtualserver.org/LVS 相关术语VS: Virtual Server&#xff0c;负责调度RS:…

【Linux】Ubuntu22.04安装zabbix

官方文档&#xff1a;zabbix安装文档 环境如下 环境版本nginx1.26.3zabbix7.0.16mysql8.0.41 安装nginx和mysql 一键部署脚本 部署zabbix #!/bin/bash wget https://repo.zabbix.com/zabbix/7.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.0ubuntu22.04_…

C++ - 仿 RabbitMQ 实现消息队列--sqlite与gtest快速上手

目录 SQLite 什么是 SQLite 为什么要用 SQLite SQLite3 C/C API 介绍 SQLite3 C/C API 使用 GTest GTest 是什么 GTest 使用 TEST 宏 断言 事件机制 全局事件 TestSuite 事件 SQLite 什么是 SQLite SQLite 是一个进程内的轻量级数据库&#xff0c;它实现了自给自足…

Web3.0 学习方案

Web3.0 学习方案 一、学习方案 &#xff08;一&#xff09;入门阶段 1. 了解 Web3.0 基础概念 学习内容&#xff1a; Web3.0 的起源、愿景、与 Web2.0 的区别区块链的基本概念&#xff1a;分布式账本、哈希、公钥/私钥、共识机制&#xff08;PoW、PoS、DPoS、PBFT 等&#xff0…