基于μC/OS-Ⅱ微差压变送器的设计

基于μC/OS-Ⅱ微差压变送器的设计

摘 要

随着工业的发展,越来越多的场合需要对液位、蒸汽、气体等微小压力的变化进行测量,因此有必要设计一款适应以上场合的微差压变送器。本文设计的检测装置主要用于检测环境的微差压,可进一步判断气体流动方向以及测量管道气体流量,然后将测量值转变成4~20mA电流信号输出。本装置可应用于医疗事业,例如保护性隔离、阻断空气中病菌传播、呼吸机压力检测等。
该系统以STM32作为中央处理器,采用SM5651作为压力传感器采集压力数据,采集的模拟量经过运放处理、AD采样、数字滤波后,通过RS485通信模块与PC机通信,将数据实时传输至PC端。支持标准的Modbus通信协议,可通过操作PC端进行Modbus通信地址的修改。同时本系统设计了人机交互界面,通过液晶屏可以实时显示数据,可以通过按键调整压力上限和下限,从而实现低压报警和高压报警功能;能够实时绘制压力随时间变化曲线,实时监控压力变化情况,方便工作人员对数据的观察与分析。
该检测装置创新之处在于采用μC/OS-Ⅱ操作系统,提高系统的实时性,设计了人性化的人机交互界面,可用于数据监控、曲线绘制等;设计了恒流源供电电路为SM5651芯片供电,同时为STM32设计了备用电源以防止断电时后备寄存器数据丢失以及RTC时钟错乱。本变送器具有体积小、适应性强、更加人性化等特点。
关键词:微差压变送器;Modbus通信协议;μC/OS-Ⅱ操作系统;人机交互

1 系统的总体方案

本检测装置采用μC/OS-Ⅱ操作系统,通过SM5651传感器对压力数据进行采集,主控单元对数据进行处理分析,通过D/A以及电压-电流转换模块输出标准的4~20mA电流。通过RS485通信模块将数据传输至PC端,支持标准的Modbus通信协议,可通过PC端修改从机Modbus通信地址。通过人机交互模块实时显示传感器采集的数据以及Modbus通信地址,用于实时监控数据的变化,绘制数据曲线、报警提示。系统的总体框图如图1所示。
在这里插入图片描述

图1 系统总体设计框图

2 功能需求分析

(1)环境微差压数据的采集、滤波以及数据显示;
(2)压力数据曲线实时绘制,并支持数据显示与图像显示一键切换功能;
(3)设定报警阈值,即超过设定的上限和下限值会触发报警机制,在显示屏上显示“高压报警”“低压报警”“正常运行”等信息,支持声光报警和消音报警一键切换功能;
(4)支持标准的modbus通信协议,可将数据实时传输至PC端,并可以利用modbus调试精灵修改从机设备的通信地址;
(5)编写时钟显示函数,可实时显示系统当前时间。

3 视频展示

基于μCOS-II微差压变送器的设计

4 程序实现

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"	 
#include "adc.h"
#include "beep.h" 
#include "exti.h" 
#include "rtc.h" 
#include "timer.h"
#include "modbus_uart.h"
#include "modbus.h"
#include "dac.h"
#include "includes.h"
#include "stdbool.h"

/*0:压力值;
1:压力上限;
2:压力下限;
3:运行状态(1:正常,2:低压;3:高压);
4:时;
5:分;
6:秒;
7:本机ID*/
u16 Reg[30];

u16 set_up_limiting=1950;							//压力上限
u16 set_down_limiting=100;                          //压力下限
u16 warn;
bool showimagestatus=false;                         //用于存放数据_图形界面切换标志
u16 P[54];                                          //用于存放压力历史数据
u16 tim_hour[40],tim_minute[40],tim_second[40];     //用于存放历史时间


//函数声明
void reg_send(void);
int warning(u8 x);
void cleartingle(u16 ms_time);
void limiting_setting(void);

//开始任务
#define  Start_TASK_PRIO		8               	 					//任务优先级
#define  Start_STK_SIZE 		128                                     //任务堆栈大小	
OS_STK   Start_TASK_STK[Start_STK_SIZE];                                //任务堆栈	
void     Start_task(void *p_arg);                                       //任务函数声明

//界面显示任务
#define Display_TASK_PRIO		4										//任务优先级
#define Display_STK_SIZE 		128										//任务堆栈大小	
OS_STK  Display_TASK_STK[Display_STK_SIZE];				        	    //任务堆栈	
void    Display_task(void *p_arg);										//任务函数声明

//通信任务
#define Communication_TASK_PRIO		5                                   //任务优先级
#define Communication_STK_SIZE 		128                             	//任务堆栈大小	
OS_STK  Communication_TASK_STK[Communication_STK_SIZE];                 //任务堆栈	
void    Communication_task(void *p_arg);                                //任务函数声明

//报警任务
#define warning_TASK_PRIO		6               	                    //任务优先级
#define warning_STK_SIZE 		128                                     //任务堆栈大小	
OS_TCB  warning_TaskTCB;                                                //任务控制块
OS_STK  warning_TASK_STK[warning_STK_SIZE];                             //任务堆栈	
void    warning_task(void *p_arg);                                      //任务函数声明

//阈值修改任务
#define Limiting_change_TASK_PRIO		7               				//任务优先级
#define Limiting_change_STK_SIZE 		128               				//任务堆栈大小	
OS_STK  Limiting_change_TASK_STK[Limiting_change_STK_SIZE];             //任务堆栈	
void    Limiting_change_task(void *p_arg);                              //任务函数声明


int main(void)
{
	OS_CPU_SR cpu_sr=0;
	
	delay_init();	    	 									//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);             //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	                                        //串口初始化为115200
 	LED_Init();			                                        //LED端口初始化
	LCD_Init();			                                    	//LCD初始化
 	Adc_Init();		  	                             	        //ADC初始化
	Dac1_Init();				                                //DAC初始化
	KEY_Init();                                     			//按键初始化
	BEEP_Init();                                   			    //蜂鸣器初始化
	EXTIX_Init();                                 			    //中断初始化
	RTC_Init();	  	                             			    //RTC初始化
	Timer2_Init();                                  		    //定时器初始化
	Modbus_Init();                                 			    //modbus初始化
	LED0=1;
	LED1=1;
	LED2=1;
	BEEP=0;
	modbus.myadd=1;                                             //modus从机地址初始化为1
	
	OSInit();  													//UCOS初始化
	OS_ENTER_CRITICAL();  										//进入临界区(关闭中断)
	
	//创建开始任务
	OSTaskCreate(
					Start_task,                                    //任务函数名
					(void*)0,                                      //参数值地址
					OS_STK*)&Start_TASK_STK[Start_STK_SIZE-1],     //任务堆栈大小
					Start_TASK_PRIO                                //任务优先级
				); 
	
	OS_EXIT_CRITICAL();                                            //退出临界区(开中断)
	OSStart();                                                     //开始任务
}

//开始任务
void Start_task(void *pdata)
{
	OS_CPU_SR cpu_sr=0;                                            //临界区使用
	OSStatInit();                                                  //开启任务
	OS_ENTER_CRITICAL();                                           //进入临界区(关闭中断)
	
	//压力采集任务
	OSTaskCreate(
					Display_task,                                                           //任务函数名
					(void*)0,                                                               //参数值地址
					(OS_STK*)&Display_TASK_STK[Display_STK_SIZE-1],                         //任务堆栈大小
					Display_TASK_PRIO                                                       //任务优先级
				);
								
	//通信任务						
	OSTaskCreate(
					Communication_task,                                                     //任务函数名
					(void*)0,                                                               //参数值地址
					(OS_STK*)&Communication_TASK_STK[Communication_STK_SIZE-1],             //任务堆栈大小
					Communication_TASK_PRIO                                                 //任务优先级
				);
								
	//报警任务
	OSTaskCreate(
					warning_task,														   //任务函数名
					(void*)0,															   //参数值地址
					(OS_STK*)&warning_TASK_STK[warning_STK_SIZE-1],                        //任务堆栈大小
					warning_TASK_PRIO													   //任务优先级
				);
	//阈值修改任务
	OSTaskCreate(
				Limiting_change_task,													   //任务函数名
				(void*)0,																   //参数值地址
				(OS_STK*)&Limiting_change_TASK_STK[Limiting_change_STK_SIZE-1],            //任务堆栈大小
				Limiting_change_TASK_PRIO                                                  //任务优先级
							);
	
	OSTaskSuspend(Start_TASK_PRIO);                                                        //挂起开始任务
	OS_EXIT_CRITICAL();                                                                    //退出临界区(开中断)
}
 
//压力采集任务函数
void Display_task(void *p_arg)
{
	u8 b;
	u8 t=0;
	u8 n=0;
	u16 adcx,dacx;
	u16 pressure,temp;
	u16 electric;
	u16 j=0,tim_counter=0;
	p_arg = p_arg;
	
	while(1)
	{
		if(KEY2)
		{
			cleartingle(10);
			 if(KEY2)
			 {
					while(KEY2);//有此语句只能单次按,无此语句可以按一下连续增加	
					showimagestatus=!showimagestatus;
			 }
		}
		
		if(!showimagestatus)
		{ 
			OSTaskResume(7);
			//标题,时间显示
			LCD_Clear(WHITE);
			BACK_COLOR=WHITE;			
			POINT_COLOR=RED;//设置字体为红色 
			LCD_ShowString(30,50,200,16,16,"Differential pressure"); 	
			LCD_ShowString(65,70,200,16,16,"transmitter");
			LCD_ShowString(30,110,200,16,16,"    -  -  ");	   
			LCD_ShowString(130,110,200,16,16,"  :  :  ");	
			
			//显示提示信息
			POINT_COLOR=BLUE;//设置字体为蓝色
			LCD_ShowString(30,130,200,16,16,"digit:");	      
			LCD_ShowString(30,150,200,16,16,"Output:          .  mA");
			LCD_ShowString(30,170,200,16,16,"pressure:           Pa");
			LCD_ShowString(30,190,200,16,16,"Up_limiting:        Pa");
			LCD_ShowString(30,210,200,16,16,"down_limiting:      Pa");
			LCD_ShowString(30,230,200,16,16,"ID:");	
			
			if(t!=calendar.sec)
			{
				POINT_COLOR=BLACK;
				t=calendar.sec;
				LCD_ShowNum(30,110,calendar.w_year,4,16);									  
				LCD_ShowNum(70,110,calendar.w_month,2,16);									  
				LCD_ShowNum(94,110,calendar.w_date,2,16);	 
				LCD_ShowNum(130,110,calendar.hour,2,16);									  
				LCD_ShowNum(154,110,calendar.min,2,16);									  
				LCD_ShowNum(178,110,calendar.sec,2,16);
			}
			
			POINT_COLOR=BLACK;                                    //设置字体为黑色
			LCD_ShowxNum(156,190,set_up_limiting,4,16,0);         //显示上限数值
			LCD_ShowxNum(154,210,set_down_limiting,4,16,0);       //显示下限数值
			LCD_ShowxNum(154,230,modbus.myadd,4,16,0);            //显示ID号
			adcx=Get_Adc_Average(ADC_Channel_1,10);               //获取ADC数字量
			pressure=adcx;
			DAC_SetChannel1Data(DAC_Align_12b_R,adcx);
			dacx=DAC_GetDataOutputValue(DAC_Channel_1);
			POINT_COLOR=BLACK;                                    //设置字体为黑色
			LCD_ShowxNum(156,130,dacx,4,16,0);                    //显示ADC/DAC的值
			temp=(float)pressure*(2000.0/4095);
			pressure=temp;
			warn=temp;
			LCD_ShowxNum(156,170,pressure,4,16,0);                //显示差压值
			Reg[0]=pressure;
			temp=(float)(temp*16.0/2000+4);
			electric=temp;
			LCD_ShowxNum(150,150,electric,2,16,0);                //显示电流值
			temp-=electric;
			temp*=1000;
			LCD_ShowxNum(170,150,temp,2,16,0x80);
			P[j]=pressure;
			j++;
			if(j==54) j=0;
			tim_hour[tim_counter]=calendar.hour;
			tim_minute[tim_counter]=calendar.min;
			tim_second[tim_counter]=calendar.sec;	
			tim_counter++;
			if(tim_counter==40) tim_counter=0;
			cleartingle(500);	
		}
		
		if(showimagestatus)
		{
			OSTaskSuspend (7);
			LCD_Clear(LIGHTBLUE);					
			POINT_COLOR=BLUE;
			BACK_COLOR=LIGHTBLUE;
					
			LCD_ShowString(80,20,200,16,24,"Image");
			LCD_ShowString(20,268,200,16,12,"0");
			LCD_ShowString(10,66,200,16,12,"KPa");
								
			LCD_DrawLine(30,280,240,280);           //绘制横坐标
			LCD_DrawLine(240,280,232,277);          //箭头
			LCD_DrawLine(240,280,232,283);		 
			LCD_ShowString(25,285,220,12,12,"00:00:00 00:00:00 00:00:00 00:00:00");
			
			LCD_DrawLine(70,280,70,277);            //横坐标四个点
			LCD_DrawLine(110,280,110,277);
			LCD_DrawLine(150,280,150,277);
			LCD_DrawLine(190,280,190,277);	
									 
			LCD_DrawLine(30,280,30,80);             //绘制纵坐标
			LCD_DrawLine(30,80,27,88);              //箭头
			LCD_DrawLine(30,80,33,88);
			LCD_DrawLine(30,230,33,230);            //0.5
			LCD_ShowString(5,224,200,16,12,"0.5");	 
			LCD_DrawLine(30,180,33,180);            //1.0	 
			LCD_ShowString(5,174,200,16,12,"1.0");	
			LCD_DrawLine(30,130,33,130);            //1.5
			LCD_ShowString(5,124,200,16,12,"1.5");
			LCD_ShowString(5,80,200,16,12,"2.0"); 
			LCD_ShowString(205,60,200,16,12,"Pa"); 
		
			//横坐标显示
     	    LCD_ShowxNum(25,285,tim_hour[9],2,12,0x80);
			LCD_ShowxNum(43,285,tim_minute[9],2,12,0x80);
			LCD_ShowxNum(61,285,tim_second[9],2,12,0x80);
						
			LCD_ShowxNum(79,285,tim_hour[19],2,12,0x80);
			LCD_ShowxNum(97,285,tim_minute[19],2,12,0x80);
			LCD_ShowxNum(115,285,tim_second[19],2,12,0x80);
						
			LCD_ShowxNum(133,285,tim_hour[29],2,12,0x80);
			LCD_ShowxNum(151,285,tim_minute[29],2,12,0x80);
			LCD_ShowxNum(169,285,tim_second[29],2,12,0x80);
						
			LCD_ShowxNum(187,285,tim_hour[39],2,12,0x80);
			LCD_ShowxNum(205,285,tim_minute[39],2,12,0x80);
			LCD_ShowxNum(223,285,tim_second[39],2,12,0x80);
						
			adcx=Get_Adc_Average(ADC_Channel_1,10);//获取ADC数字量
			pressure=adcx;
			DAC_SetChannel1Data(DAC_Align_12b_R,adcx);
			temp=(float)pressure*(2000.0/4095);
			pressure=temp;
			warn=temp;
			POINT_COLOR=RED;
			LCD_ShowxNum(175,60,pressure,4,12,0);//显示差压值
			LCD_DrawRectangle(170,55,220,72);
								
			for(n=0;n<54;n++)//绘制曲线
			{
				LCD_DrawLine(4*n+30,-0.1*P[n]+280,4*(n+1)+30,-0.1*P[n+1]+280);
			}
			for(b=1;b<54;b++)
			{
				P[53]=pressure;
				P[b-1]=P[b];
			}
			for(b=1;b<40;b++)
			{
				tim_hour[39]=calendar.hour;
				tim_hour[b-1]=tim_hour[b];
							
				tim_minute[39]=calendar.min;
				tim_minute[b-1]=tim_minute[b];
							
				tim_second[39]=calendar.sec; 
				tim_second[b-1]=tim_second[b];
				
			}
			cleartingle(500);	
			
		}
		OSTimeDlyHMSM(0,0,0,500); //延时500ms
	}	
}

//通信任务函数
void Communication_task(void *p_arg)
{
	p_arg = p_arg;
	
	while(1)
	{
		Mosbus_Event();           //处理MODbus数据
	  reg_send();               //数据收发
		OSTimeDlyHMSM(0,0,0,500); //延时500ms
	}
}
//报警任务
void warning_task(void *p_arg)
{
	
	bool waring_status=true;
	p_arg = p_arg;
	while(1)
	{
		if(KEY0)
		{
			cleartingle(5);//延时去抖
			if(KEY0)	
			{
				while(KEY0);
				waring_status=!waring_status;
			}
		}
		if(waring_status)
		{
			Reg[3]=warning(1);//声光警报
		}
		else
		{
			Reg[3]=warning(0);//消音报警
		}
		OSTimeDlyHMSM(0,0,0,500); //延时500ms
	}
}

//阈值修改任务
void  Limiting_change_task(void *p_arg)
{
	p_arg = p_arg;
	
	while(1)
	{
		limiting_setting();
		OSTimeDlyHMSM(0,0,0,500); //延时500ms
	}
}

void cleartingle(u16 ms_time)
{    
    u16 i=0;      
    while(ms_time--)
    { 
        i=12000;       // 以ms为单位
        while(i--) ;    
    }
}

void reg_send(void)
{
	if(modbus.rcbuf[1]==6&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==1)) set_up_limiting=Reg[1];    //写压力上限
	if(modbus.rcbuf[1]==3&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==1)) Reg[1]= set_up_limiting;   //读压力上限
	
	if(modbus.rcbuf[1]==6&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==2)) set_down_limiting=Reg[2];  //写压力下限
	if(modbus.rcbuf[1]==3&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==2)) Reg[2]= set_down_limiting; //读压力下限
	
	if(modbus.rcbuf[1]==6&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==7)) modbus.myadd=Reg[7];       //写ID
	if(modbus.rcbuf[1]==3&&(modbus.rcbuf[2]*256+modbus.rcbuf[3]==7)) Reg[7]= modbus.myadd;      //读ID
}

//x=1声光报警;x=0消音报警
//返回3:高压报警
//返回2:低压报警
//返回1:正常运行
//
int warning(u8 x)
{
	if(warn>set_up_limiting)
	{
		LED0=0;//红亮
		LED1=1;//绿灭
		LED2=1;//黄灭
		BEEP=x;
		LCD_Fill(0,296,240,320,RED);
		POINT_COLOR=BLUE;//设置字体为蓝色
		LCD_ShowString(60,300,200,16,16,"Warning:HIGH");
		return 3;		
	}
	
	if(warn<100)
	{
		LED2=0;//黄亮
		LED0=1;//红灭
		LED1=1;//绿灭
		BEEP=x;
		LCD_Fill(0,296,240,320,RED);
		POINT_COLOR=BLUE;//设置字体为蓝色
		LCD_ShowString(60,300,200,16,16,"Warning:LOW");
		return 2;	
	}
	
	if(warn>100&&warn<set_up_limiting)
	{
		LED1=0;//绿亮
		LED0=1;//红灭
		LED2=1;//黄亮
		BEEP=0;
		LCD_Fill(0,296,240,320,GREEN);
		POINT_COLOR=BLUE;//设置字体为蓝色
		LCD_ShowString(60,300,200,16,16,"Runing...");
		return 1;
	}
	return 0;
}

//上下限设置函数
void limiting_setting(void)
{
	//上限加
	if(KEY1)
	{
		cleartingle(10);//延时去抖
		if(KEY1)			
		{
			//while(KEY1);//有此语句只能单次按,无此语句可以按一下连续增加
			set_up_limiting+=10;	
			LCD_ShowxNum(156,190,set_up_limiting,4,16,0);
		}
	}
	
	//上限减
	if(KEY3)
	{
		cleartingle(10);//延时去抖
		if(KEY3)					
		{
			//while(!KEY3);//有此语句只能单次按,无此语句可以按一下连续增加
			set_up_limiting-=10;	
			LCD_ShowxNum(156,190,set_up_limiting,4,16,0);
		}
	}			
}

如需全部资料可加qq798860621

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值