Modbus从机,怎么把Reg与变量对应,征求方法
如题,下位机板子,用modbus协议通讯,参照资料,已经把freemodbus移植完成,测试通过。现在到了定义modbus寄存器地址功能的阶段,征求好的办法。
自己想到了几个方法,1、在原项目文件中,将需要与外界沟通的变量,与modbus寄存器数组地址对应赋值; 2、另起定时器,周期性的完成变量与寄存器数组的对应赋值;3、使用指针数组 4、其它方案,盼有相关经验的指教下
字典模式,不再定义其他变量,直接用REG当变量 这还用选??
肯定3啊 3、使用指针数组->3.使用指针 定义一个大数组,全局变量定义成数组里的单元,MODEDUB取数组的值就行了. danfeidie 发表于 2015-12-7 12:24
3、使用指针数组->3.使用指针
我也主观上倾向于3,觉得实现最为简单,而且维护方便。
但数组指针不熟悉,而且用不好的话, 很容易跑飞死机。
另外还有一个难点,就是比特位,和数组的某一位建立对应关系 3DA502 发表于 2015-12-7 12:13
字典模式,不再定义其他变量,直接用REG当变量
字典模式? 愿闻其详
直接用REG当变量,这个方法最直观,但工作量最大,需要改动原项目的程序,是个麻烦事情。 本帖最后由 cgzasa 于 2015-12-7 15:04 编辑
Firman 发表于 2015-12-7 13:12
定义一个大数组,全局变量定义成数组里的单元,MODEDUB取数组的值就行了.
我原项目建立了一个结构体,把用到的相关参数,全部打包进去了,使用起来也方便。
现在要解决的,是把结构体的成员,和modbus的寄存器地址建立对应关系。
项目原功能已经基本成型,如果为了通讯, 推翻前面程序,这个想法不敢去想啊 cgzasa 发表于 2015-12-7 14:54
我原项目建立了一个结构体,把用到的相关参数,全部打包进去了,使用起来也方便。
现在要解决的,是把结 ...
做指针指向你的结构体不就能提取变量了么? 本帖最后由 cgzasa 于 2015-12-7 15:46 编辑
Firman 发表于 2015-12-7 15:09
做指针指向你的结构体不就能提取变量了么?
是有这个想法
16位访问的变量,用指针应该好处理
那种比特位访问的reg,不好操作啊 1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。
就这么简单,别搞复杂了。 danfeidie 发表于 2015-12-7 15:50
1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。 ...
懂了,我刚开始考虑的思路,是保持原项目程序不动的基础上,加入实现modbus协议功能。在原程序结构体定义并开区,modbus用指针数组指向定义。
经你这么一说,明白了,这样应该更合理,自己去考虑怎么实现吧,谢谢
本帖最后由 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的对应关系。 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. bujie8010 发表于 2015-12-7 16:44
我的做法是用宏定义联系变量和reg,int Parameter定义在global.c中,下面是global.h中的内容:
extern ...
这个方法,节省程序空间,也节省ram,是个好办法。缺陷是,变量多了,看起来不太清晰。要是能用结构体打包一下,可读性会好一些 Firman 发表于 2015-12-7 16:46
MODEBUS 定义BYTE传输,若BIT提取,也就是提取0或1.
省RAM的做法,你可以把每个BIT都定义好标志.
extern UC...
BitFlag4() 这个子函数是实现什么功能?
另外,这个是只能提取bit值,还是说能提取,也能赋值bit位? 我用第2种,定时器刷新,改动最小,移植最方便,多耗资源,但一般也不差这么点资源; chinaboy25 发表于 2015-12-9 21:12
我用第2种,定时器刷新,改动最小,移植最方便,多耗资源,但一般也不差这么点资源; ...
这个方式有个疑问,比如是可读写的数据类型,如保持寄存器,你的定时器刷新时,从哪边往哪边刷新? 单向刷新,要么只能读,要么只能写,而双向刷新如何实现? danfeidie 发表于 2015-12-7 15:50
1.MODBUS用数组开区。
2.定义指针变量,指向数组中的某个地址。
3.从此,该指针变量就当做你平时用的变量。 ...
我也觉得不错,现在维护前人写的程序,整个程序都是REG,大概有1000来个,而且REG还是全局的,直接操作REG,全部糊在一起,那个维护痛苦啊,其他同事都不太愿意维护这样的程序 cgzasa 发表于 2015-12-7 16:58
这个方法,节省程序空间,也节省ram,是个好办法。缺陷是,变量多了,看起来不太清晰。要是能用结构体打 ...
结构体打包适合那些寄存器不多的情况,多了还是别这样搞,REG容易错位 cgzasa 发表于 2015-12-9 20:31
BitFlag4() 这个子函数是实现什么功能?
另外,这个是只能提取bit值,还是说能提取,也能赋值bit位? ...
BitFlag4是一个BYTE的全局变量.
#define ..........是指向BitFlag4的每一位,可用位操作. mangoes 发表于 2015-12-10 08:06
我也觉得不错,现在维护前人写的程序,整个程序都是REG,大概有1000来个,而且REG还是全局的,直接操作RE ...
弄清楚思绪,用结构体重定义打包,用结构体方式替换一下,前期头痛一点,后续会方便很多 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 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,也不会报错了。 这中间涉及到位带操作的一些概念,可以参照这个STM32位带操作
http://blog.sina.com.cn/s/blog_779edb0b01018nu9.html
本帖最后由 chinaboy25 于 2015-12-11 09:56 编辑
cgzasa 发表于 2015-12-9 23:26
这个方式有个疑问,比如是可读写的数据类型,如保持寄存器,你的定时器刷新时,从哪边往哪边刷新? 单向 ...
加个历史纪录,你刷新的历史纪录也刷新,协议刷新的就会和历史纪录不同,至于刷新时间看根据系统应用来,不过一般很只把保持寄存器当输入用,没必要做成双向的; mangoes 发表于 2015-12-10 08:09
结构体打包适合那些寄存器不多的情况,多了还是别这样搞,REG容易错位
看晕了!
这样是不是很简单??
u16 Area4;
u16 *p_reg1=&Area4; // 假设用在第100个地址处;
以后调用这个变量直接*p_reg1;
完了。 danfeidie 发表于 2015-12-11 23:08
看晕了!
这样是不是很简单??
u16 Area4;
以前的同事喜欢用宏定义 “#define COM3_SETFORMAT ArryReg”这样的形式去弄,弄到程序要调试的话很麻烦,现在我也是用你所说的这种方法去做的,想不到其他更好的方法了
页:
[1]