搜索
bottom↓
回复: 16

开源PLC学习笔记03(再从51开始 通讯)——2013_11_8

[复制链接]

出0入0汤圆

发表于 2013-11-8 15:42:57 | 显示全部楼层 |阅读模式
本帖最后由 oldbeginner 于 2013-11-8 15:45 编辑

感觉通讯这块真得很难,首先STM32等通讯代码不提,只看简易PLC的通讯代码就很复杂,其中还夹杂着IAP,我IAP还没有学习,不过边研究边学吧。
*************************************************
找到51实现通讯的代码有:
1、简易PLC v1.1.5版 (成型2009年10月)
2、简易PLC v1.0.1版  http://www.amobbs.com/thread-3579264-1-4.html (成型2009年9月)
3、znl3512的AT89S51单片机仿三菱FX1N PLC 下载程序 (成型2009年6月)http://www.amobbs.com/thread-3608835-1-2.html
4、biscuit2的三菱FX1N下载协议的51代码例子 http://www.ourplc.com/2011-11/25/1846281673.html (成型2011年11月)

**************************************************
简单看了一下,没有一个能理清思路的。就简不就繁,选择简易PLC v1.0.1来分析,感觉v1.0.1的注释比v1.1.5要详细,而且也能通过通讯测试。biscuit2的代码最新,但是太长而且没有注释,不是初学者的菜。

和刚开始看PLC代码教训一样,即使通讯只是一部分,也超过我目前的理解力。直接看成型的行不通,于是按图索骥又找到了几个帖子:

1、三菱FX系列PLC下载通信协议说明-----开源 PLC 编程重点参考资料

2、三菱FX-PLC 的通讯协议参考

**************************************************
学习思路如下:只实现FX1N的通讯,因为资料比较齐全;只关注要用到的通讯指令,并利用串口通讯程序模拟一下,只确认报文输入是否和资料一致,反正我也不会串口监控;把整个通讯程序流程和实现理解清楚;移植通讯程序到某个能验证的MCU上。

阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入0汤圆

 楼主| 发表于 2013-11-8 16:01:28 | 显示全部楼层


在图片所显示的帖子中,PC和PLC通讯的过程被展示出来。如果不是代码太长,而且过程复杂,就是一个普通的报文程序,所以看看能否让代码更简洁些,这样更容易理解。





以上就是已知的格式,看是否能简化。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-8 20:02:49 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-8 22:00 编辑
oldbeginner 发表于 2013-11-8 16:01
在图片所显示的帖子中,PC和PLC通讯的过程被展示出来。如果不是代码太长,而且过程复杂,就是一个普通的 ...


选择简易PLC v1.0.1看来不是明智的,通讯就是一个函数FX1NProcessing。但是在V1.0.1版中,这个函数对我来说是一个巨无霸,复制到word当中有9页,字符数是9000多。在看这个函数过程中,怀疑,急躁,沮丧各种非正能量接踵而至,根据经验,该从其中撤了。

****************
还是转到v1.1.15,这次好多了,函数FX1NProcessing复制到word当中只有5页,字符数量也少一半。当还是太大,为了分解这个函数,理了一下结构。


有了这张图就可以重点突破了,选择read code作为突破口。read code只有1页,字符数量也是有955个,相比较刚开始看的一下少了90%的内容,难度也自然会下降一个数量级。
read code也可以分成三个部分:
1、计算得到读取地址;
2、计算得到读取字节数;
3、计算 sumdata 并发送读取字节。

感觉可以入手具体分析了。

补充:附上通讯函数。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-8 20:58:46 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-8 21:05 编辑


//        Read code 功能第一部分,计算得到读取地址:

for(i=4;i<8;i++)UartReceiveBuffer=ascto0F(UartReceiveBuffer);
ReadAddr =UartReceiveBuffer[4]<<12;
ReadAddr+=UartReceiveBuffer[5]<<8;
ReadAddr+=UartReceiveBuffer[6]<<4;
ReadAddr+=UartReceiveBuffer[7];

这里为了代码简洁,把UartReceiveBuffer写为Buffer,串口接收缓冲直接写成缓冲

for(i=4;i<8;i++)
   Buffer=ascto0F(Buffer);

ReadAddr =Buffer[4]<<12;
ReadAddr+=Buffer[5]<<8;
ReadAddr+=Buffer[6]<<4;
ReadAddr+=Buffer[7];

**************************

现在就可以了解其中的定义了,

//接收数据缓冲(定义在uart.c中),全局变量
volatile unsigned char UartReceiveBuffer[InLEN];        

又有一个关键字要学了,至少入门资料没用到过,volatilevolatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。http://baike.baidu.com/link?url= ... qe8-xRWpsl3nn_Xq3qq,看这个链接还是挺复杂的,不过暂时了解一下就可以了,目前重点不在这里。

有一个InLEN需要找到定义(在uart.h中)
//        超时收 接收缓冲区长度        
#define InLEN                        142               

转化为自己的理解,Buffer就是一个无符号字符数组,为什么142?等到后面看会不会自动出现解释。
unsigned char Buffer[142];

ReadAddr是在通讯函数中定义的一个局部变量,根据表面字意,就是要读的地址
unsigned int  ReadAddr=0;

unsigned int和unsigned char同时出现,uchar是一个8位无符号数,表示范围0到255,而uint是十六位无符号数,表示范围0到65535。http://zhidao.baidu.com/link?url ... CMvq_8B8KAjkTZ1c83K

ascto0F是在同一个文件中定义的函数,功能描述:将ASCII 转换成0~F。ascto0F是一个短小的函数,而且功能描述清晰,直接拿来使用即可,这样可以把精力都集中在要理解的功能上。

ReadAddr =Buffer[4]<<12;
在理解这个赋值上有些疑惑,找了一个资料http://bbs.21ic.com/icview-178995-1-1.html
感觉要实现的功能是把Buffer[4]的低4位移到ReadAddr的高4位;

然后
ReadAddr=ReadAddr+Buffer[5]<<8;
然后再让ReadAddr的高8位和Buffer[5]相加作为新的高8位;

然后
ReadAddr=ReadAddr+Buffer[6]<<4;
然后再让ReadAddr的中间8位和Buffer[6]相加作为新的ReadAddr;

最后,
ReadAddr=ReadAddr+Buffer[7];
最后让ReadAddr的低8位和Buffer[7]相加作为新的ReadAddr。

如果没猜错,Buffer[4]到Buffer[7]就是接收报文对应的地址位置,而且地址就像上面的计数一样。暂时不继续向下了解Read code功能,下一步要查看通讯协议确认一下。

出0入0汤圆

发表于 2013-11-8 21:21:01 | 显示全部楼层
太强了,支持触摸屏不?

出0入0汤圆

 楼主| 发表于 2013-11-8 21:32:06 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-8 21:36 编辑
oldbeginner 发表于 2013-11-8 20:58
//        Read code 功能第一部分,计算得到读取地址:

for(i=4;i


看了一下http://www.amobbs.com/thread-3394474-1-1.html里的通讯协议,估计是正确的,另外也可以理解为什么Buffer[]要4位4位左移。


因为Buffer=ascto0F(Buffer);
Buffer的值是从0到F,所以高4位都是0。

ReadAddr =Buffer[4]<<12;
ReadAddr+=Buffer[5]<<8;
ReadAddr+=Buffer[6]<<4;
ReadAddr+=Buffer[7];

例如
Buffer[4]=8
Buffer[5]=0
Buffer[6]=2
Buffer[7]=E

则ReadAddr=802E。
*************************
至此,Read code功能的第一部分了解,可以开始了解第二部分了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-8 22:10:51 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-9 16:32 编辑

小结一下报文再继续
例子
STX ,   CMD ,    ADDRESS,               BYTES,  ETX,  SUM
02H,    30H,   31H,30H,46H,36H,    30H,34H, 03H, 37H,34H

指令解释如下:
  STX,           "E01",           "10F6",         "04",                ETX,         "74"
报文开始,    读命令,     地址10F6H处,   04H字节数据,   报文结束,   累加方式和校验
Buffer[0]  Buffer[1:3]   Buffer[4:7]        Buffer[8:9]      Buffer[10]      Buffer[11:12]

其中 SUM=CMD+......+ETX=30h+31h+30h+46h+36h+30h+34h+03h=74h;
累加和超过两位取低两位,转换成ascii码,分 SUM(upper),SUM(lower)二次传送。
                                                            Buffer[11]        Buffer[12]


边研究代码边学习协议看起来可行。

进入Read code的第二部分

//        计算得到读取字节数:
for(i=8;i<10;i++)Buffer=ascto0F(Buffer);
ReadLen = Buffer[8]*16+Buffer[9];
UartSendchar(STX)

首先
for(i=8;i<10;i++)
       Buffer=ascto0F(Buffer);
得到Buffer[8]和Buffer[9]转换值,在上面的报文协议中可知,Buffer[8]和Buffer[9]表示要读出的字节数,

剩余代码,
ReadLen = Buffer[8]*16+Buffer[9];
UartSendchar(STX);

其中,
ReadLen是定义在这个通讯函数内部的局部变量,        unsigned char ReadLen=0;
因为Buffer[8]和Buffer[9]组成一个16位的数,保存要读取的字节数,高位是Buffer[8],所以Buffer[8]*16就是高位的值,再加上Buffer[9]就完整了。
然后
UartSendchar(STX);
根据字面理解,串口发出STX指令,需要确认一下协议和该函数的定义。

// 函数名称: UartSendchar
//uart.c中定义
// 功能描述:放入一个字节到发送缓冲区

UartSendchar这个函数代码有点多,暂时了解功能即可,继续研究协议,为什么要发送STX。

找到定义(FX1N.h)
#define ENQ         0x05                        //请求  
#define ACK         0x06                         //PLC 接收正确响应  
#define NACK         0x15                         //PLC 接收错误响应
#define STX         0x02                         //报文开始
#define ETX         0x03                         //报文结束


PLC回答是以STX开头的,这也可以理解了。

可以进入Read code第三部分了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-9 13:08:33 | 显示全部楼层
oldbeginner 发表于 2013-11-8 22:10
小结一下报文再继续
例子
STX ,   CMD ,    ADDRESS,               BYTES,  ETX,  SUM

//Read code第三部分,  计算 sumdata 并发送读取字节
                    sumdata=0;
                    for(i=1;i<=ReadLen;i++)
                    {
                        if(ReadAddr==0x01c0)
                              Readdat=0x0a;            //    此地址,目前只见过读,没有见过写.
                        else
                        {
                             //    读FlashData
                            Readdat=IAPFlashReadMode(ReadAddr+i-1);   
                        }

                        tempascdata=hextoasc(Readdat);
                        ucdata=tempascdata>>8;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);
                        ucdata=tempascdata&0xff;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);
                    }
                    UartSendchar(ETX);

末尾还有些打酱油的没添加,不过已经够长了。

sumdata是定义在这个函数里的局部变量,    unsigned char sumdata=0;
  if(ReadAddr==0x01c0)
                              Readdat=0x0a; 增加了理解难度,暂时去掉,这样变为
for(i=1;i<=ReadLen;i++)
         {
            Readdat=IAPFlashReadMode(ReadAddr+i-1);   //    读FlashData
            。。。。。
          }
应该是把要读的字节一个一个从Flash中读出。

终于要接触没有学过的IAP了,原则是在这里理解IAP不能喧宾夺主,点到为止。找到定义
// 函数名称: IAPFlashReadMode
//
// 功能描述:  单字节读取模式
// 输 入:  unsigned int codeaddr
// 定义在IAP.c中         
// 输 出:  unsigned char
unsigned char IAPFlashReadMode(unsigned int codeaddr)    // 读模式
{
    ISPCR=0x83;            // ISPCR.7=1,启用ISP
                        // ISPCR[2:0]=011, 假设MPC82系列运行在11.0592M
    IFMT=0x01;            // 进入读模式
    IFADRH=codeaddr>>8;    // 这个字节必须在IAP存储区
    IFADRL=codeaddr;
    SCMD=0x46;            // 触发的ISP处理
    SCMD=0xb9;            // MCU将会停止运行.直到处理完成    // 触发IAP
    return IFD;            // 读出的数据
}
还需要寄存器信息(reg_mpc82g516.h)
// (ISP)
sfr ISPCR     = 0xE7;
sfr IFMT      = 0xE5;
sfr IFD       = 0xE2;
sfr IFADRH    = 0xE3;
sfr IFADRL    = 0xE4;

sfr SCMD      = 0xE6;
需要再复习一下sfr,sfr 并标准C 语言的关键字,而是Keil 为能直接访问80C51 中的SFR 而提供了一个新 的关键词,其用法是: sfr 变量名=地址值

不是很理解。把i=1带入,看看结果
Readdat=IAPFlashReadMode(ReadAddr+i-1);

即Readdat=IAPFlashReadMode(ReadAddr);
然后调用IAP函数
    IFADRH=ReadAddr>>8;    // 这个字节必须在IAP存储区
    IFADRL=ReadAddr;
然后
    Readdat=IFD;
不是很理解,可能需要看mpc82g516手册,目前没有精力可分散。暂时勉强理解为读出了所需的数据。

然后集中精力理解后面的代码
                        tempascdata=hextoasc(Readdat);
                        ucdata=tempascdata>>8;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);
                        ucdata=tempascdata&0xff;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);




出0入0汤圆

 楼主| 发表于 2013-11-9 13:46:15 | 显示全部楼层
oldbeginner 发表于 2013-11-9 13:08
//Read code第三部分,  计算 sumdata 并发送读取字节
                    sumdata=0;
                 ...


                        tempascdata=hextoasc(Readdat);
                        ucdata=tempascdata>>8;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);
                        ucdata=tempascdata&0xff;
                        sumdata = sumdata + ucdata;
                        UartSendchar(ucdata);
//========================
// 函数名称: hextoasc
//定义在FX1N.c中
// 功能描述:  HEX 转 ASCII
//
// 输 入:  unsigned char hexdata
//         
// 输 出:  unsigned int


读法挺怪异的,怪不得代码也不好理解。


而tempascdata是定义在函数内部的一个局部变量,   unsigned int  tempascdata=0; 16位的
                        tempascdata=hextoasc(Readdat);
ucdata和sumdata也是一个局部变量,    unsigned char ucdata=0;    unsigned char sumdata=0;
把Readdat转换成ASCII,再看后面的(把UartSendchar顺序提前,增强理解)
                        ucdata=tempascdata>>8;
                        UartSendchar(ucdata);
                        sumdata = sumdata + ucdata;
                     
tempascdata>>8,向右移8位,再发送,和上图协议一致。然后发送低8位
                        ucdata=tempascdata&0xff;
                        UartSendchar(ucdata);
                        sumdata = sumdata + ucdata;
sumdata是检验和。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-11-9 14:15:56 | 显示全部楼层
oldbeginner 发表于 2013-11-9 13:46
tempascdata=hextoasc(Readdat);
                        ucdata=tempascdata> ...

第三部分末尾
                    UartSendchar(ETX);
                    sumdata = sumdata + ETX;
                    usum=sumdata&0x0f;
                    tempsum=(usum<10)?(usum+0x30):(usum+0x41-0x0a);
                    sumdata>>=4;
                    usum=sumdata&0x0f;
                    Readdat=(usum<10)?(usum+0x30):(usum+0x41-0x0a);
                    UartSendchar(Readdat);
                    UartSendchar(tempsum);    //    注意此处sum 发送顺序
1、发送ETX,结束符号
               UartSendchar(ETX);
2、校验和继续加
                    sumdata = sumdata + ETX;
3、   usum和tempsum都是局部unsigned char变量,取校验和低4位,然后转换成对应的ASCI保存到tempsum。
                    usum=sumdata&0x0f;
                    tempsum=(usum<10)?(usum+0x30):(usum+0x41-0x0a);

4、取校验和的高4位,然后转换成对应的ASCI保存到Readdat中。
                    sumdata>>=4;
                    usum=sumdata&0x0f;
                    Readdat=(usum<10)?(usum+0x30):(usum+0x41-0x0a);
5、分两次发送检验和
                    UartSendchar(Readdat);
                    UartSendchar(tempsum);    //    注意此处sum 发送顺序

至此,Read code第一遍理解完成,里面还有些细节问题不清楚,不过还是先回到大结构整理一下。

出0入0汤圆

 楼主| 发表于 2013-11-9 15:18:17 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-11-9 15:19 编辑
oldbeginner 发表于 2013-11-9 14:15
第三部分末尾
                    UartSendchar(ETX);
                    sumdata = sumdata + ETX;


笔记03主要是找到理解通讯协议和代码的一个入门点,就是Read code功能,理解了这个Read code代码要实现的功能后,再返回到协议当中去。从而对协议和FX1NProcessing有个全局认识。

接下部分将在笔记04中理解:
http://www.amobbs.com/forum.php? ... page%3D1#pid7104970

出0入0汤圆

发表于 2013-11-10 19:17:06 | 显示全部楼层
好东西啊

出0入0汤圆

发表于 2013-11-24 10:55:11 | 显示全部楼层
刚入门能这么详细分析, 赞一个

出0入0汤圆

发表于 2015-6-23 10:18:45 | 显示全部楼层
这样的资料太少了,像楼主这样的太少了,顶

出0入0汤圆

发表于 2016-2-28 23:50:05 | 显示全部楼层
厉害,谢谢楼主

出0入0汤圆

发表于 2016-3-10 21:31:11 | 显示全部楼层
楼主有没有plc工控电路原理图啊?

出0入0汤圆

发表于 2016-3-21 09:25:28 | 显示全部楼层
简易PLC v1.1.5版
这个没法下载,是屏蔽了吗?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 19:52

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表