基于树莓派的开关量数据采集系统设计与实现
2024-09-14白永强张正娟孙琦
摘要:基于树莓派功能强大、接口丰富的特点,利用树莓派的多路GPIO接口,在Qt开发环境中设计并实现了一种可靠性高、实用性强、使用方便的开关量数据采集系统。本文简要介绍了树莓派及其GPIO接口、访问GPIO接口使用的wiringPi库等内容,从系统硬件设计原理、接口定义、软件组成结构、数据帧结构以及实现中的关键代码模块等几个方面进行了详细的阐述。
关键词:树莓派;GPIO;wiringPi;指定源组播;开关量数据采集系统
中图分类号:TP399 文献标识码:A
文章编号:1009-3044(2024)23-0005-05
开放科学(资源服务)标识码(OSID)
0 引言
航天发射场一体化指挥决策系统(以下简称一体化指挥决策系统)是基于航天发射综合测试信息,具备信息获取、传输、处理,以及对航天发射任务态势进行分析判断、指挥决策和综合保障等功能的信息系统,是航天发射任务实施的神经中枢[1]。近年来,一体化指挥决策系统的软硬件进行了全面的自主可控改造。改造过程中,不仅要满足当前在用的航天发射任务的需求,考虑到未来航天发射任务的需求,还需要适应一些老旧型号任务的需求,确保在某个老旧型号任务启动时,系统能够正常使用。
在某型号航天发射任务中,使用Intel + Windows平台下安装在工控机上的开关量采集卡实现测试数据的采集,但目前开关量采集卡无法适配自主可控的软硬件环境。树莓派(Raspberry Pi) 是一种单板机电脑,其GPIO(General Purpose I/O Ports) 接口能够替代开关量采集卡的功能。因此,在航天发射任务的软硬件自主可控环境中,研制基于树莓派的开关量采集系统(以下简称开关量采集系统),实现开关量数据的采集是一种行之有效的解决方案。
1 树莓派及其相关软硬件简介
1.1 树莓派简介
树莓派由英国慈善组织Raspberry Pi基金会开发,是基于ARM控制器和Linux系统的单板机电脑,于2012年3月正式发售。其大小和一张信用卡相同,具备计算机主机的所有功能,支持多种开发语言,主流的有C语言和Python语言。树莓派包含多类接口,如USB、RJ45、HDMI和GPIO等[2]。自问世以来,树莓派主要经历了B+、3B、3B+、4B、5B等型号的演变。图1展示了树莓派4B的外观布局图。
1.2 树莓派的GPIO接口简介
树莓派的GPIO是一种数字输入/输出接口,可用于控制和监测电子元件。输入引脚可用于读取传感器或按键等外部设备输入的信号,输出引脚可用于向外部设备(如LED等)输出电流或电压。树莓派4B有40个GPIO引脚,它们被分成两排,并从编号1到40进行标号。表1为树莓派4B的GPIO引脚对照表[3],其中2个引脚提供5V的输出电压,2个引脚提供3.3V的输出电压,8个接地为0V,其他28个端口可以提供输入输出功能。
树莓派的GPIO引脚编号分为3种:
第1种编号是物理引脚BOARD编号,这种编号方式与树莓派电路板上的物理引脚一一对应,其优势在于在电路板升级更新后,不需要重复编写代码,也无须考虑树莓派版本的问题;
第2种编码为BCM(Broadcom SOC Channel) 编号,BCM编号是SOC芯片上GPIO控制器内部使用的编号,侧重CPU寄存器;
第3种编号为wiringPi编号,侧重实现逻辑,其优点是方便编程。
本系统的研制使用了wiringPi编号方式。
1.3 wiringPi库简介
开关量数据采集系统的研制在Linux操作系统中的Qt平台下使用了wiringPi库。wiringPi是一个强大而灵活的C语言库,提供了简化的树莓派GPIO访问接口,允许开发者配置和使用树莓派的GPIO引脚。通过几个简单的函数调用,可以设置引脚模式(输入或输出)、读取或写入引脚状态,以及处理中断等。wiringPi使用底层的BCM2835芯片库,提供对树莓派GPIO的低级别访问,因此能够实现高精度的计时和快速的响应速度。
wiringPi需要在系统中安装,下载地址为:https://project-downloads.drogon.net/wiringpi-latest.deb。在Linux操作系统中使用 sudo dpkg -i wiringpi-latest.deb 命令进行安装。
在Qt开发环境中使用wiringPi库前,需要包含wiringPi.h文件。主要用到以下函数:
1) 初始化:int wiringPiSetup();
2) 设置针脚输入/输出模式:void pinMode(int pin, int mode);
3) 设置针脚信号为上拉/下拉:void pullUpDnControl(int pin, int pud);
4) 信号上升沿/下降沿/上升或下降触发工作函数:wiringPiISR(int pin, int edgetype, void (*function)(void));
5) 向针脚写入高/低电平:void digitalWrite(int pin, int value);
6) 读取针脚电平:int digitalRead(int pin)。
2 开关量采集系统的设计
针对某型号航天发射任务,火箭系统通过48孔航空插座向一体化指挥决策系统发送22路开关量数据。系统利用树莓派具有多路GPIO输入/输出的特点,实现对开关量信号的实时采集,并将其转换为一体化指挥决策系统内部数据收发所需的网络帧结构。为保证运行的可靠性,系统使用两块树莓派以主/备方式工作。主备同时采集开关量数据,但仅有主用设备向外发送数据。当主用设备出现故障时,启动备用设备。主备切换通过心跳机制实现。
2.1 硬件设计原理
开关量数据采集系统的硬件连接电路原理如图2所示。开关量信号发生端有多路开关,所有开关的公共端接+3.3V电源,另一端分两路通过防逆流二极管分别接至两块树莓派的GPIO端口。每块树莓派通过GPIO输出连接发光二极管,用于提供工作状态指示。为了实现安全关机,系统使用关机按键连接两块树莓派的GPIO端口以输入关机信号。系统为树莓派提供+5V电源,为开关量信号发送端提供+3.3V电源。系统加电即树莓派开机。
开关量数据采集系统实现以下功能:
1) 开关量信号采集。系统采集信号共22路,两块树莓派以主备方式同时采集,但只有主机向外发送采集结果数据。为了防止两块树莓派之间信号相互干扰,每一路开关信号分支都增加防逆流二极管,保证信号单向传输。树莓派所有采集端子的初始状态为低电平,当信号发送端开关闭合后,采集端子采集到高电平,此时应用程序处理并向外发送采集到的数据。
2) 心跳监测。为了保证系统的可靠性,通过使用两块树莓派同时采集数据,并使用心跳线通信。主机定时向备机发送心跳信号(即定时置高低电平),当备机监测不到主机心跳时,备机切换为主机,承担主机工作。主机向外发送采集数据,备机不向外发送。
3) 工作状态指示。两块树莓派各有一个端子连接发光二极管,以闪烁状态指示系统工作状态。主机闪烁频率为每秒1次,备机闪烁频率为每2秒1次。发光状态通过交替高低电平实现。
4) 关机信号。系统设置了一个关机按键,实现两块树莓派同时安全关闭系统。两块树莓派的关机采集端子起始电平设为高电平,分别通过防逆流二极管连接同一个开关。开关闭合后电平拉低,树莓派采集到低电平信号后,通过应用程序实现关机。
2.2 树莓派GPIO接口定义
两块树莓派都配置了GPIO端子扩展板,其接口与航空插座连接定义如表2所示。
2.3 软件模块组成结构
系统软件使用Qt开发,由4个模块组成,即主模块MainWidget、控制模块Controller、采集模块Collector和发送模块Sender,软件组成结构如图3所示。
主模块JhiysDeGH6XxIJgP1Lm2OOVj15dBMAMohBpn5MwshgY=MainWidget实现软件配置文件数据的读取,控制模块Controller的创建,并向所有模块传递其所需的配置数据。控制模块Controller完成采集模块Collector、发送模块Sender的创建,控制Collector模块的采集线程,向MainWidget、Sender模块传递采集到的数据。采集模块Collector通过线程完成数据的采集并向Controller发送采集到的数据,此外还负责LED发光二极管的控制、关机控制、心跳发送与接收、主备切换等工作。发送模块Sender完成采集数据,按照一体化指挥决策系统内部帧结构组帧,并以指定源组播的方式向网络发送。
指定源组播(Source-Specific Multicast,SSM) 是一种组播服务模型,在一个发送者和多个接收者之间进行通信。在SSM中,组播接收者在加入组播组时可以指定接收或者拒绝来自特定组播源的组播流量,即匹配上了源地址才接收组播流量,否则丢弃流量。SSM实现了路由协议的简化,使组播传输的稳定性及安全性得到了增强。并且在实施SSM业务时,除了要求网络端支持网络组播外,还需要网络和应用支持IGMP v3协议[4]。
2.4 数据帧结构设计
开关量数据采集软件的数据组帧后以UDP指定源组播方式向一体化指挥决策系统内部发送,帧结构如表3所示。
具体的数据帧结构简介如下:
1) 版本(VER) 。用于表示UDP组播协议的版本号,用1个字节表示。
2) 任务代号(MID) 。用于表示当前任务的代号,用2个字节表示。
3) 信源(SID) 。用于表示信息发送方的标识。
4) 信宿(DID) 。用于表示信息接收方的标识。
5) 数据分类标志(BID) 。用于表示传输的数据为数值型、状态型和指令型等类别,开关量数据为状态型数据。
6) 数据段序号(No) 。用于表示对发送的数据段连续计数,发送第一个数据段时,计数值为1,以后每发送一个数据段,计数值加1,0~232-1循环计数。
7) 数据处理标志(FLAG) 。用于表示发送的数据是模拟数据还是真实数据,分别用00H和01H表示。
8) 备用(B) 。用于后续扩充使用。
9) 发送日期(DATE) 。表示信源发送该数据段的日期,相对于2000年1月1日的累计天数,2000年1月1日为第1天。
10) 发送时间(TIME) 。表示数据段发出时的标准时刻(北京时),用4个字节(DWORD) 的无符号长整型数表示,量化单位为0.1毫秒。例如,当前时间是8:10:30,则时间值为(8×3600 + 10×60 + 30) ×10 000 = 294 300 000(0.1毫秒)。
11) 数据域长(L) 。数据域长指该应用数据包内有效数据域(DATA) 的字节长度,用2字节无符号整数表示,范围为0~65535。
12) 有效数据(DATA) 。表示需要传输的各类数据,比如本系统中每次采集到的22个开关量数据。
3 开关量采集系统的实现
系统实现中使用了多线程技术,除了主线程之外,还包括与主线程并行运行的数据采集线程和数据发送线程。其中,数据采集线程用于采集开关量的数据,数据发送线程用于向网络发送采集到的数据。多线程技术的优点是防止线程“堵塞”,增强用户体验和程序的效率;缺点是代码的复杂程度大大提高,而且对硬件的要求也相应提高[5]。
3.1 软件实现中使用的配置文件说明
软件研制使用配置文件的作用主要是方便在后续使用过程中, 参数需要更改时不更动软件源代码。开关量数据采集软件开发中使用了两个配置文件,包括系统配置参数文件和用于系统采集参数的开关量数据配置文件。
1) config文件。用于设置系统需要的各种配置参数。
- activestandby=active // 主备配置,active为主用,standby为备用
- heartbeat=30 // 心跳信号使用的GPIO端口wPi编号,固定值
- shutdown=8 // 关机信号使用的GPIO端口wPi编号,固定值
- ledlight=9 // LED发光二极管信号使用的GPIO端口wPi编号,固定值
- multigroup=232.99.99.99:25100 // 组播地址与端口号,可配置多个,中间用“;”分割
- tablenum=9999 // 组帧使用的参数表号
- openinterval=10000 // 发送“接通”后到下一次发送“接通”的间隔,单位毫秒
2) gpio-wPi-config文件。用于设置系统采集的22个开关量数据。
- gpio-wPi=0, index=1, text=参数1
- gpio-wPi=1, index=2, text=参数2
- gpio-wPi=12, index=3, text=参数3
- ……
文件中每一行对应一个开关量数据的采集配置,gpio-wPi为采集输入对应的wPi编号,index为组帧使用的参数编号,text为定义的参数名称。其中gpio-wPi对应的wPi编号取自表1(IDSD,IDSC,SDA,SCL除外)。
3.2 软件实现中的关键代码
3.2.1 树莓派GPIO端口的初始化
void Collector::initGPIO()
{
for(int i=0; i<GPIOConfigCount; i++)
{
pinMode(ListGPIOConfig.at(i).gpio, INPUT);//配置树莓派引脚的IO模式为INPUT
pullUpDnControl(ListGPIOConfig.at(i).gpio,PUD_DOWN); //对已设置IO模式为INPUT的输入引脚设置为“低电平”
wiringPiISR(ListGPIOConfig.at(i).gpio, INT_EDGE_BOTH, &int_collect);//中断处理注册函数,注册的函数会在中断发生时执行
}
pinMode(GPIOShutdown, INPUT);//配置树莓派引脚的IO模式为INPUT
pullUpDnControl(GPIOShutdown, PUD_UP); //对已设置IO模式为INPUT的输入引脚设置为“高电平”
wiringPiISR(GPIOShutdown, INT_EDGE_BOTH, &int_shutdown); //中断处理注册函数,注册的函数会在中断发生时执行
}
3.2.2 读取树莓派端口的状态
void Collector::in_collecting()
{
_mutex.lock();//锁定互斥量,用于线程同步
for(int i=0; i<GPIOConfigCount; i++)
{
GPIOConfig config_1 = ListGPIOOnce.at(i);
QTime curtime = QTime::currentTime();
if(qAbs(config_1.collecttime.msecsTo(curtime)) >= OpenInterval)
{
config_1.state = 0;
}
ListGPIOOnce.replace(i, config_1);
GPIOConfig config = ListGPIOConfig.at(i);
int data = digitalRead(config.gpio); //读取树莓派一个端口的电平值
if(data == LOW)
{
config.state = 0;
}
else
{
config.state = 1;
}
}
_mutex.unlock();//解锁互斥量
}
3.2.3 树莓派端口状态读取线程和组帧后发送线程的创建
Controller::Controller(QObject *parent) : QObject(parent)
{
Collector *collector = new Collector;
InstanceCollector = collector;
//创建数据采集线程,用于读取树莓派各端口的数据
collector->moveToThread(&workerThread);
connect(this, SIGNAL(out_heartbeating()), collector, SLOT(in_heartbeating()));
connect(this, SIGNAL(out_collecting()), collector, SLOT(in_collecting()));
connect(collector, SIGNAL(out_result(bool,QByteArray)), this, SLOT(in_result(bool,QByteArray)));
connect(&workerThread, SIGNAL(finished()), collector, SLOT(deleteLater()));
//创建发送线程,用于读取树莓派各端口的数据后组帧向系统网络发送
Sender *sender = new Sender;
sender->moveToThread(&workerThread2);
connect(this, SIGNAL(out_result_share(bool,QByteArray)), sender, SLOT(in_result_share(bool,QByteArray)));
connect(&workerThread2, SIGNAL(finished()),
sender, SLOT(deleteLater()));
workerThread2.start();
workerThread.start();
_timerCollect.setInterval(250);//创建250毫秒间隔的时间片
connect(&_timerCollect, SIGNAL(timeout()), this, SLOT(doCollecting()));
_timerCollect.start();
}
4 结束语
经测试,基于树莓派使用Qt开发语言实现的开关量数据采集系统具有高稳定性和强可靠性。在自主可控软硬件环境中无法安装使用开关量采集卡的情况下,该系统能够确保某型号航天发射任务中开关量数据的有效采集和一体化指挥决策系统的正常运行。
参考文献:
[1] 白永强,高家智,张正娟.自主可控软硬件在航天发射场一体化指挥决策系统中的应用[J]. 导弹试验技术,2020(3):55-58.
[2] 王梁栋.基于树莓派的智能家居温度远程监控系统的设计[J].信息通信,2018,31(5):65-66.
[3] 郭亮,王冠南,孙红静,等.基于Raspberry Pi的可视化温湿度测量系统设计[J].单片机与嵌入式系统应用,2016,16(9):57-60,68.
[4] 崔永强,白永强.基于银河麒麟环境指定源组播的研究与实现[J].电子技术与软件工程,2018(18):32-33.
[5] 睿峰科技.苹果iOS 6开发从入门到实战:iOS开发必备应用开发实战与案例[M].北京:当代中国出版社,2013.
【通联编辑:唐一东】