压力检测采用MS5803采集压力的变化,采用I2C通讯。湿度检测使用SHT15实现,采用类式I2C通讯方式,通过GPIO模拟。
3、软件设计
软件的设计采用IAR EWARMV7.8和ST标准库V3.5实现。具体的软件设计包括上位远传通讯、模拟量输入输出控制、逻辑控制、热电阻温度采集、温湿度和压力数据采集以及加热和温度控制。
为了让软件更好地适应更换传感器和应用不同场合的功能增减要求,我们在设计软件时使用了一个配置文件来配置更能的使用和增减。这个配置文件就是一个头文件,定义了一些宏来控制条件编译,节选部分配置文件如下:
/*定义以太网通讯功能的使能,1:启用;0:禁用*/
#ifndef Ethernet_ENABLE
#define Ethernet_ENABLE (1)
#endif
/*定义串口上位通讯使能,1:启用;0:禁用*/
#ifndef UPPER_SERIAL_ENABLE
#define UPPER_SERIAL_ENABLE (0)
#endif
/*定义片上Flash存取使能,1:启用;0:禁用*/
#ifndef STORAGE_ENABLE
#define STORAGE_ENABLE (0)
#endif
/*定义模拟量输入是否启用,1:启用;0:禁用*/
#ifndef ANALOG_INPUT_ENABLE
#define ANALOG_INPUT_ENABLE (1)
#endif
/*定义模拟量输出是否启用,1:启用;0:禁用*/
#ifndef ANALOG_OUTPUT_ENABLE
#define ANALOG_OUTPUT_ENABLE (1)
#endif
/*定义数字量操作是否启用,1:启用;0:禁用*/
#ifndef DIGITAL_ENABLE
#define DIGITAL_ENABLE (1)
#endif
/*定义温湿度计是否启用,1:启用;0:禁用*/
#ifndef HYGRO_THERMO_ENABLE
#define HYGRO_THERMO_ENABLE (1)
#endif
/*压力变送器是否启用,1:启用;0:禁用*/
#ifndef PRESS_TRANS_ENABLE
#define PRESS_TRANS_ENABLE (1)
#endif
/*热电阻采集电路是否启用,1:启用;0:禁用*/
#ifndef RTD_COLLECT_ENABLE
#define RTD_COLLECT_ENABLE (0)
#endif
/*备用串行设备是否启用,1:启用;0:禁用*/
#ifndef SERIAL_SPARE_ENABLE
#define SERIAL_SPARE_ENABLE (0)
#endif
/*温控器是否启用,1:启用;0:禁用*/
#ifndef THERMOSTAT_ENABLE
#define THERMOSTAT_ENABL (0)
#endif
/*定义串口上位通讯方式的选择,1:RS232;0:RS485*/
#ifndef UPUSART_COMM_TYPE
#define UPUSART_COMM_TYPE (0)
#endif
/*定义压力变送器MS4515DO的通讯方式的选择,1:SPI;0:I2C*/
#ifndef MS4515DO_COMM_TYPE
#define MS4515DO_COMM_TYPE (0)
#endif
/*定义启用的压力变送器类型,1:MS5837;0:MS5803*/
#ifndef PRESSURE_TRANSMITTER_TYPE
#define PRESSURE_TRANSMITTER_TYPE (0)
#endif
/*定义MS5803(MS5837)采集值是否启用滤波功能,1:启用;0:禁用*/
#ifndef MS5803_FILTER_ENABLE
#define MS5803_FILTER_ENABLE (1)
#endif
上位远传通讯包括有以太网通讯和串口通讯,以太网通讯用于连接远程计算机,出口通讯用于连接触摸屏。均使用Modbus协议,支持03、06、16等功能码。
//解析接收到的数据
uint16_t ReceivedDataParsing(uint8_t*rxBuffer,uint8_t *txBuffer)
{
uint16_tlength=0;
uint8_tfuctionCode=rxBuffer[7];
switch(fuctionCode)
{
caseREAD_HOLDING_REGISTERS:
{
uint16_t startAddress=rxBuffer[8];
startAddress=(startAddress<<8)+(uint16_t)rxBuffer[9];
uint16_t RegisterNumber=rxBuffer[10];
RegisterNumber=(RegisterNumber<<8)+(uint16_t)rxBuffer[11];
//RegisterNumber=0;
//读取对应寄存器数值
uint8_t returnData[REGISTERAMOUNT*2+2];
GetRegisterValue(startAddress,RegisterNumber,returnData);
uint16_t byteCount=0;
txBuffer[byteCount++]=rxBuffer[0];
txBuffer[byteCount++]=rxBuffer[1];
txBuffer[byteCount++]=rxBuffer[2];
txBuffer[byteCount++]=rxBuffer[3];
uint16_t byteAmount=RegisterNumber*2+3;
txBuffer[byteCount++]=(byteAmount>>8);
txBuffer[byteCount++]=byteAmount;
txBuffer[byteCount++]=rxBuffer[6];
for(int i=0;i
{
txBuffer[byteCount++]=returnData;
}
length=byteCount;
break;
}
caseWRITE_SINGLE_REGISTER:
{
//txBuffer=rxBuffer;//将指针赋值,不能改变数组的值
uint16_t ByteAmount=rxBuffer[4];
ByteAmount=(ByteAmount<<8)+(uint16_t)rxBuffer[5];
length=ByteAmount+6;
uint16_tobjectRegister=0;
objectRegister=rxBuffer[8];
objectRegister=(objectRegister<<8)+(uint16_t)rxBuffer[9];
uint16_t setValue=rxBuffer[10];
setValue=(setValue<<8)+(uint16_t)rxBuffer[11];
//将设定值写到对应的寄存器
SetRegisterValue(objectRegister,setValue);
for(int i=0;i
{
txBuffer=rxBuffer;
}
break;
}
caseWRITE_MULTI_REGISTER:
{
length=12;
SetMultiRegisterValue(rxBuffer+8);
for(int i=0;i
{
txBuffer=rxBuffer;
}
txBuffer[5]=0x06;
break;
}
default:
{
break;
}
}
returnlength;
}
模拟量输入输出控制的控制较为简单,直接使用DMA操作方式,实现简单方便,不用详述。数字量的控制方式以及温度的SPI操作与前面文章中的一致。
温湿度采集使用的SHT15模块,它的通讯协议类是于I2C方式,但是用STM32自带的I2C来操作却不方便,所以使用GPIO来实现通讯。为了方便在不同的系统使用所以对时钟和数据引脚的操作采用的回掉函数来实现。
先便写SHT15(实际上SHT1X均可用)的操作函数,并在此时用弱化的操作函数:
/*读取DATA引脚位,弱化的函数,无操作,必须在应用中实现该函数,并自动调用*/
__weak uint8_t ReadDataPinBit(void)
{
return 0;
}
/*将DATA线设置为输入输出方向模式*/
__weak void SetDataPineDirection(IODirectiondirection)
{
}
事实上是,什么也不做,然后在最终的应用中来实现这两个函数,以实现相应的操作,对于不同的系统只需在应用中实现不同操作。
/*读取DATA引脚位*/
uint8_t ReadDataPinBit(void)
{
returnGPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
}
/*将DATA线设置为输入输出方向模式*/
void SetDataPineDirection(IODirection direction)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
if(direction)
{
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
}
else
{
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
}
GPIO_Init( GPIOB, &GPIO_InitStructure);
}
同时使用函数执政数组的方式来简化SCK和DATA引脚的操作函数的编写:
/*定义操作GPIO管脚的函数指针*/
void (*OperationSHT1xIO[])(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)={GPIO_ResetBits,GPIO_SetBits};
/*定义SHT1X总线引脚操作函数指针*/
BusPinOperationSetBusPin[]={OperationSckPin,OperationDataPin};
/*操作SCK引脚,设置高低操作*/
void OperationSckPin(BusPinValue value)
{
OperationSHT1xIO[value](GPIOB,GPIO_Pin_6);
}
/*操作DATA引脚,设置高低操作*/
void OperationDataPin(BusPinValue value)
{
OperationSHT1xIO[value](GPIOB,GPIO_Pin_7);
}
同时定义几个枚举类型:
typedef enum{
Set=1,
Reset=(!Set)
}BusPinValue;
typedef enum{
Out=1,
In=(!Out)
}IODirection;
typedef enum{
SckPin=0,
DataPin=1
}SHT1XPin;
这样要操作那个引脚就非常方便了:
/*将data线设置为输出模式*/
SetDataPineDirection(Out);
/*将DATA引脚置位*/
SetBusPin[DataPin](Set);
压力数据采集的采用I2C总线,对STM32的I2C通讯网上文章很多此处不再重复,需要提一下的是整个操作也是适用函数指针来实现毁掉的方式来简化编程过程。另外STM32的I2C好像比较容易出现死锁的情况,不知为何,有待解惑。
加热和温度控制采用RS485通讯,使用宇电的AI-BUS协议,这个比较简单,不再多说。
4、结果验证
经过差不多2周的调试,现在设备已经成型,效果还是比较理想的,首先来一张调试图片:
这是现实的DAC输出电压:
这是数据远传以Modscan读取数据的结果:
来一张组装后的图片:
最后来一组测试是的上位操作及显示画面:
最后说一下这次的一点体会,以往程序调试没问题就算好了,由于这个产品对时间非常敏感,有些测试过程必须在很短的时间(以毫秒为单位)完成。所以这次我再软解的整体结构上下了一些功夫,让速度提升了好几倍,所以我觉得规划好程序结构也是一很重要的方面。
`