📄 wirecomm485.c
字号:
//=========================================================================
// 通信程序(szb 2006/03/13) v1.0.0
//
//
//=========================================================================
#ifndef _COMM485
#define _COMM485
#include "datastru.h"
#include "func.h"
#endif
///////////////////////////////////////////////////////////////////////////
//name:RS485comminit()
//function:RS485通信初始化,USART0
//parameter:none
//return:none
//现将波特率设定为9600bps
///////////////////////////////////////////////////////////////////////////
void RS485comminit(void)
{
unsigned char i;
UBR00 = 0x55; // 409600hz 4800 - 85.33 **
UBR10 = 0x00;
UMCTL0 = 0x92;
UCTL0 = CHAR; // 8位
UTCTL0 = SSEL1; // UCLK = SMCLK
ME1 = URXE0 + UTXE0; // 允许USART0 TXD/RXD
IE1 |= URXIE0; // 允许接收中断
origRS485 = RqueueRS485;
headRS485 = RqueueRS485;
tailRS485 = RqueueRS485;
hoffsetRS485 = 0;
toffsetRS485 = 0;
tlengthRS485 = 0;
for (i=0;i<RqueueRS485_Length;i++) //每次复位后,清除接收和发送队列
RqueueRS485[i] = 0;
for (i=0;i<TqueueRS485_Length;i++)
TqueueRS485[i] = 0;
}
///////////////////////////////////////////////////////////////////////////
//name:RS485offset()
//function:计算循环队列头尾指针偏移量的函数
//parameter:Offset-原偏移量的值,n为移位位数,length为接收队列长度
//return:Offset-新的指针偏移量
//explain:每次在对接收队列进行写入和读出操作时,需要移动头尾指针。我们所
// 设计的接收队列是一个循环队列。为了使头尾指针不超出循环队列规定
// 的范围,我们将接收队列的首地址定位在orig变量中,又分别定义头尾
// 指针的偏移量hoffset以及toffset。每次头尾指针移位的时候,先计算
// 他们的偏移量。最终的头尾指针的位置由偏移量加上orig得到。而偏移
// 量由以下计算函数确定,保证了偏移量的范围在0-(length-1)之间,
// 从而保证了头尾指针不会超出循环队列的范围。
///////////////////////////////////////////////////////////////////////////
int RS485offset(int Offset, int n, int length)
{
return Offset = (Offset + n) % length;
}
///////////////////////////////////////////////////////////////////////////
//name:*RS485voffset()
//function:已知头指针head在循环队列中的指向,求*(headRS485 + n)的值.n可正可负.
//parameter:int n为head前或后偏移量的位数
//return:Pdata = headRS485 + n
//explain:当头指针head比较靠近循环队列的末尾时,(head+n)很可能就指到循环队列外
// 面去了为了防止这种情况发生,需要根据情况先判断(headRS485 + n)是否会溢出,如果
// 溢出则将其重新指回循环队列内部的元素,使队列真正成为一个循环队列.
//update:2003.2.20 by wjg
///////////////////////////////////////////////////////////////////////////
unsigned char *RS485voffset(int n)
{
unsigned char *Pdata;
Pdata = origRS485 + RS485offset(headRS485 - origRS485, n, RqueueRS485_Length);
return Pdata;
}
///////////////////////////////////////////////////////////////////////////
//name:RS485ally()
//function:将两个单字节数据合成为一个字数据字的函数
//parameter:unsigned int *p-待处理数据首位的地址
//return:b-合成后的字数据
//explain:将接收到的一帧数据中相邻的两个字节组合成一个字数据,由于先接收
// 到的数据是字数据的高字节,后接收到的数据才是该字数据的低字节。
// 所以,需要将相邻的两个字节调换位置,然后再依次写入一个字数据的
// 低8位和高8位,从而得到通信中需要的字数据。该函数就是为了完成这
// 个功能而设计的。
///////////////////////////////////////////////////////////////////////////
unsigned int RS485ally(unsigned char *p)
{
unsigned int b;
b = *p;
b = (b << 8) | (0x00FF & (*(origRS485 + (p + 1 - origRS485) % RqueueRS485_Length)));
return b;
}
///////////////////////////////////////////////////////////////////////////
//name:RS485crc_count()
//function:CRC校验码计算函数
//parameter:p-需校验数据的头指针
// length-需校验数据的长度
//return:crcvalue:校验后得到的CRC校验码(16位)
///////////////////////////////////////////////////////////////////////////
unsigned int RS485crc_count(unsigned char *p, unsigned int length, unsigned int Send_Recieve)
{
unsigned int crcvalue;
unsigned char AH = 0xFF, AL = 0xFF;
unsigned char GH = 0xA0, GL = 0x01;
unsigned char i, j;
for(i = 0;i < length;i++, p++)
{
if (Send_Recieve == 1)// 因接收队列是循环队列,所以其中数据的CRC需要特殊处理
{
p = origRS485 + (p - origRS485) % RqueueRS485_Length;
}
AL = (*p) ^ AL;
for(j = 0;j < 8;j++)
{
if((AL & 0x01) == 1) // AL最低位为1
{
AL = AL >> 1;
if((AH & 0x01) == 1)
AL = AL | 0x80;
AH = AH >> 1;
AH = AH ^ GH;
AL = AL ^ GL;
}
else
{
AL = AL >> 1;
if((AH & 0x01) == 1)
AL = AL | 0x80;
AH = AH >> 1;
}
}
}
return crcvalue = (unsigned int)(AL * 0x100 + AH); //低位在前,高位在后
}
///////////////////////////////////////////////////////////////////////////
//name:RS485crc_check()
//function:接收队列CRC校验码检验函数
//parameter:datahead-所有需校验数据的首地址
// crcpointer-CRC校验码的首地址
// Send_Recieve-待检查CRC的 数组是否循环队列
// CommType-通信类型
//return:0-数据不合法
// 1-数据合法
//update:2003.2.20 by wjg
///////////////////////////////////////////////////////////////////////////
unsigned int RS485crc_check(unsigned char *datahead, unsigned char *crcpointer, unsigned int Send_Recieve)
{
unsigned int mycrcvalue, tempcrc;
mycrcvalue = RS485crc_count(datahead, (crcpointer - datahead), Send_Recieve);
if (crcpointer - origRS485 >= RqueueRS485_Length - 1)
tempcrc = ((*(origRS485 + (crcpointer - origRS485) % RqueueRS485_Length)) << 8) | (0x00FF & (*(origRS485 + (crcpointer + 1 - origRS485) % RqueueRS485_Length)));
else
tempcrc = RS485ally(crcpointer);
if (mycrcvalue == tempcrc) return 1;
else return 0;
}
///////////////////////////////////////////////////////////////////////////
//name:RS485MoveHead()
//function:将头指针在循环队列中移动n位
//parameter:int step,头指针要移动的步数;unsigned char *CommType,通信类型
//return:none
//update:2003.2.20 by wjg
///////////////////////////////////////////////////////////////////////////
void RS485MoveHead(int step)
{
hoffsetRS485 = RS485offset(hoffsetRS485, step, RqueueRS485_Length);
headRS485 = origRS485 + hoffsetRS485;
}
///////////////////////////////////////////////////////////////////////////
//name:RS485Send_Tqueue()
//function:发送队列中的数据发送过程
//parameter:unsigned int MoveOffset,数据发送后头指针head要移动的步长
//return:none
//update:2006.2.20 szb
///////////////////////////////////////////////////////////////////////////
void RS485Send_Tqueue(unsigned int MoveOffset)
{
numberRS485 = 0;
TXBUF0 = TqueueRS485[numberRS485];
IE1 |= UTXIE0 + URXIE0;
RS485MoveHead(MoveOffset);
}
///////////////////////////////////////////////////////////////////////////
//name: operate034()
//function: 功能代码为0x03或0x04时的处理函数
//parameter:unsigned char *CommType,通信类型(RS485或者CAN),现在的写法有丢针隐患,应设立重发机制,注意此处还应补充对FRAM使能标志的操作
//return: none
///////////////////////////////////////////////////////////////////////////
void RS485operate03(void)
{
unsigned char SOEBYTEnum;//发送记录的条数,最大255字节,也就是说最大一次能发31条记录
unsigned int crccalculate;
unsigned int i;
if (RS485crc_check(headRS485, headRS485 + 7, 1) == 0) //如果是后续查询帧
{
RS485MoveHead(1);
return;
}
/*应答初始查询帧*/
if (RS485ally(RS485voffset(3)) == 0xFFFF)
{
SOEamount_send = FM_word_read(FRAM_NUM_SOE); //FRAM_NUM_SOE 表示保存在FRAM中的尚未发送过的事件数目
TqueueRS485[0] = *headRS485;// 构造发送队列
TqueueRS485[1] = 0x03;
TqueueRS485[2] = 2;
TqueueRS485[3] = (unsigned char)(SOEamount_send >> 8);//先发送高字节数据
TqueueRS485[4] = (unsigned char)SOEamount_send;//发送低字节数据
tlengthRS485 = 7;
crccalculate = RS485crc_count(TqueueRS485, tlengthRS485 - 2, 0);
TqueueRS485[tlengthRS485 - 1] = (unsigned char)crccalculate;
TqueueRS485[tlengthRS485 - 2] = (unsigned char)(crccalculate >> 8);
RS485Send_Tqueue(9); //发送数据,接收队列的头指针后移9位,表示已处理完对该帧的应答
}
else if (RS485ally(RS485voffset(3)) == 0xAAAA)//对所有数据的接收确认
{
SOEPosition = FM_word_read(FRAM_POINT_SOE);
if (SOEamount_send > SOEamount)
SOEamount_send = 0;
SOEamount = SOEamount - SOEamount_send;
FM_word_write(FRAM_NUM_SOE,SOEamount); //这可以防止在通讯过程中有新的记录加入的情况
if(SOEPosition + SOEamount_send * 8 > 0x7FF8) //当FRAM的空间写完时,重新从首地址开始写
FM_word_write(FRAM_POINT_SOE,SOEPosition + SOEamount_send * 8 - 0x7FF8);
else
FM_word_write(FRAM_POINT_SOE,SOEPosition + SOEamount_send * 8);
SOEamount_send = 0;//必需清除SOEamount_send中的记录数,否则如果多次收到终端的确认帧
//SOEamount = SOEamount - SOEamount_send会运算溢出
RS485MoveHead(9); //对于最后的确认帧不再应答
}
else
{
SOEPosition_send = FM_word_read(FRAM_POINT_SOE) + (RS485ally(RS485voffset(3)) - 1) * 8;//FRAM_POINT_SOE 保存SOE记录指针,它指向准备发送的SOE记录
if (SOEPosition_send > 0x7FF8)//边界判断
SOEPosition_send = SOEPosition_send - 0x7FF8;
SOEBYTEnum = (unsigned char)(RS485ally(RS485voffset(5)) - RS485ally(RS485voffset(3)) + 1);//发送记录的条数 = (索要记录终止编号 - 索要记录初始编号 + 1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -