项目介绍
使用带屏十二指神探作为主控和显示设备,读取和显示数据
基本任务:
能够读取温度、湿度、压力数据,显示在屏幕中 (√)
额外任务:
当前时间显示 (√)
温湿度曲线显示 (√)
项目设计
整个项目主要为上图所示的几个部分,rp2040通过I2C与NSHT30、RX8025T通信读取所需数据,通过ADC读取NSPAS3的输出电压,计算后得到各个数据,然后通过屏幕显示出来。
主控/显示——带屏12指神探
主控芯片:采用树莓派Pico核心芯片RP2040
- 双Arm Cortex M0+内核,可以运行到133MHz
- 264KBSRAM,板卡上外扩2MBFlash
- 性能强大、高度灵活的可编程IO(PIO)可用于高速数字接口
- 拥有2个UART、2个SPI、2个I2C、16个PWM通道以及4路12位精度ADC
- 支持MicroPython、C、C++编程
- 拖拽UF2固件至U盘的方式烧录固件,方便快捷
板上功能
- TYPE-C接口用于供电和数据传输
- 一个boot按键用于进入boot模式
- 两个可程控按键和一个拨轮用于自定义功能
- 搭载240*240分辨率的LCD彩屏,通过SPI接口进行通信,控制器为常用的ST7789芯片,例程丰富便于开发
- 扩展接口包含5v、3.3v输出、GND。9个GPIO,可同时使能最多三个通道ADC
应用场景:
- 逻辑分析仪、daplink、电压电流表、
- 协议发生器、多功能控制切换
- DIY扩展板:信号发生器、高速数据采集、桌面天气时钟小组件、智能家居总控台
开发环境:
- Micropython:简单易学、资料丰富、例程繁多,适用于初学者快速上手及使用
- C/C++:语句效率更高,适合发挥RP2040更极致的性能
- Arduino:有适配RP2040芯片的扩展包,方便c语言上手开发
在本次开发中选用Micropython作为开发环境
传感器
温湿度传感器 NSHT30-QDNR:
• 相对湿度(RH)传感器:
- 工作范围:0%RH~100%RH
- 精度:±3%RH(典型值)
• 温度传感器:
- 工作温度范围:-40℃~125℃
- 精度:±0.3℃(典型值)
• 相对湿度和温度补偿的数字输出
• 宽电源电压范围:2.0V~5.5V
• I2C数字接口,通信速率高达1MHz
- 两个可选地址
- 带有CRC校验的数据保护
• 低功耗:平均电流3.2μA
• 8-Pin LGA和DFN封装可选
压力传感器 NSPAS3NII5RRAI:
• 宽工作温度范围-40℃~130℃
• 全温域内高精度:
0℃~85℃内优于±1%F.S.
-40℃~130℃内优于±1.5%F.S.
• 支持-24V~28V过反压保护能力
• 支持5V供电及绝对电压输出模式下18V以内直接供电
• 含氟凝胶保护,兼容油气环境
• 优于0.8ms的快速响应时间
• 支持绝对输出/比例输出,输出曲线可定制
• 断线检测,输出钳位,输出报警功能
• 压力量程10kPa~400kPa
• AEC-Q100认证
• 封装:SOP-8 (7.0mm x 7.0mm)
其他
RX8025T 实时时钟芯片
- 内置高稳定度的32.768KHz 的DTCXO (数字温度补偿晶体振荡器)
- 支持I2C 总线的高速模式(400K)。
- 定时报警功能(可设定:天,日期,小时,分钟)
- 固定周期定时中断功能。
- 时间更新中断功能。
- 32.768KHz频率输出(具有使能OE功能)
- 闰年自动调整功能。(2000 到2099)
- 宽范围接口电压:2.2V 到 5.5V
- 宽范围的时间保持电压:1.8V 到 5.5V
- 低电流功耗:0.8uA/3V (Typ.)
- 工作温度:-45℃~85℃
画原理图、PCB制板过程中遇到的问题
1.温湿度传感器需要远离热源
根据纳芯微NSHT30使用设计指南PCB 的Layout建议:
1.温湿度传感器芯片位置尽量远离热源;
2.降低温湿度传感器芯片与PCB间的热传导;(芯片周围不覆铜,走线尽量细,PCB上芯片周围开孔,
降低热传导,或者采用柔性软板将温湿度传感器与测试主板进行物理隔离)
设计了如下的PCB布局:
传感器在PCB上特意伸出,并且没有覆铜,尽量降低了热传递和远离了其他可能发热的芯片
2.压力传感器的输出采样
压力传感器NSPAS3的输出范围为0~5V,而RP2040在3.3V供电下的ADC采样范围为0~3.3V,所以不能直接连接两个引脚。需要在压力传感器的输出脚上进行分压。
使用两个10K、1%的电阻分压,使输出范围变为0~2.5V,落在了ADC采样范围内。
实现结果展示
传感器扩展板
第一屏:显示从上至下依次为温度、湿度、气压、时间
第二屏:时间显示
第三屏:温湿度曲线显示
遇到的问题:
1.代码移植问题:nsht30的代码复用了sht30的代码,完美适配,RX8025T的microPython参考资料较少,需要从C开始移植;
2.气压读数波动问题:由于是从4095扩充到65535,后几位的读数波动较大,需要使用滤波算法,使用均值后读数明显稳定了;
关键代码及说明
NSHT30测量代码:
class SHT30:
"""
SHT30 sensor driver in pure python based on I2C bus
"""
POLYNOMIAL = 0x131 # P(x) = x^8 + x^5 + x^4 + 1 = 100110001
ALERT_PENDING_MASK = 0x8000 # 15
HEATER_MASK = 0x2000 # 13
RH_ALERT_MASK = 0x0800 # 11
T_ALERT_MASK = 0x0400 # 10
RESET_MASK = 0x0010 # 4
CMD_STATUS_MASK = 0x0002 # 1
WRITE_STATUS_MASK = 0x0001 # 0
# MSB = 0x2C LSB = 0x06 Repeatability = High, Clock stretching = enabled
MEASURE_CMD = b'\x2C\x10'
STATUS_CMD = b'\xF3\x2D'
RESET_CMD = b'\x30\xA2'
CLEAR_STATUS_CMD = b'\x30\x41'
ENABLE_HEATER_CMD = b'\x30\x6D'
DISABLE_HEATER_CMD = b'\x30\x66'
def __init__(self, i2c=None, delta_temp=0, delta_hum=0, i2c_address=DEFAULT_I2C_ADDRESS):
if i2c is None:
raise ValueError('An I2C object is required.')
self.i2c = i2c
self.i2c_addr = i2c_address
self.set_delta(delta_temp, delta_hum)
time.sleep_ms(50)
def is_present(self):
"""
Return true if the sensor is correctly conneced, False otherwise
"""
return self.i2c_addr in self.i2c.scan()
def set_delta(self, delta_temp=0, delta_hum=0):
"""
Apply a delta value on the future measurements of temperature and/or humidity
The units are Celsius for temperature and percent for humidity (can be negative values)
"""
self.delta_temp = delta_temp
self.delta_hum = delta_hum
def _check_crc(self, data):
# calculates 8-Bit checksum with given polynomial
crc = 0xFF
for b in data[:-1]:
crc ^= b
for _ in range(8, 0, -1):
if crc & 0x80:
crc = (crc << 1) ^ SHT30.POLYNOMIAL
else:
crc <<= 1
crc_to_check = data[-1]
return crc_to_check == crc
def send_cmd(self, cmd_request, response_size=6, read_delay_ms=100):
"""
Send a command to the sensor and read (optionally) the response
The responsed data is validated by CRC
"""
try:
self.i2c.writeto(self.i2c_addr, cmd_request)
if not response_size:
return
time.sleep_ms(read_delay_ms)
data = self.i2c.readfrom(self.i2c_addr, response_size)
for i in range(response_size//3):
if not self._check_crc(data[i*3:(i+1)*3]): # pos 2 and 5 are CRC
raise SHT30Error(SHT30Error.CRC_ERROR)
if data == bytearray(response_size):
raise SHT30Error(SHT30Error.DATA_ERROR)
return data
except OSError:
raise SHT30Error(SHT30Error.BUS_ERROR)
except Exception as ex:
raise ex
def clear_status(self):
"""
Clear the status register
"""
return self.send_cmd(SHT30.CLEAR_STATUS_CMD, None)
def reset(self):
"""
Send a soft-reset to the sensor
"""
return self.send_cmd(SHT30.RESET_CMD, None)
def status(self, raw=False):
"""
Get the sensor status register.
It returns a int value or the bytearray(3) if raw==True
"""
data = self.send_cmd(SHT30.STATUS_CMD, 3, read_delay_ms=20)
if raw:
return data
status_register = data[0] << 8 | data[1]
return status_register
def measure(self, raw=False):
"""
If raw==True returns a bytearrya(6) with sensor direct measurement otherwise
It gets the temperature (T) and humidity (RH) measurement and return them.
The units are Celsius and percent
"""
data = self.send_cmd(SHT30.MEASURE_CMD, 6)
if raw:
return data
t_celsius = (((data[0] << 8 | data[1]) * 175) / 0xFFFF) - 45 + self.delta_temp
rh = (((data[3] << 8 | data[4]) * 100.0) / 0xFFFF) + self.delta_hum
return t_celsius, rh
def measure_int(self, raw=False):
"""
Get the temperature (T) and humidity (RH) measurement using integers.
If raw==True returns a bytearrya(6) with sensor direct measurement otherwise
It returns a tuple with 4 values: T integer, T decimal, H integer, H decimal
For instance to return T=24.0512 and RH= 34.662 This method will return
(24, 5, 34, 66) Only 2 decimal digits are returned, .05 becomes 5
Delta values are not applied in this method
The units are Celsius and percent.
"""
data = self.send_cmd(SHT30.MEASURE_CMD, 6)
if raw:
return data
aux = (data[0] << 8 | data[1]) * 175
t_int = (aux // 0xffff) - 45
t_dec = (aux % 0xffff * 100) // 0xffff
aux = (data[3] << 8 | data[4]) * 100
h_int = aux // 0xffff
h_dec = (aux % 0xffff * 100) // 0xffff
return t_int, t_dec, h_int, h_dec
RX8025读写代码:
class RX8025T():
def __init__(self, i2c):
self.i2c = i2c
self.tb = bytearray(1)
self.rb = bytearray(1)
self.buf = bytearray(7)
self.DT = [0] * 8
def setReg(self, reg, dat):
self.tb[0] = dat
self.i2c.writeto_mem(RX8025T_I2C_ADDRESS, reg, self.tb)
def getReg(self, reg):
self.i2c.readfrom_mem_into(RX8025T_I2C_ADDRESS, reg, self.rb)
return self.rb[0]
def TOBCD(self, dat):
n0=n1=0
n0=dat%10
n1=(dat//10)%10
return (n1<<4|n0)
def FROMBCD(self, dat):
n0=n1=0
n0=dat&0x0f
n1=(dat>>4)*10
return n0+n1
def year(self, year = None):
if year == None:
return self.FROMBCD(self.getReg(RX8025T_REG_YEAR)) + 2000
else:
self.setReg(RX8025T_REG_YEAR, self.TOBCD(year%100))
def month(self, month = None):
if month == None:
return self.FROMBCD(self.getReg(RX8025T_REG_MONTH))
else:
self.setReg(RX8025T_REG_MONTH, self.TOBCD(month))
def day(self, day = None):
if day == None:
return self.FROMBCD(self.getReg(RX8025T_REG_DAY))
else:
self.setReg(RX8025T_REG_DAY, self.TOBCD(day))
def weekday(self, weekday = None):
if weekday == None:
return int(math.log2(self.getReg(RX8025T_REG_WEEKDAY)))
else:
d=1<<weekday
self.setReg(RX8025T_REG_WEEKDAY, d)
def hour(self, hour = None):
if hour == None:
return self.FROMBCDD(self.getReg(RX8025T_REG_HOUR))
else:
self.setReg(RX8025T_REG_HOUR, self.TOBCD(hour))
def minute(self, minute = None):
if minute == None:
return self.FROMBCD(self.getReg(RX8025T_REG_MINUTE))
else:
self.setReg(RX8025T_REG_MINUTE, self.TOBCD(minute))
def second(self, second = None):
if second == None:
return self.FROMBCD(self.getReg(RX8025T_REG_SECOND))
else:
self.setReg(RX8025T_REG_SECOND, self.TOBCD(second))
def datetime(self, DT=None):
if DT == None:
self.i2c.readfrom_mem_into(RX8025T_I2C_ADDRESS, RX8025T_REG_SECOND, self.buf)
self.DT[0] = self.FROMBCD(self.buf[6]) + 2000
self.DT[1] = self.FROMBCD(self.buf[5])
self.DT[2] = self.FROMBCD(self.buf[4])
self.DT[3] = int(math.log2(self.buf[3]))
self.DT[4] = self.FROMBCD(self.buf[2])
self.DT[5] = self.FROMBCD(self.buf[1])
self.DT[6] = self.FROMBCD(self.buf[0])
self.DT[7] = 0
return self.DT
else:
self.buf[0] = self.TOBCD(DT[6])
self.buf[1] = self.TOBCD(DT[5])
self.buf[2] = self.TOBCD(DT[4])
self.buf[3] = 1<<DT[3]
self.buf[4] = self.TOBCD(DT[2])
self.buf[5] = self.TOBCD(DT[1])
self.buf[6] = self.TOBCD(DT[0])
self.i2c.writeto_mem(RX8025T_I2C_ADDRESS, RX8025T_REG_SECOND, self.buf)
NSPAS3均值及换算:
while(i < 16):
val = adc.read_u16() # read a raw analog value in the range 0-65535
val1 += val
i += 1
val = val1 >> 4
pka = val*3.3*2*105/4.25/65535
以上为报告全部!!!