找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5703|回复: 12
收起左侧

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

[复制链接]
ID:209219 发表于 2017-6-9 13:26 | 显示全部楼层 |阅读模式
20黑币
   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;
}

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

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

把串口一换到电表上

把串口一换到电表上

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

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

电表138000122774

电表138000122774
回复

使用道具 举报

ID:47286 发表于 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是否正常工作了 这样有利于找到问题 我就是写了个双向直接转发的程序 连串口屏自动联机都可以
回复

使用道具 举报

ID:209219 发表于 2017-6-9 14:14 | 显示全部楼层
请大家帮帮分析分析
回复

使用道具 举报

ID:47286 发表于 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中断
}
回复

使用道具 举报

ID:59827 发表于 2017-6-9 14:37 来自手机 | 显示全部楼层
首先问一下楼主,你串口一能够正常和电表通讯吗?
回复

使用道具 举报

ID:209219 发表于 2017-6-9 17:02 | 显示全部楼层
lshhjx 发表于 2017-6-9 14:37
首先问一下楼主,你串口一能够正常和电表通讯吗?

串口一能正常通讯我确定,但是和电表通讯没有成功,我每次发送数据,模块的发送指示灯都会亮,但是接收指示灯一直没有亮,应该是通讯没有成功。
回复

使用道具 举报

ID:209219 发表于 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
回复

使用道具 举报

ID:47286 发表于 2017-6-9 21:13 | 显示全部楼层
宁采尘 发表于 2017-6-9 17:22
我串口一波特率发生器用的T1,串口二用的独立的波特率发生器。因为DLT645-1997通信规约要求,与电表通信 ...

我大概看了一下你说的DLT645-1997 这是个通讯协议 从你说的情况感觉不是单片机串口的问题 是和DLT645-1997通讯没成功 你可以这样 先不用串口2 直接把上位机并联到串口1上 相当于三机互连 然后看单片机发送给电表和电表反馈的数据是什么 根据反馈修改程序 直到调通它们后 再考虑串口2转发的问题
回复

使用道具 举报

ID:209219 发表于 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;
                }
        }
}                        
回复

使用道具 举报

ID:47286 发表于 2017-6-10 19:50 | 显示全部楼层
宁采尘 发表于 2017-6-10 11:24
串口一接收不到反馈信息,我现在也不确定是电表没接收到数据,还是我的接收数据程序有问题。我可以确定的 ...

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

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

昨天看你说那个协议的时候 也是匆匆一扫 它好象有什么对位或者应答之类的要求 你这程序里有么
回复

使用道具 举报

ID:209219 发表于 2017-6-12 09:31 | 显示全部楼层
dzbj 发表于 2017-6-10 19:50
大致扫了一下你的程序 我觉得没什么问题 有个建议是  s1_send_char(uart_2[a]); 这种函数尽量不放在中断 ...

通信规约上没应答之类,此类通讯应该是半双工通信,数据的发送和接收都有起始符,结束符,和校验码,我在发送和接收中加了判断起止结束位和校验码,奇偶校验位(以前没加,因为校验码都计算好了没有问题,所以本不想判断了),对比和参考了一下前人写的液晶显示的51与电表通信。最后通信成功,将返回数据处理后与电表电量一致。但问题真正原因还是没找到,不加判断起始结束就没法接收吗(例如,判断起始位68H,如果不判断是不是68H,来的数据我都接收也应该可以啊,又不是应答之类的),我觉得问题不是出在这里。
回复

使用道具 举报

ID:209219 发表于 2017-6-12 09:34 | 显示全部楼层
dzbj 发表于 2017-6-10 19:50
大致扫了一下你的程序 我觉得没什么问题 有个建议是  s1_send_char(uart_2[a]); 这种函数尽量不放在中断 ...

总之还是要谢谢您能在工作之余还能帮我分析问题,真的非常感谢,一个人的思路毕竟是有局限的,谢谢前辈的帮助,有机会再一起讨论
回复

使用道具 举报

ID:290231 发表于 2018-5-1 14:21 来自手机 | 显示全部楼层
老哥,你解决了吗,我也碰到同样的问题,能讲下吗
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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