若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/79771763
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
树莓派开发专栏(点击传送门)
上一篇:《树莓派开发笔记(六):GPIO口的UART的使用(串口通讯)》
下一篇:《树莓派开发笔记(八):GPIO口的I2C使用(BME280三合一传感器:测量温度、湿度、气压、海拔高度) 》
前话
本章节我们开发GPIO口的SPI使用,连接BME280三合一传感器,采集气压、温度、湿度,计算海拔高度。
Demo:GPIO口的SPI通讯
SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。
(1)SDI – SerialData In,串行数据输入;
(2)SDO – SerialData Out,串行数据输出;
(3)SCLK – Serial Clock,时钟信号,由主设备产生;
(4)CS – Chip Select,从设备使能信号,由主设备控制,注意:如果示波器测量不到,请降低频率到10000;SPI在发送接收数据的时候CS片选才会拉低生效。
其中,CS是从芯片是否被主芯片选中的控制信号,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。这就使在同一条总线上连接多个SPI设备成为可能。
负责通讯的3根线SDI、SDO、SCLK。通讯是通过数据交换完成的,SPI是串行通讯协议,数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。因此,至少需要8次时钟信号的改变(上沿和下沿为一次),才能完成8位数据的传输。
SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCLK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。
SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
BME280
BME280是一款集成温度、湿度、气压,三位一体的环境传感器。具有高精度,多功能,小尺寸等特点,如下图:
BME280模块,设备地址默认为0x77。下面是读取数据的指令,数据的读出是从0xf7到0xfc读做(温度和压力)或从0xf7到0xfe(温度、压力、湿度等)数据以无符号形式读出。
控制指令集,如下图:
读取数据指令集,分为压力,温度,适度,如下图:
BME280的SPI写入方法(特别注意)
查看数据手册,我们看到BME280的写入方法,需要将第七位置0。
如上图,读取F4h则发送F4h,写入F4h则是发送4h。
开启SPI接口
sudo raspi-config
按照下图步骤选取
然后重启。
查看系统是否启动spi,如下图:
电路原理图
关键代码
初始化代码
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); _bme280ForSpi.initPressureTemperatureMode(); _bme280ForSpi.initHumidity(); startTimer(1000); }
每隔1s获取一次
void MainWindow::timerEvent(QTimerEvent *event) { Q_UNUSED(event); ui->label_i2cP->setText(QString("%1 Pa").arg(_bme280ForSpi.getPressure())); ui->label_i2cT->setText(QString("%1 ℃").arg(_bme280ForSpi.getTemperatureC())); ui->label_i2cH->setText(QString("%1 %").arg(_bme280ForSpi.getHumidity())); ui->label_i2cA->setText(QString("%1 m").arg(_bme280ForSpi.getAltitudeMeters())); }
模块代码
bme280forspi.h
#ifndef BME280FORSPI_H #define BME280FORSPI_H #include <QObject> #include "spi.h" //Register names: #define BME280_DIG_T1_LSB_REG 0x88 #define BME280_DIG_T1_MSB_REG 0x89 #define BME280_DIG_T2_LSB_REG 0x8A #define BME280_DIG_T2_MSB_REG 0x8B #define BME280_DIG_T3_LSB_REG 0x8C #define BME280_DIG_T3_MSB_REG 0x8D #define BME280_DIG_P1_LSB_REG 0x8E #define BME280_DIG_P1_MSB_REG 0x8F #define BME280_DIG_P2_LSB_REG 0x90 #define BME280_DIG_P2_MSB_REG 0x91 #define BME280_DIG_P3_LSB_REG 0x92 #define BME280_DIG_P3_MSB_REG 0x93 #define BME280_DIG_P4_LSB_REG 0x94 #define BME280_DIG_P4_MSB_REG 0x95 #define BME280_DIG_P5_LSB_REG 0x96 #define BME280_DIG_P5_MSB_REG 0x97 #define BME280_DIG_P6_LSB_REG 0x98 #define BME280_DIG_P6_MSB_REG 0x99 #define BME280_DIG_P7_LSB_REG 0x9A #define BME280_DIG_P7_MSB_REG 0x9B #define BME280_DIG_P8_LSB_REG 0x9C #define BME280_DIG_P8_MSB_REG 0x9D #define BME280_DIG_P9_LSB_REG 0x9E #define BME280_DIG_P9_MSB_REG 0x9F #define BME280_DIG_H1_REG 0xA1 #define BME280_CHIP_ID_REG 0xD0 //Chip ID Online value is 0x60 all the time #define BME280_RST_REG 0xE0 //Softreset Reg #define BME280_DIG_H2_LSB_REG 0xE1 #define BME280_DIG_H2_MSB_REG 0xE2 #define BME280_DIG_H3_REG 0xE3 #define BME280_DIG_H4_MSB_REG 0xE4 #define BME280_DIG_H4_LSB_REG 0xE5 #define BME280_DIG_H5_MSB_REG 0xE6 #define BME280_DIG_H6_REG 0xE7 #define BME280_CTRL_HUMIDITY_REG 0xF2 //Ctrl Humidity Reg #define BME280_STAT_REG 0xF3 //Status Reg #define BME280_CTRL_MEAS_REG 0xF4 //Ctrl Measure Reg #define BME280_CONFIG_REG 0xF5 //Configuration Reg #define BME280_PRESSURE_MSB_REG 0xF7 //Pressure MSB #define BME280_PRESSURE_LSB_REG 0xF8 //Pressure LSB #define BME280_PRESSURE_XLSB_REG 0xF9 //Pressure XLSB #define BME280_TEMPERATURE_MSB_REG 0xFA //Temperature MSB #define BME280_TEMPERATURE_LSB_REG 0xFB //Temperature LSB #define BME280_TEMPERATURE_XLSB_REG 0xFC //Temperature XLSB #define BME280_HUMIDITY_MSB_REG 0xFD //Humidity MSB #define BME280_HUMIDITY_LSB_REG 0xFE //Humidity LSB class BME280ForSpi : public QObject { Q_OBJECT public: enum PRESSURE_OVERSAMPLING { PRESSURE_OVERSAMPLING_SKIPPED = 0x00, PRESSURE_OVERSAMPLING_PLUS_ONE = 0x01, PRESSURE_OVERSAMPLING_PLUS_TWO = 0x02, PRESSURE_OVERSAMPLING_PLUS_FOUR = 0x03, PRESSURE_OVERSAMPLING_PLUS_EIGHT = 0x04, PRESSURE_OVERSAMPLING_PLUS_SIXTEEN = 0x05 }; enum TEMPERATURE_OVERSAMPLING { TEMPERATURE_OVERSAMPLING_SKIPPED = 0x00, TEMPERATURE_OVERSAMPLING_PLUS_ONE = 0x01, TEMPERATURE_OVERSAMPLING_PLUS_TWO = 0x02, TEMPERATURE_OVERSAMPLING_PLUS_FOUR = 0x03, TEMPERATURE_OVERSAMPLING_PLUS_EIGHT = 0x04, TEMPERATURE_OVERSAMPLING_PLUS_SIXTEEN = 0x05 }; enum MODE_OF_PRESSURE_TEMPERATUR { MODE_OF_PRESSURE_TEMPERATUR_SLEEP = 0x00, MODE_OF_PRESSURE_TEMPERATUR_FORCED = 0x01, MODE_OF_PRESSURE_TEMPERATUR_MORMAL = 0x03 }; enum HUMIDITY_OVERSAMPLING { HUMIDITY_OVERSAMPLING_SKIPPED = 0x00, HUMIDITY_OVERSAMPLING_PLUS_ONE = 0x01, HUMIDITY_OVERSAMPLING_PLUS_TWO = 0x02, HUMIDITY_OVERSAMPLING_PLUS_FOUR = 0x03, HUMIDITY_OVERSAMPLING_PLUS_EIGHT = 0x04, HUMIDITY_OVERSAMPLING_PLUS_SIXTEEN = 0x05 }; struct Calibration { uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; uint8_t dig_H1; int16_t dig_H2; uint8_t dig_H3; int16_t dig_H4; int16_t dig_H5; uint8_t dig_H6; }; public: explicit BME280ForSpi(QObject *parent = 0); bool isOnline(); void initPressureTemperatureMode( PRESSURE_OVERSAMPLING p = PRESSURE_OVERSAMPLING_PLUS_ONE, TEMPERATURE_OVERSAMPLING t = TEMPERATURE_OVERSAMPLING_PLUS_ONE, MODE_OF_PRESSURE_TEMPERATUR m = MODE_OF_PRESSURE_TEMPERATUR_MORMAL); void initHumidity(HUMIDITY_OVERSAMPLING h = HUMIDITY_OVERSAMPLING_PLUS_ONE); void reset(); public slots: float getTemperatureC(); float getTemperatureF(); float getPressure(); float getHumidity(); float getAltitudeMeters(); float getAltitudeFeet(); private: Spi _spi; Calibration _calibration; int32_t _tFine; int _channel; int _speed; }; #endif // BME280FORSPI_H
bme280forspi.cpp
#include "bme280forspi.h" BME280ForSpi::BME280ForSpi(QObject *parent) : QObject(parent) { _spi.init(); ... } bool BME280ForSpi::isOnline() { // according to 0xD0 "id", value is 0x60 return _spi.readData(BME280_CHIP_ID_REG) == 0x60; } void BME280ForSpi::initPressureTemperatureMode(BME280ForSpi::PRESSURE_OVERSAMPLING p, BME280ForSpi::TEMPERATURE_OVERSAMPLING t, BME280ForSpi::MODE_OF_PRESSURE_TEMPERATUR m) { ... } void BME280ForSpi::initHumidity(BME280ForSpi::HUMIDITY_OVERSAMPLING h) { uchar uc; uc = (uchar)h; _spi.writeData(BME280_CTRL_HUMIDITY_REG, uc); } void BME280ForSpi::reset() { _spi.writeData(BME280_RST_REG, 0xB6); } float BME280ForSpi::getTemperatureC() { ... int32_t adc_T = ((uint32_t)_spi.readData(BME280_TEMPERATURE_MSB_REG) << 12) | ((uint32_t)_spi.readData(BME280_TEMPERATURE_LSB_REG) << 4) | ((_spi.readData(BME280_TEMPERATURE_XLSB_REG) >> 4) & 0x0F); ... output = output / 100; return output; } float BME280ForSpi::getTemperatureF() { float output = getTemperatureC(); output = (output * 9) / 5 + 32; return output; } float BME280ForSpi::getPressure() { int32_t adc_P = ((uint32_t)_spi.readData(BME280_PRESSURE_MSB_REG) << 12) | ((uint32_t)_spi.readData(BME280_PRESSURE_LSB_REG) << 4) | ((_spi.readData(BME280_PRESSURE_XLSB_REG) >> 4) & 0x0F); int64_t var1, var2, p_acc; ... return (float)p_acc; } float BME280ForSpi::getHumidity() { ... int32_t adc_H = ((uint32_t)_spi.readData(BME280_HUMIDITY_MSB_REG) << 8) | ((uint32_t)_spi.readData(BME280_HUMIDITY_LSB_REG)); ... return (float)((var1>>12) >> 10); } float BME280ForSpi::getAltitudeMeters() { ... return heightOutput; } float BME280ForSpi::getAltitudeFeet() { ... heightOutput = getAltitudeMeters() * 3.28084; return heightOutput; }
SPI模块代码
spi.h
class Spi : public QObject { Q_OBJECT public: enum CHOICE { CHOICE_CE0 = 0x00, CHOICE_CE1 = 0x01 }; public: explicit Spi(QObject *parent = 0); bool init(CHOICE c = CHOICE_CE0, int speed = 500000); signals: public slots: void writeData(int reg, uchar data); uchar readData(int reg); private: int _fd; int _channel; int _speed; }; #endif // SPI_H
spi.cpp
#include "spi.h" #include "wiringPi.h" #include "wiringPiSPI.h" #include <QDebug> #include <QByteArray> Spi::Spi(QObject *parent) : QObject(parent) { wiringPiSetup(); … } bool Spi::init(Spi::CHOICE c, int speed) { … _fd = wiringPiSPISetup(_channel, _speed); … } void Spi::writeData(int reg, uchar data) { … ret = wiringPiSPIDataRW(_channel, ch, 2); } uchar Spi::readData(int reg) { … wiringPiSPIDataRW(_channel, data, 3); … }
运行效果
上一篇:《树莓派开发笔记(六):GPIO口的UART的使用(串口通讯)》
下一篇:《树莓派开发笔记(八):GPIO口的I2C使用(BME280三合一传感器:测量温度、湿度、气压、海拔高度) 》
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/79771763