oipk 发表于 2015-1-6 19:07:33

基于MODBUS的远程控制项目

这段时间由于做这个东西紧赶慢赶的,由于项目比较急,一个星期不算modbus写了3000行代码并且调试成功。
硬件端,我所做的就是把所有的引脚引出来,RS232接到DTU,其实也是串口操作。其中,用32个脚扫描按键,16个脚控制继电器,8个行程开关输入,老实说STM32的所有的高级功能都没有用到。参考硬件图。
真正核心的是什么呢?小弟贴个图大家就知道了(参考软件图)。
为什么我说是线程呢?核心时基发布的事件,在主程序中,分时执行,这样主程序是一个很好的树状结构,各个线程之间通过这种方式来去耦合一起避免临界区的问题。他们做电脑软件的,天天都是面向对象,那么,我们C语言,通过提取事物的特性,提取属性和方法。各个模块对内负责,对外部输入数据负责。这个时候你会问,为啥不用全局变量呢?全局变量的好处就是哪个文件都可以用,但是,谁用,谁什么时候用并不是很清楚。单个文件的变量就比较清楚了,只是在同步的时候你需要多写一个函数。
无论是modbus还是按键输入,都通过数据打包,修改核心管理层,然后核心管理层再往设备操作层写入输入。我在各个设备层最设备做了IO映射表,这样我们无论怎么修改或者增加设备都只需实例化一个操作类型就可以了。



核心的设备层,我对单个设备做了一个封装(我们是拿来控制起保停和正反转电路),(用起保停做个例子)大致就是设备的操作状态(开,关,default(数据缓冲))、设备计数值(切换状态的时候需要流程性延时,切换状态的时候每执行一次就加一个,切换完成就赋值0,这也是我把各个模块叫线程的主要原因)、继电器1(起保停电路SB1),继电器2(SB2)。
typedef struct
{
      EquError Error ;//设备错误
      SKS_EquOperating usSKS_EquOperating; //操作状态
      u8 OperateStatus;
      u16 OperTimeCountdown; //设备计数值
      u8 Relay_NO; //高四位表示继电器对应映射表的横坐标,低四位是纵坐标
//      u8 Relay_NC; //(后来控制方案修改,只要一个继电器)
}SKS_Operate_Pro; //起保停控制结构体

volatile staticSKS_Operate_Pro SKSOperateP={{PortErr,SKSDefault,0,0,0x11},                                                                                                                                                                                    {PortErr,SKSDefault,0,0,0x12},                                                                                                                                                                                              {PortErr,SKSDefault,0,0,0x13}                                                                                                                                                                                                                        };//操作实体

抽象化成这样,基本包括了操作的所有要用的东西。

顺便晒一下映射表
static IOInitStruct RelayArray={
      {{GPIOC,GPIO_Pin_10} ,{GPIOC,GPIO_Pin_11} ,{GPIOC,GPIO_Pin_12} ,{GPIOC,GPIO_Pin_13}},
      {{GPIOC,GPIO_Pin_14} ,{GPIOC,GPIO_Pin_15} ,{GPIOC,GPIO_Pin_9 } ,{GPIOC,GPIO_Pin_8 }},
      {{GPIOC,GPIO_Pin_7 } ,{GPIOC,GPIO_Pin_6 } ,{GPIOC,GPIO_Pin_0 } ,{GPIOC,GPIO_Pin_1 }},      
      {{GPIOC,GPIO_Pin_2 } ,{GPIOC,GPIO_Pin_3 } ,{GPIOC,GPIO_Pin_4 } ,{GPIOC,GPIO_Pin_5 }},      
                                                                                                                                                                };



按键我也做了同样的映射表,为了啥,为了安装的时候操作方便,增加设备也很方便
SKS_KeysState_Pro SKS_KeyStaTab={{PortErr,0,{KeyDefault,0x01,0},{KeyDefault,0x00,0}},
                                                                                                {PortErr,0,{KeyDefault,0x03,0},{KeyDefault,0x02,0}},
                                                                                                {PortErr,0,{KeyDefault,0x21,0},{KeyDefault,0x20,0}}/**/
      };

这样扫描和控制就完全分开了。这个时候大家会问,怎么连接到一起呢?
typedef union
{
      USHORT Ushort;
                struct
                        {   
                              unsigned char low_byte;   
                              unsigned char high_byte;   
                        } ushortByte;
                struct
                        {
                              UCHAR EquState:3; //设备操作状态
                              UCHAR EqusType:2;//设备类型
                              UCHAR Operator:1;//操作者
                              UCHAR EquOpSta:1;//设备状态,忙或者空闲
                              UCHAR OpDiffer:1;//操作差分
                              
                              UCHAR ContrVal:3;//控制值
                              UCHAR FrsFidPo:1;//正反转是否找到参考位置
                              UCHAR FrsStepP:1;//步进标识
                              UCHAR InvalidV:3;//未使用
                              
                        }EquHoldingType;
}UshortUnion;

这个事我们对应的数据结构的联合体,我也郁闷了好久,怎么才能对每个byte的每个位都能独立操作呢,各种与或运算都不科学,以为对齐方式会很坑爹,用之后发现STM32很人性化,我按着常规思维去写,居然对齐了。其实反过来也没什么,联合体里面复制复制粘贴粘贴不就可以了嘛。

整个控制对应到一个核心操作数组
volatile staticUshortUnion usEquipmentStateBuf={0x0000};

无论是Modbus还是按键扫描的数据都通过下面这个函数来操作设备层。
BOOL WriteEquControlDat(UCHAR EquByteLo,UCHAR EquByteHi, UCHAR off,ValOperater valOperator);
一个设备对应一个16位的无符号短整型,用联合体的类型声明并且对齐,然后考虑到正反转和起保停操作,低位只能操作状态,高位才能操作执行值(起保停电路没有执行值),修改完操作数据之后,发布同步设备层到操作层的事件,然后把写入的数据写入操作层,操作层检测到有要执行的数据就自动执行了。
恩,整个程序的大致流程就讲清楚了,代码我贴上,还有很多不完善的地方(比较赶时间)另外项目初期,软件硬件有大BUG,欢迎大家吐槽。
这次目的很明确,为了挣开发板,大家要是觉得可以就给个赞或者回复什么的,小弟在这先谢谢大家,另外这个是我发的第一个帖子,希望大家不要太狠。


石头-395391 发表于 2015-1-6 19:34:14

这个必须赞 !

巅峰残狼 发表于 2015-1-6 20:38:26

谢谢分享

紫苑少年 发表于 2015-1-6 20:54:21

赞一下!!

12956 发表于 2015-1-6 21:22:53

貌似不错

nktxsj 发表于 2015-1-6 21:54:19

O(∩_∩)O哈哈~,过来顶你一下;P

Aihe 发表于 2015-1-6 21:58:25

下载要金币,太坑了,没仔细看,金币没了

oipk 发表于 2015-1-6 22:14:13

nktxsj 发表于 2015-1-6 21:54
O(∩_∩)O哈哈~,过来顶你一下

谢谢啦,:$

oipk 发表于 2015-1-6 22:14:43

Aihe 发表于 2015-1-6 21:58
下载要金币,太坑了,没仔细看,金币没了

谢谢你老的金币,:lol

oipk 发表于 2015-1-6 22:15:13

12956 发表于 2015-1-6 21:22
貌似不错

谢谢支持
页: [1] 2 3 4 5
查看完整版本: 基于MODBUS的远程控制项目