設置 | 登錄 | 註冊

作者共發了5篇帖子。

【程序】ATMega16A顯示ENC28J60網卡的數據包個數,以及網線連接狀態

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,所以程序運行一段時間就會因為緩衝區滿而停止接收數據包。

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。