用纳芯微传感器芯片制作的温湿度计、气压计、高度计和解压摆件
该项目使用了纳芯微传感器芯片,实现了温湿度测量、电机角度测量、大气压测量的设计,它的主要功能为:测量环境温湿度、测量人体温度、测量电机角度(解压)、测量大气压,测量海拔高度。。
标签
lvgl
温度传感器
十二指神探
气压
WeDesign第6期
IIC
磁力
aramy
更新2024-12-25
92

硬件介绍:

WeDesign第6期活动提供了4款来自纳芯微的传感器:分别是温度传感器NST112D-CWLR、温湿度传感器NSHT30、压力传感器NSPAS3、角度传感器NSM3013。外加一个带屏幕的十二指神探,使用的是rp2040的主控芯片。

任务选择:

一次拿到4个传感器,就想着把他们都跑起来,学习一下传感器的使用,也练习一下阅读传感器的相关文档。所以基本上任务2、任务3、任务4都有做了一些。

硬件制作

1、绘制电路图,是温度传感器NST112D-CWLR、温湿度传感器NSHT30这两个传感器使用的是IIC通讯方式,并且是3.3V供电,参考着芯片的手册,测量范围都是-40~125℃,用来测量室温绰绰有余。外围电路非常简单。虽然十二指神探已经有IIC的上拉电阻了,为了以后的拓展性,给每个芯片还是安排了额外的上拉电阻。然后与十二指神探的SDA(IO20)、SCL(IO21)管脚相连。NSHT30还有额外的两个管脚,ALERT和nRESET,这里和十二指神探的IO22、IO23连接,没有留意这两个管脚的功能,给后边埋下了隐患。

image.png

压力传感器NSPAS3、角度传感器NSM3013这两个传感器查看文档,是使用5V供电,并且都是使用电压输出。这里就产生了个问题,十二指神探的ADC是测量0~3.3V的电压,这里的5V输出电压,明显就不适用了,所以额外增加两个电阻,进行分压。将0~5V的输出电压映射到3.3V内。压力传感器NSPAS3、角度传感器NSM3013这两个传感器的电路图和封装图官方有提供,但是需要注册才能下载。于是偷懒,随便找了个相同封装的芯片来绘制电路图。外围电路参考着官方文档,使用官方推荐的配置进行设计。

image.png

在绘制压力传感器NSPAS3、角度传感器NSM3013这两个传感器时,犯了个大错。没有仔细看十二指神探的电路图,错误地将这两个传感器的测量管脚接到了十二指神探的IO24、IO25脚,正好错过了ADC的测量管脚,直到打板焊完电路才发现,最终使用跳线方式解决。

2、绘制PCB。为节约打板费用,将3颗传感器都放置在一个PCB板子上。两个温度计传感器,在PCB上做了镂空处理,减少PCB的温度传导。角度传感器NSM3013周边增加了螺丝孔,用来固定电机。每个传感器 都额外引出焊盘,方便后期将传感器部分单独割下来使用。

image.png

image.png

3、焊接。这里边有个温度传感器NST112D-CWLR,真的是小啊!但是好处在于这颗芯片背面植了锡,降低了点点难度。这是自己做的挑战度最高的一次焊接了。这里我使用锡焊膏加电熨斗方式焊接。

image.png

image.png

image.png

问题1:第一次焊接完成,使用IIC扫描方式读取设备,很遗憾只读到一个IIC地址,NSHT30没有读到。于是我又用电熨斗将NSHT30拆下来重新焊接,折腾了许久,成功地将NST112D也搞掉了。NST112D搞掉了好麻烦,正面的丝印一旦遇到焊油,就根本无法看清了。又是一个正方形结构,无法判断安装方向。就这样废了一颗NST112D芯片。最后取了一个新的NST112D和NSHT30,极其费力地重新焊上去,虽然还是扫描不到NSHT30的地址,但是总觉着自己焊接没有问题。

次日,冷静下来仔细检查,终于发现问题了。之前绘制电路图时,NSHT30的nRESET管脚,我是连接了十二指神探的IO22,这个管脚文档中有介绍,低电平有效。所以NSHT30工作时需要设置高电平,就这个疏忽,折腾了好久,还搞毁了两颗芯片。将22管脚拉高后,成功读取到了芯片的IIC地址。

image.png

问题2:压力传感器、角度传感器使用AD去读取。编程读取IO24、IO25脚的电压值,直接导致电脑跳出USB口故障,仔细去查看十二指神探的说明,才发现完美错过了ADC的管脚。无奈只能通过跳线解决。将排母对应的线割掉,然后压力传感器接到了IO26,角度传感器接到了IO28。

软件实现

软件使用Vscode+platformio,使用arduino编程。使用了lvgl8.4.0,界面设计使用了GUI-Guider。

image.png

1、按键输入。由于有4个传感器,传感器NST112D-CWLR和NSHT30都有测量温度功能,所以讲他俩放在一个页面上。角度传感器和压力传感器各自单独放置一页。使用lvgl的tableview控件进行显示,设计了三个页面,轮流切换。十二指神探显示屏不具备触摸屏功能,所以这里将拨轮作为输入拨轮左右拨动时可以选择不同的页面展示。这里使用了“OneButton”库。

OneButton enterButton(KEYOK, true);
OneButton prevButtton(KEYL, true);
OneButton nextButton(KEYR, true);

SHTSensor sht;
/*Initialize your keypad*/
static void keypad_init(void)
{
    // pinMode(KEYA, INPUT_PULLUP);
    // pinMode(KEYB, INPUT_PULLUP);
    pinMode(KEYL, INPUT_PULLUP);
    pinMode(KEYOK, INPUT_PULLUP);
    pinMode(KEYR, INPUT_PULLUP);
    enterButton.attachClick([]()
                            { ENTER_BUTTON = true; });
    prevButtton.attachClick([]()
                            { PREV_BUTTON = true; });
    nextButton.attachClick([]()
                           { NEXT_BUTTON = true; });
    // Serial.println("keypad init");
}

2、读取温湿度传感器NSHT30。这里使用了arduino-sht的库,参考着库的例程,还是很容易读取到NSHT30的温度、湿度数据的。这里额外需要注意NSHT30的nRESET管脚需要拉高。

void setup()
{
    pinMode(22, OUTPUT);
    digitalWrite(22, HIGH);
    Wire.begin();
    analogReadResolution(16);           //调整ADC分辨率为16位
    Serial.begin(115200);
    keypad_init();
    sht.init();
    sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
    Lvgl_Init();
}

3、读取温度传感器NST112D-CWLR。这颗传感器尺寸太小了,但是焊好后,读取还是挺容易的。

// 读取nst112温度
float readTemperatureC()
{
    float temp;
    Wire.beginTransmission(NTS112_ADDRESS);
    Wire.write(0x00);
    Wire.endTransmission();
    Wire.requestFrom(NTS112_ADDRESS, (uint8_t)2);
    uint16_t t = Wire.read();
    temp = (float)t;
    t = Wire.read();
    temp += (float)t / 256.00;
    Wire.endTransmission();
    return temp;
}

参考着官方的设计指南文档,这颗芯片可以贴着手腕测量身体体温,并提供了测量公式。使用这个公式,可以结合环境温度,共同测量出身体的温度,测量了一下,身体温度36.3,感觉挺准的。

// 根据环温和人体皮肤的温度,计算出人体核心的温度
        TEMP_Core = 0.0336 * TEMP_Skin * TEMP_Skin - 0.546 * TEMP_Skin + 1.7082 * TEMP_Envir - 0.0514 * TEMP_Envir * TEMP_Skin + 17.626;
        return TEMP_Core;

image.png

但是统计了一下,感觉这个测量公式只是降低了这个传感器的权重,测量值从31度到36度,体温变化也就1度多点,感觉不太可靠。

4、角度传感器。角度传感器 NSM3013A-Q1SPR 使用了14 位线性 DAC 模拟量输出。这里为了妥协压力传感器,程序中的ADC统一设置为16位分辨率。这个角度传感器通过感知磁铁磁力线来获得角度信息。硬件上设计了一个电机支架,电机的轴上粘着一颗径向磁铁, NSM3013A可以实时获得电机旋转的角度。有点点缺憾,这里使用的AD方式读取电机的角度,没有中断,所以对电机旋转的圈数计算有局限性,当过高速度旋转时,就会出现记不准现象。ADC读取到的数值为16位精度,所以首先转换成电压值:mgValue * 3.3 / 65535.0 。因为有电阻分压,两颗电阻分别是6.8k和11k。测量的电压值按分压电阻还原回去就需要除以 11/(11+6.8)=0.617977528 最后将这个电压值,映射到360度的范围上。

   // 处理磁力计 将磁力计映射到0~359度 角度上
    angel = ((float)mgValue * 3.3 / 65535.0 / 0.617977528) * 360.0 / 5.0;
    if (oldangel == 1000)
        oldangel = angel; // 首次测量
    snprintf(strbuf, 15, "%d", (int)angel);
    lv_arc_set_value(guider_ui.screen_arc_angle, (int)angel);
    lv_label_set_text(guider_ui.screen_label_angle, strbuf);
    // 计算两次角度差值,如果有突变,就旋转了1圈
    if (oldangel - (int)angel > 240)
    {
        cicle++;
    }
    if (oldangel - (int)angel < -240)
    {
        cicle--;
    }
    snprintf(strbuf, 15, "%d", cicle);
    lv_label_set_text(guider_ui.screen_label_circle, strbuf);
    oldangel = angel;

5、压力传感器压力传感器NSPAS3官方资料很少,而且全都是英文的。为了找这个传感器的输出分辨率,查看了文档好久,都没能找到。最后在一张图中找到标注为16位DAC输出。

image.png

仔细阅读这份文档,还获得了,如何从测量的电压转换为大气压的公式。

image.png

于是一番编程计算,获取了当前的大气压。和角度传感器一样,使用了电阻分压,计算出真实电压后,套入上边的公式,就可以计算出当前的大气压了。再参考气压-位势高度公式,可以计算出当前的海拔高度。

image.png

    paValue = analogRead(PAPIN); // 读取气压计
    // LV_LOG_USER("%d   %d \n", mgValue, paValue);
    // 将气压读取到的值放入数组中,并计算平均值
    pressval = getAvgPressure(paValue);
    pressval = (((pressval) * 3.3 / 65535.0 / 0.617977528 / 5.0) - (-0.008095)) / 0.008095; // 换算成当前气压值
    snprintf(strbuf, 15, "%.1fkPa", pressval);
    // LV_LOG_USER("%s \n", strbuf);
    lv_label_set_text(guider_ui.screen_label_pa_val, strbuf);
    // 计算当前海拔高度
    // hightval = Hypsometric(pressval, temp1 < temp2 ? temp1 : temp2);
    hightval = Hypsometric(pressval, temp1);
    // snprintf(strbuf, 15, "%.1fm", hightval);
    snprintf(strbuf, 15, "%dm", (int)hightval);
    lv_label_set_text(guider_ui.screen_label_hight_val, strbuf); // 海拔

很可惜,读取到的气压值波动挺大的,导致海拔数据也是上下乱窜。观察数据,采用均值滤波法剔除气压波动,使用200组数据取均值,得到的气压值基本准确,但是海拔数据还是波动的厉害。

// 处理气压值,使用数组 并返回平均值
float getAvgPressure(uint16_t pressure)
{
    praval[pra_index] = pressure;
    pra_index++;
    if (pra_index >= PRANUM)
    {
        pra_index = 0;
    }
    uint32_t sum = 0;
    for (uint8_t i = 0; i < PRANUM; i++)
    {
        sum += praval[i];
    }
    return (float)sum / (float)PRANUM;
}


// Hypsometric算法计算海拔高度
/// CurrentPressure:当前大气压<
/// CurrentTemperature:当前温度
float Hypsometric(float CurrentPressure, float CurrentTemperature)
{
    return (float)(pow((PRESSURE / CurrentPressure), (float)1.0 / 5.257) - 1.0) * (CurrentTemperature + 273.15) / 0.0065;
}

6、读取传感器并显示。这里用户UI使用了Lvgl,Lvgl在图形展示上还是挺方便的。使用了tableview控件,使用三个界面来展示不同传感器的信息。设置了一个定时器,每60ms读取一次传感器,并且刷新屏幕。使用拨杆旋钮作为输入,控制界面的切换,用来选择查看不同传感器的内容。



效果展示

1、温度湿度展示。使用的是摄氏度。没有测量到体温时,体温部分不显示。

image.png

2、体温展示。两个温度计靠的太近了,测量体温时需要NSHT30感知环境温度,靠太近了,会有影响。

image.png

3、角度展示。当电机旋转时,屏幕上的圆弧会跟着变化。圆中黄色的字符表示旋转的圈数,红色的代表旋转角度(单位:度)。摆在桌面上用来解压效果不错。

image.png

image.png

4、气压和海拔高度。压力传感器获得的数据波动还是有点大,这里使用200组数据做算数平均(每组数据采集时间间隔60ms),通过平均值计算的海拔高度,波动还是有点大,但是用来短时间内,测量高度差还是够用的。

image.png


心得体会

感谢硬禾学堂发起WeDesign活动,让我了解到国产的优秀芯片技术。参加活动同时,还挑战的自我,第一次焊接如此小的芯片,成就感满满。

KiCad文件
使用说明
全屏
附件下载
rp2040_design.zip
团队介绍
单片机业余爱好者,瞎捣鼓小能手。
团队成员
aramy
单片机业余爱好者,瞎捣鼓小能手。
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号