|  | 
          1樓
          巨大八爪鱼
          2017-4-22 21:59
          
          
            晶振:外部11.0592MHz本程序对晶振的要求不高,使用内部8MHz晶振也是可以的。
 【main.c】
 // 晶振: 外部11.0592MHz
 #include <avr/interrupt.h>
 #include <avr/io.h>
 #include <avr/sfr_defs.h>
 #include "ENC28J60.h"
 
 const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
 uint8_t flag_disp = 0; // 是否插了网线, 初值必须为0 (开机时只有网线是插上的, 才会自动触发LINK中断)
 uint16_t num_disp = 0;
 
 int main(void)
 {
 // 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(CS02); // 开定时器0: 设为256分频, 总溢出时间约为5.926ms
 
 ENC28J60_Init();
 GICR |= _BV(INT2); // 开网卡中断
 while (1);
 }
 
 // 网卡中断
 ISR(INT2_vect)
 {
 uint8_t cnt, status;
 // 现在INT2为低电平
 ENC28J60_SetBits(EIE, EIE_INTIE, ENCCLR); // 该语句执行完毕后, INT2引脚会回到高电平, 之后新来的网卡中断都将处于pending状态
 // 如果在执行该函数期间恰好又来了一个中断, 那么肯定能被本次中断函数处理到
 GICR &= ~_BV(INT2);
 sei(); // 允许数码管扫描中断抢占本中断, 防止数码管闪烁
 
 status = ENC28J60_Read(EIR); // 获取所有网卡中断的状态
 // 一个一个处理:
 if (status & EIR_PKTIF)
 {
 /* 收到新数据包 */
 ENC28J60_SelectBank(1);
 cnt = ENC28J60_Read(EPKTCNT); // 获取数据包个数
 num_disp += cnt;
 while (cnt--)
 ENC28J60_SetBits(ECON2, ECON2_PKTDEC, ENCSET);
 }
 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(PORTC3) | _BV(PORTC0); // 熄灭之前点亮的数码管
 PORTA = 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;
 }
 
 | 
    
      |  | 
          2樓
          巨大八爪鱼
          2017-4-22 22:00
          
          
            【ENC28J60.h】#ifndef ENC28J60_H_
 #define ENC28J60_H_
 
 // 注: ISP下载口不使用SPI的片选端SS
 #define ENC28J60_CS0 (PORTB &= ~_BV(PORTB4))
 #define ENC28J60_CS1 (PORTB |= _BV(PORTB4))
 
 #define ENC_RECV_START 0x5dc // 接收缓冲区起点
 
 /* 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_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 ERXEDL 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 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_ */
 
 | 
    
      |  | 
          3樓
          巨大八爪鱼
          2017-4-22 22:00
          
          
            【ENC28J60.c】#include <avr/io.h>
 #include <avr/sfr_defs.h>
 #include "ENC28J60.h"
 
 // 注意: 执行这些函数时一定要先关闭网卡中断!!! 防止SPI序列被破坏
 
 void ENC28J60_Init(void)
 {
 ENC28J60_CS1; // 空闲状态下CS应该为高电平
 ENC28J60_SystemReset();
 
 // 设置接收缓冲区的起点, 终点保持默认的0x1fff
 ENC28J60_Write(ERXSTL, ENC_RECV_START & 0xff);
 ENC28J60_Write(ERXSTH, ENC_RECV_START >> 8);
 ENC28J60_Write(ERXRDPTL, ENC_RECV_START & 0xff); // 读指针位置也要设置为相同的值
 ENC28J60_Write(ERXRDPTH, ENC_RECV_START >> 8);
 
 // 配置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, 'B'); // 第一个字节必须为偶数才是单播MAC地址
 ENC28J60_Write(MAADR2, 'R');
 ENC28J60_Write(MAADR3, 'M');
 ENC28J60_Write(MAADR4, 'N');
 ENC28J60_Write(MAADR5, 'E');
 ENC28J60_Write(MAADR6, 'T');
 
 // 配置PHY
 ENC28J60_WritePhy(PHCON1, PHCON1_PDPXMD); // 全双工模式
 
 // 允许接收数据包
 ENC28J60_Write(ECON1, ECON1_RXEN);
 ENC28J60_Write(EIE, EIE_PKTIE | EIE_LINKIE | EIE_INTIE); // 如果收到了数据包, 或网络连接发生变化, 就触发中断
 ENC28J60_WritePhy(PHIE, PHIE_PLNKIE | PHIE_PGEIE); // 配置PHY中断 (监测网络连接变化)
 }
 
 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;
 }
 
 | 
    
      |  | 
          4樓
          巨大八爪鱼
          2017-4-22 22:02
          
          
            【注】本程序只接收数据包,不发送数据包。
 
 | 
    
      |  | 
          5樓
          巨大八爪鱼
          2017-4-22 22:05
          
          
            由于在程序中没有及时移动读指针ERXRDPTL,所以程序运行一段时间就会因为缓冲区满而停止接收数据包。 |