ymodem协议C语言接收
时间: 2023-08-29 17:12:28 浏览: 186
Ymodem.zip_likeii1_ymodem com limit_ymodem 官方_ymodem蓝牙_用ymodem c
ymodem协议是一种常用于在串口通信中进行文件传输的协议,它支持数据的校验、重传等功能。下面是一个基于C语言的ymodem协议接收代码示例:
```
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<termios.h>
#include<errno.h>
#define SOH 0x01 //起始字节
#define STX 0x02 //起始字节
#define EOT 0x04 //结束字节
#define ACK 0x06 //确认字节
#define NAK 0x15 //否定字节
#define CAN 0x18 //取消传输字节
#define CRC16 0x43 //CRC校验方式
#define BUFFER_SIZE 1024 //缓冲区大小
#define FILENAME_SIZE 128 //文件名长度
int serial_fd = -1; //串口文件描述符
char file_name[FILENAME_SIZE]; //接收的文件名
char buf[BUFFER_SIZE]; //数据缓冲区
//计算CRC16校验码
unsigned short cal_crc(char *ptr, int len)
{
unsigned short crc = 0;
int i;
while(len--)
{
crc = crc ^ (*ptr++ << 8);
for(i=0;i<8;i++)
{
if(crc & 0x8000) crc = (crc << 1) ^ 0x1021;
else crc = crc << 1;
}
}
return crc;
}
//串口初始化
int serial_init(char *device)
{
struct termios options;
//打开串口
serial_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if(serial_fd == -1)
{
perror("open serial port");
return -1;
}
//设置串口参数
tcgetattr(serial_fd, &options);
cfmakeraw(&options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag |= CLOCAL | CREAD;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;
tcsetattr(serial_fd, TCSANOW, &options);
return 0;
}
//接收文件
int receive_file()
{
int i = 0, j = 0, buf_len = 0, file_len = 0, count = 0, res = 0, max_count = 10;
unsigned short crc = 0, crc_recv = 0;
unsigned char packet_num = 0, packet_num_recv = 0;
char file_buf[BUFFER_SIZE] = {0};
FILE *fp = NULL;
//发送文件接收确认请求
write(serial_fd, &NAK, 1);
while(1)
{
//读取一个数据包
res = read(serial_fd, buf, 1);
if(res < 1)
{
count++;
if(count > max_count)
{
printf("receive file timeout!\n");
return -1;
}
else
{
write(serial_fd, &NAK, 1);
continue;
}
}
//判断数据包类型
if(buf[0] == SOH)
{
buf_len = 128;
}
else if(buf[0] == STX)
{
buf_len = 1024;
}
else if(buf[0] == EOT)
{
write(serial_fd, &ACK, 1);
break;
}
else if(buf[0] == CAN)
{
printf("transfer cancel by sender!\n");
return -1;
}
else
{
printf("unknown packet type!\n");
return -1;
}
//读取剩余的数据部分
res = read(serial_fd, buf+1, buf_len+3);
if(res < buf_len+3)
{
printf("read packet error!\n");
return -1;
}
//计算校验和
crc = cal_crc(buf+3, buf_len);
crc_recv = buf[buf_len+3] << 8 | buf[buf_len+2];
if(crc != crc_recv)
{
printf("crc check error!\n");
write(serial_fd, &NAK, 1);
continue;
}
//确认数据包序号
packet_num_recv = buf[1];
if(packet_num_recv != packet_num + 1)
{
printf("packet num error!\n");
write(serial_fd, &NAK, 1);
continue;
}
packet_num = packet_num_recv;
//保存文件数据
memcpy(file_buf+j, buf+3, buf_len);
j += buf_len;
file_len += buf_len;
//发送数据包接收确认
write(serial_fd, &ACK, 1);
}
//保存接收的文件
fp = fopen(file_name, "wb");
if(fp == NULL)
{
printf("open file error!\n");
return -1;
}
fwrite(file_buf, 1, file_len, fp);
fclose(fp);
printf("receive file success!\n");
return 0;
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("usage: %s /dev/ttyS0\n", argv[0]);
return -1;
}
//初始化串口
if(serial_init(argv[1]) == -1)
{
return -1;
}
//接收文件名
memset(buf, 0, BUFFER_SIZE);
while(1)
{
read(serial_fd, buf+i, 1);
if(buf[i] == 0)
{
break;
}
i++;
}
strcpy(file_name, buf);
printf("receive file name: %s\n", file_name);
//接收文件
receive_file();
close(serial_fd);
return 0;
}
```
在这个代码示例中,我们通过调用`serial_init`函数初始化了串口并打开了文件描述符。然后,在`receive_file`函数中,我们通过读取串口数据来获取文件数据包,并进行数据校验和处理。最后,我们将接收到的文件保存到本地磁盘上。
需要注意的是,ymodem协议接收端在接收文件名时,需要读取一个以零字节结尾的字符串,因此我们需要在`read`函数读取数据时进行一些特殊处理。
阅读全文