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 2.0

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

 

 

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

 

AVR LAN. ENC28J60. ARP

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

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

*