您的位置:首页 > 编程语言

【抗干扰代码】AVR单片机自动复位由于电压不足导致ENC28J60网卡出现的故障

2017-05-28 18:32 393 查看
【芯片环境】

单片机:ATMega16A

晶振:外部11.0592MHz

蜂鸣器接在PD7上,网卡中断为INT2

数码管段选PA,位选从高位到低位为PC0到PC7

【main.c】

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sfr_defs.h>
#include <stdio.h>
#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\r\nContent-Length: 86\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n<b>Hello World!</b><br>This is a very <i>long</i> string!!!<br><b>Connection ID: </b>"));
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 <avr/io.h>
#include <avr/sfr_defs.h>
#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\r\n"), ENC28J60_Read(EIE), ENC28J60_Read(EPKTCNT), ENC28J60_Read(ECON1), ENC28J60_Read(ERXFCON), ENC28J60_Read(EIR));
for (i = 0; buf[i] != '\0'; i++)
{
UDR = buf[i];
while ((UCSRA & _BV(UDRE)) == 0);
}
}


用一个功率较小的手机充电器上的USB接口作为单片机的电源来测试。测试过程中由于电流不足、电压不稳定,蜂鸣器响了十几次(出现Request timed out.的地方),但最终网卡都成功复位了。

【可靠性测试结果】

C:\Users\Octopus>ping 192.168.1.50 -n 100

Pinging 192.168.1.50 with 32 bytes of data:

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=15ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=10ms TTL=128

Reply from 192.168.1.50: bytes=32 time=33ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=28ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=15ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=9ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=42ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=15ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=9ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=27ms TTL=128

Reply from 192.168.1.50: bytes=32 time=35ms TTL=128

Reply from 192.168.1.50: bytes=32 time=14ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Ping statistics for 192.168.1.50:

Packets: Sent = 100, Received = 82, Lost = 18 (18% loss),

Approximate round trip times in milli-seconds:

Minimum = 1ms, Maximum = 42ms, Average = 5ms

C:\Users\Octopus>

在下面的测试中使用单根USB线供电,电压环境极不稳定。最后单片机进入睡眠模式也未能使网卡恢复正常工作,数码管最终显示1 0524,ping的结果为“Reply from 192.168.1.2: Destination host unreachable.”。

不过几分钟过去后,蜂鸣器响了起来,网卡再一次恢复工作。

【测试结果】

C:\Users\Octopus>ping 192.168.1.50 -n 100

Pinging 192.168.1.50 with 32 bytes of data:

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=10ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=22ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=15ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=12ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=21ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=10ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=11ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.2: Destination host unreachable.

Reply from 192.168.1.50: bytes=32 time=2005ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=1002ms TTL=128

Request timed out.

Request timed out.

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Request timed out.

Reply from 192.168.1.2: Destination host unreachable.

Reply from 192.168.1.2: Destination host unreachable.

Reply from 192.168.1.2: Destination host unreachable.

Reply from 192.168.1.2: Destination host unreachable.

Ping statistics for 192.168.1.50:

Packets: Sent = 100, Received = 47, Lost = 53 (53% loss),

Approximate round trip times in milli-seconds:

Minimum = 1ms, Maximum = 2005ms, Average = 76ms

C:\Users\Octopus>

C:\Users\Octopus>ping 192.168.1.50 -n 100

Pinging 192.168.1.50 with 32 bytes of data:

Reply from 192.168.1.2: Destination host unreachable.

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Request timed out.

Request timed out.

Ping statistics for 192.168.1.50:

Packets: Sent = 9, Received = 5, Lost = 4 (44% loss),

Approximate round trip times in milli-seconds:

Minimum = 2ms, Maximum = 4ms, Average = 2ms

Control-C

^C

C:\Users\Octopus>

现在改用两根USB线来供电,网卡终于稳定工作。

不过为了保险起见,程序中仍应该保留网卡自动复位的代码。

【测试结果】

C:\Users\Octopus>ping 192.168.1.50 -n 100

Pinging 192.168.1.50 with 32 bytes of data:

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=11ms TTL=128

Reply from 192.168.1.50: bytes=32 time=37ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=46ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=10ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=13ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=20ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=13ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=27ms TTL=128

Reply from 192.168.1.50: bytes=32 time=29ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=9ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=44ms TTL=128

Reply from 192.168.1.50: bytes=32 time=5ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=9ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=13ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=17ms TTL=128

Reply from 192.168.1.50: bytes=32 time=70ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=9ms TTL=128

Reply from 192.168.1.50: bytes=32 time=4ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=7ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=71ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=32ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=6ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=16ms TTL=128

Reply from 192.168.1.50: bytes=32 time=22ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Reply from 192.168.1.50: bytes=32 time=3ms TTL=128

Reply from 192.168.1.50: bytes=32 time=2ms TTL=128

Reply from 192.168.1.50: bytes=32 time=1ms TTL=128

Ping statistics for 192.168.1.50:

Packets: Sent = 100, Received = 100, Lost = 0 (0% loss),

Approximate round trip times in milli-seconds:

Minimum = 1ms, Maximum = 71ms, Average = 7ms

C:\Users\Octopus>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AVR enc28j60 uip 网卡 故障
相关文章推荐