四十一.TFT客户端移植

一.tftp作用:在嵌入式开发中进行数据传输


二.交互过程:
1.客户端发送请求包(请求报文)
2.根据选项参数,服务器决定是否发送应答确认包,进而客户端发送确认接收包,这步是可选的。
3.服务器发送数据包1,。服务器会将要传输的数据拆分成许多小块,每512字节一个块。(数据报文)
4.客户端回应数据包1(应答报文)
5.服务器如果没有收到数据包1回应包,就会重复发送数据包1直到超时.收到了就继续发送数据包2.
6.客户端回应数据包2.
7.以此类推,服务器发送数据包n,客户端回应数据包n。

8.。。。。。。。。。。。。。


三.报文格式:


1.tftp是属于UDP协议的,UDP又是属于IP协议的。IP又是属于以太网的。所以客户端必须知道目的端的mac地址,所以之前要发送一个ARP包。

2.根据操作码来区分不同的tftp报文。

3.操作码 = 1:表示客户端要从服务器下载数据,属于请求读。后面跟上要读些的文件名,再追加一个0.再后面是读取的模式,分为ASCiI码和普通的八位二进制数格式,末尾追加一个0.

4.操作码 = 2:表示请求从客户端上传数据到服务器。 后面格式和读取(下载)一样。

5.操作码 = 3:表示数据报文,后面紧跟块编号,末尾就是真正的数据内容

6.操作码 = 4:表示应答报文,后面以他应答的数据块编号结尾,

7.操作码 = 5:表示出错。后面紧跟差错码和差错信息。


四.修改网卡驱动
1.在arp.c增加网络处理函数。判断是什么类型的包。之前的代码默认是一个ARP包,但实际情况很复杂,所以要判断网卡接收到的是什么包。

2.在dm9000.c的中断处理里面对参数进行修改。包括buffer,以及调用通用处理函数,而不是简单地调用ARP应答解析函数。


五.IP协议格式

相应的数据结构为
/*IP头部结构体*/
typedef struct ip_hdr
{
    ETH_HDR ethhdr;
    u8 vhl;
    u8 tos;
    u16 len;
    u16 ipid;
    u16 ipoffset;
    u8 ttl;
    u8 proto;
    u16 ipchksum;
    u8 srcipaddr[4];
    u8 destipaddr[4];
}IP_HDR;
六.UDP协议格式

数据结构
/*UDP头部结构体*/
typedef struct udp_hdr
{
    IP_HDR iphdr;
    u16 sport;
    u16 dport;
    u16 len;
    u16 udpchksum;
}UDP_HDR;

七。关于DM9000打开接收广播功能的datasheet部分



void 
dm9000_active()
{
	/* RX enable */
	dm9000_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);//要加上RCR_ALL,否则他会自动丢弃来自外部的广播包
	/* Enable TX/RX interrupt mask */
	dm9000_write(DM9000_IMR, IMR_PAR);
		
}


八.处理触发中断的网络包
/*对来自外部的包进行处理*/
void net_handle(u8* buf,u32 len)
{
	ETH_HDR* p = (ETH_HDR*)buf;
	//printf("dm_handle int%x\r\n",SWAP(p->frame_type));
	switch(SWAP(p->frame_type)){
		case PROTO_ARP:
			arp_respond(buf,len);	//ARP协议应答,目的是获取PC的MAC地址
			break;
		case PROTO_IP:
			ip_respond(buf,len);	//对数据传送的包进行处理
			break;
		default:
			break;
	}
	return;
}

九.对IP协议的包以及从属于IP协议的UDP协议的包进行处理
extern void tftp_process(u8* buf,u32 len,u16 port);
/*对UDP协议的包,进行处理。TFTP传输是属于UDP协议的,而UDP又是属于IP协议的*/
void udp_respond(u8* buf,u32 len)
{
	
	
}

/*对IP协议的包进行处理,因为可能涉及到IP协议的情况不只是TFTP传输,所以为以后程序功能升级做准备,就单独写一个判断函数*/
void ip_respond(u8* buf,u32 len)
{
	IP_HDR* iphdr = (IP_HDR*)buf;
	//printf("dm_ip int\r\n");
	if(iphdr->proto == PROTO_UDP)
		udp_respond(buf,len);
}

十.TFTP发送请求信号,准备读取服务器的数据
/*客户端发送读取服务器数据请求*/
void tftp_request(const char* filename)
{
	u8* p_tftp = &sendbuf[200];	//tftp头部开始地址
	u32 tftp_index = 0;			//作为下标表示位置的移动
	UDP_HDR* udphdr;
	u8* iphdr;
	
	/*1.构建tftp头部*/
	
	p_tftp[0] = 0x00;		//网络字节序(大端模式),写入操作码
	p_tftp[1] = 0x01;
	
	tftp_index += 2;		//下标移动
	
	sprintf(&p_tftp[tftp_index],"%s",filename);	//写入文件名
	tftp_index += strlen(filename);
	p_tftp[tftp_index] = '\0';			//文件名尾部追加0
	tftp_index += 1;
	
	sprintf(&p_tftp[tftp_index],"%s","octet");	//写入数据传输模式
	tftp_index += strlen("octet");
	p_tftp[tftp_index] = '\0';			//尾部追加0
	tftp_index += 1;
	
	/*2.构建udp头部*/
	
	udphdr = (UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
	iphdr = (u8*)udphdr + sizeof(ETH_HDR);
	
	udphdr->sport = SWAP(48915);			//参数默认选这些
        udphdr->dport = SWAP(69);
        udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
        udphdr->udpchksum = 0x00;
           
	
	/*3.构建ip头部*/
	
	 udphdr->iphdr.vhl = 0x45;				//固定值
    	 udphdr->iphdr.tos = 0x00;				//固定值
    	 udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
    	 udphdr->iphdr.ipid = SWAP(0x00);			//固定值
     	 udphdr->iphdr.ipoffset = SWAP(0x4000);			//固定值
    	 udphdr->iphdr.ttl = 0xff;				//固定值
     	 udphdr->iphdr.proto = 0x11;				//固定值
    	 memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);		
    	 memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
    	 udphdr->iphdr.ipchksum = 0;
    	 udphdr->iphdr.ipchksum = checksum(iphdr,20);	//ip头部长度固定为20个字节
    
    	 
    
    	 memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
    	 memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
    	 udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
    	 
    	 dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}

十一.客户端接收到来自服务器端的数据包的应答函数
/*客户端响应服务器已正常收到来自服务器的数据包*/
void tftp_send_ack( u16 blocknum,u16 serverport)
{
	u8* p_tftp = &sendbuf[200];	//tftp头部开始地址
	u32 tftp_index = 0;			//作为下标表示位置的移动
	UDP_HDR* udphdr;
	u8* iphdr;
	
	/*1.构建tftp头部*/
	
	p_tftp[0] = 0x00;		//网络字节序(大端模式),写入操作码
	p_tftp[1] = 0x04;
	
	tftp_index += 2;		//下标移动
	
	p_tftp[2] = (blocknum & 0xff00) >> 8;		//网络字节序(大端模式),写入块编号,要反转高低位
	p_tftp[3] = blocknum & 0xff;
	
	tftp_index += 2;		//下标移动

	
	
	/*2.构建udp头部*/
	
	udphdr =(UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
	iphdr = (u8*)udphdr + sizeof(ETH_HDR);
	
	udphdr->sport = SWAP(48915);			//参数默认选这些
        udphdr->dport = serverport;//这是目的端口号,即服务器端口号,可以从来自服务器的数据包获取
        udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
        udphdr->udpchksum = 0x00;
        
        
	
	/*3.构建ip头部*/
	
	 udphdr->iphdr.vhl = 0x45;				//固定值
    	 udphdr->iphdr.tos = 0x00;				//固定值
    	 udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
    	 udphdr->iphdr.ipid = SWAP(0x00);			//固定值
     	 udphdr->iphdr.ipoffset = SWAP(0x4000);			//固定值
    	 udphdr->iphdr.ttl = 0xff;				//固定值
     	 udphdr->iphdr.proto = 0x11;				//固定值
    	 memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);		
    	 memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
    	 udphdr->iphdr.ipchksum = 0;
    	 udphdr->iphdr.ipchksum = checksum(iphdr,20);	//ip头部长度固定为20个字节
    
    	 
    
    	 memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
    	 memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
    	 udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
    	 
    	 dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}

十二.TFTP客户端处理函数
/*TFTP对数据传输的处理*/
void tftp_process(u8* buf,u32 len,u16 port)
{
	u32 i = 0,length =0;
	u8* tftp_download_addr = (u8*)TFTP_START_ADDR;
	TFTP_PAK* p_pkg = (TFTP_PAK*)(buf + sizeof(UDP_HDR));
	length = len - sizeof(UDP_HDR) - 4;
	
	
	/*判断接收到的包是否是数据包*/
	if(SWAP(p_pkg->opcode) == 3){
		
		/*判断接收到的包是否是最后一个*/
		if(SWAP(p_pkg->blocknum) == cur_blocknum){
			
			tftp_download_addr = (u8*)(TFTP_START_ADDR + (cur_blocknum - 1)*512);
	
			/*复制数据到指定内存地址*/
			for(i = 0;i < length;i++){
				
				*(tftp_download_addr++) = *(p_pkg->data + i);
			}
			
			/*客户端发送接收到数据包的应答信号*/
			tftp_send_ack(SWAP(p_pkg->blocknum),port);
			
			cur_blocknum++;//更新标志块编号
			printf("tftp: %d\r\n",cur_blocknum);
			
			/*如果是最后一个数据包,则复位标志块编号和内存起始地址,为下一次传输做准备*/
			if(length < 512){
				cur_blocknum = 1;
				tftp_download_addr = (u8*)TFTP_START_ADDR;
				printf("Download Completely!\n\r");
			}
				
		}
	
		
	}	
	
}

6410完整代码
/****************************
@File:tftp.c
@
@Tiny6410裸机下学期代码
@tftp协议配置文件
@Author:小君君
@****************************/

#include "common.h"
#include "string.h"
#include "arp.h"

u8 sendbuf[1024];
u32 cur_blocknum = 1;
#define TFTP_START_ADDR 0x51000000


/*对IP包进行校检*/
u16 checksum(u8 *ptr, int len)
{
    u32 sum = 0;
    u16 *p = (u16 *)ptr;

    while (len > 1)
    {
        sum += *p++;
        len -= 2;
    }
    
    if(len == 1)
        sum += *(u8 *)p;
    
    while(sum>>16)
        sum = (sum&0xffff) + (sum>>16);
    
    return (u16)((~sum)&0xffff);
}



/*客户端发送读取服务器数据请求*/
void tftp_request(const char* filename)
{
	u8* p_tftp = &sendbuf[200];	//tftp头部开始地址
	u32 tftp_index = 0;			//作为下标表示位置的移动
	UDP_HDR* udphdr;
	u8* iphdr;
	
	/*1.构建tftp头部*/
	
	p_tftp[0] = 0x00;		//网络字节序(大端模式),写入操作码
	p_tftp[1] = 0x01;
	
	tftp_index += 2;		//下标移动
	
	sprintf(&p_tftp[tftp_index],"%s",filename);	//写入文件名
	tftp_index += strlen(filename);
	p_tftp[tftp_index] = '\0';			//文件名尾部追加0
	tftp_index += 1;
	
	sprintf(&p_tftp[tftp_index],"%s","octet");	//写入数据传输模式
	tftp_index += strlen("octet");
	p_tftp[tftp_index] = '\0';			//尾部追加0
	tftp_index += 1;
	
	/*2.构建udp头部*/
	
	udphdr = (UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
	iphdr = (u8*)udphdr + sizeof(ETH_HDR);
	
	udphdr->sport = SWAP(48915);			//参数默认选这些
        udphdr->dport = SWAP(69);
        udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
        udphdr->udpchksum = 0x00;
           
	
	/*3.构建ip头部*/
	
	 udphdr->iphdr.vhl = 0x45;				//固定值
    	 udphdr->iphdr.tos = 0x00;				//固定值
    	 udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
    	 udphdr->iphdr.ipid = SWAP(0x00);			//固定值
     	 udphdr->iphdr.ipoffset = SWAP(0x4000);			//固定值
    	 udphdr->iphdr.ttl = 0xff;				//固定值
     	 udphdr->iphdr.proto = 0x11;				//固定值
    	 memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);		
    	 memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
    	 udphdr->iphdr.ipchksum = 0;
    	 udphdr->iphdr.ipchksum = checksum(iphdr,20);	//ip头部长度固定为20个字节
    
    	 
    
    	 memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
    	 memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
    	 udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
    	 
    	 dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}


/*客户端响应服务器已正常收到来自服务器的数据包*/
void tftp_send_ack( u16 blocknum,u16 serverport)
{
	u8* p_tftp = &sendbuf[200];	//tftp头部开始地址
	u32 tftp_index = 0;			//作为下标表示位置的移动
	UDP_HDR* udphdr;
	u8* iphdr;
	
	/*1.构建tftp头部*/
	
	p_tftp[0] = 0x00;		//网络字节序(大端模式),写入操作码
	p_tftp[1] = 0x04;
	
	tftp_index += 2;		//下标移动
	
	p_tftp[2] = (blocknum & 0xff00) >> 8;		//网络字节序(大端模式),写入块编号,要反转高低位
	p_tftp[3] = blocknum & 0xff;
	
	tftp_index += 2;		//下标移动

	
	
	/*2.构建udp头部*/
	
	udphdr =(UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
	iphdr = (u8*)udphdr + sizeof(ETH_HDR);
	
	udphdr->sport = SWAP(48915);			//参数默认选这些
        udphdr->dport = serverport;//这是目的端口号,即服务器端口号,可以从来自服务器的数据包获取
        udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
        udphdr->udpchksum = 0x00;
        
        
	
	/*3.构建ip头部*/
	
	 udphdr->iphdr.vhl = 0x45;				//固定值
    	 udphdr->iphdr.tos = 0x00;				//固定值
    	 udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
    	 udphdr->iphdr.ipid = SWAP(0x00);			//固定值
     	 udphdr->iphdr.ipoffset = SWAP(0x4000);			//固定值
    	 udphdr->iphdr.ttl = 0xff;				//固定值
     	 udphdr->iphdr.proto = 0x11;				//固定值
    	 memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);		
    	 memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
    	 udphdr->iphdr.ipchksum = 0;
    	 udphdr->iphdr.ipchksum = checksum(iphdr,20);	//ip头部长度固定为20个字节
    
    	 
    
    	 memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
    	 memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
    	 udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
    	 
    	 dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}


/*TFTP对数据传输的处理*/
void tftp_process(u8* buf,u32 len,u16 port)
{
	u32 i = 0,length =0;
	u8* tftp_download_addr = (u8*)TFTP_START_ADDR;
	TFTP_PAK* p_pkg = (TFTP_PAK*)(buf + sizeof(UDP_HDR));
	length = len - sizeof(UDP_HDR) - 4;
	
	
	/*判断接收到的包是否是数据包*/
	if(SWAP(p_pkg->opcode) == 3){
		
		/*判断接收到的包是否是最后一个*/
		if(SWAP(p_pkg->blocknum) == cur_blocknum){
			
			tftp_download_addr = (u8*)(TFTP_START_ADDR + (cur_blocknum - 1)*512);
	
			/*复制数据到指定内存地址*/
			for(i = 0;i < length;i++){
				
				*(tftp_download_addr++) = *(p_pkg->data + i);
			}
			
			/*客户端发送接收到数据包的应答信号*/
			tftp_send_ack(SWAP(p_pkg->blocknum),port);
			
			cur_blocknum++;//更新标志块编号
			printf("tftp: %d\r\n",cur_blocknum);
			
			/*如果是最后一个数据包,则复位标志块编号和内存起始地址,为下一次传输做准备*/
			if(length < 512){
				cur_blocknum = 1;
				tftp_download_addr = (u8*)TFTP_START_ADDR;
				printf("Download Completely!\n\r");
			}
				
		}
	
		
	}	
	
}


/****************************
@File:main.c
@
@Tiny6410裸机下学期代码
@网卡测试文件
@Author:小君君
@****************************/

#include "common.h"

int main(void)
{
	int num = 1000;
	
	
	
	led_init();//LED的GPIO初始化
	
	button_init();//按键初始化
	
	
	
	led_on();//点亮4颗LED
	
	uart_init();//串口初始化
	
	putchar('a');
	putchar('\r');
	putchar('\n');
	putchar('b');
	putchar('\r');
	putchar('\n');
	
	
	dma_init();//DMA初始化
	dma_start();//启动DMA发送数据到串口
	
	uart_init();//串口再次初始化,使得串口恢复中断或者轮询模式
	
	lcd_init();
	lcd_clear_screen(0xFFFFFF);
	
	
	
	dm9000_init();
	
	//dm9000_arp();
	
	while(1){
		
		printf("=================================================\n\r");
		printf("===================JUN-BOOT======================\n\r");
		printf("0.Send the ARP to get yhe host's MAC address\n\r");
		printf("1.Download the linux kernel from tftp\n\r");
		printf("2.Boot linux OS from SDRAM\n\r");
		printf("3.Junjun is a houmorous\n\r");
		printf("=================================================\n\r");
		printf("===================LCD_TEST======================\n\r");
		printf("4.清屏\n\r");
		printf("5.画横线\n\r");
		printf("6.画竖线\n\r");
		printf("7.画十字架\n\r");
		printf("8.画同心圆\n\r");
		printf("9.AD转换\n\r");
		printf("10.Send the ARP to get yhe host's MAC address\n\r");
		printf("11.Download the linux kernel from tftp\n\r");
		printf("12.Boot linux OS from SDRAM\n\r");
		printf("                                                   \n\r");
		printf("请输入0-12任意一个数字:\n\r");
		
		scanf("%d",&num);	
		
		switch(num){
			case 0:
				printf("请支持成都国嵌\n\r");
				break;
			case 1:
				printf("国嵌学院=====打造你的嵌入式人生\n\r");
				break;
			case 2:
				printf("学ARM,学Linux,学C++,学安卓,学嵌入式,就到成都国嵌学院\n\r");
				break;
			case 3:
				printf("只要你肯努力,你的明天就会等你!!\n\r");
				break;
			case 4:
				lcd_clear_screen(0x000000);
				break;
			case 5:
				lcd_clear_screen(0x000000);
				lcd_draw_hline(HEIGHT/2, 100, WIDTHEIGHT-100, 0xff0000);
				break;
			case 6:
				lcd_clear_screen(0x000000);
				lcd_draw_vline(WIDTHEIGHT/2, 50, HEIGHT-50, 0xff0000);
				break;
			case 7:
				lcd_clear_screen(0x000000);
				lcd_draw_cross(HEIGHT/2, WIDTHEIGHT/2, 20, 0x777777);
				break;
			case 8:
				lcd_clear_screen(0x000000);
				lcd_draw_circle();
				break;
			case 9:
				read_adc(0);
				break;
			case 10:
				arp_request();
				break;
			case 11:
				tftp_request("zImage");//一定要放对位置,而且文件名要一致,目前的程序只支持zImage
				break;
			case 12:
				printf("the function will be completed next time!\n\r");
				break;
			default:
				printf("只要你肯努力,你的明天就会等你!!\n\r");
				break;
			
		}
	}
	return 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值