电子DIY小家 发表于 2024-4-7 09:41:19

PLC 实战, 让你的STC32变成FX3U-PLC, 教程(一)建立连接

PLC 实战, STC32 变成 FX3U-PLC
PLC, 教程(一), 建立连接
前言   首先在开始之前我们得先知道什么是FX3U。


    百度百科里对他是这么定义的,说白了他就是一个集成了一些常用的外设(例如adc,dac,串口等)的带驱动程序的一个控制器,我们只需要用梯形图或相对应的语言就可以快速的开发(下图是一个端口闪烁的程序,只需要一行即可轻松完成一个闪烁,换成单片机的话,我们抛开新建工程那些步骤,还需要初始化端口,还需要通过延时或者实际做端口取反,但是PLC里这些常见的底层代码都已经做好了,只需要用图形进行调用即可,该软件为GX Developer 8.86,因为这个软件的特殊性,就不在论坛放安装包了,直接在Q群文件下载即可)



对于一些复杂的功能来说他可以快速的帮助我们开发,但是他也会存在一些缺点。

区别对比单片机PLC
硬件裸芯片(外围电路全部需要自己搭建,可以自由扩展)常用的外围电路全部集成,只需要接线即可(功能单一)
成本价格低,可以按照需求自由搭建成本高,甚至很多都是进口
开发难度绝大多数都是C语言开发,需要一定语言基础可以用梯形图开发,掌握了逻辑和方法一天就能快速上手
可靠性对软件和硬件的设计要求极高,一般人设计的并不稳定大品牌的都已经在无数场合应用且相对可靠



    其实说了这么多不难看出,其实对于有能力的人来说,单片机加上外围电路,在设计好底层的驱动,就可以作为一个PLC了,这就诞生了我们这一个系列章节的主题,用STC32去仿一个PLC,这里我们也叫工控板(后文均用工控板一词代替PLC)。
    当然,其实市面上PLC非常的多,有很多的系列,这里着重以FX系列的这一款来分析。

一、原理分析
   
    首先,用过FX系列的的小伙伴可能都知道,这个型号基本都可以通过这个RS232下载程序,而232的本质就是就是串口,所以很明显我们只需要通过单片机的一个串口就可以轻松的实现我们的工控板教程了。

    其次我们还需要知道这个软件和工控板之间是怎么连接的。点击上图的按钮,在点开一个串口监控软件,在进行如下配置,去监控一下串口连接的过程,我们很轻易就可以实现连接。

经过监控,可以发现数据的内容如下图所示:


应答过程分为如下几步:
1.上位机发送05
2.下位机回应06
3.上位机发送02 30 30 45 30 32 30 32 03 36 43
4.下位机回应02 41 38 35 45 03 46 36

5.上位机发送02 30 30 45 43 41 30 32 03 38 45
6.下位机回应02 36 38 33 46 03 45 41


7.上位机发送02 30 30 45 30 32 30 32 03 36 43
8.下位机回应02 41 38 35 45 03 46 36

9.上位机发送02 30 30 45 43 41 30 32 03 38 45
10.下位机回应02 36 38 33 46 03 45 41


上面的上位机就是gx的软件,下位机就是代指工控板,经过如上的几步即可实现让GX将工控板识别为FX3U



二、代码实现
1.因为代码工作量非常庞大,所以这里第一步需要STC的芯片设置为仿真芯片(注意这里主频设置为22.1184Mhz),然后这里用LINK的第二组串口作为下载的串口,硬件连接如下图所示,设置为仿真芯片的过程见如下帖子:https://www.stcaimcu.com/forum.php?mod=viewthread&tid=324


LINK1D的下载口直接插板子的下载口,link上的S-TXD接STC的P1.1,S-RXD接板子上的P1.0。

2.编写串口代码,由于stc32g有串口的发送/接收DMA,这里直接用DMA实现收发,虽然这一版没有串口超时中断,但是我们还是可以用软件模拟的方式来实现超时检测,具体代码实现参考试验箱的这个代码即可,这里简单带过


这里的DMA的代码主要分为如下几步:
(1)串口初始化
void InitHardUart(void)
{
    T2R = 0;                //Timer stop
    T2_CT = 0;      //Timer2 set As Timer
    T2x12 = 1;      //Timer2 set as 1T mode
    T2H = (u8)((65536 - MAIN_Fosc / Baudrate2 / 4) / 256);
    T2L = (u8)((65536 - MAIN_Fosc / Baudrate2 / 4) % 256);
    ET2 = 0;    //禁止中断
    T2R = 1;                //Timer run enable
   

    S2CFG |= 0x01;   //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
    S2CON = (S2CON & 0x3f) | 0xc0;    //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
    ES2   = 1;         //允许中断
    S2REN = 1;         //允许接收
    S2_S= 0;         //UART2 switch to: 0: P1.0 P1.1,1: P4.6 P4.7

    DMA_UR2T_CFG = 0x80;                //bit7 1:Enable Interrupt
    DMA_UR2T_STA = 0x00;
    DMA_UR2T_AMT = 0x10;                //设置传输总字节数(低8位):n+1
    DMA_UR2T_AMTH = 0x00;                //设置传输总字节数(高8位):n+1
    DMA_UR2T_TXAH = (u8)((u16)&g_tUart2.g_TxBuf >> 8);
    DMA_UR2T_TXAL = (u8)((u16)&g_tUart2.g_TxBuf);
//                DMA_UR2T_CR = 0xc0;                        //bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送

    DMA_UR2R_CFG = 0x80;                //bit7 1:Enable Interrupt
    DMA_UR2R_STA = 0x00;
    DMA_UR2R_AMT = 0xFF;                //设置传输总字节数(低8位):n+1
    DMA_UR2R_AMTH = 0xFF;                //设置传输总字节数(高8位):n+1
    DMA_UR2R_RXAH = (u8)((u16)&g_tUart2.g_RxBuf >> 8);
    DMA_UR2R_RXAL = (u8)((u16)&g_tUart2.g_RxBuf);
    DMA_UR2R_CR = 0xa1;                        //bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO

    g_tUart2.com= COM2;             // 端口号
    g_tUart2.baud = Baudrate2;       // 波特率
   

    g_tUart2.txBufSize = TX_length;      // 发送缓冲区大小
    g_tUart2.rxBufSize = RX_length;      // 接收缓冲区大小
    g_tUart2.rxCnt = 0;                                                // 接收到的新数据个数
    g_tUart2.txCnt = 0;                                                // 待发送的数据个数
//                g_tUart2.SendBefor = 0;                                                // 发送数据前的回调函数
//                g_tUart2.SendOver = 0;                                                // 发送完毕后的回调函数
    g_tUart2.ReciveNew = 0;                                                // 接收到新数据后的回调函数
   
    g_tUart2.txflag = 1;
    g_tUart2.rxflag = 0;
    g_tUart2.timeout =0;      
}先配置串口的波特率和定时器,在配置接收DMA和发送的DMA,最后清空串口数组的数据,这里需要注意的是串口的发送和接收缓冲区必须设置为XDATA区域,否则会出现不可预知的错误。

(2)系统时钟配置
void Timer0_Init(void)                //1毫秒@22.1184MHz
{
      AUXR |= 0x80;                        //定时器时钟1T模式
      TMOD &= 0xF0;                        //设置定时器模式
      TL0 = 0x9A;                              //设置定时初始值
      TH0 = 0xA9;                              //设置定时初始值
      TF0 = 0;                              //清除TF0标志
      TR0 = 1;                              //定时器0开始计时
      ET0 = 1;                              //使能定时器0中断
}这里配置了一个1ms的定时器,用来做串口超时的判断

(3)串口收发代码配置(注意串口需要设置为7-E-1,波特率9600)
    if( Time_Run_Last!=Time_Run)
    {
      if(Usart_RecCheck(&g_tUart2,Time_Run-Time_Run_Last)==2)
      {
            length = Usart_RecLength(&g_tUart2);
            
            Usart_Send(&g_tUart2,&g_tUart2.g_RxBuf,length);
            Usart_RecSet(&g_tUart2,200);
      }
      
      if( Time_Run>5000 )                                                                                                //时间点间隔处理
            Time_Run -=5000;      
      Time_Run_Last = Time_Run;
    }这里判断串口接收成功,就获取串口接收到的数据长度(因为串口接收DMA只有到达设定长度的数据才会触发DMA中断,所以这里要手动获取长度),接收到的数据都保存在了串口数组comm_port.g_RxBuf里,这几切记读取一次之后需要清除数据才能接收下一次的数据,不然数据会一直叠加上去。

(4)通信数据判断
从上面的代码不难看出,上位机发送的第一个指令05就是通信的指令,这个指令明显和别的不太一样,别的都是02开头,03结尾,这个只有个孤零零的05,所以这个指令单独处理,上位机发送05过来,就直接给他回个06
            //--------------------------检测CPU指令--------------------------
            if(( length==1 )&&( comm_port.g_RxBuf == 0x05 ))
            {
                comm_port.g_TxBuf = 0x06;
                comm_port.txCnt =1;
            }
其次,下面的两个型号识别指令,
            //--------------------------检测通信指令--------------------------
            if(( length>2 )&&( comm_port.g_RxBuf==0x03 )) //判断传送结束
            {
                sum = CheckSum( comm_port.g_RxBuf,length );
                              if((comm_port.g_RxBuf == Ascll)&&(comm_port.g_RxBuf == Ascll))// 计算数据和状态 数据是否正常
                              {               
                                        Process_switch2();
                              }
                              else            //错误应答
                              {
                                        comm_port.g_TxBuf =0x15;
                                        comm_port.txCnt =1;
                              }               
            }先判断这个接收到的数据包是否是02开头,03结尾,其次校验数据包,校验正确在执行,错误的话直接发送一个15返回。
校验正确的话,在吧数据转化下格式,

void switch_read_data2(void)
{
      u16 temp;
      for(temp=2; temp<(length -4); temp++)
      {
                comm_port.g_TxBuf=hex]*0x10;
                comm_port.g_TxBuf+=hex];
                temp++;
      }
}接收到的数据转为程序数据,即ascll转为hex数据,例如02 30 30 45 30 32 30 32 03 36 43这个命令执行完就是comm_port.g_TxBuf=0X0E, comm_port.g_TxBuf=0X02


然后再来判断这个命令的功能,
void PC_READ_byte2(void)         //读字
{
      u16 prog_address =comm_port.g_TxBuf*0x100 +comm_port.g_TxBuf;   //计算数据操作起始地址
      switch(prog_address)
      {
      case 0x0ECA:
            read_plc_tyte2(101);
            break;,
            
      case 0x0E02:
            read_plc_tyte2(1);
            break;
            
      default:
//            read_other_data2();
            break;
      }
}
还是以02 30 30 45 30 32 30 32 03 36 43为例,执行完数据格式转换之后comm_port.g_TxBuf=0X0E, comm_port.g_TxBuf=0X02,这里prog_address 就是0x0e02,直接读取内部存储的一个数据返回即可,这数据就是类似于识别码的一个指令,直接手动填入即可{:5_284:}

至此这个识别的过程就完成了。需要源代码的小伙伴群文件下载即可,有问题的小伙伴欢迎帖子下面留言~
开源不易,禁止商用!



xiangzichen 发表于 2024-4-7 09:47:34

我是第一个来的??
开贴大吉,开始学习!

咫尺天涯 发表于 2024-4-7 10:04:10

学习了

yinds 发表于 2024-4-7 10:27:08

厉害了,是不是我下载上你的工控板建立连接的程序,就可以直接用三菱的那个梯形图来编程了啊,牛{:4_174:}

电子DIY小家 发表于 2024-4-7 10:37:54

yinds 发表于 2024-4-7 10:27
厉害了,是不是我下载上你的工控板建立连接的程序,就可以直接用三菱的那个梯形图来编程了啊,牛 ...

三菱的t型图下载马上更新,最后可以当三菱的plc来用这个板子

李鑫发 发表于 2024-4-7 11:22:49

坐等大神最终作品

molo 发表于 2024-4-7 12:08:45

学习学习,谢谢!

hu3507 发表于 2024-4-7 12:13:15

{:sweat:}有没有原文件分享的

xxxevery 发表于 2024-4-7 12:26:06

坐等开源

电子DIY小家 发表于 2024-4-7 12:50:13

hu3507 发表于 2024-4-7 12:13
有没有原文件分享的

软件什么的群文件下载
页: [1] 2 3 4 5 6
查看完整版本: PLC 实战, 让你的STC32变成FX3U-PLC, 教程(一)建立连接