.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 #include #include #include #include #include #include #include #include #include #include /* Xmodem Frame form: */ #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;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;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,在记事本中是黑块。 因篇幅问题不能全部显示,请点此查看更多更全内容