AVR Урок 41. LAN. ENC28J60. ARP. Часть 4



 

Урок 41

 

Часть 4

 

LAN. ENC28J60. ARP

 

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

 

Теперь добавим функцию заполнения таблицы ARP

 

//--------------------------------------------------

void arp_table_fill(enc28j60_frame_ptr *frame)

{

uint8_t i;

}

//--------------------------------------------------

 

Добавим на данную функцию прототип и вызовем её в net.c в функции eth_read в том месте, где у нас осталось незаполненным тело условия

 

else if(res==2)

{

  arp_table_fill(frame);

}

 

Вернёмся в arp.c и продолжим писать нашу функцию заполнения ARP-таблицы.

Добавим указатель на ARP-сообщение

 

uint8_t i;

arp_msg_ptr *msg = (void*)frame->data;

 

Начнём добавлять запись

 

arp_msg_ptr *msg = (void*)frame->data;

//Добавим запись

arp_rec[current_arp_index].ipaddr=msg->ipaddr_src;

memcpy(arp_rec[current_arp_index].macaddr,msg->macaddr_src,6);

 

Теперь мы дошли до того момента, когда нам нужно будет в строку нашей таблицы ARP добавить количество секунд. прошедших с момента включения питания контроллера. Но где мы их возьмём, у нас же они не считаются нигде? Тут нам поможет только таймер, не будем же мы подключать часовой модуль. Может быть впоследствии мы будем пользоваться временем из интернета, полученным с помощью NTP, но пока только таймер. Да и не тяжело нам добавить таймер. Мы это постоянно делали. Правда в Atmega328 таймеры немного отличаются по организации от Atmega8, но это очень незначительно.

 

 

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

 

extern USART_prop_ptr usartprop;

//--------------------------------------------------

void init_timer(void)

{

  TCCR0A |= (1<<WGM01); //режим устанавливаем режим СТС (сброс по совпадению)

  OCR0A = 0xFF; //записываем в регистр число для сравнения

  TIMSK0 |= (1<<OCIE0A); //устанавливаем бит разрешения прерывания 0-ого счетчика по совпадению с OCR0A

  TCCR0B |= (0<<CS02)|(1<<CS01)|(1<<CS00); // устанавливаем предделитель 64

  //тем самым получаем - частота тактирования / предделитель / 256 = 976,5625 (около милисекунды)

}

//-----------------------------------------

 

Добавим на данную функцию прототип и вызовем её в main()

 

int main(void)

{

  init_timer();

  USART_Init (16); //115200

 

Вернёмся в net.c и добавим две глобальные переменные

 

unsigned int tim_cnt=0;//счетчик тиков таймера

uint32_t clock_cnt=0;//счетчик секунд

char str1[60]={0};

 

Добавим функцию-обработчик прерывания по совпадению данного таймера в этом же файле

 

//-----------------------------------------

ISR (TIMER0_COMPA_vect)

{

  tim_cnt++;

  //считаем секунды и записываем их в clock_cnt

  if(tim_cnt>=1000)

  {

    tim_cnt=0;

    clock_cnt++;

  }

}

//-----------------------------------------

 

Код у нас простейший и в объяснении не нуждается. Секунды у нас будут, конечно, неточными, но это и не страшно, плюс минус несколько секунд и даже минут нам не страшны.

 

 

Перейдём в файл arp.c и добавим туда наши глобальные переменные

 

extern char str1[60];

extern unsigned int tim_cnt;//счетчик тиков таймера

extern uint32_t clock_cnt;//счетчик секунд

 

Продолжим дальше функцию arp_table_fill и добавим в запись таблицы количество секунд, которые нам насчитал таймер

 

memcpy(arp_rec[current_arp_index].macaddr,msg->macaddr_src,6);

arp_rec[current_arp_index].sec = clock_cnt;

 

Дальше увеличиваем позицию записи в таблице

 

arp_rec[current_arp_index].sec = clock_cnt;

if(current_arp_index<4) current_arp_index++;

else current_arp_index=0;

 

И по окончанию функции давайте эту таблицу посмотрим

 

  else current_arp_index=0;

  //смотрим ARP-таблицу

  for(i=0;i<5;i++)

  {

    sprintf(str1,"%ld.%ld.%ld.%ld - %02X:%02X:%02X:%02X:%02X:%02X - %lurn",

      arp_rec[i].ipaddr & 0x000000FF,(arp_rec[i].ipaddr>>8) & 0x000000FF,

      (arp_rec[i].ipaddr>>16) & 0x000000FF, arp_rec[i].ipaddr>>24,

      arp_rec[i].macaddr[0],arp_rec[i].macaddr[1],arp_rec[i].macaddr[2],

      arp_rec[i].macaddr[3],arp_rec[i].macaddr[4],arp_rec[i].macaddr[5],

      arp_rec[i].sec);

    USART_TX((uint8_t*)str1,strlen(str1));

  }

}

 

Соберём код, прошьём контроллер и попробуем послать ARP-запросы из терминальной программы на различные адреса, которые мы знаем в своей сети

 

image05

 

Как мы видим, таблица соответствия адресов у нас нормально заполняется, только вот если мы посылаем запрос на существующий IP он у нас добаляется ещё раз. Это непорядок.

 

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

 

 

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

 

 

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

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

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

 

 

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

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

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

 

 

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

AVR LAN. ENC28J60. ARP

 

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

AVR LAN. ENC28J60. ARP

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

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

*