- // BMP581 SPI通信参数
- uint8_t bmp581_cs = 10; // BMP581 片选引脚
- uint32_t clockFrequency = 100000; // 设置SPI时钟频率
- // ST7789 显示屏引脚定义
- #define TFT_CS 6 // 设置软件SPI的片选引脚
- #define TFT_RST 4 // 显示屏复位引脚
- #define TFT_DC 2 // 显示屏数据/控制命令引脚
- #define TFT_MOSI 8 // 软件SPI的MOSI引脚
- #define TFT_SCLK 7 // 软件SPI的SCK引脚
- // 传感器和显示屏的对象创建与初始化
- BMP581 pressureSensor;
- Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
- // 定义显示屏参数
- #define SCREEN_WIDTH 240
- #define SCREEN_HEIGHT 320
- #define ST77xx_PURPLE 0x862F
- #define VALUE_SIZE 3
- #define LABEL_SIZE 1
- // 颜色定义
- #define BACKGROUND ST77XX_BLACK
- #define TEXT_COLOR ST77XX_WHITE
- #define TEMP_COLOR ST77xx_PURPLE
- #define PRESS_COLOR ST77XX_CYAN
- #define ALT_COLOR ST77XX_GREEN
- #define BOX_COLOR ST77XX_ORANGE
2.2 初始化配置
配置串口通信波特率为115200,ST7789显示屏大小、方向和交互内容显示,开启BMP581的SPI通信连接,绘制显示屏标题和数据内容标签
- void setup() {
- // 开启串口监视器并设置波特率为115200
- Serial.begin(115200);
- Serial.println("BMP581 with ST7789 Display Example");
- // 初始化SPI
- SPI.begin();
- // 初始化显示屏
- tft.init(SCREEN_WIDTH, SCREEN_HEIGHT);
- tft.setRotation(3);
- tft.fillScreen(BACKGROUND);
- tft.setTextColor(TEXT_COLOR);
- // 初始化BMP581传感器
- while (pressureSensor.beginSPI(bmp581_cs, clockFrequency) != BMP5_OK) {
- Serial.println("Error: BMP581 not connected, check wiring and CS pin!");
- tft.setCursor(10, 10);
- tft.setTextSize(2);
- tft.print("Sensor not found!");
- delay(1000);
- tft.fillScreen(BACKGROUND);
- }
- Serial.println("BMP581 connected!");
- drawStaticElements();
- }
2.3 读取传感器数据
loop函数循环获取实时的大气压强和温度数据,并通过经验公式转换为海拔高度数据,将获得的数据实时更新到TFT显示屏界面上
- void loop() {
- // 从寄存器获取到数值
- bmp5_sensor_data data = {0, 0};
- int8_t err = pressureSensor.getSensorData(&data);
- if (err == BMP5_OK) {
- // 将气压数据转换以百帕为单位 (1 hPa = 100 Pa)
- float pressure_hPa = data.pressure / 100.0;
- // 使用经验公式计算海拔高度数据
- float altitude = (1013.25 - pressure_hPa) / 12 * 100;
- // 更新屏幕
- updateTextDisplay(data.temperature, pressure_hPa, altitude);
- // 打印串口监视数据
- Serial.print("Temperature (C): ");
- Serial.print(data.temperature);
- Serial.print("\tPressure (hPa): ");
- Serial.print(pressure_hPa);
- Serial.print("\tAltitude (m): ");
- Serial.println(altitude);
- } else {
- Serial.print("Error getting data from sensor! Error code: ");
- Serial.println(err);
- }
- delay(1000); // 每秒更新一次数据
- }
2.4 UI界面更新
- void drawStaticElements() {
- tft.fillScreen(BACKGROUND);
-
- // 绘制标题
- tft.setTextSize(1);
- tft.setTextColor(ST77XX_YELLOW);
- tft.setCursor(SCREEN_WIDTH/2 + 120, 10);
- tft.print("BMP581 SENSOR");
-
- // 绘制温度数据容器
- drawDataBox(30, 10, "TEMPERATURE", "(C)", TEMP_COLOR);
-
- // 绘制气压数据容器
- drawDataBox(30, 90, "PRESSURE", "(hPa)", PRESS_COLOR);
-
- // 绘制海拔数据容器
- drawDataBox(30, 170, "ALTITUDE", "(m)", ALT_COLOR);
- }
- void drawDataBox(int x, int y, const char* label, const char* unit, uint16_t color) {
- // 绘制数据容器
- tft.drawRoundRect(x, y, SCREEN_WIDTH - 60, 60, 10, BOX_COLOR);
- // 绘制数据标题
- tft.setTextSize(LABEL_SIZE);
- tft.setTextColor(color);
- tft.setCursor(x + 15, y + 10);
- tft.print(label);
- // 绘制数据单位
- tft.setTextSize(LABEL_SIZE - 1);
- tft.setCursor(x + SCREEN_WIDTH - 60 - 40, y + 10);
- tft.print(unit);
- }
- void updateTextDisplay(float temp, float pressure, float altitude) {
- updateDataValue(30, 10, temp, 1, TEMP_COLOR); // 更新温度数据
- updateDataValue(30, 90, pressure, 1, PRESS_COLOR); // 更新气压数据
- updateDataValue(30, 170, altitude, 1, ALT_COLOR); // 更新海拔数据
- }
- void updateDataValue(int x, int y, float value, int decimals, uint16_t color) {
- // 清除旧数据
- tft.fillRect(x + 10, y + 30, SCREEN_WIDTH - 80, 25, BACKGROUND);
- // 写入新数据
- tft.setTextSize(VALUE_SIZE);
- tft.setTextColor(color);
- tft.setCursor(x + 15, y + 30);
- tft.print(value, decimals);
- }
2.5 项目完整代码获取
通过网盘分享的文链接:
https://pan.baidu.com/s/125lFvyjRd98dkqMkYh0TSA?pwd=d4m4
三、实际效果展示
3.1 显示屏信息解读
成功运行后,显示屏将分为三个区域显示:
- 温度区:灰色标签,显示摄氏度
- 气压区:红色标签,显示百帕
- 海拔区:紫色标签,显示米
3.2 视频演示效果
将通过传感器获取到的气压值与app海拔仪气压值进行对比
3.3 串口监视器数据
同时,串口监视器将每秒输出一次数据:
四、SPI冲突解决方案详解
4.1 问题现象
当BMP581和ST7789共享硬件SPI总线时:显示屏无法正常显示,传感器数据读取不稳定,系统可能完全无法工作
4.2 根本原因
SPI总线需要独占访问:
两个设备共享MOSI、MISO、SCK信号线
片选(CS)信号控制不足
总线竞争导致数据冲突
4.3 方案一
软件SPI驱动显示屏:将显示屏改为软件SPI驱动,与传感器的硬件SPI物理隔离。
- // ST7789使用软件SPI
- #define TFT_CS 6 // 显示屏片选
- #define TFT_RST 4 // 复位引脚
- #define TFT_DC 2 // 数据/命令选择
- #define TFT_MOSI 8 // 软件SPI数据引脚
- #define TFT_SCLK 7 // 软件SPI时钟引脚
- // 创建显示屏对象(使用软件SPI)
- Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
优势:
- 完全避免硬件SPI冲突
- 简化编程逻辑
- 更稳定的通信表现
- 灵活的引脚分配
4.4 方案二
共享SPI总线+显式CS控制:通过精确控制CS引脚状态,确保同一时间只有一个设备使用SPI总线。
- // BMP581 SPI参数
- uint8_t bmp581_cs = 10; // BMP581芯片选择引脚
- uint32_t clockFrequency = 100000; // BMP581的SPI时钟频率
- // ST7789显示屏引脚配置
- #define TFT_CS 6 // 显示屏芯片选择引脚(与BMP581不同)
- Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); //使用硬件SPI驱动方式
- void setup() {
- // ...其他初始化...
-
- // 初始化CS引脚
- pinMode(TFT_CS, OUTPUT);
- digitalWrite(TFT_CS, HIGH); // 初始取消选择显示屏
- pinMode(bmp581_cs, OUTPUT);
- digitalWrite(bmp581_cs, HIGH); // 初始取消选择传感器
- }
- void loop() {
- // 读取传感器数据
- digitalWrite(TFT_CS, HIGH); // 取消选择显示屏
- err = pressureSensor.getSensorData(&data);
- digitalWrite(bmp581_cs, HIGH); // 取消选择传感器
-
- // 更新显示
- digitalWrite(bmp581_cs, HIGH); // 确保传感器已取消选择
- updateTextDisplay(...);
- }
关键点:
- 通信前确保另一个设备被取消选择
- 通信后立即取消选择当前设备
- 初始化时所有CS引脚设为HIGH
- 软件SPI的MOSI和SCK引脚共用,片选(CS)引脚需要单独设置
五、海拔计算与精度说明
代码中使用简化的海拔计算公式:
float altitude = (1013.25 - pressure_hPa) / 12 * 100;
计算原理
1013.25 hPa:标准海平面气压
气压梯度:每下降12 hPa,海拔升高约100米
精度考虑 ,实际测量中可能存在10-50米的误差,主要因素包括:
- 当地气象条件变化
- 温度对气压的影响
- 传感器本身的测量误差
- 公式本身的近似性
六、常见问题解决
1.显示屏白屏或花屏
检查RES引脚连接
确认软件SPI引脚配置正确
尝试降低软件SPI速度:
在tft.init()后添加tft.setSPISpeed(10000000)
2.传感器读取失败
检查硬件SPI连接
确保CS引脚配置正确
测量传感器供电电压(应为3.3V)
3.数据显示异常 检查引脚定义是否正确
确认显示屏旋转方向设置合适
验证传感器数据在串口的输出是否正常
七、方案对比与选择建议特性 | 方案一(软件SPI) | 方案二(硬件SPI+CS控制) |
实现难度 | 简单 ★☆☆ | 中等 ★★☆ |
稳定性 | 高 ★★★ | 中 ★★☆ |
性能 | 中 ★★☆ | 高 ★★★ |
资源占用 | 较高(需要额外引脚) | 低(共享SPI引脚) |
推荐场景 | 初学者/快速实现 | 高性能应用/引脚受限 |
推荐选择:
- 对于大多数应用,方案一(软件SPI驱动显示屏) 是更简单可靠的选择
- 只有在需要高速刷新或引脚资源紧张时才考虑方案二
八、总结
本文详细介绍了在零知增强板上实现BMP581传感器与ST7789显示屏协同工作的完整过程,重点解决了SPI总线冲突问题。关键点包括:
- 硬件连接:正确连接SPI设备,特别是CS引脚
- SPI冲突解决:
推荐方案:使用软件SPI驱动显示屏
备选方案:共享硬件SPI+精确CS控制
- 数据采集与显示:实时获取环境数据并直观展示
- 海拔计算:使用简化公式计算海拔高度
通过本教程,开发者可以快速构建稳定可靠的环境监测系统,更多零知开发教程:
https://www.lingzhilab.com/freesources.html