您的当前位置:首页正文

xmodem收发c程序实例

2021-10-04 来源:V品旅游网


.h 文件

/*****************************************************************************/

/*

* xmodem

*/

/*************************************************************************************

*

* This program is free software; you can redistribute it and/or

* modify it under the terms of the GNU General Public License as

* published by the Free Software Foundation; either version 2 of

* the License, or (at your option) any later version.

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston,

* MA 02111-1307 USA

*************************************************************************************/

#include

#include

#include

#include/*printf*/

#include/*open*/

#include/*bzero*/

#include/*exit*/

#include/*times*/

#include/*pid_t*/

#include/*termios,tcgetattr(),tcsetattr()*/

#include

#include/*ioctl*/

#include

#include/*bzero*/

/*

Xmodem Frame form: <255-blk #><--128 data bytes-->

*/

#define XMODEM_SOH 0x01

#define XMODEM_STX 0x02

#define XMODEM_EOT 0x04

#define XMODEM_ACK 0x06

#define XMODEM_NAK 0x15

#define XMODEM_CAN 0x18

#define XMODEM_CRC_CHR 'C'

#define XMODEM_CRC_SIZE 2 /* Crc_High Byte + Crc_Low Byte */

#define XMODEM_FRAME_ID_SIZE 2 /* Frame_Id + 255-Frame_Id */

#define XMODEM_DATA_SIZE_SOH 128 /* for Xmodem protocol */

#define XMODEM_DATA_SIZE_STX 1024 /* for 1K xmodem protocol */

#define USE_1K_XMODEM 0 */

/* 1 for use 1k_xmodem 0 for xmodem

#define TIMEOUT_USEC 0

#define TIMEOUT_SEC(buflen,baud) (buflen*20/baud+2)/*接收超时*/

#define TIMEOUT_USEC 0

// 是否使用1K-xModem协议

#if (USE_1K_XMODEM)

#define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_STX

#define XMODEM_HEAD XMODEM_STX

#else

#define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_SOH

#define XMODEM_HEAD XMODEM_SOH

#endif

#define SERIAL_DEVICE \"/dev/ttyS0\" //串口1

#define MYBAUDRATE B115200

#define RECV_END 0x11 //自定义文件结束符

/*

* This function calculates the CRC used by the \"Modem Protocol\".

* The first argument is a pointer to the message block. The second argument is the number of bytes in

* the message block. The message block used by the Modem contains 128 bytes.

* The function return value is an integer which contains the CRC.

*/

void delay(int x)

{

int y;

for(;x>0;x--)

for(y=10;y>0;y--);

}

Protocol

unsigned short GetCrc16 ( char *ptr, unsigned short count )

{

unsigned short crc, i;

crc = 0;

while(count--)

{

crc = crc ^ (int) *ptr++ << 8;//从packet_data中取一个字节数据,强转为16为int,再把低八位移到高八位,赋值给crc

for(i = 0; i < 8; i++)

{

if(crc & 0x8000)//判断数据的最高位数据是否为1

crc = crc << 1 ^ 0x1021; // CRC-ITU

else

crc = crc << 1;

}

}

return (crc & 0xFFFF);

}

/*******************************************

*receivedata

*返回实际读入的字节数

*

********************************************/

int PortRecv(int fdcom,char* data,int datalen,int baudrate)

{

int readlen,fs_sel;

int readnum=0x00;

char readtemp=0;

fd_set fs_read;

struct timeval tv_timeout;

FD_ZERO(&fs_read);

FD_SET(fdcom,&fs_read);

tv_timeout.tv_sec=TIMEOUT_SEC(datalen,baudrate);

tv_timeout.tv_usec=TIMEOUT_USEC;

fs_sel=select(fdcom+1,&fs_read,NULL,NULL,NULL);

if(fs_sel)

{

//while(read(fdcom,&readtemp,1));

readnum=0;

while((readtemp!=RECV_END)&&(readnum<=1000))

{

if(read(fdcom,&readtemp,1)>0)

{

data[readnum]=readtemp;

readnum++;

delay(1);

}

}

//printf(\"readnum is:%d\\n\

return readnum;

//readlen=read(fdcom,data,datalen);

//return(readlen);

}

else

{

perror(\"select\");

return(-1);

}

return(readnum);

}

/*

* 串口初始化.

* Baudrate: 115200 8个数据位, 1个停止位,无奇偶校验位

*/

int Initial_SerialPort(void)

{

int fd;

struct termios options, oldtio;

//打开一个串口设备SERIAL_DEVICE,COM1

fd = open( SERIAL_DEVICE ,

O_RDWR | //O_RDWR:读写标志

O_NOCTTY | //O_NOCTTY:通知linux,本程序不成为打开串口的终端

O_NDELAY ); //O_NDELAY:通知linux,此程序不关心DCD信号线所处状态

if ( fd == -1 )

{

/* open error! */

perror(\"Can't open COM1!\");

return -1;

}

// 恢复串口为阻塞状态,用来等待串口数据的读入

if(fcntl(fd, F_SETFL, 0) < 0)

printf(\"fcntl failed!\\n\");

else

;

// 保存原串口属性

if(tcgetattr(fd, &oldtio) != 0) {

perror(\"setup serial 1\");

return -1;

}

/* Get the current options for the port... */

tcgetattr(fd, &options);

/* Set the baud rates to BAUDRATE... */

cfsetispeed(&options,MYBAUDRATE);

cfsetospeed(&options,MYBAUDRATE);

tcsetattr(fd, TCSANOW, &options);

if (0 != tcgetattr(fd, &options))

{

perror(\"SetupSerial 1\");

return -1;

}

/*

* 8bit Data,no partity,1 stop bit...

*/

options.c_cflag &= ~PARENB;//无奇偶校验

options.c_cflag &= ~CSTOPB;//停止位,1位

options.c_cflag &= ~CSIZE; //数据位的位掩码

options.c_cflag |= CS8; //数据位,8位

/* inter-character timer, timeout VTIME * 0.1 */

options.c_cc[VTIME] = 0;

/* blocking read until VMIN character arrives */

options.c_cc[VMIN] = 0;

tcflush(fd,TCIFLUSH);//处理未接收字符,刷新

/* Choosing Raw Input */

options.c_lflag &= ~( ICANON //非终端模式

| ECHO //不本地回显

| ECHOE //不删除前一显示字符

| ISIG);//不发送与INTR、QUIT和SUSP相对应的信号SIGINT、SIGQUIT和SIGTSTP到tty设置对应前台进程的所有进程

options.c_oflag &= ~OPOST;

/*

* Set the new options for the port...

*/

if (tcsetattr(fd, TCSANOW, &options) != 0) //激活新配置

{

perror(\"SetupSerial error\");

return -1 ;

}

return fd ; //打开并初始化好串口1后,返回设备号fd

}

/*

* 等待串口空闲恢复空闲状态

*/

void ClearReceiveBuffer(int fd)

{

unsigned char tmp;

while ((read(fd,&tmp,1)) > 0);

return;

}

/*****************************************************************************/

c 源文件

/*

* 功能:通过串口,用xmodem协议发送文件。

*

* 说明:运行程序,按提示输入数字选择相应功能

*

* 其他:功能有待完善,如添加接收和聊天功能

*

* 日期:2011-4-23

*/

#include\"send.h\"

int main(int argc,char *argv[])

{

int fd;

char *data_file_name;

char packet_data[XMODEM_DATA_SIZE];

char frame_data[XMODEM_DATA_SIZE XMODEM_FRAME_ID_SIZE + 1];

unsigned char tmp;

unsigned char num,num1;

XMODEM_CRC_SIZE + +

char name1[100]={};

char sr,ss,cr,cs;

char kk;

int fdcom;

int RecvLen,RecvLen1;

FILE *datafile;

FILE *received;

int complete,retry_num,pack_counter,read_number,write_number,i;

unsigned short crc_value;

unsigned char ack_id;

char name[100]={};

char receivefilename[100];

char RecvBuf[1000];

char RecvBuf1[1000];

if(argc!=1)

{

printf(\"Usage:%s\\n\

printf(\"eg:\");

printf(\"./serial\\n\");

exit(-1);

}

else

{

printf(\"XMODEM started...\\r\\n\");

}

/* open serial port */

if ((fd = Initial_SerialPort()) == -1)

{

printf(\"Can't open COM1\\n\");

return -1 ;

}

while(1)

{

sr='\\0';

printf(\"please choose: 1-send || 2-receive || 3-exit!\\n\");

sr=getchar();

ss=getchar();

kk=1;

while(kk)

{

pack_counter = 0; // 包计数器清零

complete = 0;

retry_num = 0;

ClearReceiveBuffer(fd);//等待串口恢复空闲状态,发送数据则一直等待,直到读不到串口发送的数据,退出循环等待

if(sr=='1')

{

printf(\"please input the sendfile path and name:\\n\");

printf(\"eg:/home/hyb/work/serial/send.txt\\n\");

while((cr=getchar())!='\\n')

{

receivefilename[num]=cr;

num++;

}

for(i=0;iname[i]=receivefilename[i];

num=0;

//只读方式打开一个准备发送的文件,如果不存在就报错,退出程序。

if((datafile=fopen(name,\"rb\"))==NULL)

{

printf(\"\\n can't open this file or not exist! \\n\");

return -1;

}

else

{

printf(\"Ready to send the file:%s\\n...\\n......\\n\

}

bzero(&name,sizeof(name));

printf(\"Waiting for singnal NAK!\\n...\\n......\\n\");

while((read(fd,&ack_id,1)) <= 0);//从串口读一个字节(NAK信号)到 ack_id 中,读不到数据则等待,直到读到数据后退出循环

printf(\"The Singnal NAK: %c ok\\n\打印接收到的NAK信息

while(!complete)

{

switch(ack_id)

{

case XMODEM_CRC_CHR: // 接收到字符'C'开始启动传输,并使用CRC校验

printf(\"begining to Send file %s...\\n\

case XMODEM_ACK: //0x06

retry_num = 0;

pack_counter++;

read_number = fread(packet_data, sizeof(char), XMODEM_DATA_SIZE, datafile);

//从打开的datafile指向的文件中读取

//XMODEM_DATA_SIZE 个(char)数据,

//放到packet_data这个数组中

if(read_number > 0)//read_number为返回的读取实际字节数

{

if(read_number < XMODEM_DATA_SIZE_SOH)

{

printf(\"Start filling the last frame!\\r\\n\");

for(; read_number < XMODEM_DATA_SIZE; read_number++)

packet_data[read_number] = 0x1A; // 不足128字节用0x1A填充

}

frame_data[0] = XMODEM_HEAD; // 帧开始字符

frame_data[1] = (char)pack_counter; // 信息包序号

frame_data[2] = (char)(255 - frame_data[1]); // 信息包序号的补码

for(i=0; i < XMODEM_DATA_SIZE; i++) // 128字节的数据段

frame_data[i+3] = packet_data[i];//把收到的字符和信息头一起打包

crc_value = GetCrc16(packet_data, XMODEM_DATA_SIZE); // 16位crc校验

frame_data[XMODEM_DATA_SIZE_SOH+3] = (unsigned char)(crc_value >> 8);// 高八位数据

frame_data[XMODEM_DATA_SIZE_SOH+4] = (unsigned char)(crc_value); //低八位数据

/* 发送133字节数据 */

write_number = write( fd, frame_data, XMODEM_DATA_SIZE_SOH + 5);//向串口写一个包数据,即133字节数据

printf(\"The %d pack(frame size:%d)has been

sended!!\\n\

printf(\"waiting for next ACK... \\n......\\n\");

while((read(fd,&ack_id,1)) <= 0);

if(ack_id == XMODEM_ACK)

printf(\"ACK Ok!!Ready sending next pack!\\r\\n\");

else

{

printf(\"ACK Error!\\r\\n\");

printf(\"%c\\r\\n\

}

}

else // 文件发送完成

{

ack_id = XMODEM_EOT;

complete = 1;

printf(\"Complete ACK have coming,\");

while(ack_id != XMODEM_ACK)

{

ack_id = XMODEM_EOT;

write_number = write(fd,&ack_id,1);

while((read(fd, &ack_id, 1)) <= 0);

}

printf(\"Send file successful\\r\\n\");

kk=0;

fclose(datafile);

}

break;

case XMODEM_NAK:

if( retry_num++ > 10)

{

printf(\"Retry too many times,Quit!\\r\\n\");

complete = 1;

}

else //重试,发送

{

write_number = write(fd, frame_data, XMODEM_DATA_SIZE + 5);

printf(\"Retry for ACK,%d,%d...\

while((read(fd, &ack_id, 1)) <= 0);

if( ack_id == XMODEM_ACK )

printf(\"OK\\r\\n\");

else

printf(\"Error!\\r\\n\");

}

break;

default:

printf(\"Fatal Error!\\r\\n\");

complete = 1;

break;

}

}

}

else if(sr=='2')

{

printf(\"please input the receivefile path and name:\\n\");

printf(\"eg:/home/hyb/work/serial/receive.txt\\n\");

while((cr=getchar())!='\\n')

{

receivefilename[num1]=cr;

num1++;

}

for(i=0;iname1[i]=receivefilename[i];

num1=0;

//打开一个接收文件,如果不存在就创建它!若失败则提示错误,退出程序。

if((received=fopen(name1,\"w+\"))==NULL)

{

printf(\"\\n can't creat receivefile %s! \\n\

return -1;

}

else

{

printf(\"creat receivefile:%s success!\\n\

printf(\"Waiting for receive file....\\n.....\\n.........\\n\");

}

bzero(&name1,sizeof(name1));

//ack_id='c';

//while((read(fd, &cs, 1)) <= 0);

//printf(\"cs is %c\\n\

RecvLen=PortRecv(fd,RecvBuf,1,115200);//从串口接收数据

printf(\"%d\\n\

if(RecvLen>1&&(RecvBuf[0]!='\\n'))

{

for(i=1;i<=RecvLen;i++) //把接收到的数据写入文件

{

fputc(RecvBuf[i-1],received);

}

}

printf(\"Receive flie complete!\\n\");

fclose(received); //写入完毕,关闭文件

printf(\"\\n\");

kk=0;

sr='\\0';

}

else if(sr=='3')

{

printf(\"Byebye!!\\n\"); //退出!

close(fd);

return 0;

}

else

{

kk=0;

printf(\"choose error: %c!!\\n\

}

}

}

fclose(datafile);

close(fd);

kk=0;

return 0;

}

/*****************************************************************************/

说明:linux下编译运行,按提示:输入1 选择发送文件,用超级终端,选择接收文件(xmodem),填写路径文件名。确定便可以接收到程序发送的文件

输入 2 选择接收。用串口调试助手发送文件,收到后则返回菜单。

输入3 则退出程序。

其他:注意,串口调试助手给程序发送的文件结束符要为 0x11,在记事本中是黑块。

因篇幅问题不能全部显示,请点此查看更多更全内容