AVR Урок 40. LAN. ENC28J60. Часть 2



 

Урок 40

 

Часть 2

 

LAN. ENC28J60

 

В предыдущей части нашего урока мы познакомились с микросхемой ENC28J60, создали и настроили проект и добавили каркасы некоторых функций.

 

Так как к интерфейсу SPI мы будем обращаться только в файле enc28j60.c, то в файле enc28j60.h добавим слеующие макросы

 

#include «main.h»

//—————————————————

#define MOSI 3

#define MISO 4

#define SCK 5

#define SS 2

#define SS_SELECT() PORTB&=~(1<<SS) //CS = L

#define SS_DESELECT() PORTB|=(1<<SS) //CS = H

//————————————————

 

В файле  enc28j60.c напишем функцию инициализации SPI

 

#include «enc28j60.h»

//———————————————-

void SPI_ini(void)

{

  DDRB|=(1<<SS)|(1<<MOSI)|(1<<SCK); //ножки SPI на выход

  DDRB&=~(1<<MISO); //MISO на вход

  SS_DESELECT();

  SPCR = (1<<SPE)|(1<<MSTR); //Enable SPI, Master

  SPSR |= (1<<SPI2X);//Double SPI Speed (fosc/2 = 8 MHz)

}

//—————————————-

 

 

Ниже в этом же файле напишем функцию обмена байтами по интерфейсу SPI

 

//—————————————-

uint8_t SPI_ChangeByte(uint8_t bt)

{

  SPDR = bt; //отправим байт в регистр данных

  while(!(SPSR & (1<<SPIF))) //подождем пока данные передадутся

  ;

  return SPDR; //вернём регистр данных

}

//—————————————-

 

 

Также, пользуясь вышенаписанной функцией, напишем функции отправки и приёма байта по шине SPI

 

//—————————————-

void SPI_SendByte(uint8_t bt)

{

SPI_ChangeByte(bt);

}

//—————————————-

uint8_t SPI_ReceiveByte(void)

{

uint8_t bt = SPI_ChangeByte(0xFF);

return bt; //вернём регистр данных

}

//—————————————-

 

В функции инициализации вызовем инициализацию шины SPI

 

void enc28j60_ini(void)

{

  SPI_ini();

 

Теперь необходимо изучить и понять, каким образом отправляются команды и данные в микросхему, а также читаются данные.

Во-первых, любая команда состоит из опкода и аргумента

 

image03

Опкод — это своего рода указатель типа команды (чтение управляющего регистра, запись управляющего регистра, чтение буфера, запись буфера, установка бита в регистре, очистка бита в регистре, программная (мягкая) перезагрузка. Состоит он из трёх старших битов байта команды, а в пяти младших байтах (в аргументе) уже остальная информация команды (адрес регистра, битовая маска и т.д.).

Дальше передаётся при необходимости байт данных, либо при опкодах чтения принимается байт данных с шины MISO.

Теперь давайте посмотрим, как именно передаются данные в микросхему

 

image04

И также посмотрим, как принимаются данные из управляющих регистров

 

image05

 

 

В случае если используются регистры MAC или MII, то чтение происходит с пропуском ложного байта

 

image07

 

В заголовочный файл enc28j60.h напишем серию макросов, которые нам потребуются впоследствии, в основном там будут адреса необходимых регистров, а также некоторые управляющие биты. Также здесь мы добавим желающий физический адрес (MAC-адрес) нашего устройства. Адрес можно задавать любой, так как он не присвоен микросхеме жестко, то есть это очень даже задаваемая величина. Главное чтобы адрес не совпал с каким-то другим в нашей сети, а также по некоторым сложившимся заключениям нежелательно во избежание проблем в самый первый байт что-то вообще писать, лучше оставить все биты там пустыми, ну, по крайней мере в адресе не должен быть выставлен 40-й бит.

 

enc28j60.h

#define SS_DESELECT() PORTB|=(1<<SS)  //CS = H
//————————————————
#define ENC28J60_MAXFRAME 512
#define MAC_ADDR   {0x00,0x13,0x37,0x01,0x23,0x45}
//————————————————
#define ADDR_MASK        0x1F
#define BANK_MASK        0x60
//————————————————
//All-bank registers
#define EIE              0x1B
#define EIR              0x1C
#define ESTAT            0x1D
#define ECON2            0x1E
#define ECON1            0x1F
//————————————————
// Bank 0 registers
#define ERDPT           (0x00|0x00)
#define EWRPT           (0x02|0x00)
#define ETXST           (0x04|0x00)
#define ETXND           (0x06|0x00)
#define ERXST           (0x08|0x00)
#define ERXND           (0x0A|0x00)
#define ERXRDPT         (0x0C|0x00)
//————————————————
// Bank 1 registers
#define EPMM0           (0x08|0x20)
#define EPMM1           (0x09|0x20)
#define EPMCS   (0x10|0x20)
#define ERXFCON         (0x18|0x20)
#define EPKTCNT   (0x19|0x20)
//————————————————
// Bank 2 registers
#define MACON1          (0x00|0x40|0x80)
#define MACON2          (0x01|0x40|0x80)
#define MACON3          (0x02|0x40|0x80)
#define MACON4          (0x03|0x40|0x80)
#define MABBIPG         (0x04|0x40|0x80)
#define MAIPG   (0x06|0x40|0x80)
#define MAMXFL          (0x0A|0x40|0x80)
#define MIREGADR  (0x14|0x40|0x80)
#define MIWR            (0x16|0x40|0x80)
//————————————————
// Bank 3 registers
#define MAADR1           (0x00|0x60|0x80)
#define MAADR0           (0x01|0x60|0x80)
#define MAADR3           (0x02|0x60|0x80)
#define MAADR2           (0x03|0x60|0x80)
#define MAADR5           (0x04|0x60|0x80)
#define MAADR4           (0x05|0x60|0x80)
#define MISTAT           (0x0A|0x60|0x80)
//————————————————
#define ERXFCON_UCEN     0x80
#define ERXFCON_ANDOR    0x40
#define ERXFCON_CRCEN    0x20
#define ERXFCON_PMEN     0x10
#define ERXFCON_MPEN     0x08
#define ERXFCON_HTEN     0x04
#define ERXFCON_MCEN     0x02
#define ERXFCON_BCEN     0x01
//————————————————
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE        0x80
#define EIE_PKTIE        0x40
#define EIE_DMAIE        0x20
#define EIE_LINKIE       0x10
#define EIE_TXIE         0x08
#define EIE_WOLIE        0x04
#define EIE_TXERIE       0x02
#define EIE_RXERIE       0x01
//————————————————
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT        0x80
#define ESTAT_LATECOL    0x10
#define ESTAT_RXBUSY     0x04
#define ESTAT_TXABRT     0x02
#define ESTAT_CLKRDY     0x01
//————————————————
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST      0x80
#define ECON1_RXRST      0x40
#define ECON1_DMAST      0x20
#define ECON1_CSUMEN     0x10
#define ECON1_TXRTS      0x08
#define ECON1_RXEN       0x04
#define ECON1_BSEL1      0x02
#define ECON1_BSEL0      0x01
//————————————————
// ENC28J60 ECON1 Register Bit Definitions
#define ECON2_PKTDEC     0x40
//————————————————
#define MACON1_LOOPBK    0x10
#define MACON1_TXPAUS    0x08
#define MACON1_RXPAUS    0x04
#define MACON1_PASSALL   0x02
#define MACON1_MARXEN    0x01
//————————————————
#define MACON3_PADCFG2   0x80
#define MACON3_PADCFG1   0x40
#define MACON3_PADCFG0   0x20
#define MACON3_TXCRCEN   0x10
#define MACON3_PHDRLEN   0x08
#define MACON3_HFRMLEN   0x04
#define MACON3_FRMLNEN   0x02
#define MACON3_FULDPX    0x01
//————————————————
#define MISTAT_BUSY      0x01
//————————————————
// PHY registers
#define PHCON1           0x00
#define PHSTAT1          0x01
#define PHHID1           0x02
#define PHHID2           0x03
#define PHCON2           0x10
#define PHSTAT2          0x11
#define PHIE             0x12
#define PHIR             0x13
#define PHLCON           0x14
//————————————————
#define PHCON2_HDLDIS    0x0100
//————————————————
// PHLCON
#define PHLCON_LACFG3  0x0800
#define PHLCON_LACFG2  0x0400
#define PHLCON_LACFG1  0x0200
#define PHLCON_LACFG0  0x0100
#define PHLCON_LBCFG3  0x0080
#define PHLCON_LBCFG2  0x0040
#define PHLCON_LBCFG1  0x0020
#define PHLCON_LBCFG0  0x0010
#define PHLCON_LFRQ1  0x0008
#define PHLCON_LFRQ0  0x0004
#define PHLCON_STRCH  0x0002
//—————————————————
#define ENC28J60_READ_CTRL_REG       0x00
#define ENC28J60_READ_BUF_MEM        0x3A
#define ENC28J60_WRITE_CTRL_REG      0x40
#define ENC28J60_BIT_FIELD_SET       0x80
#define ENC28J60_BIT_FIELD_CLR       0xA0
#define ENC28J60_SOFT_RESET          0xFF
//————————————————
#define RXSTART_INIT        0x0000  // start of RX buffer, room for 2 packets
#define RXSTOP_INIT         0x0BFF  // end of RX buffer
//————————————————
#define TXSTART_INIT        0x0C00  // start of TX buffer, room for 1 packet
#define TXSTOP_INIT         0x11FF  // end of TX buffer
//————————————————
#define MAX_FRAMELEN      1500
//————————————————

 

Теперь вернёмся в файл реализации enc28j60.c и напишем функцию операции записи байта в регистр

 

//—————————————-

static void enc28j60_writeOp (uint8_t op, uint8_t address, uint8_t data)

{

  SS_SELECT();

  SPI_SendByte(op | (address & ADDR_MASK));

  SPI_SendByte(data);

  SS_DESELECT();

}

//—————————————-

 

Во входящих аргументах функции у нас будет отдельно опкод, сдвинутый соответственно заранее на своё место в три старшие биты, адрес регистра и собственно байт данных.

ADDR_MASK — макрос для маски адреса регистра, который будет обнулять первые три бита, освобождая пространство для опкода

Ну, а теперь, операция чтения

 

//—————————————-

static uint8_t enc28j60_readOp (uint8_t op, uint8_t address)

{

  SS_SELECT();

  SPI_SendByte(op | (address & ADDR_MASK));

  SPI_SendByte(0x00);

  //пропускаем ложный байт

  if (address & 0x80)

  SPI_SendByte(0x00);

  uint8_t result = SPDR;

  SS_DESELECT();

  return result;

}

//—————————————-

 

Произведём «мягкий» сброс в функции инициализации и проверим, что всё перезагрузилось

 

SPI_ini();

enc28j60_writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);

_delay_ms(2);

//проверим, что всё перезагрузилось

while (!enc28j60_readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY)

;

 

В следующей части нашего урока мы напишем ещё несколько служебных функций, настроим буферы микросхемы, а также её канальный уровень и напишем функцию записи данных в регистры PHY.

 

 

Предыдущая часть Программирование МК AVR Следующая часть

 

 

Техническая документация:

Документация на микросхему ENC28J60

Перечень ошибок ENC28J60 (Errata)

 

 

Приобрести плату Atmega 328p Pro Mini можно здесь.

Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером

Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)

AVR LAN. ENC28J60

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

AVR LAN. ENC28J60

12 комментариев на “AVR Урок 40. LAN. ENC28J60. Часть 2
  1. Sergey:

    Не весьма понятно условие

    //пропускаем ложный байт

      if (address & 0x80)

    что конкретно так проверяется

    • Ничего не проверяется. Есть лишний байт, его не обрабатываем.

      • kostya:

        Тоже не до конца понял как обрабатывается это условие if (address & 0x80)
        Пробовал это условие в тестовом режиме, начинает выполняться если оно сравнивается с чем-то ( в частности с 0x80). Может быть все зависит от компилятора? Использую Atmel studio 7.

        • kostya:

          Хотя нет. Разобрался. В данном случае, if (address & 0x80), проверяется именно старший (7) бит. И если он (7 бит) равен 1, тогда условие истинно. Во всех других (кроме как 7 бит = 1) условие лож.

  2. Иван:

    Здравствуйте, а можно поподробнее почему: "в адресе не должен быть выставлен 40-й бит"

    • Здравствуйте. Я думаю, что это уже уходит далеко за рамки нашего занятия.

      Тем более я сам не знаю, почему.

      Точно так же прочитал в технической документации, уже не помню в какой, вот и решил поделиться (лучше бы не делился не полчил бы провокационный вопрос).

  3. Иван:

    Ок, понятно. просто я такой информации не нашел. Наверное, да лучше сразу отсылать к первоисточникам. Спасибо вам за уроки.

  4. Andy:

    Доброго дня !
    можете прокоментировать обьявление
    #define MAADR1           (0x00|0x60|0x80)
    то что находиться в скобках
    1- адрес МААDR в банке 3
    2- ?
    3- ?

  5. Alexander:

    почему у вас опкод в дифайнах не совпадаетс документацией?

  6. Alexander:

    насчет дифайнов — всё верно, не досмотрел.

    а для чего нам нужна функция SPI_ChangeByte?
    если только для двух последующих функций, то их блоки можно переписать без функции SPI_ChangeByte.
    Или другой смысл несёт эта функция?

  7. Алексей A:

    Подключил библиотеку usart. но у меня ошибка с 328 мегой. как будто он не знает что у него есть USART на борту. UBRRH, RXCIE, UDR и т.д. он не знает, Библиотеки подключил, не пойму в чем подвох, с тем же usart проектом но только на 8 меги все работает

    • Алексей A:

      Значит решил я залезть в стандартные библиотеки и нашел там значения UBRR0H, RXCIE0, UDR0 я так понимаю в нем 2 usart планировалось или для удобства сделали, в общем не нашел только URSEL. и еще из уроков по usart помню там про вектора что то говорилось 11 и 12 а тут 18, короче у меня не подходит старая библиотека под мегу 328

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*