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 2.0

Ethernet LAN Сетевой Модуль можно купить здесь (модуль SD SPI в подарок) ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

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

 

AVR LAN. ENC28J60

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

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

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

      if (address & 0x80)

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

  2. Иван:

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

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

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

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

  3. Иван:

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

  4. Andy:

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

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

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

*