📄 serialcom.c
字号:
/*
* FileName : <serialCom.c>
* Description : 二个函数 供 操作串口 使用
*
*
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"reg51.h"
#include"serialCom.h"
//子程序声明
void createFrame(unsigned char protocol,unsigned char cmd);
void Com_Transmit(void);
//变量定义
unsigned char idata _aFrameHeader[1][2] = {{0x55,0xAA}}; //RSX协议的帧头"55AA"
Structserial idata serial[1];//COM1
unsigned char idata _aInBuffer[SIZE_IN_BUFFER];//64byte接收缓冲区
unsigned char idata _aOutBuffer[SIZE_OUT_BUFFER];//64byte发送缓冲区
//---------------------------------------------------------------
// 函数名称 : Com_Open
// 详细说明 : 首先关中断,根据入口参数,完成COM1的 初始化,开中断
// 入口参数 : <1> comport---初始化某个串口
// <2> mode---[ b7 <=> 0 = RS232; 1 = RS485 ]
// [b3~b0 <=> 指定协议类型 ]
// <3> baudNumber---[ 1~255 <=> 300bps * 1~255]
// [ 0 <=> default = ? ]
// 出口参数 : 无
// 备注: 其中涉及对串口变量 serial[0]---公共变量
//----------------------------------------------------------------
void Com_Open(unsigned char mode, unsigned char baudNumber)
{
//关各级中断
EA = 0;
RI = 0;
TI = 0;
//初始化 串口
SCON = 0x50; // 串口工作方式1 : 8-bit UART, REN = 1 : 使能接收
PCON = 0x80; // B7 -> SMOD = 0 波特率不变;SMOD = 1 波特率 x 2
//定时器1装入默认参数
TMOD = (TMOD & 0x0f) | 0x20; // 定时方式2,八位自动重装入
switch (baudNumber)
{
case 0x01 : TH1 = 0x8a; break; // 300bps X 1
case 0x02 : TH1 = 0xc5; break; // 300bps X 2
case 0x03 : TH1 = 0xd8; break; // 300bps X 3
case 0x04 : TH1 = 227; break; // 300bps X 8 = 2400bps
case 0x08 : TH1 = 241; break; // 300bps X 16 = 4800bps
case 0x10 : TH1 = 249; break; // 300bps X 32 = 9600bps
case 0x20 : TH1 = 252; break; // 300bps X 64 = 19200bps
default: TH1 = 241; break; // 300bps X 8 = 2400bps
}
//串口结构体的初始化
serial[0].status = 0x00; //准备读取帧头
serial[0].mode = 0x80 & mode; //取最高位b7 [b7==1 232] [b7==0 485]
serial[0].protocol = 0x0f & mode; //取低四位b3~b0 默认是 0 => RSX protocol
serial[0].local = 0x99; //串口设备地址 = 99H
serial[0].cmd = 0x00; //命令字 ->默认 0
serial[0].length = 0; //报文字节长度 ->默认 0
serial[0].checkSum = 0x00; //检验和 初始化 ->0
serial[0].inAddress = 0x00; //存数据偏置 初始化 ->0
serial[0].outNumber = 0x00; //取数据偏置 初始化 ->0
serial[0].outAddress = 0x00; //存数据偏置 初始化 ->0
serial[0].timer = 0xFF; //这个常数与 中断时间 最长报文长度 有关系
serial[0].token = 0x00; // 初始状态 发送缓冲区空
//初始化 接收 发送 缓冲区
memset(_aInBuffer,'\0',SIZE_IN_BUFFER);
memset(_aOutBuffer,'\0',SIZE_OUT_BUFFER);
//使能各级中断
EA = 1; // 使能全局中断
ES = 1; // 使能串口中断
TR1 = 1; // 使能串口接收中断
}
//---------------------------------------------------------------
// 函数名称 : Com_Event
// 详细说明 : 中断服务子程序,完成接收和发送任务
// 接收时完成报文解析;发送时由outNumber字段控制
//---------------------------------------------------------------
void Com_Event(void) interrupt 4
{
unsigned char ch;
if (RI)
{
RI=0;
ch = SBUF;
if (serial[0].token == 0x0F) //在缓冲区不满的情况下,接收数据
{
return;
}
switch (serial[0].status )//解释报文-根据接收到的字节转移状态
{
case 0x00 :
if (ch == _aFrameHeader[serial[0].protocol][serial[0].status])//
{
serial[0].status = 0x01;
serial[0].timer = 0xFF;
}else
{
serial[0].status = 0x00;
}
break;
case 0x01 :
if (ch == _aFrameHeader[serial[0].protocol][serial[0].status])
{
serial[0].status = 0x02;
serial[0].checkSum = 0x00;//使用前清零
}else
{
serial[0].status = 0x00;
}
break;
case 0x02 ://比较是否是 发给自己的报文
if (ch == serial[0].local)
{
serial[0].status = 0x03;
serial[0].checkSum += serial[0].local;//校验和
}else
{
serial[0].status = 0x00;
}
break;
case 0x03 ://接收 CMD 1byte
serial[0].cmd = ch;
serial[0].checkSum += serial[0].cmd;
serial[0].status = 0x04;
break;
case 0x04 ://接收 报文长度 1byte
serial[0].length = ch;
serial[0].checkSum += serial[0].length;
serial[0].status = 0x05;
break;
case 0x05 ://接收 报头检验和 1byte
if ( ch == serial[0].checkSum )
{
serial[0].status = 0x06;
serial[0].checkSum = 0x00;//准备为后面报文接收做校验和
if (serial[0].length == 0)
{
serial[0].token = 0xF0; //完整接收到报文
serial[0].status = 0x00; //
}else//有正文
{
//在这里还应该判断 是否上次报文处理已经不再需要 接收缓冲区里的正文 如果有正文的话
serial[0].inAddress = 0; //准备将正文存入数据缓冲区
}
}else//报头校验和 出错
{
serial[0].status = 0x00;
}
break;
case 0x06 ://接收 报文内容 Xbyte 根据前面接收到的长度
serial[0].checkSum += ch;
*(_aInBuffer + serial[0].inAddress++) = ch;
if (serial[0].inAddress == SIZE_IN_BUFFER) //接收缓冲区 满
{
serial[0].token = 0x0F; //置标志位
serial[0].inAddress = 0; //接收指针到栈顶
}
if ( (--serial[0].length) == 0 )
{
serial[0].status = 0x07;
serial[0].length = serial[0].inAddress;
}
break;
case 0x07 ://接收 报文内容校验和 1byte
if ( ch == serial[0].checkSum )
{
//置报文接收完整标志
serial[0].token = 0xF0;//收到完整报文
serial[0].status = 0x00;
}else//报文内容校验和出错
{
serial[0].status = 0x00;
serial[0].inAddress = 0x00;
}
break;
default :
//将串口状态重置为 0x00
serial[0].status = 0x00;
serial[0].inAddress = 0x00;
break;
}//end switch(_sserial[1].status)
}// end if (RI)
if (TI)
{
TI = 0; //软件请零
serial[0].token = 0xF8;//应答报文中
if (serial[0].outNumber == 0)//判断是否发完
{
serial[0].token = 0xff;//应答报文完成
return; //退出中断
}
SBUF = *(_aOutBuffer + serial[0].outAddress); //发送缓冲区数据送入SBUF
serial[0].outAddress++; //发送偏置 + 1
serial[0].outNumber--; //待发送的数据个数 - 1
}// end if (TI)
}//end Com_Event
//-----------------------------------------------------------------------------------
// 函数名称 : Com_Operate(unsigned char comport)
// 详细说明 : 解释报文
// 入口参数 : comport---选择要操作的串口
// 出口参数 : 无
// 备注:当 comport = 0x01 时 作测试发送用例,只是发送 012345
// 当 comport = 0x00 时 作测试接收发送用例
//-----------------------------------------------------------------------------------
void Com_Operate(unsigned char comport)
{
switch (comport)
{//选择 串口
case 0x00://COM1
//判断当前串口要进行的操作
//if (serial[0].token == 0xff) return;//应答报文处理完成
//if (serial[0].token == 0xf8) return;//应答报文处理中
if (serial[0].token == 0xF0) //收到了完整报文
{
//有没有可能在报文应答未结束时,又收到新的待处理报文
createFrame(serial[0].protocol,serial[0].cmd);
Com_Transmit();//TI = 1
return;
}
break;
// end case 0x00 COM1
case 0x01://COMX
break;
default://使用COM1 这个可以用来测试
//在报文组装完成,确定发送 起始地址 发送个数
createFrame(0,0);//收到 05 05 99 00 01 9A 发送 012345
// 启动发送中断
Com_Transmit();//TI = 1
break;
}//end switch(comport)
}//end Com_Operate()
//-----------------------------------------------------------------------------------
// 函数名称 : createFrame(unsigned char protocol,unsigned char cmd)
// 详细说明 : 按帧结构组织报文
// 入口参数 : <1> protocol---组织报文要使用的协议
// <2> cmd---协议中具体的命令字
//-----------------------------------------------------------------------------------
void createFrame(unsigned char protocol,unsigned char cmd)
{
unsigned char i = 0; //循环计数
unsigned char num = 0; //取数控制
unsigned char checksum = 0x00; //校验和计算
unsigned char code *pflash;
switch (cmd)//根据命令字
{
case 0x00://测试 收到 05 05 99 00 01 9A 发送 012345
for (i = 0; i < 6; i++)
{
_aOutBuffer[i] = i + 0x30; //计算校验和
}
serial[0].outNumber = 6; //包含 帧头的报文 字节个数 控制发送
serial[0].outAddress = 0; //发送的起始地址
break;
case 0x01://读取片内Flash数据 起始地址 起的 N个byte 数据
//注意应答报文中数据最大长度不应超过 64-7
memcpy(_aOutBuffer,_aFrameHeader[protocol],2); //55 AA
*(_aOutBuffer+2) = serial[0].local; //99
*(_aOutBuffer+3) = serial[0].cmd; //01
num = _aInBuffer[2]; //要取数据的个数
*(_aOutBuffer+4) = num; //正文长度
checksum = 0;
for (i = 2; i < 5; i++)
{
checksum += _aOutBuffer[i]; //计算校验和
}
*(_aOutBuffer+5) = checksum;
pflash = (unsigned char code *)(_aInBuffer[0] + (_aInBuffer[1]<<8)); //要取数据的起始地址
checksum = 0;
for (i = 0; i < num; i++)
{
_aOutBuffer[6+i] = *pflash; //数据存入发送缓冲区
checksum += *pflash++; //计算校验和
}
*(_aOutBuffer+6+num) = checksum; //添入校验和字段
serial[0].outNumber = num + 7; //包含 帧头的报文 字节个数 控制发送
serial[0].outAddress = 0; //发送的起始地
break;
default:// 收到 05 05 99 01 00 9A 发送 05 05 99 81 00 1A
memcpy(_aOutBuffer,_aFrameHeader[protocol],2); //帧头 根据协议
*(_aOutBuffer+2) = serial[0].local; //串口设备地址
*(_aOutBuffer+3) = serial[0].cmd + 0x80; //命令字 + 80H
*(_aOutBuffer+4) = serial[0].length; //报文长度
checksum = 0;
for (i = 2; i < 5; i++)
{
checksum += _aOutBuffer[i]; //计算校验和
}
*(_aOutBuffer+5) = checksum; //填入 校验和 字段
checksum = 0; //校验和清零
num = serial[0].length;//接收到的报文中有正文,将正文拷贝至发送缓冲区
for (i = 0; i < num; i++)//这样应该是安全的,num = 0
{
_aOutBuffer[6+i] = _aInBuffer[i];
checksum += _aInBuffer[i];
}
*(_aOutBuffer+6+num) = checksum; //正文校验和
serial[0].outNumber = num + 7; //包含 帧头的报文 字节个数 控制发送
serial[0].outAddress = 0; //发送的起始地址
break;
}//end switch(protocol)
}
//-----------------------------------------------------------------------------------
// 函数名称 : Com_Transmit()
// 详细说明 : 发帧 一次发完
//-----------------------------------------------------------------------------------
void Com_Transmit(void)
{
TI = 1; //启动 中断 发数
}
/*
//-----------------------------------------------------------------------------------
// 函数名称 : overTime()
// 详细说明 : 检测报文是否接收超时
// 备注: 到底用那个定时器 尚未定下来
//-----------------------------------------------------------------------------------
void overTime(void)
{
if ( serial[0].status>0x00 && serial[0].status<0x08)
{
serial[0].timer--;
if (serial[0].timer == 0)
{
serial[0].status = 0;
}
}
}
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -