基于纳芯微传感器的监测仪
该项目使用了kicad软件、纳芯微传感器、带屏幕的十二指神探,实现了具备温湿度、压力和磁角度监测工能的检测仪的设计,它的主要功能为:实时采集周围的温湿度、气压、以及磁角度。
标签
lvgl
纳芯微
十二指神探
lshy
更新2024-12-26
65


基于纳芯微传感器的监测仪

项目介绍



本项目使用带屏幕的十二指神探作为主控,采集传感器上的数据,并在屏幕中显示出来。传感器使用的是纳芯微的3种传感器:NSHT30、NSPAS3、NSM3013。pcb的设计使用kicad软件。

项目设计思路



十二指神探通过iic采集nsht30中的温湿度数据,通过adc采集nspas3和nsm3013中的数据。数据采集完成后,使用lvgl的ui框架,将数据显示出来。






代码框架



十二指神探的主控为树莓派的rp2040,他支持c、c++、python等语言。在本项目中,通过vscode中的platformio插件,使用arduino框架进行代码的编写。使用platformio编写的首要问题就是库的安装,这个过程是十分缓慢且易出问题。但是在一切安装好后,就可以愉快的编写代码,且具备代码提示和跳转功能,编写起来方便。但可惜的是,platformio中的arduino似乎不支持双核,不能很好的利用rp2040的资源。

ui



十二指神探使用lvgl,这个当前时髦流行的ui框架,给每个传感器数值创建了一个界面。界面之间可以通过十二指神探的波动开关进行切换。其中ui界面的绘制,通过nxp的gui guider软件进行设计。在gui guider中创建4个屏幕。







并在屏幕中填入对应的图像及文本。文本用于显示对应的数据,图像使用和当前数据类型相关的素材。素材可以在阿里开源的图标库(iconfont-阿里巴巴矢量图标库)中查找,里面有很多各种各样的素材。



gui guider设计好ui后可以生成对应的c代码,将生成的代码放入代码工程内,就可以运行了。



如果出现#include "lvgl.h"的编译错误,那将生成代码中与lvgl头文件相关的部分,修改成工程中,lvgl的实际位置。



如果在设计ui时,添加了界面间切换的动画,但是烧录进去测试发现又没有。那是因为生成的代码直接将上一个界面给删除了,需要对生成的代码进行修改,将lv_obj_clean(act_scr);注释,这样动画就能正常显示了。

void ui_load_scr_animation(lv_ui *ui, lv_obj_t **new_scr, bool new_scr_del, bool *old_scr_del, ui_setup_scr_t setup_scr,

lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool is_clean, bool auto_del)

{

lv_obj_t *act_scr = lv_scr_act();



#if LV_USE_GUIDER_SIMULATOR && LV_USE_FREEMASTER

if (auto_del)

{

gg_edata_task_clear(act_scr);

}

#endif

if (auto_del && is_clean)

{

// lv_obj_clean(act_scr);

}

if (new_scr_del)

{

setup_scr(ui);

}

lv_scr_load_anim(*new_scr, anim_type, time, delay, auto_del);

*old_scr_del = auto_del;

}



界面切换后,需要将老界面移除组,并将新界面加入组中,避免波动开关的控制切换到其他界面。这个是生成代码中没有的,需要手动的在event_init.c文件中更改。



static void screen_1_event_handler(lv_event_t *e)

{

lv_event_code_t code = lv_event_get_code(e);

switch (code)

{

case LV_EVENT_SCREEN_LOADED:

{

// sensor_refresh();

ui_animation(guider_ui.screen_1_img_humi, 800, 0, lv_obj_get_y(guider_ui.screen_1_img_humi), 28, &lv_anim_path_overshoot, 0, 0, 0, 0, (lv_anim_exec_xcb_t)lv_obj_set_y, NULL, NULL, NULL);

ui_animation(guider_ui.screen_1_label_humi, 800, 0, lv_obj_get_y(guider_ui.screen_1_label_humi), 162, &lv_anim_path_overshoot, 0, 0, 0, 0, (lv_anim_exec_xcb_t)lv_obj_set_y, NULL, NULL, NULL);

break;

}

case LV_EVENT_PRESSED:

{

guider_ui.screen_1_label_humi = NULL;

lv_group_remove_obj(guider_ui.screen_1);

ui_load_scr_animation(&guider_ui, &guider_ui.screen_2, guider_ui.screen_2_del, &guider_ui.screen_1_del, setup_scr_screen_2, LV_SCR_LOAD_ANIM_MOVE_LEFT, 200, 200, true, true);

break;

}

default:

break;

}

}



void events_init_screen_1(lv_ui *ui)

{

lv_obj_add_event_cb(ui->screen_1, screen_1_event_handler, LV_EVENT_ALL, ui);



lv_group_add_obj(lv_group_get_default(), guider_ui.screen_1);

}

传感器驱动

nsht30



nsht30是温湿度传感器,它支持连续采集模式。在初始化中,通过iic指令,将其配置为联系采集。nsht30_read是通过e000指令,去读取传感器内部的数据,读取完成后,通过公式计算实际的温湿度数据。

arduino::MbedI2C i2c(p20, p21);

void nsht30_init(void)

{

i2c.begin();

i2c.beginTransmission(0x44);

i2c.write(0x20); // 发送一个字节的数据

i2c.write(0x32); // 发送一个字节的数据

i2c.endTransmission(); // 结束传输

delay(100); // 等待一段时间

}

float temperature, humi;

void nsht30_read(void)

{

uint8_t rec[6];

uint8_t cnt = 0;

i2c.begin();

i2c.beginTransmission(0x44);

i2c.write(0xe0); // 发送一个字节的数据

i2c.write(0x00); // 发送一个字节的数据

i2c.endTransmission(); // 结束传输



i2c.requestFrom(0x44, 6);



while (i2c.available())

{ // 等待数据到来

int c = i2c.read(); // 读取一个字节的数据

// Serial.println(c); // 通过串口打印出来

rec[cnt++] = c;

}



if (cnt == 6)

{

temperature = (uint16_t)(rec[0] << 8 | rec[1]);

temperature = -45 + 175 * temperature / 65535;

humi = (uint16_t)(rec[3] << 8 | rec[4]);

humi = 100 * humi / 65535;

}

}

nspas3



nspas3是输出模拟信号,属于可以直接采用arduino的库函数analogRead进行读取,并通过公式转成实际的压力数据。

(float)analogRead(A1) / 1023 + 0.00095) / 0.008095



nsm3013a



nsm3013a也是输出模拟信号,同样直接使用analogRead进行读取,通过简单的公式进行转换,这里并没有安装手册中的要求进行处理。

analogRead(A2) / 2





应用代码



首先初始化lvgl,注册一个编码器,并将编码器绑定到组中。这样实体的波动开关就能控制界面。

void lvglinit()

{



lv_init();



// // OR use this initializer (uncomment) if using a 1.14" 240x135 TFT:

tft.init(); // Init ST7789 240x135

// tft.setRotation(2);

// tft.setSPISpeed(40000000);

Serial.println(F("Initialized"));

tft.setRotation(0);



static lv_disp_draw_buf_t draw_buf;

const size_t DISP_BUF_SIZE = sizeof(lv_color_t) * (LV_HOR_RES * 40); // best operation.

// lv_color_t buf[DISP_BUF_SIZE];

static lv_color_t buf1[DISP_BUF_SIZE];

static lv_color_t buf2[DISP_BUF_SIZE];

lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_BUF_SIZE); /*Initialize the display buffer*/



/*-----------------------------------

* Register the display in LVGL

*----------------------------------*/



static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/

lv_disp_drv_init(&disp_drv); /*Basic initialization*/



/*Set up the functions to access to your display*/

/*Set the resolution of the display*/

disp_drv.hor_res = LV_HOR_RES;

disp_drv.ver_res = LV_VER_RES;

/*Used to copy the buffer's content to the display*/

disp_drv.flush_cb = my_disp_flush;

/*Set a display buffer*/

disp_drv.draw_buf = &draw_buf;

// disp_drv.full_refresh = 1;



/*Finally register the driver*/

lv_disp_drv_register(&disp_drv);



enterButton.attachClick([]()

{ ENTER_BUTTON = true; });

prevButtton.attachClick([]()

{ PREV_BUTTON = true; });

nextButton.attachClick([]()

{ NEXT_BUTTON = true; });



static lv_indev_drv_t indev_drv;

lv_indev_drv_init(&indev_drv);

indev_drv.type = LV_INDEV_TYPE_ENCODER;

indev_drv.read_cb = lv_encoder_read;

// indev_drv.read_timer = button_read_timer;

// indev_drv.user_data = lv_input_event;

static lv_indev_t *lv_encoder_indev = lv_indev_drv_register(&indev_drv);

lv_group_t *g = lv_group_create();

lv_indev_set_group(lv_encoder_indev, g);

lv_group_set_default(g);

button_read_timer = lv_timer_create([](lv_timer_t *timer)

{

enterButton.tick();

nextButton.tick();

prevButtton.tick(); },

5, NULL);

}



注册一个1秒的定时器,实时采集对应界面的数据,并改变标签值,实现数据的显示。

   lv_timer_t *timer = lv_timer_create(display_humi_and_temp, 1000, NULL);



void display_humi_and_temp(lv_timer_t *timer)

{

Serial.println("asdf");



{

Serial.println("zxv");

if (guider_ui.screen_label_temperature != NULL)

{

char buf[32];

nsht30_read();

sprintf(buf, "%2.2f", temperature);

lv_label_set_text(guider_ui.screen_label_temperature, buf);

}

}



{

Serial.println("afsghdg");

if (guider_ui.screen_1_label_humi != NULL)

{

char buf[32];

nsht30_read();

sprintf(buf, "%2.2f", humi);

lv_label_set_text(guider_ui.screen_1_label_humi, buf);

}

}



{

Serial.println("2q3");

static uint8_t count1 = 0;

if (guider_ui.screen_2_label_press != NULL)

{

char buf[32] = {0};

sprintf(buf, "%2.2fkpa", ((float)analogRead(A1) / 1023 + 0.00095) / 0.008095);

lv_label_set_text(guider_ui.screen_2_label_press, buf);

Serial.println(buf);

}

}



{



if (guider_ui.screen_3_label_1 != NULL)

{

char buf[32];

sprintf(buf, "%d", cal_angle(analogRead(A2) / 2));

lv_label_set_text(guider_ui.screen_3_label_1, buf);

}

}



// sprintf(buf, "press: %2.2fkpa", ((float)analogRead(A1) / 1023 + 0.00095) / 0.008095);

// lv_label_set_text(label_press, buf);



// sprintf(buf, "angle: %d", cal_angle(analogRead(A2) / 2));

// lv_label_set_text(label_angle, buf);

}




实物展示

上电后,屏幕就会自动的显示logo和数字,并周期刷新数值。通过十二指神探的波动开关,可以进行界面的切换,去获取其他信息。

十二指神探温度采集界面



十二指神探湿度采集界面





十二指神探压力采集界面







十二指神探磁力角度显示界面






传感器pcb板






整体连接






心得体会

这次活动所使用的传感器资料很全面,数据输出方式也是常见的类型,所以在数据采集上,不存在什么大问题。不过其中温湿度传感器的封装有点过于小了,导致手动焊接困难,如果想用这款传感器还是推荐smt。

本次活动,pcb设计要求使用kicad,这个软件和立创cad一样是免费的,但是操作逻辑上却有着很大不同,使用上会明显感觉有些不便,但是通过本次活动,也算是初步使用和了解了一下这个绘图软件,拓展了技能树。

主控我使用的是十二指神探,它的芯片是rp2040。这款单片机和常见的stm32这类单片机有着很大不同,它是双核的,不方便直接使用keil开发。这对于习惯stm32的开发流程的我来说,有一定的挑战。它可以使用c和python编程,由于实在对python不是很了解,最后我选择了使用c/c++ 语言。基于语言又确定了arduino框架,该框架帮助我迅速的完成了基础驱动的适配,使得我能更注重应用上的实现。


KiCad文件
使用说明
全屏
附件下载
程序.zip
十二指神探程序
团队介绍
个人
团队成员
lshy
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号