完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
开发Atmel Studio 7.0
使用的开发锐志51开发+AVR转环境接板座紧板 :40脚直插的ATMega16 Flash容量:16KB SRAM容量:1KB(这个是最缺的资源) 晶振:1.0592MHz,熔丝位配置为EE C1(使用外部晶振) 如果换用其他的晶振,除了串口的波特率寄存器以及两个定时器的定时重装值需要修改以外,其他应该都不需要修改。 UIP版本: UIP-1.0 通过UIP协议栈和ENC28J60网卡实现一个非常简单的HTTP服务器。 工程下载地址:https://pan.baidu.com/s/1slLXFTb 程序编译后的大小如下:(开了UDP功能) 开了串口输出: Program Memory Usage : 8912 bytes 54.4 % Full Data Memory Usage : 874 bytes 85.4 % Full 没开串口输出: Program Memory Usage : 8272 bytes 50.5 % Full Data Memory Usage : 874 bytes 85.4 % Full 【接线方式】 ENC28J60上的SPI接收AVR对应的SPI接口上,共4根线: PB4 CS PB5 MOSI PB6 MISO PB7 SCK 网卡中断输出接PB2(外部中断2) 【uip部分】 首先,新建一个空白项目,项目名称为ENC28J60,先解压下载的uip-master.zip文件,打开“我的文档/Atmel Studio/7.0/ENC28J60/ENC28J60/”文件夹,把解压出来的uip文件夹复制进去,进入后的uip文件夹,复制掉Makefile.include,然后把解压出来的unix/clock-arch.h(注意是.h)和unix/uip-conf.h都复制到uip文件夹里面,并把uip文件夹添加到项目中。 打开uipopt.h,找到127行的UIP_FIXEDETHADDR,把它注释掉: //#define UIP_FIXEDETHADDR 0 // 注释掉!打开clock-arch.h,把CLOCK_CONF_SECOND由1000改为50: #define CLOCK_CONF_SECOND 50 // 1秒=50*20ms 打开uip-conf.h文件,按下面的中文提示修改代码: /** * addtogroup uipopt * @{ */ /** * name 项目特定的配置选项 * @{ * * uIP 有许多配置选项,可以为每个项目覆盖 *。这些保存在特定于项目的 uip-conf.h * 文件中,所有配置名称都带有前缀 UIP_CONF。*/ /* * 版权所有 (c) 2006,瑞典计算机科学研究所。* 版权所有。* * 允许以源代码和二进制形式重新分发和使用,无论是否修改 * 满足以下条件: * 1. 源代码的重新分发必须保留上述版权 * 通知、此条件列表和以下内容免责声明。* 2. 以二进制形式重新分发必须复制上述版权 * 通知,此条件列表以及随分发提供的 * 文档和/或其他材料中的以下免责声明。* 3. 未经特别事先书面许可,不得使用研究所名称或其贡献者的名称 * 来支持或推广源自本软件的产品 *。* * 本软件由研究所和贡献者“按原样”提供,并且 * 任何明示或暗示的保证,包括但不限于 * 针对特定用途的适销性和适用性的暗示保证。在任何情况下,机构或贡献者均不对任何直接的、间接的、偶然的、特殊的、示范性的或后果性的*损害(包括但不限于采购替代品*或服务;利润;或业务中断)* 无论是由于何种原因造成的,并且基于任何责任理论,无论是在合同中、严格的责任或侵权行为(包括疏忽或其他方面)以任何方式引起的 * 出于对本软件的使用, * 这种损害。* * 此文件是 uIP TCP/IP 堆栈的一部分 * * $Id: uip-conf.h,v 1.6 2006/06/12 08:00:31 adam Exp $ */ /** * file * 示例uIP 配置文件 * author * Adam Dunkels 《adam@sics.se》 */ #ifndef __UIP_CONF_H__ #define __UIP_CONF_H__ #include 《inttypes.h》 /** * 8 位数据类型 * * 此 typedef 定义了整个 uIP 使用的 8 位类型。* * hideinitializer */ typedef uint8_t u8_t; /** * 16 位数据类型 * * 此 typedef 定义了整个 uIP 使用的 16 位类型。* * hideinitializer */ typedef uint16_t u16_t; /** * 统计数据类型 * * 这个 typedef 定义了用于在 * uIP 中保存统计数据的数据类型。* * hideinitializer */ typedef unsigned short uip_stats_t; /** * 最大 TCP 连接数。* * hideinitializer */ #define UIP_CONF_MAX_CONNECTIONS3 // 原值40,占用35字节内存。由于40*35=1400B》1KB,所以这个值改小/** * 最大监听TCP端口数。* * hideinitializer */ //#define UIP_CONF_MAX_LISTENPORTS 40 // 使用uip默认的值就行了,不用字节自定义。每占2 /** * uIP缓冲区大小。* * hideinitializer */ #define UIP_CONF_BUFFER_SIZE 420 /** * CPU 字节顺序。* * hideinitializer */ #define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN /** * 登录或注销 * * hideinitializer */ #define UIP_CONF_LOGGING 0 // 关闭功能日志 /** * UDP 支持开或关 * * hideinitializer */ #define UIP_CONF_UDP 1 // 开启UDP 功能/** * UDP 校验和开或关* * hideinitializer */ #define UIP_CONF_UDP_CHECKSUMS 1 /** * uIP 统计开或关* * hidender */ #define UIP_CONF_STATISTICS 1 /* 这里我们包括我们在项目中使用的应用程序的头文件。*/ // 把这下面的文件都注释掉 /*#include “smtp.h”*/ /*#include “hello-world.h”*/ /*#include “telnetd.h”*/ /* #include “webserver.h”*/ /*#include “dhcpc.h”*/ /*#include “resolv.h” 无效uip_udp_appcall(无效);#include 《stdio.h》 // 包含printf的定义 // 配置MAC地址 #define UIP_FIXEDETHADDR 1 #define UIP_ETHADDR0 0x42 // 第一个字节必须为偶数才是单MAC地址 #define UIP_ETHADDR1 0x52 #define UIP_ETHADDR2 0x4d #define UIP_ETHADDR3 0x4e #define UIP_ETHADDR4 0x45 #define UIP_ETHADDR5 0x54 #endif /* __UIP_CONF_H__ */ /** @} */ /** @} */ 无效uip_udp_appcall(无效);#include 《stdio.h》 // 包含printf的定义 // 配置MAC地址 #define UIP_FIXEDETHADDR 1 #define UIP_ETHADDR0 0x42 // 第一个字节必须为偶数才是单MAC地址 #define UIP_ETHADDR1 0x52 #define UIP_ETHADDR2 0x4d #define UIP_ETHADDR3 0x4e #define UIP_ETHADDR4 0x45 #define UIP_ETHADDR5 0x54 #endif /* __UIP_CONF_H__ */ /** @} */ /** @} */ 【网卡驱动部分】 ENC28J60.h: PHSTAT1 0x01 #define PHID1 0x02 #define PHID2 0x03 #define PHCON2 0x10 #define PHSTAT2 0x11 #define PHSTAT2_LSTAT _BV(10) #define PHIE 0x12 #define PHIE_PLNKIE _BV(4) #define PHIE_PLNKIE _BV(4) #define PNKIE _BVIEH1PH1GIRP 0x14 #define ENCCLR 0xa0 #define ENCSET 0x80 #define ENC28J60_GetBank() (ENC28J60_Read(ECON1) & ECON1_BSEL) // 获取当前Bank号#define ENC28J60_IsPluggedIn() ((ENC28J60_2) STATIC //STATIC是否插有网线(并接通) #define ENC28J60_WriteBufferByte(data) ENC28J60_SetBits(0, data, 0x7a) //向写入单个字节#define SPI_Read() SPIWrite(0xff) void ENC28J60_Init(void); uint8_t ENC28J60_Read(uint8_t 地址);无效 ENC28J60_ReadBuffer(uint8_t *buf, uint16_t len); uint16_t ENC28J60_ReadPhy(uint8_t addr); void ENC28J60_SelectBank(uint8_t bank); 无效 ENC28J60_SetBits(uint8_t 地址,uint8_t 掩码,uint8_t 值);无效ENC28J60_SystemReset(无效);无效ENC28J60_Write(uint8_t地址,uint8_t值);void ENC28J60_WriteBuffer(uint8_t *data, uint16_t len); 无效 ENC28J60_WritePhy(uint8_t 地址,uint16_t 值);uint8_t SPI_Write(uint8_t 数据); #endif /* ENC28J60_H_ */ ENC28J60.c: #include 《avr/io.h》 #include 《avr/sfr_defs.h》 #include “uip/uip.h” #include “ENC28J60.h” //注意:执行这些函数时一定要先关闭网卡中断!! !防止SPI序列被破坏 void ENC28J60_Init(void) { ENC28J60_CS1; // 状态下CS应该为高关系 ENC28J60_SystemReset(); //设置接收的起点和起点 ENC28J60 EN_Write(ERX,0); // 起点设为0可以避免网卡错误使读路径跑飞 ENC28J60_Write(ERXSTH, 0); ENC28J60_Write(ERXNDL, ENC_RECV_END & 0xff); ENC28J60_Write(ERXNDH, ENC_RECV_END 》》 8); ENC28J60_Write(ERXRDPTL, 0); // 数据保护路径的位置 ENC28J60_Write(ERXRDPTH, 0); // 配置MAC while ((ENC28J60_Read(ESTAT) & ESTAT_CLKRDY) == 0); // 等待MAC和PHY电阻稳定ENC28J60_SelectBank(2); ENC28J60_Write(MACON1, MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN); // 允许接收,开流量控制 ENC28J60_Write(MACON3, MACON3_PADCFG_0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); ENC28J60_Write(MACON4, MACON4_DEFER); ENC28J60_Write(MABBIPG,0x15);ENC28J60_Write(MAIPGL,0x12);ENC28J60_Write(MAIPGH,0x0c);// 设置网卡地址 ENC28J60_SelectBank(3); ENC28J60_Write(MAADR1, UIP_ETHADDR0); ENC28J60_Write(MAADR2, UIP_ETHADDR1); ENC28J60_Write(MAADR3, UIP_ETHADDR2); ENC28J60_Write(MAADR4, UIP_ETHADDR3); ENC28J60_Write(MAADR5, UIP_ETHADDR4); ENC28J60_Write(MAADR6, UIP_ETHADDR5); // 配置PHY ENC28J60_WritePhy(PHCON1, PHCON1_PDPXMD); // 全双工模式 // 允许接收数据包 ENC28J60_Write(EIE, EIE_PKTIE | EIE_LINKIE | EIE_INTIE); //如果收到了数据包,或网络连接发生变化,就触发中断 ENC28J60_WritePhy(PHIE, PHIE_PLNKIE | PHIE_PGEIE); //配置PHY中断(监测网络连接变化) ENC28J60_Write(ECON1, ECON1_RXEN); } uint8_t ENC28J60_Read(uint8_t addr) { uint8_t 数据;ENC28J60_CS0; SPI_Write(addr & 0x1f); 数据 = SPI_Read(); // ETH 缓存 if (addr & 0x80) data = SPI_Read(); // MAC和MII电机需要再读一次 ENC28J60_CS1; 返回数据;} void ENC28J60_ReadBuffer(uint8_t *data, uint16_t len) { ENC28J60_CS0; SPI_Write(0x3a); while (len--) *data++ = SPI_Read(); ENC28J60_CS1;} uint16_t ENC28J60_ReadPhy(uint8_t addr) { uint16_t 数据;ENC28J60_SelectBank(2); ENC28J60_Write(MIREGADR,地址);ENC28J60_SetBits(MICMD, MICMD_MIIRD, ENCSET); ENC28J60_SelectBank(3); 而 (ENC28J60_Read(MISTAT) & MISTAT_BUSY); ENC28J60_SelectBank(2); ENC28J60_SetBits(MICMD, MICMD_MIIRD, ENCCLR); 数据 = ENC28J60_Read(MIRDL); 数据 |= ENC28J60_Read(MIRDH) 《《 8; 返回数据;} void ENC28J60_SelectBank(uint8_t bank) { uint8_t value = ENC28J60_Read(ECON1); 银行 &= ECON1_BSEL; if ((value & ECON1_BSEL) != bank) { value = (value & ~ECON1_BSEL) | 银行; ENC28J60_Write(ECON1, value); } } // 值:ENCSET/ENCCLR void ENC28J60_SetBits(uint8_t addr, uint8_t mask, uint8_t value) { ENC28J60_CS0; SPI_Write((addr & 0x1f) | value); SPI_Write(掩码);ENC28J60_CS1;} void ENC28J60_SystemReset(void) { ENC28J60_CS0; SPI_Write(0xff); ENC28J60_CS1;} void ENC28J60_Write(uint8_t addr, uint8_t value) { ENC28J60_SetBits(addr, value, 0x40); } void ENC28J60_WriteBuffer(uint8_t *data, uint16_t len) { ENC28J60_CS0; SPI_Write(0x7a); 而 (len--) SPI_Write(*data++); ENC28J60_CS1;} void ENC28J60_WritePhy(uint8_t addr, uint16_t value) { ENC28J60_SelectBank(2); ENC28J60_Write(MIREGADR,地址);ENC28J60_Write(MIWRL, value & 0xff); ENC28J60_Write(MIWRH,值》》 8);ENC28J60_SelectBank(3); 而 (ENC28J60_Read(MISTAT) & MISTAT_BUSY); } uint8_t SPI_Write(uint8_t 数据) { SPDR = 数据; 而 ((SPSR & _BV(SPIF)) == 0); 返回 SPDR;} ENC28J60_Write(MIWRL, value & 0xff); ENC28J60_Write(MIWRH,值》》 8);ENC28J60_SelectBank(3); 而 (ENC28J60_Read(MISTAT) & MISTAT_BUSY); } uint8_t SPI_Write(uint8_t 数据) { SPDR = 数据; 而 ((SPSR & _BV(SPIF)) == 0); 返回 SPDR;} ENC28J60_Write(MIWRL, value & 0xff); ENC28J60_Write(MIWRH,值》》 8);ENC28J60_SelectBank(3); 而 (ENC28J60_Read(MISTAT) & MISTAT_BUSY); } uint8_t SPI_Write(uint8_t 数据) { SPDR = 数据; 而 ((SPSR & _BV(SPIF)) == 0); 返回 SPDR;} 【主程序】 main.c: // 晶振:外部11.0592MHz #include 《avr/interrupt.h》 #include 《avr/io.h》 #include 《avr/pgmspace.h》 #include 《avr/sfr_defs.h》 #include “uip/uip_arp. h” #include “uip/timer.h” #include “uip/uip.h” #include “ENC28J60.h” //#include “USART.h” // 如果包含了这个头文件,将向串口输出发送和接收的数据包,以便调试#define ETHHDR ((struct uip_eth_hdr *)&uip_buf[0]) const uint8_t seg8[] PROGMEM = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x80,0x80, ; // 共阳数码管段码表 uint8_t flag_disp = 0; // 是否插了网,初值必须为0(启动时只有网线是插上的,线会自动触发LINK_上中断) uint8t pkt_in = 0; // 是否有数据包进来 uint16_t num_disp = 0; // 数码管显示的数字clock_time_t clocktime = 0; // 当前时钟值 clock_time_t clock_time(void) { return clocktime; } void u_appcall(void) /*下面的代码太浪费SRAM!const char *str = “HTTP/1. } } void uip_udp_appcall(void) { } void myapp_init(void) { uip_listen(HTONS(80)); } void read_packet(void) { uint16_t status[2]; 静态 uint16_t next_ptr = 0; #ifdef USART_H_ USART_SendString(PSTR(”Recv: EPKTCNT=“)); ENC28J60_SelectBank(1); USART_SendNumber(ENC28J60_Read(EPKTCNT)); USART_SendString(PSTR(”, start=“)); USART_SendNumber(next_ptr); #endif ENC28J60_SelectBank(0); num_disp = ENC28J60_Read(ERXWRPTL) | (ENC28J60_Read(ERXWRPTH) 《《 8); // 数码管上显示接收写轨迹的位置 ENC28J60_Write(ERDPTL, next_ptr & 0xff); // 将读处移动到当前数据包 ENC28J60_Write(ERDPTH, next_ptr 》》 8); ENC28J60_ReadBuffer((uint8_t *)&next_ptr, sizeof(next_ptr)); // 读取下一个数据包的位置 ENC28J60_ReadBuffer((uint8_t *)status, 大小(状态));// 状态向量 uip_len = status[0] - 4; // 数据包大小 #ifdef USART_H_ USART_SendString(PSTR(”, len=“)); USART_SendNumber(uip_len); USART_SendString(PSTR(”, next=“)); USART_SendNumber(next_ptr); USART_SendString(PSTR(”, status=“)); USART_SendNumber(状态[1]); USART_SendString(PSTR(”rn“)); #endif if (uip_len 《= UIP_BUFSIZE) { ENC28J60_ReadBuffer(uip_buf, uip_len); #ifdef USART_H_ output_packet(); #endif } else uip_len = 0; // 内存不足,删除 // 注意:数据包与数据包之间可能有填充字节 ENC28J60_Write(ERXRDPTL, next_ptr & 0xff); // 允许之后接收的数据区域覆盖 ENC28J60_Write(ERXRDPTH, next_ptr 》》 8); ENC28J60_SetBits(ECON2, ECON2_PKTDEC, ENCSET); // 数据包数减1 } void send_packet(void) { GICR &= ~_BV(INT2); ENC28J60_SelectBank(0); ENC28J60_Write(ETXSTL, ENC_SEND_START & 0xff); // 数据首地址 ENC28J60_Write(ETXSTH, ENC_SEND_START 》》 8); ENC28J60_Write(ETXNDL, (ENC_SEND_START + uip_len) & 0xff); // 数据尾地址 ENC28J60_Write(ETXNDH, (ENC_SEND_START + uip_len) 》》 8); ENC28J60_Write(EWRPTL, ENC_SEND_START & 0xff); // 设置写轨迹位置 ENC28J60_Write(EWRPTH, ENC_SEND_START 》》 8); ENC28J60_WriteBufferByte(0); // 写入控制字节 ENC28J60_WriteBuffer(uip_buf, uip_len); // 写入要发送的数据 ENC28J60_SetBits(ECON1, ECON1_TXRTS, ENCSET); // 开始发送 #ifdef USART_H_ USART_SendString(PSTR(”Send: len=“)); USART_SendNumber(uip_len); USART_SendString(PSTR(”rn“)); 输出数据包();#endif while (ENC28J60_Read(ECON1) & ECON1_TXRTS); // 等待发送完毕 GICR |= _BV(INT2); } int main(void) { struct timer arp_timer, period_timer; uint8_t i; uip_ipaddr_t ipaddr; // 看门狗配置 WDTCR = _BV(WDE) | _BV(WDP2) | _BV(WDP1) | _BV(WDP0); #ifdef USART_H_ // 串口配置 UBRRL = 5; // 波特率:115200 UCSRB = _BV(TXEN); // 允许发送 // 由于SRAM空间不足,printf功能无法使用#endif // SPI端口配置DDRB = _BV(DDB7) | _BV(DDB5) | _BV(DDB4); SPSR = _BV(SPI2X); // 选择2分频: 11.0592MHz/2=5.5296MHz, 远最高允许速度20MHz SPCR = _BV(SPE) | _BV(MSTR); // 开SPI 设为主模式 // 重启配置(INT2_PB2): 下降沿触发MCUCSR &= ~_BV(ISC2); // 注意:即使GICR中的INT2没有打开,但只要INT2上有下降沿,GIFR中的INTF2标志也会置位//只有此后打开了INT2中断和中断,才执行中断函数 // 数码管动态扫描配置 DDRA = 0xff; // 配置段选端口PORTA = 0xff; // 数码数码管 DDRC =_BV(DDB7) | _BV(DDB6) | _BV(DDB5) | _BV(DDB4) | _BV(DDB3) | _BV(DDB0); // 配置位选端口sei(); // 开总中断TIMSK |= _BV(TOIE0); // 开中断中断 TCNT0 = 0xff; // 先让儿童一次,点亮数码管 TCCR0 |=_BV(02); // 开定时器0: 设为256分频, 总时间约5.92 ms // uip时钟定时器TIMSK |=_BV(TOIE2); TCNT2 = 40;// 定时 20ms TCCR2 |= _BV(CS22) | _BV(CS20); // 1024 分频 timer_set(&arp_timer, CLOCK_SECOND * 10); timer_set(&periodic_timer, CLOCK_SECOND / 2); ENC28J60_Init(); uip_init(); uip_ipaddr(ipaddr, 192, 168, 1, 50); // IP地址 uip_sethostaddr(ipaddr); uip_ipaddr(ipaddr, 192, 168, 1, 1); // 网关 uip_setdaddr(ipaddr); uip_ipaddr(ipaddr, 255, 255, 255, 0); // 子网掩码 uip_setnetmask(ipaddr); myapp_init(); GICR |= _BV(INT2); // 开网卡中断 while (1) { // 读取一个数据包 if (pkt_in) { asm(”wdr“); GICR &= ~_BV(INT2); // 进入临界区之前必须关网中断!读包();ENC28J60_SelectBank(1); if (!ENC28J60_Read(EPKTCNT)) // 若已接收完全部数据包 { pkt_in = 0; ENC28J60_SetBits(EIE, EIE_PKTIE, ENCSET); // 则重开数据包中断中断 } GICR |= _BV(INT2); // 重开网卡中断 } if (uip_len 》 0) { asm(”wdr“); if (ETHHDR-》type == htons(UIP_ETHTYPE_IP)) { uip_arp_ipin(); uip_input(); 如果 (uip_len 》 0) { uip_arp_out(); 发送数据包();} } else if (ETHHDR-》type == htons(UIP_ETHTYPE_ARP)) { uip_arp_arpin(); 如果(uip_len 》 0)send_packet(); } } else if (timer_expired(& period_timer)) { asm(”wdr“); // 喂狗 timer_reset(&periodic_timer); for (i = 0; i 《 UIP_CONNS; i++) { uip_periodic(i); 如果 (uip_len 》 0) { uip_arp_out(); 发送数据包();} } #if UIP_UDP for (i = 0; i 《 UIP_UDP_CONNS; i++) { uip_udp_periodic(i); 如果 (uip_len 》 0) { uip_arp_out(); 发送数据包();} } #endif } if (timer_expired(&arp_timer)) { timer_reset(&arp_timer); uip_arp_timer(); } } } // 网卡中断ISR(INT2_vect) { uint8_t status; //现在INT2为低数据 ENC28J60_SetBits(EIE, EIE_INTIE, ENCCLR); //该语句执行完毕后,INT2以后会回到高,该中断之后会出现新的结局状态//如果执行期间功能又来了,那么肯定会被本次中断中断函数处理到 sei(); // 允许数码管扫描中断占本中断,防止数码管发光 status = ENC28J60_Read(EIR); // 获取所有网卡中断的状态 // 一个处理:if (status & EIR_PKTIF) { /* 收到新数据包 */ pkt_in = 1; ENC28J60_SetBits(EIE, EIE_PKTIE, ENCCLR); // 暂时关闭该中断 } if (status & EIR_LINKIF) { ENC28J60_ReadPhy(PHIR); // 清除中断标志 flag_disp = ENC28J60_IsPluggedIn(); } // 处理其他中断: if (status & 。。。。) {。。。。} // 不能加else!GICR &= ~_BV(INT2); ENC28J60_SetBits(EIE, EIE_INTIE, ENCSET); //如果还有新来的中断没处理,那么INT2将出现下降沿,退出后再次执行本函数cli(); GICR |= _BV(INT2); //退出时将自动执行sei(); } // 动态扫描 //每次只到数码管一位,从低位高位 ISR(TIMER0_OVF_vect) { static uint16_t numbuf; 静态 uint8_t 掩码 = _BV(PORTC7); TCNT0 = 0x90;//数码管点亮的时间:(256-144)/256 * 5.926ms = 2.592625ms if (mask == _BV(PORTC7)) numbuf = num_disp; // 重装数字PORTC |= _BV(PORTC7) | _BV(PORTC6) | _BV(PORTC5) | _BV(PORTC4) | _BV(PORTC3) | _BV(PORTC0); // PORT之前发生的数码管 = pgm_read_byte(&seg8[numbuf% 10]); // 设置显示字符PORTC &= ~mask; // 开启数码管 // 下一次要开启的数码管 mask 》》= 1; if (mask == _BV(PORTC2)) { mask = _BV(PORTC0); numbuf = flag_disp; } else if (mask ==) // 若已扫描完后0 mask = _BV(PORTC7); // 则回到最低位 else numbuf /= 10; } // uip 中断中断 ISR(TIMER2_OVF_vect) { TCNT2 = 40; 时钟++;} // 则回到最低位 else numbuf /= 10; } // uip 中断中断 ISR(TIMER2_OVF_vect) { TCNT2 = 40; 时钟++;} // 则回到最低位 else numbuf /= 10; } // uip 中断中断 ISR(TIMER2_OVF_vect) { TCNT2 = 40; 时钟++;} 【调试用的串口输出】 USART.h: #ifndef USART_H_ #define USART_H_ void USART_SendChar(uint8_t ch); void USART_SendNumber(uint16_t num); void USART_SendString(const char *str); // str必须加PSTR() void output_packet(void); #endif /* USART_H_ */ USART.c: #include 《avr/io.h》 #include 《avr/pgmspace.h》 #include ”uip/uip.h“ void USART_SendChar(uint8_t ch) { UDR = ch; 而 ((UCSRA & _BV(UDRE)) == 0); } void USART_SendNumber(uint16_t num) { uint8_t i, ch; for (i = 0; (i & 7) 《 5; i++) { if (i == 0) { ch = ‘0’ + num / 10000; 数量 %= 10000; } else { ch = ‘0’ + num / 1000; 数量 = 数量 % 1000 * 10; } if (ch != ‘0’ || (i & 0x84) != 0) { i |= 0x80; USART_SendChar(ch); } } } void USART_SendString(const char *str) { char c; while ((c = pgm_read_byte(str++)) != ‘ ’) USART_SendChar(c); } void output_packet(void) { uint16_t i; for (i = 0; i 《 uip_len; i++) { if ((uip_buf 》》 4) 》 9) USART_SendChar(‘A’ + (uip_buf 》》 4) - 10); else USART_SendChar(‘0’ + (uip_buf 》》 4)); if ((uip_buf & 0x0f) 》 9) USART_SendChar(‘A’ + (uip_buf & 0x0f) - 10); else USART_SendChar(‘0’ + (uip_buf & 0x0f)); } USART_SendString(PSTR(”rn“)); } 将这五个文件添加到项目中后编译,然后用AVR Fighter和ISP下载线下载到开发板上,并把网线插到路由器上,一段时间后,数码管上显示的数字如下: 1 00686 左边的1表示插了网线,右边的686是当前接收缓冲区写指针的位置。 在电脑或手机上用浏览器访问: http://192.168.1.50/,可以看到网页内容: Hello World! This is a very long string!!! 注意MAC地址的第一个字节必须为偶数。因为如果为奇数,有些路由器就会将该MAC地址识别为多播地址,导致发出去的数据包丢失。 uip协议栈中本身没有Flash版本的uip_send函数,我们可以在uip.c的底部仿照uip_send函数添加一个uip_send_P函数: // 添加的函数 void uip_send_P(const void *data) { int len = strlen_P(data); if(len 》 0) { uip_slen = len; if(data != uip_sappdata) { memcpy_P(uip_sappdata, (data), uip_slen); } } } 该函数取消了原有的len参数,自动计算字符串的长度。 然后,在uip.c的头部包含以下头文件: #include 《avr/pgmspace.h》 在uip.h文件最后添加该函数的声明: // 。。。。。。。。。 void uip_send_P(const void *data); #endif 最后修改uip_appcall函数: void uip_appcall(void) { if (uip_newdata() || uip_rexmit()) uip_send_P(PSTR(”HTTP/1.1 200 OKrnContent-Length: 59rnKeep-Alive: timeout=5, max=100 rn连接:Keep-AlivernContent-Type:text/htmlrnrn《b》Hello World!《/b》《br》这是一个很《i》长《/i》细绳!!!”)); } 编译结果: (没开串口输出) 的程序存储器用法:8300字节50.7%完整 数据存储器的使用:874字节85.4%完整 (开了串口输出) 的程序存储器用法:8942字节54.6%完整 数据存储器的使用:874 bytes 85.4 % Full Flash占用空间有些增加是因为我们新定义了uip_send_P函数。 |
|
|
|
只有小组成员才能发言,加入小组>>
2563 浏览 0 评论
1159浏览 2评论
762浏览 1评论
518浏览 0评论
273浏览 0评论
454浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-28 10:25 , Processed in 1.481461 second(s), Total 101, Slave 83 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号