Xmodem通信协议实例
在工作时串口通信的过程中需要传输文件,这里就就需要使用通信协议,此时选择的是Xmodem协议作简要研究
1、什么是Xmodem协议
Xmodem协议是串口通信中广泛使用到的异步文件传输协议。以128字节块的形式传输数据,并且每个块都使用一个校验过程来进行错误检测。在校验过程中如果接收方关于一个块的检验和与它在发送方的检验相同时,接收方就向发送方发送一个确认字节<ACK>。如果有错则发送一个字节<NAK>要求重发。以保证传输过程中的正确性,但是由于需要对每个块都要进行检验,显得效率比较低。
2、Xmodem协议相关控制字符
SOH 0x01 //Xmodem数据头
STX 0x02 //1K-Xmodem数据头
EOT 0x04 //发送结束
ACK 0x06 //认可响应
NAK 0x15 //不认可响应
CAN 0x18 //撤销传送
CTRLZ 0x1A //填充数据包
3、标准Xmodem协议(每个数据包含有128字节数据)帧格
Xmodem包格式
Byte1 Byte2 Byte3 Byte4~131 Byte132~133
Start Of Header Packet Number ~(Packet Number) Packet Data 16-Bit CRC
Xmodem协议的传输数据单位为信息包,包含一个标题开始字符<SOH>或者<STX>,一个单字节包序号,一个单字节包包序号的补码,128个字节数据和一个双字节的CRC16校验
4、数据包说明
对于标准Xmodem协议来说,如果传送的文件不是128的整数倍,那么最后一个数据包的有效内容肯定小于帧长,不足的部分需要用CTRL-Z(0x1A)来填充
5、如何启动传输
Xmodem协议的传输由接收方启动,接收方向发送方发送"C"或者NAK(这里的NAK是用来启动传输的。下面我们用到的NAK是用来对数据产生重传机制)。其中接收方发送NAK信号表示接收方打算用累加和校验;发送字符"C"则表示接收方打算使用CRC校验。
6、传输过程
当接收方发送的第一个"C"或者NAK到达发送方,发送方认为可以发送第一个数据包了,传输启动。发送方接着接着应该将数据以每次128字节的数据加上包头,包号,包号补码,末尾加上校验和,打包成帧格式传送。发送方发了第一个包后就等待接收方的确认字节<ACK>,收到接收方传来的<ACK>确认,就认为数据包被接收方正确接收,并且接收方要求发送方继续发送下一个包;如果发送方收到接收方传来的<NAK>(这里的表示重发),则表示接收方请求重发刚才的数据包;如果发送方收到接收方传来的<CAN>字节,则表示接收方请求无条件停止传输。
7、结束传输
如果发送方正常传输完全部数据,需要结束传输,正常结束需要发送方发送<EOT>通知接收方。接收方回以<ACK>进行确认。如果接收方发送<CAN>给发送方也可以强制停止传输,发送方受到<CAN>后不需要发送<EOT>确认,此时传输已经结束。
8、Xmodem协议代码:
#include "BA_UART_CONFIG.h"
#include "BA_XModem.h" #define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define CTRLZ 0x1A
#define DLY_1S 1000
#define MAXRETRANS 25 static int last_error = ; void out_buff(unsigned char *buff, int size)
{
int arg = ;
UART_HANDLER uart = getUartHandler(); writeSysUart(uart, buff, size, &arg);
} struct sysUartWaitArgStruct sysXmodemUartArg =
{
OS_OPT_PEND_BLOCKING,
,
NULL,
};
int in_buff(unsigned char *buff, int time_out)
{
int arg = ;
int qSize = ;
int readSize = ;
UART_HANDLER uart = getUartHandler(); last_error = ; sysXmodemUartArg.timeout = time_out; if(RETURN_RESULT_ERROR_NOERR ==
ctrlSysUart(uart, DEVICE_CONTROL_WAIT_EVENT, (UART_ARG)(&sysXmodemUartArg)))
{
qSize = uart->recvDQ.q.curSize;
if(qSize > )
{
readSize = readSysUart(uart, buff, qSize, &arg);
}
} if(readSize == )
last_error = ; return (readSize);
}
int calcrc(const unsigned char *ptr, int count)
{
int crc;
char i; crc = ;
while (--count >= )
{
crc = crc ^ (int) *ptr++ << ;
i = ;
do
{
if (crc & 0x8000)
crc = crc << ^ 0x1021;
else
crc = crc << ;
} while (--i);
} return (crc);
}
static int check(int crc, const unsigned char *buf, int sz)
{
if(crc)
{
unsigned short crc = calcrc(buf, sz);
unsigned short tcrc = (buf[sz]<<)+buf[sz+]; if (crc == tcrc)
return ;
}
else
{
int i = ;
unsigned char cks = ; for(i = ; i < sz; i ++)
{
cks += buf[i];
} if (cks == buf[sz])
return ;
} return ; }
//recv_buff_size == 384
int xmodemReceive(unsigned char *dest, int destsz)
{
unsigned char xbuff[];
int bufsz = ;
int crc = ;
unsigned char trychar = 'C';
unsigned char packetno = ;
int c = ;
int len = ;
int retry = ;
int retrans = MAXRETRANS;
int recvSize = ; for(;;)
{
for(retry = ; retry < ; retry ++)
{
if(trychar)
{
xbuff[] = trychar;
out_buff(xbuff, );
} recvSize = in_buff(xbuff, (DLY_1S)<<);
c = xbuff[]; if (last_error == )
{
switch(c)
{
case SOH:
bufsz = ;
goto start_recv;
case STX: {
xbuff[] = CAN;
out_buff(xbuff, );
}
return -;
case EOT: {
xbuff[] = ACK;
out_buff(xbuff, );
}
return len;
case CAN: in_buff(xbuff, DLY_1S);
c = xbuff[];
if(c == CAN)
{
{
xbuff[] = ACK;
out_buff(xbuff, );
}
return -;
}
break;
default:
break;
}
}
} if (trychar == 'C')
{
trychar = NAK;
continue;
} {
xbuff[] = CAN;
out_buff(xbuff, );
out_buff(xbuff, );
out_buff(xbuff, );
} return -; start_recv:
if(trychar == 'C')
crc = ; trychar = ; if(recvSize != (bufsz + (crc ? : ) + ))
goto reject; if(xbuff[] == (unsigned char)(~xbuff[]) &&
(xbuff[] == packetno || xbuff[] == (unsigned char)packetno - ) &&
check(crc, &xbuff[], bufsz))
{
if(xbuff[] == packetno)
{
int count = destsz - len; if (count > bufsz)
count = bufsz; if (count > )
{
memcpy(&dest[len], &xbuff[], count);
len += count;
} packetno ++; retrans = MAXRETRANS+;
} if(-- retrans <= )
{
{
xbuff[] = CAN;
out_buff(xbuff, );
out_buff(xbuff, );
out_buff(xbuff, );
}
return -;
} {
xbuff[] = ACK;
out_buff(xbuff, );
} continue;
} reject:
{
xbuff[] = NAK;
out_buff(xbuff, );
} }
} //send_buff_size == 140
int xmodemTransmit(unsigned char *src, int srcsz)
{
unsigned char xbuff[];
int bufsz = ;
int crc = -;
unsigned char packetno = ;
int i = ;
int c = ;
int len = ;
int retry = ; for(;;)
{
for( retry = ; retry < ; ++retry)
{
in_buff(xbuff, (DLY_1S)<<);
c = xbuff[]; if(last_error == )
{
switch(c)
{
case 'C':
crc = ;
goto start_trans;
case NAK:
crc = ;
goto start_trans;
case CAN:
in_buff(xbuff, DLY_1S);
c = xbuff[];
if(c == CAN)
{
{
xbuff[] = ACK;
out_buff(xbuff, );
}
return -;
}
break;
default:
break;
}
}
} {
xbuff[] = CAN;
out_buff(xbuff, );
out_buff(xbuff, );
out_buff(xbuff, );
} return -; for(;;)
{
start_trans:
xbuff[] = SOH;
bufsz = ;
xbuff[] = packetno;
xbuff[] = ~packetno; c = srcsz - len;
if(c > bufsz)
c = bufsz; if(c >= )
{
memset(&xbuff[], , bufsz); if (c == )
{
xbuff[] = CTRLZ;
}
else
{
memcpy(&xbuff[], &src[len], c); if (c < bufsz)
xbuff[ + c] = CTRLZ;
} if(crc)
{
unsigned short ccrc = calcrc(&xbuff[], bufsz); xbuff[bufsz + ] = (ccrc>>) & 0xFF;
xbuff[bufsz + ] = ccrc & 0xFF;
}
else
{
unsigned char ccks = ; for(i = ; i < bufsz + ; i ++)
{
ccks += xbuff[i];
} xbuff[bufsz + ] = ccks;
} for(retry = ; retry < MAXRETRANS; retry ++)
{
out_buff(xbuff, bufsz + + (crc ? : )); in_buff(xbuff, DLY_1S);
c = xbuff[]; if(last_error == )
{
switch(c)
{
case ACK:
packetno ++;
len += bufsz;
goto start_trans;
case CAN:
in_buff(xbuff, DLY_1S);
c = xbuff[];
if(c == CAN)
{
{
xbuff[] = ACK;
out_buff(xbuff, );
} return -;
}
break;
case NAK:
break;
default:
break;
}
}
}
{
xbuff[] = CAN;
out_buff(xbuff, );
out_buff(xbuff, );
out_buff(xbuff, );
} return -;
}
else
{
for(retry = ; retry < ; retry ++)
{
{
xbuff[] = EOT;
out_buff(xbuff, );
} in_buff(xbuff, (DLY_1S)<<);
c = xbuff[]; if(c == ACK)
break;
} return ((c == ACK) ? len : -);
}
}
}
}
Xmodem通信协议实例的更多相关文章
- AVR之BOOTLOADER技术详解(转)
源:http://blog.csdn.net/zhenhua10/article/details/6442412 ATmega128具备引导加载支持的用户程序自编程功能(In-System Progr ...
- WSDL 文档-一个简单的 XML 文档
WSDL 文档是利用这些主要的元素来描述某个 web service 的: <portType>-web service 执行的操作 <message>-web service ...
- CAN总线协议 学习笔记
1.CAN总线网络 CAN总线网络主要挂在CAN_H和CAN_L,各个节点通过这两条线实现信号的串行差分传输,为了避免信号的反射和干扰,还需要在CAN_H和CAN_L之间接上120欧姆的终端电阻,但是 ...
- MapReduce源码分析之新API作业提交(二):连接集群
MapReduce作业提交时连接集群是通过Job的connect()方法实现的,它实际上是构造集群Cluster实例cluster,代码如下: private synchronized void co ...
- 最近学习工作流 推荐一个activiti 的教程文档
全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...
- 通过实例来分析I2C基本通信协议
本文旨在用最通俗易懂的方式.让大家明确I2C通信的过程到底是怎么回事. I2C起源于飞利浦公司的电视设计,但之后朝通用路线发展,各种电子设计都有机会用到I2C 总的来说,I2C能够简单归纳为,两根线, ...
- 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
1.前言 有过移动端开发经历的开发者都深有体会:移动端IM的开发,与传统PC端IM有很大的不同,尤其无线网络的不可靠性.移动端硬件设备资源的有限性等问题,导致一个完整的移动端IM架构设计和实现都充满着 ...
- 多平台下Modbus通信协议库的设计(一)
1.背景 1.1.范围 MODBUS 是 OSI 模型第 7 层上的应用层报文传输协议, 它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信. 自从 1979 年出现工业串行链路的事实标准以 ...
- JAVA上百实例源码以及开源项目
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...
随机推荐
- Swift_初识Swift
初识Swift语言 Swift结合了C和OC的优点并且不受C兼容性的限制.Swift采用安全的编程模式并添加了很多新特性,这将是编程更简单,更灵活也更有趣,Swift是基于成熟而且倍受喜爱的Cocoa ...
- 2015年蓝桥杯C/C++ B组题目题解
1. 输入一个字符串,求它包含多少个单词.单词间以一个或者多个空格分开. 第一个单词前,最后一个单词后也可能有0到多个空格.比如:" abc xyz" 包含两个单词,"a ...
- sql 数据库中只靠一个数据,查询到所在表和列名
有时候我们想通过一个值知道这个值来自数据库的哪个表以及哪个字段,在网上搜了一下,找到一个比较好的方法,通过一个存储过程实现的.只需要传入一个想要查找的值,即可查询出这个值所在的表和字段名. 前提是要将 ...
- tp3.2关联模型 BELONGS_TO
<?php namespace Home\Model; use Think\Model\RelationModel; class AttenModel extends RelationModel ...
- php 当前时间计算操作
首先要设置时间为中国时区 date_default_timezone_set('PRC'); 对于获取当前时间戳后的各种时间计算 数据库保存最好用时间戳 当前时间time() 上一天 echo dat ...
- Functions should do one thing一个函数应该只做一件事
if you take nothing else away from this guide other than this, you'll be ahead of many developers. 如 ...
- Live disk migration with libvirt blockcopy
nova采用 libvirt blockcopy(python API virDomainBlockRebase)来做live snapshot. Create the base image: $ ...
- rsync工具
rsync工具 一.介绍 1.可以实现 本地数据 <----------> 远程数据/本地数据 的传输 2.两种通信方式(man rsync) (1)remote shell(一个冒号 ...
- Pycharm如何打断点
一. python代码的调试方式 1. 使用print语句打印出来 2. 在编辑工具中,加断点跟踪(打断点) 3. 使用日志模块,输出到日志中 下面我们来看一下如何打断点 二. 环境 python 3 ...
- Android中字体颜色的设置
1.在Android中经常看到设置的颜色为八位的十六进制的颜色值,例如: 1 2 3 public static final class color { public static final ...