⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serialcom.c

📁 C51 串口通信 包括解析报文
💻 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 + -