标题: 基于12单片机通过双串口查询电表电量的问题,麻烦大家帮我看一下,感激不尽(DLT64... [打印本页]

作者: 宁采尘    时间: 2017-6-9 13:26
标题: 基于12单片机通过双串口查询电表电量的问题,麻烦大家帮我看一下,感激不尽(DLT64...
   STC12系列单片机拥有双串口,所以我用串口一与电表通信,串口二与电脑通信,以单片机为枢纽,把串口一收到到数据再从串口二发送给电脑,同样电脑端发送查询数据,hex模式,通过串口二传递给串口一再发送出去,具体请看我程序。问题就出在这,电脑发送的数据发送出去了,却没有收到电表回复的数据。
  我单独测试过串口一二与电脑通信没有问题,并将两串口同时接到电脑,在电脑端打开两个串口助手,也能完成通信。当我把串口一摘下经过TTL转232模块、232转485模块再接到电表测试时,收不到电表传回的信息。
  一开始我是直接用的TTL转485与电表通信的,也是没有成功,在论坛看到有位前辈用TTL转232再232转485与电表通信成功了,他是用的51 ,单串口1602显示的,然后按照他硬件电路试了一下还是没有成功。我想问题应该是出在我程序上了,麻烦各位前辈帮我看一看有什么问题,通信规约我已经仔细看过了,程序要求不需要太复杂,只要求发送同一条指令(我已存到数组中),能接收到电表返回的数据信息就行,具体数据我再进行处理就好,麻烦大家帮我完成这一步,在下感激不尽,有地方我说的不明白的请再询问我,我真的被这个程序块搞坏了。。。
  下面是我程序,麻烦大家帮我分析一下。




/*要实现功能:通过单片机两个串口,串口二连接在电脑端,
              串口一连接电表,完成查询正向有功功率任务。
  目前进度:  两串口均测试可与电脑单独通讯没有问题,
              把串口一接入电表无法接收到返回信息
*/
#include <stc12c5a60s2.h>
#include "intrins.h"
#include "uart.h"
uchar flag=0,dat,a=0,b=0;

uchar code uart[]={0xFE,0xFE,0xFE,0x68,0x74,0x27,0x12,0x00,
                             0x80,0x13,0x68,0x01,0x02,0x43,0xC3,0x19,0x16};
                                   //读正向有功总电能命令帧,电表
uchar data uart_1[36];
uchar data uart_2[36];
#define S2TI 0x02      // 串口2发送中断请求标志位
#define S2RI 0x01      // 串口2接收中断请求标志位
sbit REDE=P1^4;

void delay(uint x)          //  延时子函数
{
        uint i,j;

        for(i=x;i>0;i--)
          for(j=110;j>0;j--);
}

void main()
{
    uchar m;
        UartInit_1();        //初始化串口一          //  为遵循DLT645-1997与电表通信规约
        UartInit_2();        //初始化串口二           //两串口        波特率均设为1200bps
        while(1)
        {               
                  if(flag=0)
                  {
                   for(m=0;m<17;m++)                //上电先通过串口一发送读正向有功总电能命令帧
                   {                                                //根据DLT645-1997规约编辑的命令帧,串口波特率1200bps
                      s1_send_char(uart[m]);                  
                   }
                   flag=1;          //标志变为1,等待串口一接收数据后标志再次变为0.
                              //重复查询功能暂时不用,先用电脑手动再次发送
                  }
                   delay(1000);                                                                                
        }
          
}
void UartInit_1(void) //1200bps@12MHz
{
        TMOD=0x20;          //设置定时器1位工作方式2
        TH1=0xE8;          //方式2为8位自动重装,
                       //初值查表可得1200波特率
        TL1=0xD0;
        TR1=1;         //打开定时器1
        SM0=0;         //设置串口工作方式1,10位固定
        SM1=1;
        REN=1;         //允许串口接收数据
        EA=1;         //打开总中断
        ES=1;         //打开串口中断
}

void UartInit_2(void) //1200bps@12MHz        0xE8
{
AUXR = 0x10; //波特率不倍速
S2CON = 0x50; //8位数据,可变波特率
BRT = 0xE8; //设定独立波特率发生器重装值
IE2 |= 0x01;            //充许串口2中断
//AUXR &= 0xfb; //独立波特率发生器时钟为Fosc/12,即12T
//AUXR = 0x10; //启动独立波特率发生器

}
/*串口一发送程序*/
void s1_send_char(uchar dat)//发送端(发送的是字符)
{
        SBUF=dat;       
        while(!TI);         
        TI=0;          //清零标志
}

void s1_send_string(uchar *pt)         //发送字符串
{
        while(*pt != '\0')
        {
                s1_send_char(*pt++);
        }
}
/*串口二发送程序*/
void s2_send_char(uchar dat2)   // 发送端(发送的是字符)
{
        S2BUF = dat2;  
        while(!(S2CON&S2TI));
        S2CON &= ~S2TI;
}

void s2_send_string(uchar *pt2)           //发送字符串
{
        while(*pt2!='\0')
        {
                s2_send_char(*pt2++);
        }
}
                                                                                                                                 
void UART1() interrupt 4  //串口1中断函数          
{                                                  //串口一收到电表数据,存入uart2,再通过串口二发送到电脑                                                                                                          
    if(RI==1) //接收数据
        {       
                uart_2[a]=SBUF;                 
                s2_send_char(uart_2[a]);  //串口二发送到电脑
                a++;
                RI=0;
                if(a>=17)         //接收17位       
                {
                        a=0;
                //        flag=0;          //先不让标志位=0;想要再发送查询正向有功功率可以在电脑端串口二发送
                }
        }                                                                                                       
//        if(RI==1)                                                                                                                       
//        {
//          if(a==0&&0x68==SBUF)
//          {         
//                  uart_2[0]=SBUF;
//                  s2_send_char(uart_2[a]);                     
//          }       
//          
//          if(a>0&&uart_2[0]==0x68)
//          {
//             uart_2[a]=SBUF;
//                   s2_send_char(uart_2[a]);
//                                                                                         
//          }
//          a++;            
//          RI=0;                                                                                     
//         }
//         
//         if(a>=36)
//         {
//                 a=0;
//         }                                                                                                                  
//         flag=1;                                                                                 
}
void UART2() interrupt 8  //串口2中断函数                                          
{                                                 //如果串口二接收到电脑端发送的查询命令帧数据,
                         //先存入数组uart1,再通过串口一发送给电表                                                                                                                                             
        if(S2CON&S2RI)                                                                                         
        {                                                                                                                      
           uart_1[b]=S2BUF;
           S2CON&=~S2RI;
        }
        s1_send_char(uart_1[b]);        //让串口一发送查询命令帧数据
        b++;
         if(b>=17)            
         {
                 b=0;
         }
//         flag=1;
}

P70609-115726.jpg (3.39 MB, 下载次数: 108)

双串口均接到电脑测试没问题

双串口均接到电脑测试没问题

P70609-115822.jpg (3.16 MB, 下载次数: 105)

把串口一换到电表上

把串口一换到电表上

QQ截图20170609115657.png (362.41 KB, 下载次数: 129)

串口一二接到电脑时的测试

串口一二接到电脑时的测试

P70609-131914.jpg (2.83 MB, 下载次数: 110)

电表138000122774

电表138000122774

作者: dzbj    时间: 2017-6-9 14:13
上班呢 程序没看 你说的应用我用过 没有任何问题 你坚信一定会成功吧

我用12c5a做显示屏 屏幕是串口屏 用uart2和屏通讯 uart1+485和其它模块通讯 和你的应用差不多

1. uart2可以用计时器做波特率发生器也可以用独立波特率发生器 如果用两个串口 尽量各自用一个波特率发生器 我是因为两个串口速率不一样 其它模块是57600 屏是115200

2. 485完全无需转其它方式 直接ttl到485就没问题 这个我用过很多很多很多 但485片子不同厂家有时候会出各种莫名其妙的不通讯问题 要注意485的电源滤波电容大小 按你买品牌的手册要求高 我遇到过要求1.5uf的我用0.1uf就不行

3. uart1和2的转发 你直接在内存开个缓冲区 如果第一位不是0或者ff或者你定义的什么东西 就直接发 实验的时候 你可以不断的用串口1往上位机发缓冲区内容 观察串口2是否正常工作了 这样有利于找到问题 我就是写了个双向直接转发的程序 连串口屏自动联机都可以
作者: 宁采尘    时间: 2017-6-9 14:14
请大家帮帮分析分析
作者: dzbj    时间: 2017-6-9 14:22
另 附上我用的串口1和2初始化 基本上是用STC-ISP生成的 你对比一下看看 也许有帮助

void Init_UART1();                                                                //串口初始化 1T/定时器1作波特率发生器

void Init_UART1()        //串口初始化 1T/定时器1作波特率发生器
{
        PCON |= 0x80;                        //使能波特率倍速位SMOD
        SCON = 0x50;                        //8位数据,可变波特率
        AUXR |= 0x40;                        //定时器1时钟为Fosc,即1T
        AUXR &= 0xFE;                        //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                        //清除定时器1模式位
//        TMOD |= 0x21;                        //设定定时器1为8位自动重装方式
        TMOD |= 0x20;                        //设定定时器1为8位自动重装方式
        TL1=TH1=BAUD1;                        //设定定时初值
        ET1 = 0;                                //禁止定时器1中断
        TR1 = 1;                                //启动定时器1
        ES=1;                                        //开串口中断
}

void Init_UART2();                //串口2初始化 1T/独立波特率发生器(串口2只能使用独立波特率发生器)

void Init_Uart2()                //串口2初始化 115200bps@11.0592MHz@独立波特率发生器
{
       
        AUXR |= 0x08;                //使能波特率倍速位S2SMOD 0000 1000
        S2CON = 0x50;                //8位数据,可变波特率 0101 0000
        AUXR |= 0x04;                //独立波特率发生器时钟为Fosc,即1T 0000 0100
        BRT = BAUD2;                //设定独立波特率发生器重装值
        AUXR |= 0x10;                //启动独立波特率发生器 0001 0000
        IE2=0X01;                        //允许串口2中断
}

作者: lshhjx    时间: 2017-6-9 14:37
首先问一下楼主,你串口一能够正常和电表通讯吗?
作者: 宁采尘    时间: 2017-6-9 17:02
lshhjx 发表于 2017-6-9 14:37
首先问一下楼主,你串口一能够正常和电表通讯吗?

串口一能正常通讯我确定,但是和电表通讯没有成功,我每次发送数据,模块的发送指示灯都会亮,但是接收指示灯一直没有亮,应该是通讯没有成功。
作者: 宁采尘    时间: 2017-6-9 17:22
dzbj 发表于 2017-6-9 14:22
另 附上我用的串口1和2初始化 基本上是用STC-ISP生成的 你对比一下看看 也许有帮助

void Init_UART1();         ...

我串口一波特率发生器用的T1,串口二用的独立的波特率发生器。因为DLT645-1997通信规约要求,与电表通信波特率为1200bps,为方便,我两个都设了1200bps。发送数据,模块RXD指示灯会亮,但是TXD指示灯一直都不会亮,应该是通讯没有成功。我没有设奇偶校验,单纯的发送查询命令帧后等待串口一接收中断,好像一直没有触发。您有时间再帮我在看看吧,我也尝试其他方法。方便加QQ交流吗875576671
作者: dzbj    时间: 2017-6-9 21:13
宁采尘 发表于 2017-6-9 17:22
我串口一波特率发生器用的T1,串口二用的独立的波特率发生器。因为DLT645-1997通信规约要求,与电表通信 ...

我大概看了一下你说的DLT645-1997 这是个通讯协议 从你说的情况感觉不是单片机串口的问题 是和DLT645-1997通讯没成功 你可以这样 先不用串口2 直接把上位机并联到串口1上 相当于三机互连 然后看单片机发送给电表和电表反馈的数据是什么 根据反馈修改程序 直到调通它们后 再考虑串口2转发的问题
作者: 宁采尘    时间: 2017-6-10 11:24
dzbj 发表于 2017-6-9 21:13
我大概看了一下你说的DLT645-1997 这是个通讯协议 从你说的情况感觉不是单片机串口的问题 是和DLT645-199 ...

串口一接收不到反馈信息,我现在也不确定是电表没接收到数据,还是我的接收数据程序有问题。我可以确定的是我再电脑端的串口助手能收到我发送的查询数据。而且这串查询数据我用抄表软件发送时没问题的,唯独放到我程序里没有回应。您看一下我的发送和接收有问题吗?
//发送函数,在主函数中,截取相关部分
         while(1)
        {
                  if(k==0)        //按键按下一次发送一次查询命令
                  {
                          delay(20);
                          if(k==0)
                          {
                                  flag=0;
                          }
                  }
                  if(flag==0)
                  {
                   for(m=0;m<15;m++)
                   {
                   s1_send_char(uart[m]);                 
                   }
                   flag=1;
                  }
       //           uchar code uart[]={0xFE,0x68,0x74,0x27,0x12,0x00,
       //          0x80,0x13,0x68,0x01,0x02,0x43,0xC3,0x19,0x16};
                                   //读正向有功总电能命令帧
     }

//接收函数,截取相关部分,已将串口1并连电脑和电表。
/*串口一发送程序*/
void s1_send_char(uchar dat)//?发送端(发送的是字符)
{
        SBUF=dat;       
        while(!TI);         
        TI=0;          //清零标志
}
void UART1() interrupt 4  //串口1中断函数          
{                                                                                                                                                          
    if(RI==1)
        {       
                uart_2[a]=SBUF;
                s1_send_char(uart_2[a]);
                a++;
                RI=0;
                if(a>14)
                {
                        a=0;
                //        flag=1;
                }
        }
}                        
作者: dzbj    时间: 2017-6-10 19:50
宁采尘 发表于 2017-6-10 11:24
串口一接收不到反馈信息,我现在也不确定是电表没接收到数据,还是我的接收数据程序有问题。我可以确定的 ...

大致扫了一下你的程序 我觉得没什么问题 有个建议是  s1_send_char(uart_2[a]); 这种函数尽量不放在中断里 你这里这么用没问题 但是冲断的优先级都高于正常程序 以后写别的有可能有影响 养成个好习惯

你这段程序的意思是把串口2收到的东西用串口1转发出来是么 如果是 先不进行这个 还是我之前说的 你直接把上位机并在单片机和模块之间的通讯上 看它们之间聊的怎么样

昨天看你说那个协议的时候 也是匆匆一扫 它好象有什么对位或者应答之类的要求 你这程序里有么
作者: 宁采尘    时间: 2017-6-12 09:31
dzbj 发表于 2017-6-10 19:50
大致扫了一下你的程序 我觉得没什么问题 有个建议是  s1_send_char(uart_2[a]); 这种函数尽量不放在中断 ...

通信规约上没应答之类,此类通讯应该是半双工通信,数据的发送和接收都有起始符,结束符,和校验码,我在发送和接收中加了判断起止结束位和校验码,奇偶校验位(以前没加,因为校验码都计算好了没有问题,所以本不想判断了),对比和参考了一下前人写的液晶显示的51与电表通信。最后通信成功,将返回数据处理后与电表电量一致。但问题真正原因还是没找到,不加判断起始结束就没法接收吗(例如,判断起始位68H,如果不判断是不是68H,来的数据我都接收也应该可以啊,又不是应答之类的),我觉得问题不是出在这里。
作者: 宁采尘    时间: 2017-6-12 09:34
dzbj 发表于 2017-6-10 19:50
大致扫了一下你的程序 我觉得没什么问题 有个建议是  s1_send_char(uart_2[a]); 这种函数尽量不放在中断 ...

总之还是要谢谢您能在工作之余还能帮我分析问题,真的非常感谢,一个人的思路毕竟是有局限的,谢谢前辈的帮助,有机会再一起讨论
作者: nephty    时间: 2018-5-1 14:21
老哥,你解决了吗,我也碰到同样的问题,能讲下吗




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1