【芯片环境】
单片机:ATMega16A
晶振:外部11.0592MHz
蜂鸣器接在PD7上,网卡中断为INT2
数码管段选PA,位选从高位到低位为PC0到PC7
【main.c】
#include
#include
#include
#include
#include
#include "uip/uip_arp.h"
#include "uip/timer.h"
#include "uip/uip.h"
#include "ENC28J60.h"
#define ETHHDR ((struct uip_eth_hdr *)&uip_buf[0])
const uint8_t seg8[] PROGMEM = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; // 共阳数码管段码表
uint8_t flag_disp = 0; // 是否插了网线, 初值必须为0 (开机时只有网线是插上的, 才会自动触发LINK中断)
uint8_t pkt_in = 0; // 是否有数据包进来
uint16_t num_disp = 0; // 数码管显示的数字
clock_time_t clocktime = 0; // 当前时钟值
uint16_t next_ptr = 0;
clock_time_t clock_time(void)
{
return clocktime;
}
void uip_appcall(void)
{
char ch;
if (uip_connected())
uip_conn->appstate = 0;
if (uip_newdata())
uip_conn->appstate = 1;
if (uip_acked())
uip_conn->appstate++;
if (uip_acked() || uip_newdata() || uip_rexmit())
{
switch (uip_conn->appstate)
{
case 1:
uip_send_P(PSTR("HTTP/1.1 200 OK
Content-Length: 86
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Hello World!
This is a very long string!!!
Connection ID: "));
break;
case 2:
ch = '0' + (uip_conn - uip_conns);
uip_send(&ch, 1);
break;
case 3:
uip_close();
}
}
}
void myapp_init(void)
{
uip_listen(HTONS(80));
}
void read_packet(void)
{
uint16_t status[2];
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, sizeof(status)); // status vector
uip_len = status[0] - 4; // 数据包大小
if (uip_len <= UIP_BUFSIZE)
ENC28J60_ReadBuffer(uip_buf, uip_len);
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); // 开始发送
while (ENC28J60_Read(ECON1) & ECON1_TXRTS); // 等待发送完毕
GICR |= _BV(INT2);
}
void beep(void)
{
uint8_t old = TCCR2;
uint16_t i;
TIMSK &= ~_BV(TOIE2); // 关闭定时器2中断
TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS21); // Phase Correct PWM, 8分频: 2.71kHz
TIFR = _BV(TOV2);
for (i = 0; i < 2000; i++) // 738ms
{
while ((TIFR & _BV(TOV2)) == 0);
TIFR = _BV(TOV2); // 清除标志
}
TCCR2 = old;
TIMSK |= _BV(TOIE2); // 重开定时器2中断
}
int main(void)
{
struct timer arp_timer, periodic_timer;
uint8_t i;
uip_ipaddr_t ipaddr;
ACSR = _BV(ACD); // 禁用模拟比较器
// 蜂鸣器配置
PORTD = _BV(PORTD7);
DDRD = _BV(DDB7);
OCR2 = 0x80; // 占空比50%
if (MCUCSR & _BV(WDRF))
{
// 若触发了看门狗复位, 则蜂鸣器响铃
MCUCSR &= ~_BV(WDRF);
beep();
}
WDTCR = _BV(WDE) | _BV(WDP2) | _BV(WDP1) | _BV(WDP0); // 看门狗配置
// 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(DDC7) | _BV(DDC6) | _BV(DDC5) | _BV(DDC4) | _BV(DDC0); // 配置位选端口
sei(); // 开总中断
TIMSK |= _BV(TOIE0); // 开定时器中断
TCNT0 = 0xff; // 先让定时器溢出一次, 点亮数码管
TCCR0 |= _BV(CS02); // 开定时器0: 设为256分频, 总溢出时间约为5.926ms
// 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_setdraddr(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); // 进入临界区之前必须关网卡中断!
read_packet();
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();
if (uip_len > 0)
{
uip_arp_out();
send_packet();
}
}
else if (ETHHDR->type == htons(UIP_ETHTYPE_ARP))
{
uip_arp_arpin();
if (uip_len > 0)
send_packet();
}
}
else if (timer_expired(&periodic_timer))
{
asm("wdr"); // 喂狗
timer_reset(&periodic_timer);
for (i = 0; i < UIP_CONNS; i++)
{
uip_periodic(i);
if (uip_len > 0)
{
uip_arp_out();
send_packet();
}
}
}
if (timer_expired(&arp_timer))
{
asm("wdr");
timer_reset(&arp_timer);
uip_arp_timer();
/* ----- 抗干扰 ----- */
// 若网卡控制器自身发生了复位, 或者中断被意外关闭, 则需要重新初始化 (一般都是因为供电不足导致的)
if (!pkt_in)
{
GICR &= ~_BV(INT2); // 进入临界区, 关网卡中断, 防止时序错乱
if ((ENC28J60_Read(ECON1) & ECON1_RXEN) == 0) // 检测到RXEN意外置0, 接收模块停止工作
{
// 蜂鸣器发出警报
for (i = 0; i < 4; i++)
{
asm("wdr");
beep();
}
// 先暂时让单片机进入低功耗模式
// 增加电压不足的情况下网卡恢复正常工作的可能性
PORTA = 0xff; // 熄灭数码管
TCNT2 = 0; // 用定时器2中断作为唤醒源
// 在这里应将其他耗电量大的设备全部关闭
MCUCR |= _BV(SE) | _BV(SM0); // ADC Noise Reduction Mode
asm("sleep");
MCUCR &= ~_BV(SE);
// 网卡复位
next_ptr = 0;
ENC28J60_Init();
timer_reset(&arp_timer);
// 如果网卡电压不够, 那么这里复位后虽然配置是正确的, 但无法接收任何数据包
// 再插一根USB线供电即可解决此问题
}
GICR |= _BV(INT2);
}
}
}
}
// 网卡中断
ISR(INT2_vect)
{
uint8_t status;
// 现在INT2为低电平
ENC28J60_SetBits(EIE, EIE_INTIE, ENCCLR); // 该语句执行完毕后, INT2引脚会回到高电平, 之后新来的网卡中断都将处于pending状态
// 如果在执行该函数期间恰好又来了一个中断, 那么肯定能被本次中断函数处理到
GICR &= ~_BV(INT2); // 开全局中断前, 应防止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!
ENC28J60_SetBits(EIE, EIE_INTIE, ENCSET); // 如果还有新来的中断没处理, 那么INT2将出现下降沿, 退出后再次执行本函数, 不会和当前的函数嵌套
cli();
GICR |= _BV(INT2);
// 退出时将自动执行sei();
}
// 数码管动态扫描
// 每次只扫描一位, 从低位到高位
ISR(TIMER0_OVF_vect)
{
static uint16_t numbuf;
static uint8_t mask = _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(PORTC0); // 熄灭之前点亮的数码管
PORTA = pgm_read_byte(&seg8[numbuf % 10]); // 设置显示字符
PORTC &= ~mask; // 点亮数码管
// 下一次要点亮的数码管
mask >>= 1;
if (mask == _BV(PORTC3))
{
mask = _BV(PORTC0);
numbuf = flag_disp;
}
else if (mask == 0) // 若已扫描完一遍
mask = _BV(PORTC7); // 则回到最低位
else
numbuf /= 10;
}
// uip定时中断
ISR(TIMER2_OVF_vect)
{
TCNT2 = 40;
clocktime++;
}
【ENC28J60.c】
#include
#include
#include "uip/uip.h"
#include "ENC28J60.h"
// 注意: 执行这些函数时一定要先关闭网卡中断!!! 防止SPI序列被破坏
extern uint8_t flag_disp;
void ENC28J60_Init(void)
{
ENC28J60_CS1; // 空闲状态下CS应该为高电平
ENC28J60_SystemReset();
// 设置接收缓冲区的起点和终点
ENC28J60_Write(ERXSTL, 0); // 起点设为0可以避免网卡本身的bug使读指针跑飞
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); // 全双工模式
flag_disp = ENC28J60_IsPluggedIn(); // 获取初始网络连接状态
// 允许接收数据包
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 data;
ENC28J60_CS0;
SPI_Write(addr & 0x1f);
data = SPI_Read(); // ETH寄存器
if (addr & 0x80)
data = SPI_Read(); // MAC和MII寄存器需要再读一次
ENC28J60_CS1;
return data;
}
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 data;
ENC28J60_SelectBank(2);
ENC28J60_Write(MIREGADR, addr);
ENC28J60_SetBits(MICMD, MICMD_MIIRD, ENCSET);
ENC28J60_SelectBank(3);
while (ENC28J60_Read(MISTAT) & MISTAT_BUSY);
ENC28J60_SelectBank(2);
ENC28J60_SetBits(MICMD, MICMD_MIIRD, ENCCLR);
data = ENC28J60_Read(MIRDL);
data |= ENC28J60_Read(MIRDH) << 8;
return data;
}
void ENC28J60_SelectBank(uint8_t bank)
{
uint8_t value = ENC28J60_Read(ECON1);
bank &= ECON1_BSEL;
if ((value & ECON1_BSEL) != bank)
{
value = (value & ~ECON1_BSEL) | bank;
ENC28J60_Write(ECON1, value);
}
}
// value: ENCSET/ENCCLR
void ENC28J60_SetBits(uint8_t addr, uint8_t mask, uint8_t value)
{
ENC28J60_CS0;
SPI_Write((addr & 0x1f) | value);
SPI_Write(mask);
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);
while (len--)
SPI_Write(*data++);
ENC28J60_CS1;
}
void ENC28J60_WritePhy(uint8_t addr, uint16_t value)
{
ENC28J60_SelectBank(2);
ENC28J60_Write(MIREGADR, addr);
ENC28J60_Write(MIWRL, value & 0xff);
ENC28J60_Write(MIWRH, value >> 8);
ENC28J60_SelectBank(3);
while (ENC28J60_Read(MISTAT) & MISTAT_BUSY);
}
uint8_t SPI_Write(uint8_t data)
{
SPDR = data;
while ((SPSR & _BV(SPIF)) == 0);
return SPDR;
}
【ENC28J60.h】
#ifndef ENC28J60_H_
#define ENC28J60_H_
// 注: ISP下载口不使用SPI的片选端SS
#define ENC28J60_CS0 (PORTB &= ~_BV(PORTB4))
#define ENC28J60_CS1 (PORTB |= _BV(PORTB4))
// ENC28J60网卡本身有一个bug: 如果开机后不久数据包就来了, 只读寄存器ERXWRPT可能来不及自动更新
// 导致收到的数据包从0地址开始写入, 而没有写入指定的接收缓冲区起始点
// 解决办法有两个: 1. 读取数据包时判断ERXWRPT是不是在发送缓冲区里面, 如果是, 则直接丢弃数据包, 并重写ERXSTL寄存器
// 2.索性将发送缓冲区的起始点直接设为0, 这样不管ERXWRPT有没有自动更新, 都不会出错(这里采用这种方法)
#define ENC_RECV_END 0x19fe // 接收缓冲区终点
#define ENC_SEND_START (ENC_RECV_END + 1) // 发送缓冲区起点
/* Key Registers */
#define EIE 0x1b
#define EIE_INTIE _BV(7) // 是否输出中断
#define EIE_PKTIE _BV(6)
#define EIE_DMAIE _BV(5)
#define EIE_LINKIE _BV(4)
#define EIE_TXIE _BV(3)
#define EIE_TXERIE _BV(1)
#define EIE_RXERIE _BV(0)
#define EIR 0x1c
#define EIR_PKTIF _BV(6)
#define EIR_DMAIF _BV(5)
#define EIR_LINKIF _BV(4)
#define EIR_TXIF _BV(3)
#define EIR_TXERIF _BV(1)
#define EIR_RXERIF _BV(0)
#define ESTAT 0x1d
#define ESTAT_CLKRDY _BV(0)
#define ECON2 0x1e
#define ECON2_AUTOINC _BV(7)
#define ECON2_PKTDEC _BV(6)
#define ECON2_PWRSV _BV(5)
#define ECON2_VRPS _BV(3)
#define ECON1 0x1f
#define ECON1_TXRST _BV(7)
#define ECON1_RXRST _BV(6)
#define ECON1_DMAST _BV(5)
#define ECON1_CSUMEN _BV(4)
#define ECON1_TXRTS _BV(3)
#define ECON1_RXEN _BV(2)
#define ECON1_BSEL 0x03
/* Bank 0 */
#define ERDPTL 0x00
#define ERDPTH 0x01
#define EWRPTL 0x02
#define EWRPTH 0x03
#define ETXSTL 0x04
#define ETXSTH 0x05
#define ETXNDL 0x06
#define ETXNDH 0x07
#define ERXSTL 0x08
#define ERXSTH 0x09
#define ERXNDL 0x0a
#define ERXNDH 0x0b
#define ERXRDPTL 0x0c
#define ERXRDPTH 0x0d
#define ERXWRPTL 0x0e
#define ERXWRPTH 0x0f
#define EDMASTL 0x10
#define EDMASTH 0x11
#define EDMANDL 0x12
#define EDMANDH 0x13
#define EDMADSTL 0x14
#define EDMADSTH 0x15
#define EDMACSL 0x16
#define EDMACSH 0x17
/* Bank 1 */
#define EHT0 0x00
#define EHT1 0x01
#define EHT2 0x02
#define EHT3 0x03
#define EHT4 0x04
#define EHT5 0x05
#define EHT6 0x06
#define EHT7 0x07
#define EPMM0 0x08
#define EPMM1 0x09
#define EPMM2 0x0a
#define EPMM3 0x0b
#define EPMM4 0x0c
#define EPMM5 0x0d
#define EPMM6 0x0e
#define EPMM7 0x0f
#define EPMCSL 0x10
#define EPMCSH 0x11
#define EPMOL 0x14
#define EPMOH 0x15
#define ERXFCON 0x18
#define EPKTCNT 0x19
/* Bank 2 */
// 以M开头的寄存器地址最高位应标记为1 (读取时需要跳过dummy byte)
#define MACON1 0x80
#define MACON1_TXPAUS _BV(3)
#define MACON1_RXPAUS _BV(2)
#define MACON1_PASSALL _BV(1)
#define MACON1_MARXEN _BV(0)
#define MACON3 0x82
#define MACON3_PADCFG 0xe0
#define MACON3_PADCFG_2 _BV(7)
#define MACON3_PADCFG_1 _BV(6)
#define MACON3_PADCFG_0 _BV(5)
#define MACON3_TXCRCEN _BV(4)
#define MACON3_PHDREN _BV(3)
#define MACON3_HFRMEN _BV(2)
#define MACON3_FRMLNEN _BV(1)
#define MACON3_FULDPX _BV(0)
#define MACON4 0x83
#define MACON4_DEFER _BV(6)
#define MACON4_BPEN _BV(5)
#define MACON4_NOBKOFF _BV(4)
#define MABBIPG 0x84 // MAC Back-to-Back Inter-Packet Gap Register
#define MAIPGL 0x86 // Non-Back-to-Back Inter-Packet Gap Low Byte
#define MAIPGH 0x87
#define MACLCON1 0x88
#define MACLCON2 0x89
#define MAMXFLL 0x8a
#define MAMXFLH 0x8b
#define MICMD 0x92
#define MICMD_MIISCAN _BV(1)
#define MICMD_MIIRD _BV(0)
#define MIREGADR 0x94
#define MIWRL 0x96
#define MIWRH 0x97
#define MIRDL 0x98
#define MIRDH 0x99
/* Bank 3 */
#define MAADR5 0x80
#define MAADR6 0x81
#define MAADR3 0x82
#define MAADR4 0x83
#define MAADR1 0x84
#define MAADR2 0x85
#define EBSTSD 0x06
#define EBSTCON 0x07
#define EBSTCSL 0x08
#define EBSTCSH 0x09
#define MISTAT 0x8a
#define EREVID 0x12
#define ECOCON 0x15
#define EFLOCON 0x17
#define EPAUSL 0x18
#define EPAUSH 0x19
#define MISTAT_BUSY _BV(0)
/* PHY Registers */
#define PHCON1 0x00
#define PHCON1_PRST _BV(15) // PHY Software Reset
#define PHCON1_PLOOPBK _BV(14) // PHY Loopback
#define PHCON1_PPWRSV _BV(11) // PHY Power-Down
#define PHCON1_PDPXMD _BV(8) // PHY Duplex Mode
#define 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_PGEIE _BV(1)
#define PHIR 0x13
#define PHLCON 0x14
#define ENCCLR 0xa0
#define ENCSET 0x80
#define ENC28J60_GetBank() (ENC28J60_Read(ECON1) & ECON1_BSEL) // 获取当前Bank号
#define ENC28J60_IsPluggedIn() ((ENC28J60_ReadPhy(PHSTAT2) & PHSTAT2_LSTAT) != 0) // 判断网卡是否插有网线(并接通)
#define ENC28J60_WriteBufferByte(data) ENC28J60_SetBits(0, data, 0x7a) // 向缓冲区写入单个字节
#define SPI_Read() SPI_Write(0xff)
void ENC28J60_Init(void);
uint8_t ENC28J60_Read(uint8_t addr);
void ENC28J60_ReadBuffer(uint8_t *buf, uint16_t len);
uint16_t ENC28J60_ReadPhy(uint8_t addr);
void ENC28J60_SelectBank(uint8_t bank);
void ENC28J60_SetBits(uint8_t addr, uint8_t mask, uint8_t value);
void ENC28J60_SystemReset(void);
void ENC28J60_Write(uint8_t addr, uint8_t value);
void ENC28J60_WriteBuffer(uint8_t *data, uint16_t len);
void ENC28J60_WritePhy(uint8_t addr, uint16_t value);
uint8_t SPI_Write(uint8_t data);
#endif /* ENC28J60_H_ */
【调试用代码:串口输出寄存器状态】
void status_check(void)
{
char buf[80];
uint8_t i;
UBRRL = 5;
UCSRB = _BV(TXEN);
ENC28J60_SelectBank(1);
sprintf_P(buf, PSTR("EIE=%d, EPKTCNT=%d, ECON1=%d, ERXFCON=%d, EIR=%d
"), ENC28J60_Read(EIE), ENC28J60_Read(EPKTCNT), ENC28J60_Read(ECON1), ENC28J60_Read(ERXFCON), ENC28J60_Read(EIR));
for (i = 0; buf[i] != '