基于8051单片机的温湿度采集系统设计

发布时间:2024-01-03  

通过前面的学习,我们学会了如何用串口发送数据和接收固定字节的命令。今天,我们继续讨论串口应用协议,主要焦点还是在如何有效地使用串口发送数据。为了简化描述,假设我们基于8051单片机开发一个温湿度采集系统,周期将温度和湿度数据上报到上位机。温度和湿度定义如下:


float temperature = 25.0; //温度

float humidity = 70.0; //湿度

我们至少可以设计出以下两种不同的串口应用协议:


1.固定大小的消息:先发送float类型的温度,再发送float类型的湿度。每次传输8个字节。


温度(4字节)


湿度(4字节)


代码实现:


// 固定消息发送温度和湿度函数

void send_temperature_and_humidity(float temperature, float humidity)

{

    uart_sendFloat(temperature); //发送温度

uart_sendFloat(humidity);//发送湿度

}

字符串消息,先发送温度的字符串,再发送湿度的字符串,两者之间有明显的分隔符(例如空格、分号或冒号),结尾一般以“ ”结束。


"25.0, 70.0 "


//以字符串消息发送温度和湿度函数

void send_temperature_and_humidity_string(float temperature, float humidity)

{

    printf("%f,%f

", tempeature, humidity); //以字符串消息发送温度、湿度

}

以上两种应用协议中,哪一种最好呢?


从消息长度来看,固定消息大小都是8个字节,而字符串消息则超过8个字节且长度不定(请思考为什么?)


从可读性来看,在ASCII接收模式下,字符串消息可读性高(人可直接读),固定消息“不可读”。


从效率和保密性(假设有这一条吧),固定消息胜出,因为它发送的是原始二进制数据,占用字节最少。


为了传输更多的数据和实现更复杂的功能,我们通常在固定消息的基础上引入更多的辅助数据。例如,增加校验位保证数据传输无误,增加地址字节以区分不同设备等。但对于有些上位机(LabVIEW)来说,它对字符串数据的解释可以一步到位,无需按图索骥。


AT命令常用于控制WiFi模块、SMS模块等,就是以字符串消息实现的。


练手项目:假设有一个多点温度采集系统,架构如下图所示。

1af7613a-3745-11ed-ba43-dac502259ad0.png

其中,N值取决于具体的系统要求。试基于固定长度消息设计串口应用协议,实现多点数据的上报。


太简单了,一次传完N点温度,如下:


1#温度


2#温度


......


N#温度


顺序读取1#~N#的温度,先放到缓冲里,一次调用串口发送函数将N点数据发送出去。收工!


N很小时,没有毛病。假设N=128,则要求温度缓存数组长度至少为128个,一个温度数值占4字节(float),要128*4 = 512B,这超出了可用内存。如果不同的项目,N点都不一样,那么上位机的程序必须根据N点修改串口接收程序。累死。


怎么办?我们可以每读一个DS18B20,就将温度发送到串口。重复N次就完成一次温度采集与上报。


为了区分是哪个DS18B20,我们增加一个字节数据表示设备号(1~N)。所以,串口上报协议变为:


设备号(1字节, unsigned char)


温度(4字节,float)


N最大为255。这么做的好处一个是简化了程序,也便于上位机接收和处理数据。


关键代码:


unsigned char ds18b20_no= 1;

//读取温度并发送到串口

float temperature;

temperature = ds18b20_readTermperature(ds18b20_no); //读取温度

uart_sendUchar(ds18b20_no); //发送设备号

uart_sendFloat(temperature); //发送温度

ds18b20_readTermperature()函数是读取温度函数,本例中我们先使用模拟的(而不是真正去读一个DS18B20)。实现如下:


//读取DS18B20温度(模拟)

float ds18b20_readTemperature(unsigned char no)

{

    static unsigned char tick = 0; //为了模拟得到一个变化的温度引入的变量

    float temperature;

    tick++;

    temperature = no + tick*0.1;

    return temperature;

}

我们引入模块化编程的思想,把发送功能封装到一个函数里。函数是模块化开发的必经之路。函数的引入增强了代码的可读性和复用性,也便于修改和维护程序。经过不断积累,函数库的引入可以使开发事半功倍。例如前面我们把串口封装到uart.h和uart.c,就是模块化思想的淋漓尽致的体现。使用串口,则直接添加uart.h和uart.c到工程,然后在主程序包含uart.h,直接调用定义好的串口函数就可以访问串口。


//发送温度函数,设备号(1B)温度(4B)   

void sendTemperature(unsigned char no, float temperature)

{

   uart_sendUchar(no);

   uart_sendFloat(temperature);

}

我们使用C51编程入门(二十二)串口编程入门--串口应用协议(一)的proteus仿真电路,使用LabVIEW开发上位机来接收多点温度数据并显示。

1b0a6302-3745-11ed-ba43-dac502259ad0.png

仿真电路(没有接DS18B20,,温度模拟产生)


LabVIEW上位机能够正确接收并解码数据。LabVIEW的程序框图如下:

1b2689a6-3745-11ed-ba43-dac502259ad0.png

结束语


附上本次串口源码,如下。如果你觉得本篇文章有所帮助,请点赞,请打赏。您的支持是对我们的最大鼓励。如果需要仿真电路和串口工程源码以及LabVIEW上位机源码,请在后台留言。


下一篇文章我们将完善仿真电路,增加DS18B20元件及驱动程序,并完善LabVIEW上位机(增加温度保存功能)。如果有可能,后面会开发LabVIEW串口程序的相关教程,并提供LabVIEW源码。


完整的代码:(uart.h和uart.c略,前一篇文章已经给出)


//uart_firstdemo.c

#include "uart.h"

//#include"reg51.h"

sbit beeper_en = P2^0;

sbit key_s1 = P1^0;

char msg[] = "Welcome back.

";

unsigned char uart_rx_buffer[2];

unsigned int count = 0;

//函数定义

void delayMS(unsigned int nms);

void keyScan(); //按键扫描

float ds18b20_readTemperature(unsigned char no);    //读取DS18B20温度

void sendTemperature(unsigned char no, float temperature); //发送温度函数

void main()

{

unsignedchards18b20_no=1;//设备号

    unsigned char ds18b20_N = 3; //ds18b20总数

    float temperature; //温度

    uart_init();

    while(1)

    {

        temperature = ds18b20_readTemperature(ds18b20_no); //读温度

        sendTemperature(ds18b20_no, temperature); //发送温度

        ds18b20_no++;

        if(ds18b20_no > ds18b20_N)//已经读完所有点的温度

            {

                ds18b20_no = 1;

delayMS(1000);//等待1s左右,再开始下一次采集

            }

                   

    }

}

void keyScan()

{

floattemperature;

    if(key_s1 == 0)

    {

delayMS(10);//消抖

        if(key_s1 == 0)    //按键按下,读取并上报1#地点的温度

        {

            temperature = ds18b20_readTemperature(1); //读温度

            sendTemperature(1, temperature); //发送温度            


        }

    }

}

//延时函数

void delayMS(unsigned int nms)

{

unsignedinti,j;

    for(i=0;i

        for(j=0;j<130;j++);

}

//读取DS18B20温度(模拟)

float ds18b20_readTemperature(unsigned char no)

{

    static unsigned char tick = 0;

    float temperature;

    tick++;

    temperature = no + tick*0.1;

    return temperature;

}

//发送温度函数    

void sendTemperature(unsigned char no, float temperature)

{

   uart_sendUchar(no);

   uart_sendFloat(temperature);

}


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>