本次将NUCLEO-F412ZG应用于我们的多组分气体分析仪的实现试验,从整体上测试实际项目的应用情况。
一、项目概述
多组分气体分析仪是我公司近期研发的三个主要产品之一。采用模块化设计,可增减配置,可分析混合气体中的氧气、氢气、甲烷、丙烷等气体的组分。氧气可以采用顺磁氧传感器、电化学氧传感器两种;氢气传感器可采用热式氢气传感器;炭氢传感器采用远红外气体传感,可探测甲烷和丙烷。气体组分的分析要求能够快速反应,运行稳定,对硬件的要求比较高。
二、硬件设计
在硬件方面充分考虑系统应用的需求,AD采集、DA输出、DI输入、DO输出以及通讯等。
通讯部分主要包括串口通讯与以太网通讯。以太网的通讯采用W5500以太网控制器与MCU通过SPI总线通讯,在本项目中使用SPI2来实现通讯,以太网通讯部分的硬件设计如下:
串口通讯主要对象有两个,远红外炭氢传感器和显示屏。远红外炭氢传感器采用单总线串行通讯,TTL电平。两路接口,通常一用一备,在需要时也可接两台传感器,如一台甲烷一台丙烷。硬件接口的设计如下:
对于显示屏起通讯接口采用RS485或者RS232。主要是应为面向不同的应用场合,在需要大显示屏的时候选择的是RS485接口,在普通小显示屏是使用的是RS232接口。但这两个屏不会同时使用所以采用一个USART口,实际使用的USART2通讯口,是使用RS232还RS485选择焊接不同的器件。硬件部分的设计如下图所示:
模拟量输入输出采用SPI接口与AD7705和AD5663来完成,模拟量输入输出个两路。模拟量输入主要用于采集氢气组分数据和氧气组分数据。氢气传感器采用热式原理输出0-5VDC的信号。氧气传感器有两种:顺磁传感器和电化学传感器,顺磁传感器输出4-20mA电流信号,电化学传感器输出0-2.5VDC的信号,两者不同时采用,所以采用同一路AI通道,根据不同的传感器焊接不同的器件。硬件设计如下:
模拟量输出也是两路,通过SPI总线与MCU通讯。没路均能输出0-5VDC信号。这部分硬件设计比较简单:
数字量输入输出主要用与小型真空泵和电磁阀得控制以及按钮和状态信息的输入,硬件电路比较简单:
还有其他部分的硬件设计,相对比较平常就不在此讨论了。
三、软件调试
软件的开发环境采用IAR EWARM和STM32CubeMX,在STM32CubeMX完成基本配置并生成项目,再在IAR EWARM开发应用并调试。基本的开发调试就不讨论了,主要说一说数字量输入输出,模拟量输入输出、串口通讯以及以太网通讯几个方面的应用开发和调试。
为了让软件更好地适应更换传感器和应用不同场合的功能增减要求,我们在设计软件时使用了一个配置文件来配置更能的使用和增减。这个配置文件就是一个头文件,定义了一些宏来控制条件编译,节选部分配置文件如下:
/*多组分气体分析仪应用版本定义:
——0,用于标准的多组分气体分析仪,版本号VA1.0.1;
——1,用于标准的多组分气体分析仪,版本号VA1.0.1;
——2,用于标准的多组分气体分析仪,版本号VA1.0.1;
——3,用于便携式气体分析仪,版本号V1.0.1B;
——4,B版应用板,版本号V1.0.1B;*/
#ifndef MFC_MasterBoard_VERSION
#define MFC_MasterBoard_VERSION (0)
#endif
/*定义扩展功能的使能,一次只能使能一项*/
//#define EXT_Ethernet_ENABLE //扩展以太网通讯
//#define EXT_CAN_ENABLE //扩展CAN总线通讯
/*定义片上Flash存取使能*/
#ifndef STORAGE_ENABLE
#define STORAGE_ENABLE (1)
#endif
/*全局变量定义*/
#include "globalvariable.h"
/*显示屏控制*/
#include "lcdcommunication.h"
/*数字逻辑处理*/
#include "logicprocess.h"
/*模拟量输入输出处理*/
#include "addaprocess.h"
/*以太网通讯处理*/
#include "ethernetprocess.h"
/*红外炭氢传感器*/
#include "ndirdataprocess.h"
/*片上Flash参数存取操作*/
#if STORAGE_ENABLE > (1)
#include "storeprocess.h"
#endif
/*调试功能*/
#include "CommonConfig.h"
关于模拟量输入输出、串口通讯以及以太网通讯几个方面软件的设计及调试在前面的文章中都已经较少的比较清楚了,只有数字量输入输出没有涉及到,在总结这一篇文章中我们在软件的设计和调试上主要说说数字量的处理。在多组分气体成分分析仪中总共有5个DI输入和5个DO输出。这10个引脚的GPIO配置如下:
/*Configure GPIO pins : DI1_Pin DI2_Pin DI3_Pin DI4_Pin
DI5_Pin */
GPIO_InitStruct.Pin = DI1_Pin|DI2_Pin|DI3_Pin|DI4_Pin
|DI5_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : DO1_Pin DO2_Pin DO3_Pin DO4_Pin
DO5_Pin */
GPIO_InitStruct.Pin = DO1_Pin|DO2_Pin|DO3_Pin|DO4_Pin
|DO5_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, DO1_Pin|DO2_Pin|DO3_Pin|DO4_Pin
|DO5_Pin, GPIO_PIN_RESET);
对于DI、DO的操作我们采用定义操作函数来实现对单个通道和全部通道的操作。我们首先定义两个枚举类型分别定义如下:
//定义数字量输出通道枚举类型,规定通道的范围
typedef enum {
DOChannel1,
DOChannel2,
DOChannel3,
DOChannel4,
DOChannel5,
DOChannelNum
} DigitalOutput;
//定义数字量输入通道枚举类型,规定通道的范围
typedef enum {
DIChannel1,
DIChannel2,
DIChannel3,
DIChannel4,
DIChannel5,
DIChannelNum
} DigitalInput;
数字量输入输出的枚举主要是为了方便操作和识别,通道数量出现变化时只需要增加枚举两种的通道定义即可。此处数字量输入输出均定义了5个通道。枚举量的最后一个成员代表了通道的数量,在枚举全部通道时能够很好的避免超出范围的错误。
同时还要定义如下的结构体,用于定义需要操作GPIO目标。
//定义用于针脚操作的目标针脚类型
typedef struct{
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
}TargetPin;
有了上述的定义则可以实现前面设想的操作了,接下来我们还需要定义两个数字量输入输出通道的TargetPin类型的数组,用于存放想要操作的目标通道,和前面枚举两种定义的通道一致,此处也是5个通道。
//定义DI通道的全部目标针脚数组
TargetPin diPin[]={{GPIOE,GPIO_Pin_2},{GPIOE,GPIO_Pin_3},{GPIOE,GPIO_Pin_4}
,{GPIOE,GPIO_Pin_5},{GPIOE,GPIO_Pin_6}};
//定义DO通道的全部目标针脚数组
TargetPin doPin[]={{GPIOD,GPIO_Pin_3},{GPIOD,GPIO_Pin_4},{GPIOD,GPIO_Pin_5}
,{GPIOD,GPIO_Pin_6},{GPIOD,GPIO_Pin_7}};
有了以上2个数组就可以在避免在操作过程中大量使用条件分支语句(Switch或if语句),简化编码和避免在增加通道时号要修改函数的情况。现在如果通道数量出现变化则只需要修改枚举量和数组的值就可。或者操作的管脚出现变化则只需要修改数组的值就可以了。而不需要去修改函数体,而且函数体的编码也非常简单。
对数字量输出的操作如下,在操作全部通道时,以枚举变量作为循环变量,以枚举的最后定义的数量来控制,并以枚举量的取值作为数组下标,有效避免出现超出范围的错误,同时在通道数量和通道对应的具体针脚发生变化时,无需修改函数。
//操作全部继电器DO通道
//输入参数TargetPin *doPin为要操作的DO通道列表
//输入参数BOOL *commands欲写给DO通道的值列表
void OperationAllRelayChannel(TargetPin *doPin,BOOL *commands)
{
DigitalOutput DOChannel;
for(DOChannel=DOChannel1;DOChannel<DOChannelNum;DOChannel++)
{
OperationSingleRelayChannel(doPin[DOChannel],commands[DOChannel]);
}
}
//操作单个继电器DO通道
//输入参数TargetPin doPin为要操作的DO通道
//输入参数BOOL command欲写给DO通道的值
void OperationSingleRelayChannel(TargetPin doPin,BOOL command)
{
HAL_GPIO_WritePin(doPin.GPIOx,doPin.GPIO_Pin,command);
}
对数字量输入的操作函数的编写采用与数字量输出相同的思路。对于枚举之所以可以用作数组下标,是因为枚举没被指定值时,总是从0开始向上累加,正好与数组下标是一致的。这要做还有一个好处是,通道与具体的GPIO引脚是由TargetPin数组的赋值顺序决定的,修改非常方便。
//获取全部DI量状态输入值
//输入参数TargetPin *diPin为需要读取的DI通道列表
//输入参数BOOL *result为读取的通道值返回列表
void GetAllDIStatusInput(TargetPin *diPin,BOOL *result)
{
DigitalInput DIChannel;
for(DIChannel=DIChannel1;DIChannel<DIChannelNum;DIChannel++)
{
result[DIChannel]=GetSingleDIStatusInput(diPin[DIChannel]);
}
}
//获取单个DI量状态输入值
//输入参数TargetPin diPin是需要读取的DI通道
//返回值为读取的通道值
BOOL GetSingleDIStatusInput(TargetPin diPin)
{
uint8_t readValue;
readValue = GPIO_ReadInputDataBit(diPin.GPIOx,diPin.GPIO_Pin);
return (readValue>0)?True:False;
}
最后来一张调试数字量输入输出的截图:
多组分气体成分分析仪项目软件其他部分的设计与调试也已经过验证,在此就不多说了。
四、结果验证
多组分气体分析仪的测试机已经可以运行,大部分的功能也已经完成,测试基本完成。来几张结果图片,首先来一张调试模拟量数据采集的图片:
再来一张显示屏显示各组分的的画面:
接下来再来一张显示屏显示参数设定和操作的界面:
再来一张显示网络通讯的界面:
项目验证完成了,同时对NUCLEO-F412ZG的试用体验也结束了,现在只是STM32F412ZG价格的问题了。最后再次感谢ST和电子发烧友论坛的活动,对我们的项目验证提供了最好的帮助。希望下次还能够在参加这样的活动。