📄 meter485_s.c
字号:
/*
485接口多功能电能表通信程序
依据: DL/T 645-1997 《多功能电能表通信规约》
----------------------------------------------------------------------------------------------
UCFG1 地址:xxxxh 未编程值:63h(看门狗关闭、复位脚使能、掉电检测使能和使用内部RC振荡器)
7 6 5 4 3 2 1 0
WDTE RPE BOE WDSE - FOSC2 FOSC1 FOSC0
----------------------------------------------------------------------------------------------
WDCON 地址:A7H 不可位寻址
7 6 5 4 3 2 1 0
PRE2 PRE1 PRE0 - - WDRUN WDTOF WDCLK
复位值:111xx1?1B (注:WDCON.7,6,5,2,0在任何复位时都置1,WDCON.1在上电复位时清零,在看门狗复位时置位,不受其它任何复位影响。)
----------------------------------------------------------------------------------------------
*/
#include <REG932.H>
#include <intrins.H>
#define revision "2006/08/17/01"
//buff格式: 5字节地址 + 1字节命令 + 1字节长度 + ?字节数据
#define buffR_size 40
unsigned char xdata buffR[buffR_size];
unsigned char state; //
unsigned char R_Done; //一帧完整接收成功标志
unsigned char timeout; //超时标志
unsigned int timeout_cnt; //超时计数器
sbit _RT = P0^7; //485收发控制线
////////////////////////////////////////////////////
void sendbyte(unsigned char bSend)
{
ACC = bSend;
TB8 = P;
TI = 0;
SBUF = bSend;
while(TI==0);
TI=0;
}
/*
函数名: unsigned char Read485(unsigned char *s)
参数: 发送缓冲区, 格式: 5字节地址 + 1字节命令 + 1字节长度 + ?字节数据
返回: 1: 读取超时; 0: 正常读取, 此时buffR[]中的数据已经被更新
*/
unsigned char Read485(unsigned char *s)
{
unsigned char i,temp,CS=0x00;
for( i=0;i<buffR_size;i++ )
{
buffR[i] = 0;
}
state = 0;
R_Done = 0;
timeout = 0;
timeout_cnt = 0;
//使能485发送
_RT = 1;
//发送两个前导字节
for(i=0;i<2;i++)
{
temp = 0xFE;
sendbyte(temp);
}
//发送起始字节
temp = 0x68;
CS += temp;
sendbyte(temp);
//发送六个地址字节
for(i=0;i<6;i++) //i=0,1,2,3,4,5
{ //结束时i=6
temp = *(s+i);
CS += temp;
sendbyte(temp);
}
//发送起始字节
temp = 0x68;
CS += temp;
sendbyte(temp);
//发送命令字节
temp = *(s+i);
CS += temp; //i=6
i++; //结束时i=7
sendbyte(temp);
//发送长度字节
temp = *(s+i); //i=7
CS += temp;
i++; //结束时i=8
sendbyte(temp);
//发送数据
for(;i< *(s+7) + 8;i++) //
{ //结束时i=len+8
temp = *(s+i) + 0x33;
CS += temp;
sendbyte(temp);
}
//发送校验和
temp = CS;
sendbyte(temp);
//发送结束符
temp = 0x16;
sendbyte(temp);
//使能485接收
_RT = 0;
RTCCON = 0x60;
RTCCON = 0x63; //启动实时时钟监控是否超时
while( !R_Done )
{
if( timeout )
{
for( i=0;i<buffR_size;i++ )
{
buffR[i] = 0;
}
state = 0;
R_Done = 0;
timeout = 0;
timeout_cnt = 0;
return 1; //一帧未完整接收, 并且已经超时, 返回1
}
}
return 0; //一帧完整接收, 返回0
}
void UART_ISR(void) interrupt 4
{
//i,缓冲区元素索引; j,数据域元素索引; CS,校验和
static unsigned char i=0,j=0,CS=0;
unsigned char temp;
if(RI) //如果是接收中断
{
RI = 0;
temp = SBUF;
ACC = temp;
if( P!=RB8 )
{
goto ERR1;
}
// if( timeout )
// {
// state = 0;
// i = 0;
// j = 0;
// CS=0x00;
// } //超时之后, 当前接收到的数据被认为是一帧的第一个字节
//所以仍需要进入下面的分支判断
switch( state )
{
case 0 :
i = 0;
j = 0;
CS = 0;
if( temp != 0xfe ) //如果不是前导字节
{
goto ERR1 ;
}
else
{
state = 1;
goto END1;
}
break;
case 1 :
if( temp == 0xfe ) //如果还是前导字节
{
goto END1;
}
else
{
if( temp == 0x68 ) //如果是帧头
{
CS+=0x68;
state = 2;
goto END1;
}
else //如果不是帧头
{
goto ERR1;
}
}
break;
case 2:
buffR[i++]=temp; //接收电表通讯地址 i=0,1,2,3,4,5, 结束时 i=6
CS+=temp;
if( i==6 )
{
state = 3;
}
goto END1;
break;
case 3:
if( temp != 0x68 ) //如果不是帧头
{
goto ERR1;
}
CS+=0x68;
state = 4;
goto END1;
break;
case 4:
buffR[i++]=temp; //接收通讯命令 i=6, 结束时 i=7
CS+=temp;
state = 5;
goto END1;
break;
case 5:
buffR[i++]=temp; //接收通讯数据长度 i=7, 结束时 i=8
j=temp;
CS+=temp;
state = 6;
goto END1;
break;
case 6:
buffR[i++]=temp-0x33; //接收 ? 个数据
CS += temp; //temp是加了0x33的
if( i==j+8 ) //结束时 i=j+8
{
state = 7;
}
goto END1;
break;
case 7:
if(CS!=temp) //如果校验和不正确
{
goto ERR1;
}
state = 8;
goto END1;
break;
case 8:
if( temp != 0x16 ) //如果不是帧尾
{
goto ERR1;
}
RTCCON = 0x60; //一帧正常结束关闭实时时钟, 取消超时监控
state = 0;
R_Done = 1;
timeout = 0;
timeout_cnt = 0;
goto END2;
break;
}
ERR1:
for( i=0;i<buffR_size;i++ )
{
buffR[i] = 0;
}
state = 0;
R_Done = 0;
END1:
timeout = 0;
timeout_cnt = 0;
RTCCON = 0x60;
RTCCON = 0x63; //启动实时时钟监控是否超时
END2:
_nop_();
} //end of RI;
else //如果是发送中断
{
}
}
void CLRWDG()
{
EA = 0;
WFEED1 = 0xA5;
WFEED2 = 0x5A;
EA = 1;
}
void RTC_ISR() interrupt 10
{
unsigned char temp;
temp = RTCCON & 0x80;
if( temp == 0x80 ) //如果是由实时时钟产生的中断
{
RTCCON &= 0x7F; //清除实时时钟中断标志
timeout_cnt++;
if(timeout_cnt>500)
{
timeout = 1;
RTCCON = 0x60; //关闭实时时钟, 取消超时监控
}
}
temp = WDCON & 0x02;
if( temp ) //如果是由看门狗时钟产生的中断
{
CLRWDG();
}
}
void main ()
{
unsigned char code rev[13]=revision;
unsigned char i,ERR;
//buff格式: 5字节地址 + 1字节命令 + 1字节长度 + ?字节数据
unsigned char data buff []={0x99,0x13,0x07,0x00,0x00,0x00,0x01,0x02,0x10,0x90};
//I/O口配置:
P1M1 = 0x00;
P1M2 = 0x00;
P0M2 = 0x00;
P0M1 = 0x00;
//串口配置: 工作模式及波特率
SCON=0xD0; //9位UART, 模式3, 波特率由波特率发生器决定
SSTAT=0x00; //
BRGCON=0x00; //BRGR1 & BRGR0=(1/9600)/(1/7.3728M)=7.3728M/9600=768=0x0300
BRGR0=0xF0; //0x02F0 9600
BRGR1=0x17; //0x1800 1200
BRGCON=0x03;
//实时时钟配置:
RTCH = 0x00; //超时时间计算公式: ( (RTCHL<<7) | 0x7F ) * Tcclk
RTCL = 0x3A; //0X003A 大约1mS; 0x0006 大约0.1mS
RTCCON=0x60;
//看门狗配置:
WDL = 0xFF;
EA = 0;
WDCON = 0xE5;
WFEED1 = 0xA5;
WFEED2 = 0x5A;
EA = 1;
//中断配置:
IP0 = 0X10;
IP0H = 0X10;
ES = 1; //使能串口中断
EWDRT= 1; //使能实时时钟及看门狗中断
EA = 1;
//变量初始化
state = 0;
R_Done = 0;
timeout = 0;
timeout_cnt=0;
for( i=0;i<buffR_size;i++ )
{
buffR[i] = 0;
}
while(1)
{
CLRWDG();
ERR = 2;
//ERR = Read485(buff);
if( ERR == 1 )
{
_nop_();
}
_nop_();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -