✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
目录
[1.1 硬件清单](#1.1 硬件清单)
[1.2 接线方案](#1.2 接线方案)
[1.3 具体接线图](#1.3 具体接线图)
[1.4 连接实物图](#1.4 连接实物图)
[2.1 数据结构与定义](#2.1 数据结构与定义)
[2.2 核心数据采集](#2.2 核心数据采集)
[2.3 实时数据显示](#2.3 实时数据显示)
[2.4 结果排序和显示](#2.4 结果排序和显示)
[2.5 完整代码](#2.5 完整代码)
[3.1 操作流程](#3.1 操作流程)
[3.2 数据记录](#3.2 数据记录)
[3.3 视频演示](#3.3 视频演示)
[4.1 内部架构与功能](#4.1 内部架构与功能)
[4.2 I2C通信协议](#4.2 I2C通信协议)
[4.3 寄存器映射](#4.3 寄存器映射)
[Q1: ADS1115读取数据不稳定怎么办?](#Q1: ADS1115读取数据不稳定怎么办?)
[Q2: 显示屏出现闪烁如何解决?](#Q2: 显示屏出现闪烁如何解决?)
[Q3: 如何提高ADC采集精度?](#Q3: 如何提高ADC采集精度?)
(1)项目概述
本项目基于STM32F407VET6微控制器和ADS1115 16位高精度模数转换器,设计了一个多功能数据采集与显示系统。系统通过STM32F407VET6控制ADS1115采集四路模拟信号,并将采集到的数据实时显示在240×240的ST7789彩色LCD显示屏上。项目展示了高精度ADC数据采集、实时数据显示、多通道信号处理等关键技术。
(2)项目难点及解决方案
问题描述:多任务实时处理
解决方案:优化程序结构,采用状态机模式管理不同显示界面
一、硬件连接部分
1.1 硬件清单
组件 | 型号 | 数量 | 备注 |
---|---|---|---|
主控板 | STM32F407VET6 | 1 | 零知增强板 |
ADC模块 | ADS1115 | 1 | 16位精度 |
显示屏 | ST7789 | 1 | 240×240分辨率 |
电位器 | 10kΩ | 1 | 模拟信号输入 |
连接线 | 杜邦线 | 若干 | 信号连接 |
1.2 接线方案
STM32F407引脚 | ADS1115引脚 | ST7789引脚 | 功能描述 |
---|---|---|---|
3.3V | VDD | VCC | 电源正极 |
GND | GND | GND | 电源地 |
52 | SCL | SCL | I2C时钟 |
51 | SDA | SDA | I2C数据 |
/ | A0 | / | 模拟输入0 |
/ | A1 | / | 模拟输入1 |
/ | A2 | / | 模拟输入2 |
/ | A3 | / | 模拟输入3 |
4 | / | DC | 数据/命令 |
2 | / | RST | 复位 |
53 | / | CS | 片选 |
注意:本项目ADS1117模数转换器的引脚连接如下,A0和A2连接到3.3V电源、A1连接电位器调节ADC值、A3接到零知增强板任一模拟引脚观察悬空引脚模拟值变化
1.3 具体接线图

1.4 连接实物图

二、代码讲解部分
2.1 数据结构与定义
cpp
// 定义ST7789显示屏引脚
#define TFT_CS 53
#define TFT_RST 2
#define TFT_DC 4
// 创建ADS1115和ST7789对象
Adafruit_ADS1115 ads;
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// 游戏相关变量
struct Car {
int16_t x; // 赛车x坐标
int16_t y; // 赛车y坐标
uint16_t color; // 赛车颜色
int16_t lastADC; // 上一次ADC值
int16_t speed; // 当前速度
bool finished; // 是否完成比赛
uint32_t finishTime; // 完成时间
};
Car cars[4]; // 四辆赛车
bool gameStarted = false;
bool gameFinished = false;
uint32_t startTime = 0;
const int16_t FINISH_LINE = 200; // 终点线位置
// A1和A3通道数据显示变量
int16_t lastA1ADC = -1;
float lastA1Voltage = -1;
int16_t lastA3ADC = -1;
float lastA3Voltage = -1;
// 颜色定义
#define CAR1_COLOR ST77XX_RED
#define CAR2_COLOR ST77XX_GREEN
#define CAR3_COLOR ST77XX_BLUE
#define CAR4_COLOR ST77XX_YELLOW
#define TRACK_COLOR 0x7453
#define BACKGROUND_COLOR ST77XX_BLACK
#define DATA_BG_COLOR 0x1082
#define DATA_TEXT_COLOR ST77XX_WHITE
ST7789显示屏引脚定义到零知增强板的硬件SPI接口、Car结构体封装了每个通道的显示属性和状态、每个通道对应特定颜色
2.2 核心数据采集
cpp
void updateGame() {
bool allFinished = true;
float ret;
// 读取A1和A3通道数据并更新显示
updateChannelDataDisplay();
for (int i = 0; i < 4; i++) {
if (!cars[i].finished) {
// 读取ADC值并转换为速度
int16_t adc = ads.readADC_SingleEnded(i);
cars[i].speed = map(adc, 0, 26000, 1, 10);
// 更新赛车位置
cars[i].x += cars[i].speed;
// 检查是否到达终点
if (cars[i].x >= FINISH_LINE) {
cars[i].finished = true;
cars[i].finishTime = millis() - startTime;
cars[i].x = FINISH_LINE;
} else {
allFinished = false;
}
updateCarDisplay(i);
// 串口调试信息(仅显示A1通道)
if (i == 1) {
Serial.print("ADC");
Serial.print(i);
Serial.print(": ");
Serial.print(adc);
Serial.print(" | Voltage:");
ret = adc * 0.0001875;
Serial.print(ret);
Serial.print(" | Speed: ");
Serial.print(cars[i].speed);
Serial.print(" | Position: ");
Serial.println(cars[i].x);
}
}
}
if (allFinished && !gameFinished) {
gameFinished = true;
showResults();
}
}
电压计算公式采用:adc * 0.0001875基于6.144V量程的分辨率
2.3 实时数据显示
cpp
void updateChannelDataDisplay() {
// 读取A1和A3通道数据
int16_t a1ADC = ads.readADC_SingleEnded(1);
float a1Voltage = a1ADC * 0.0001875;
int16_t a3ADC = ads.readADC_SingleEnded(3);
float a3Voltage = a3ADC * 0.0001875;
// 只在新数据与旧数据不同时更新显示,避免闪烁
if (a1ADC != lastA1ADC) {
displayChannelData(1, a1ADC, a1Voltage, 210);
lastA1ADC = a1ADC;
lastA1Voltage = a1Voltage;
}
if (a3ADC != lastA3ADC) {
displayChannelData(3, a3ADC, a3Voltage, 225);
lastA3ADC = a3ADC;
lastA3Voltage = a3Voltage;
}
}
仅当ADC值变化时更新显示,避免不必要的屏幕刷新
2.4 结果排序和显示
cpp
void showResults() {
// ... 显示标题等代码 ...
// 确定名次
int rankings[4] = {0, 1, 2, 3};
// 冒泡排序按完成时间排序
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3 - i; j++) {
if (cars[rankings[j]].finishTime > cars[rankings[j + 1]].finishTime) {
int temp = rankings[j];
rankings[j] = rankings[j + 1];
rankings[j + 1] = temp;
}
}
}
// 显示排序结果
int yPos = 60;
for (int i = 0; i < 4; i++) {
int carIndex = rankings[i];
tft.setCursor(30, yPos);
tft.print("Channel ");
tft.print(carIndex);
tft.print(": ");
tft.fillRect(90, yPos, 10, 10, cars[carIndex].color);
tft.setCursor(105, yPos);
tft.print("Time: ");
tft.print(cars[carIndex].finishTime / 1000.0, 2);
tft.print("s");
yPos += 30;
}
}
相邻元素比较交换,最大元素"冒泡"到末尾。时间复杂度为O(n²),适合小数据量排序
2.5 完整代码
cpp
// 文件名:STM32F407_ADS1115_DataDisplay.ino
#include <Wire.h>
#include <Adafruit_ADS1X15.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
// 定义ST7789显示屏引脚
#define TFT_CS 53
#define TFT_RST 2
#define TFT_DC 4
// 创建ADS1115和ST7789对象
Adafruit_ADS1115 ads;
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// 游戏相关变量
struct Car {
int16_t x; // 赛车x坐标
int16_t y; // 赛车y坐标
uint16_t color; // 赛车颜色
int16_t lastADC; // 上一次ADC值
int16_t speed; // 当前速度
bool finished; // 是否完成比赛
uint32_t finishTime; // 完成时间
};
Car cars[4]; // 四辆赛车
bool gameStarted = false;
bool gameFinished = false;
uint32_t startTime = 0;
const int16_t FINISH_LINE = 200; // 终点线位置
// A1和A3通道数据显示变量
int16_t lastA1ADC = -1;
float lastA1Voltage = -1;
int16_t lastA3ADC = -1;
float lastA3Voltage = -1;
// 颜色定义
#define CAR1_COLOR ST77XX_RED
#define CAR2_COLOR ST77XX_GREEN
#define CAR3_COLOR ST77XX_BLUE
#define CAR4_COLOR ST77XX_YELLOW
#define TRACK_COLOR 0x7453
#define BACKGROUND_COLOR ST77XX_BLACK
#define DATA_BG_COLOR 0x1082
#define DATA_TEXT_COLOR ST77XX_WHITE
void setup(void) {
Serial.begin(115200);
Serial.println("STM32F407 ADS1115数据采集系统启动!");
// 初始化ADS1115
Wire.begin();
if (!ads.begin()) {
Serial.println("ADS1115初始化失败!");
while (1);
}
ads.setGain(GAIN_TWOTHIRDS);
// 初始化ST7789显示屏
tft.init(240, 240);
tft.setRotation(1); // 重要:设置屏幕方向
tft.fillScreen(BACKGROUND_COLOR);
// 初始化赛车
initializeCars();
// 显示开始界面
showStartScreen();
}
void loop(void) {
if (!gameStarted) {
// 等待开始信号(任意通道电压超过1V)
if (checkStartCondition()) {
startGame();
}
return;
}
if (gameFinished) {
// 游戏结束,显示结果
if (checkRestartCondition()) {
resetGame();
}
return;
}
// 主游戏循环
updateGame();
delay(50); // 控制游戏速度
}
void initializeCars() {
// 初始化四辆赛车的属性
int16_t startY[] = {40, 80, 120, 160};
uint16_t colors[] = {CAR1_COLOR, CAR2_COLOR, CAR3_COLOR, CAR4_COLOR};
for (int i = 0; i < 4; i++) {
cars[i].x = 20;
cars[i].y = startY[i];
cars[i].color = colors[i];
cars[i].lastADC = 0;
cars[i].speed = 0;
cars[i].finished = false;
cars[i].finishTime = 0;
}
}
bool checkStartCondition() {
// 检查任意通道电压是否超过1V作为开始信号
for (int i = 0; i < 4; i++) {
int16_t adc = ads.readADC_SingleEnded(i);
float voltage = adc * 0.0001875;
if (voltage > 1.0) {
return true;
}
}
return false;
}
bool checkRestartCondition() {
// 检查所有通道电压是否都低于0.5V作为重新开始信号
for (int i = 0; i < 4; i++) {
int16_t adc = ads.readADC_SingleEnded(i);
float voltage = adc * 0.0001875;
if (voltage > 0.5) {
return false;
}
}
return true;
}
void startGame() {
gameStarted = true;
startTime = millis();
drawGameScreen();
}
void resetGame() {
gameStarted = false;
gameFinished = false;
initializeCars();
tft.fillScreen(BACKGROUND_COLOR);
showStartScreen();
}
void updateGame() {
bool allFinished = true;
float ret;
// 读取A1和A3通道数据并更新显示
updateChannelDataDisplay();
for (int i = 0; i < 4; i++) {
if (!cars[i].finished) {
// 读取ADC值并转换为速度
int16_t adc = ads.readADC_SingleEnded(i);
cars[i].speed = map(adc, 0, 26000, 1, 10); // 映射ADC值到速度
// 更新赛车位置
cars[i].x += cars[i].speed;
// 检查是否到达终点
if (cars[i].x >= FINISH_LINE) {
cars[i].finished = true;
cars[i].finishTime = millis() - startTime;
cars[i].x = FINISH_LINE; // 确保不会超出终点
} else {
allFinished = false;
}
// 更新显示
updateCarDisplay(i);
// 在串口显示调试信息
if (i == 1) { // 显示A1通道的信息
Serial.print("ADC");
Serial.print(i);
Serial.print(": ");
Serial.print(adc);
Serial.print(" | Voltage:");
ret = adc * 0.0001875;
Serial.print(ret);
Serial.print(" | Speed: ");
Serial.print(cars[i].speed);
Serial.print(" | Position: ");
Serial.println(cars[i].x);
}
}
}
// 检查游戏是否结束
if (allFinished && !gameFinished) {
gameFinished = true;
showResults();
}
}
void updateChannelDataDisplay() {
// 读取A1和A3通道数据
int16_t a1ADC = ads.readADC_SingleEnded(1);
float a1Voltage = a1ADC * 0.0001875;
int16_t a3ADC = ads.readADC_SingleEnded(3);
float a3Voltage = a3ADC * 0.0001875;
// 只在新数据与旧数据不同时更新显示,避免闪烁
if (a1ADC != lastA1ADC) {
displayChannelData(1, a1ADC, a1Voltage, 210);
lastA1ADC = a1ADC;
lastA1Voltage = a1Voltage;
}
if (a3ADC != lastA3ADC) {
displayChannelData(3, a3ADC, a3Voltage, 225);
lastA3ADC = a3ADC;
lastA3Voltage = a3Voltage;
}
}
void displayChannelData(int channel, int16_t adc, float voltage, int yPos) {
// 设置文本颜色和大小
tft.setTextColor(DATA_TEXT_COLOR);
tft.setTextSize(1);
// 清除旧数据区域(局部刷新)
tft.fillRect(55, yPos, 180, 10, DATA_BG_COLOR);
// 显示通道数据
tft.setCursor(5, yPos);
tft.print("A");
tft.print(channel);
tft.print(": ADC= ");
tft.print(adc);
tft.print(" | V=");
tft.print(voltage, 3);
tft.print("V");
}
void updateCarDisplay(int carIndex) {
// 清除旧的赛车位置(绘制背景色矩形)
tft.fillRect(cars[carIndex].x - cars[carIndex].speed - 2, cars[carIndex].y - 8,
cars[carIndex].speed + 4, 16, TRACK_COLOR);
// 绘制新位置的赛车
drawCar(carIndex);
}
void drawCar(int carIndex) {
// 绘制赛车(简单的矩形表示)
tft.fillRoundRect(cars[carIndex].x, cars[carIndex].y - 6, 12, 12, 3, cars[carIndex].color);
tft.fillRoundRect(cars[carIndex].x + 10, cars[carIndex].y - 4, 6, 8, 2, ST77XX_WHITE);
}
void showStartScreen() {
tft.setTextSize(3);
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(20, 50);
tft.println("Data Display");
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
tft.setCursor(20, 80);
tft.println("Connect sensors to ADS1115:");
tft.setCursor(40, 100);
tft.println("Channel 0 - Red indicator");
tft.setCursor(40, 115);
tft.println("Channel 1 - Potentiometer");
tft.setCursor(40, 130);
tft.println("Channel 2 - Blue indicator");
tft.setCursor(40, 145);
tft.println("Channel 3 - Analog input");
tft.setCursor(30, 170);
tft.println("Adjust any channel voltage");
tft.setCursor(50, 185);
tft.println("to start data display!");
delay(500);
}
void drawGameScreen() {
tft.fillScreen(BACKGROUND_COLOR);
// 绘制赛道背景
tft.fillRect(0, 30, 240, 160, TRACK_COLOR);
// 绘制起点线
for (int y = 30; y < 190; y += 10) {
tft.drawFastVLine(20, y, 5, ST77XX_WHITE);
}
// 绘制终点线
for (int y = 30; y < 190; y += 10) {
tft.drawFastVLine(FINISH_LINE, y, 5, ST77XX_WHITE);
}
// 绘制数据背景区域
tft.fillRect(0, 200, 240, 40, DATA_BG_COLOR);
// 绘制数据标题
tft.setTextColor(DATA_TEXT_COLOR);
tft.setTextSize(1);
// 初始化A1和A3数据显示
tft.setCursor(5, 210);
tft.print("A1: ADC= --- =---V");
tft.setCursor(5, 225);
tft.print("A3: ADC= --- =---V");
// 绘制赛车初始位置
for (int i = 0; i < 4; i++) {
drawCar(i);
}
// 显示游戏状态
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
tft.setCursor(5, 5);
tft.print("Data Acquisition Active...");
}
void showResults() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(ST77XX_WHITE);
tft.fillRect(0, 0, 240, 40, ST77XX_RED);
tft.setTextSize(2);
tft.setCursor(40, 20);
tft.println("Test Complete");
tft.setTextSize(1);
// 确定名次
int rankings[4] = {0, 1, 2, 3};
// 简单的冒泡排序按完成时间排序
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3 - i; j++) {
if (cars[rankings[j]].finishTime > cars[rankings[j + 1]].finishTime) {
int temp = rankings[j];
rankings[j] = rankings[j + 1];
rankings[j + 1] = temp;
}
}
}
// 显示测试结果
int yPos = 60;
for (int i = 0; i < 4; i++) {
int carIndex = rankings[i];
tft.setCursor(30, yPos);
tft.print("Channel ");
tft.print(carIndex);
tft.print(": ");
// 显示通道颜色方块
tft.fillRect(90, yPos, 10, 10, cars[carIndex].color);
tft.setCursor(105, yPos);
tft.print("Time: ");
tft.print(cars[carIndex].finishTime / 1000.0, 2);
tft.print("s");
yPos += 30;
}
// 显示最终数据
tft.setCursor(30, 180);
tft.print("Final A1 Voltage: ");
tft.print(lastA1Voltage, 3);
tft.print("V");
tft.setCursor(30, 195);
tft.print("Final A3 Voltage: ");
tft.print(lastA3Voltage, 3);
tft.print("V");
tft.setTextSize(1);
tft.setCursor(40, 215);
tft.println("Reset all channels to restart");
}
// 辅助函数:绘制带边框的矩形
void drawBorderedRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color, uint16_t borderColor) {
tft.fillRect(x, y, w, h, borderColor);
tft.fillRect(x + 1, y + 1, w - 2, h - 2, color);
}
冒泡排序算法原理:
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成

算法步骤分解:
**①外层循环(i循环):**控制排序的轮数
对于4个元素,需要3轮比较(n-1轮)、i < 3 因为4个元素需要3轮比较
**②内层循环(j循环):**进行相邻元素的比较和交换
j < 3 - i 每完成一轮,最大的元素就会"冒泡"到末尾,因此后续比较次数减少
③比较和交换:
cars[rankings[j]].finishTime > cars[rankings[j + 1]].finishTime
如果前一个元素的完成时间大于后一个元素,则交换它们在排名数组中的位置
三、项目结果演示
3.1 操作流程
(1)系统上电,将A0和A2连接到GND(后接3.3V)、A1连接5V电位器、A3连接零知增强板A0引脚
(2)旋转电位器(大于1V时)系统启动,观察模拟值和电压值变化
将ADC值map映射到物体的移动速度,通过小车移动速度的快慢直观的感受到A1通道模拟值变化

底部显示旋转电位器的A1通道和零知增强板A0模拟引脚的A3通道具体ADC值和GAIN_TWOTHIRDS分辨率的电压值
(3)同时打开串口监视器观察通道1的ADC值和波形

3.2 数据记录
时间点 | A0电压 | A1电压 | A2电压 | A3电压 | 备注 |
---|---|---|---|---|---|
初始 | 0V | 0V | 0V | 0V | 系统启动 |
测试1 | 3.3V | 2.5V | 3.3V | 0.4V | 正常采集 |
测试2 | 3.3V | 4.8V | 3.3V | 0.4V | 电位器调节 |

对模拟值的大小进行冒泡排序,观察ADC值的变化过程
3.3 视频演示
ADS1115模数转换器实现多通道数据采集
系统启动和数据采集显示、电位器调节效果以及不同ADC值大小变化过程
四、ADS1115工作原理详解
ADS1115 具有 一个输入多路复用器 (MUX),可实现双路差分输入或 四路单端输入测量。兼容 I 2C 的 16 位低功耗精密模数转换器 (ADC)

4.1 内部架构与功能
(1)Δ-Σ调制器
以远高于奈奎斯特频率的速率采样、将量化噪声推向高频区域、将模拟信号转换为位流

(2)Multiplexer 复用器
ADS1115包含一个输入多路器,四个单电压输入或者两个差分输入。AIN0和AIN1可以与AIN3进行差分测量,多路复用器由配置寄存器中的位MUX [2:0]配置。

测量单端信号时,ADC的负输入通过多路器内的开关内部连接到GND
(3)噪声表现
采样速率不宜过高,由表格看出当采样速率大于128SPS时,分辨率在不同量程下均有所下降

(4)满量程范围
由配置寄存器中的 PGA [2:0] 位配置,可设置为 ±6.144 V、±4.096 V、±2.048 V、±1.024 V、±0.512 V、±0.256 V

4.2 I2C通信协议
(1)I2C 地址选择
ADS1115有一个地址引脚 ADDR,用于配置器件的 I2C 地址。该引脚可连接至 GND、VDD、SDA 或 SCL,通过一个引脚即可选择四种不同的地址

(2)向寄存器写入数据
要访问 ADS111x 中的特定寄存器,主机必须首先向地址指针寄存器中的寄存器地址指针位 P [1:0] 写入适当的值。

地址指针寄存器是在从机地址字节、低电平的读 / 写位以及从机成功应答之后直接写入,从机进行应答,主机发出停止条件或重复起始条件
(3)从寄存器读取数据
从 ADS111x 读取数据时,先前写入位 P [1:0] 的值决定了要读取的寄存器。要更改读取的寄存器,必须向 P [1:0] 写入新值

(4)数据格式
以二进制补码格式提供 16 位数据。正满量程(+FS)输入产生的输出代码为 7FFFh,负满量程(--FS)输入产生的输出代码为 8000h

对于超过满量程的信号,输出会钳位在这些代码上
电压计算原理
ADS1115的输出代码与输入电压的关系为:电压 = (ADC读数 × 满量程电压) / (2¹⁵ - 1)
在GAIN_TWOTHIRDS模式下:满量程电压 = 6.144V、分辨率 = 6.144V / 32767 ≈ 0.1875mV
4.3 寄存器映射
ADS1115有四个寄存器,可通过I2C接口和地址指针寄存器访问。转换寄存器存上次转换结果,配置寄存器用于更改工作模式和查询设备状态,另两个寄存器(Lo_thresh和Hi_thresh)则设定比较器功能的阈值。
(1)地址指针寄存器

地址指针寄存器的2-7位保留全部置0,最低两位写入地址指针访问对应寄存器
(2)转换寄存器(P [1:0] = 0h)
16 位转换寄存器以二进制补码格式存储最后一次转换的结果。上电后,转换寄存器被清零,并且在第一次转换完成前一直保持为 0

(3)配置寄存器(P [1:0] = 1h)
16位配置寄存器用于控制工作模式、输入选择、数据速率、满量程范围和比较器模式

eg:设置DR[2:0]位为100,控制数据速率默认的128SPS

五、常见问题解答
Q1: ADS1115读取数据不稳定怎么办?
A: 检查电源稳定性,添加滤波电容,确保I2C上拉电阻正确连接。
Q2: 显示屏出现闪烁如何解决?
A: 使用局部刷新技术,避免全屏刷新,优化刷新频率。
Q3: 如何提高ADC采集精度?
A: 使用外部基准电压,降低采样速率,添加硬件滤波。
项目资源整合:
ADS1115库文件:Adafruit_ADS1X15
ADS1115数据手册:ADS111x datasheet