cgzasa 发表于 2015-12-7 11:46:19

Modbus从机,怎么把Reg与变量对应,征求方法

如题,下位机板子,用modbus协议通讯,参照资料,已经把freemodbus移植完成,测试通过。
现在到了定义modbus寄存器地址功能的阶段,征求好的办法。

自己想到了几个方法,1、在原项目文件中,将需要与外界沟通的变量,与modbus寄存器数组地址对应赋值; 2、另起定时器,周期性的完成变量与寄存器数组的对应赋值;3、使用指针数组 4、其它方案,盼有相关经验的指教下

3DA502 发表于 2015-12-7 12:13:31

字典模式,不再定义其他变量,直接用REG当变量

danfeidie 发表于 2015-12-7 12:23:20

这还用选??
肯定3啊

danfeidie 发表于 2015-12-7 12:24:01

3、使用指针数组->3.使用指针

Firman 发表于 2015-12-7 13:12:14

定义一个大数组,全局变量定义成数组里的单元,MODEDUB取数组的值就行了.

cgzasa 发表于 2015-12-7 14:48:45

danfeidie 发表于 2015-12-7 12:24
3、使用指针数组->3.使用指针

我也主观上倾向于3,觉得实现最为简单,而且维护方便。
但数组指针不熟悉,而且用不好的话, 很容易跑飞死机。

另外还有一个难点,就是比特位,和数组的某一位建立对应关系

cgzasa 发表于 2015-12-7 14:50:56

3DA502 发表于 2015-12-7 12:13
字典模式,不再定义其他变量,直接用REG当变量

字典模式? 愿闻其详
直接用REG当变量,这个方法最直观,但工作量最大,需要改动原项目的程序,是个麻烦事情。

cgzasa 发表于 2015-12-7 14:54:45

本帖最后由 cgzasa 于 2015-12-7 15:04 编辑

Firman 发表于 2015-12-7 13:12
定义一个大数组,全局变量定义成数组里的单元,MODEDUB取数组的值就行了.

我原项目建立了一个结构体,把用到的相关参数,全部打包进去了,使用起来也方便。
现在要解决的,是把结构体的成员,和modbus的寄存器地址建立对应关系。

项目原功能已经基本成型,如果为了通讯, 推翻前面程序,这个想法不敢去想啊

Firman 发表于 2015-12-7 15:09:04

cgzasa 发表于 2015-12-7 14:54
我原项目建立了一个结构体,把用到的相关参数,全部打包进去了,使用起来也方便。
现在要解决的,是把结 ...

做指针指向你的结构体不就能提取变量了么?

cgzasa 发表于 2015-12-7 15:31:41

本帖最后由 cgzasa 于 2015-12-7 15:46 编辑

Firman 发表于 2015-12-7 15:09
做指针指向你的结构体不就能提取变量了么?

是有这个想法
16位访问的变量,用指针应该好处理
那种比特位访问的reg,不好操作啊

danfeidie 发表于 2015-12-7 15:50:38

1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。
就这么简单,别搞复杂了。

cgzasa 发表于 2015-12-7 16:26:41

danfeidie 发表于 2015-12-7 15:50
1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。 ...

懂了,我刚开始考虑的思路,是保持原项目程序不动的基础上,加入实现modbus协议功能。在原程序结构体定义并开区,modbus用指针数组指向定义。
经你这么一说,明白了,这样应该更合理,自己去考虑怎么实现吧,谢谢

bujie8010 发表于 2015-12-7 16:44:37

本帖最后由 bujie8010 于 2015-12-7 16:46 编辑

我的做法是用宏定义联系变量和reg,int Parameter定义在global.c中,下面是global.h中的内容:
extern int Parameter;
#define Baudrate Parameter
#define MBAddress Parameter
#define TBCCR0_PARA Parameter
#define ADCMode Parameter
#define ADCChanHi Parameter
#define ADCChanMi Parameter
#define ADCChanLo Parameter
#define TBCCR_ADCHi Parameter
#define TBCCR_ADCMi Parameter
#define TBCCR_ADCLo Parameter
#define DAC0Factor Parameter
#define DAC1Factor Parameter

下位机的程序包含global后直接用宏来操作reg,上位机要知道具体reg的对应关系。

Firman 发表于 2015-12-7 16:46:31

cgzasa 发表于 2015-12-7 15:31
是有这个想法
16位访问的变量,用指针应该好处理
那种比特位访问的reg,不好操作啊 ...

MODEBUS 定义BYTE传输,若BIT提取,也就是提取0或1.
省RAM的做法,你可以把每个BIT都定义好标志.
extern UC BitFlag4;            //bit flag ram
#define F_BackLight            (((bits*)&BitFlag4)->bit0) //=1 backlight on
#define F_BackupManu       (((bits*)&BitFlag4)->bit1) //backup F_ManualAuto
#define F_ErrorMode          (((bits*)&BitFlag4)->bit2) //error mode
#define F_500msChPump(((bits*)&BitFlag4)->bit3) //change pump timing toggle
#define F_KeyBuzzer          (((bits*)&BitFlag4)->bit4) //press key have a buzzer
#define F_ErrorBuzzer      (((bits*)&BitFlag4)->bit5) //error status buzzer
#define F_T30s                  (((bits*)&BitFlag4)->bit6) //timing keep pressure 30s
#define F_ScanBp             (((bits*)&BitFlag4)->bit7) //backup scan 10ms

败家的做法,每个BIT独立占一个BYTE.

cgzasa 发表于 2015-12-7 16:58:49

bujie8010 发表于 2015-12-7 16:44
我的做法是用宏定义联系变量和reg,int Parameter定义在global.c中,下面是global.h中的内容:
extern ...

这个方法,节省程序空间,也节省ram,是个好办法。缺陷是,变量多了,看起来不太清晰。要是能用结构体打包一下,可读性会好一些

cgzasa 发表于 2015-12-9 20:31:20

Firman 发表于 2015-12-7 16:46
MODEBUS 定义BYTE传输,若BIT提取,也就是提取0或1.
省RAM的做法,你可以把每个BIT都定义好标志.
extern UC...

BitFlag4() 这个子函数是实现什么功能?
另外,这个是只能提取bit值,还是说能提取,也能赋值bit位?

chinaboy25 发表于 2015-12-9 21:12:36

我用第2种,定时器刷新,改动最小,移植最方便,多耗资源,但一般也不差这么点资源;

cgzasa 发表于 2015-12-9 23:26:44

chinaboy25 发表于 2015-12-9 21:12
我用第2种,定时器刷新,改动最小,移植最方便,多耗资源,但一般也不差这么点资源; ...

这个方式有个疑问,比如是可读写的数据类型,如保持寄存器,你的定时器刷新时,从哪边往哪边刷新? 单向刷新,要么只能读,要么只能写,而双向刷新如何实现?

mangoes 发表于 2015-12-10 08:06:23

danfeidie 发表于 2015-12-7 15:50
1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。 ...

我也觉得不错,现在维护前人写的程序,整个程序都是REG,大概有1000来个,而且REG还是全局的,直接操作REG,全部糊在一起,那个维护痛苦啊,其他同事都不太愿意维护这样的程序

mangoes 发表于 2015-12-10 08:09:06

cgzasa 发表于 2015-12-7 16:58
这个方法,节省程序空间,也节省ram,是个好办法。缺陷是,变量多了,看起来不太清晰。要是能用结构体打 ...

结构体打包适合那些寄存器不多的情况,多了还是别这样搞,REG容易错位

Firman 发表于 2015-12-10 09:05:22

cgzasa 发表于 2015-12-9 20:31
BitFlag4() 这个子函数是实现什么功能?
另外,这个是只能提取bit值,还是说能提取,也能赋值bit位? ...

BitFlag4是一个BYTE的全局变量.
#define ..........是指向BitFlag4的每一位,可用位操作.

cgzasa 发表于 2015-12-10 10:04:02

mangoes 发表于 2015-12-10 08:06
我也觉得不错,现在维护前人写的程序,整个程序都是REG,大概有1000来个,而且REG还是全局的,直接操作RE ...

弄清楚思绪,用结构体重定义打包,用结构体方式替换一下,前期头痛一点,后续会方便很多

cgzasa 发表于 2015-12-10 10:13:05

mangoes 发表于 2015-12-10 08:09
结构体打包适合那些寄存器不多的情况,多了还是别这样搞,REG容易错位

我到现在为止,用结构体感觉不错啊,主要是关系清晰。
而且那些有共性的功能,用结构体就更好用了。

        MtrA.Ctrl_Status=STOP;
        MtrA.PIN_CLK=&PCout(8);//PC8
        MtrA.PIN_DIR=&PCout(9);//PC9
        MtrA.PIN_EN =&PCout(6);//PC6
        MtrA.PIN_ORI=&PCin(7);   //PC7
        MtrA.TIM_ARR=&MEM_ADDR(&TIM2->ARR); //指向TIMx->ARR
        MtrA.Para_S_Type=FULL_S_WAVE;       //启停曲线模式
        MtrA.Run_S_Len=10;                                //steps
        MtrA.S_Wave=&S_Wave_TeamA;       //曲线地址
        MtrA.Para_OriginDir=CW;   //回原点的转动方向
        MtrA.Para_OriginSteps=10;   //原点探测后,继续多少步为逻辑真实原点
        MtrA.Para_ClkPerStep=64;

       。。。

        MtrE.Ctrl_Status=STOP;   
        MtrE.PIN_CLK=&PBout(6);   //PB6
        MtrE.PIN_DIR=&PBout(7);   //PB7
        MtrE.PIN_EN =&PBout(4);   //PB4
        MtrE.PIN_ORI=&PBin(5);    //PB5
        MtrE.TIM_ARR=&MEM_ADDR(&TIM2->ARR); //指向TIMx->ARR
        MtrE.Para_S_Type=FULL_S_WAVE;       //启停曲线模式
        MtrE.Run_S_Len=10;                                //steps
        MtrE.S_Wave=&S_Wave_TeamA;       //曲线地址
        MtrE.Para_OriginDir=CW;   //回原点的转动方向
        MtrE.Para_OriginSteps=10;   //原点探测后,继续多少步为逻辑真实原点
        MtrE.Para_ClkPerStep=64;
你说的reg错位,暂时还没出现过,如果涉及到指针的操作,需要注意,否则程序会跑飞而出现HardFault

cgzasa 发表于 2015-12-10 10:27:42

Firman 发表于 2015-12-10 09:05
BitFlag4是一个BYTE的全局变量.
#define ..........是指向BitFlag4的每一位,可用位操作. ...

折腾好久,终于基本实现功能了,可能还有不合理的地方,暂时也没法找出。用modbus Poll连机测试,达到了预计功能。

//数组定义后面的是指针,方便调用
uint8_t ucRegCoilsBuf__attribute__(( at(0x20000000) )) = {0x00,0x00};
uint8_t * const pucRegCoilsBuf=(void*)(0x22000000+0x0*8*4);

这个是结构体中,指针变量的类型
        。。。
        BOOL *Update_Flag;
        BOOL *Calc_Flag;
        。。。

//这里是用指针变量,指向bit空间。

DL.Update_Flag =        (BOOL *)&pucRegCoilsBuf;           //地址1   因为位带是32位操作,所以需要乘以4
DL.Calc_Flag   =        (BOOL *)&pucRegCoilsBuf;                //地址2

测试读写bit位都正常,也就是说,不是单向赋值,而是双向的赋值。上位机可读该线圈的值,也可以更改。

另外,需要注意一点,数组定义的初始值,=0x0,会报错,
Error: L6971E: systick.o(.data) type RW incompatible with port.o(.ARM.__AT_0x20000000) type ZI in er RW_IRAM1.
初始值为非0,就不出错,更改IRAM1的Start地址(如地址+0x10),则初始值=0x0,也不会报错了。

cgzasa 发表于 2015-12-10 10:34:47

这中间涉及到位带操作的一些概念,可以参照这个STM32位带操作
http://blog.sina.com.cn/s/blog_779edb0b01018nu9.html

chinaboy25 发表于 2015-12-11 09:50:28

本帖最后由 chinaboy25 于 2015-12-11 09:56 编辑

cgzasa 发表于 2015-12-9 23:26
这个方式有个疑问,比如是可读写的数据类型,如保持寄存器,你的定时器刷新时,从哪边往哪边刷新? 单向 ...

加个历史纪录,你刷新的历史纪录也刷新,协议刷新的就会和历史纪录不同,至于刷新时间看根据系统应用来,不过一般很只把保持寄存器当输入用,没必要做成双向的;

danfeidie 发表于 2015-12-11 23:08:34

mangoes 发表于 2015-12-10 08:09
结构体打包适合那些寄存器不多的情况,多了还是别这样搞,REG容易错位

看晕了!
这样是不是很简单??
u16 Area4;
u16 *p_reg1=&Area4;         // 假设用在第100个地址处;
以后调用这个变量直接*p_reg1;
完了。

mangoes 发表于 2015-12-12 09:52:50

danfeidie 发表于 2015-12-11 23:08
看晕了!
这样是不是很简单??
u16 Area4;


以前的同事喜欢用宏定义 “#define COM3_SETFORMAT ArryReg”这样的形式去弄,弄到程序要调试的话很麻烦,现在我也是用你所说的这种方法去做的,想不到其他更好的方法了
页: [1]
查看完整版本: Modbus从机,怎么把Reg与变量对应,征求方法