Урок 71
Часть 4
LAN. ENC28J60. ARP
В предыдущей части урока мы отправили запрос ARP, а также начали писать функцию заполнения таблицы ARP.
Чтобы проверить, что наш таймер отсчитывает именно секунды, можно в данный обработчик добавить светодиодную мигалку, но я уже так проверял, поэтому мы этим заниматься не будем.
Вернёмся в файл arp.c и подключим туда наш счётчик секунд
extern char str1[60];
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,"%d.%d.%d.%d - %02X:%02X:%02X:%02X:%02X:%02X - %lurn",
arp_rec[i].ipaddr[0],arp_rec[i].ipaddr[1],arp_rec[i].ipaddr[2],arp_rec[i].ipaddr[3],
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],
(unsigned long)arp_rec[i].sec);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
}
}
Соберём код, прошьём контроллер и попробуем послать ARP-запросы из терминальной программы на различные адреса, которые мы знаем в своей сети
Как мы видим, таблица соответствия адресов у нас нормально заполняется, только вот если мы посылаем запрос на существующий IP он у нас добаляется ещё раз. Это непорядок. Поэтому давайте теперь, как мы и хотели, вернёмся в функцию ARP-запроса arp_request и пока вставим туда цикл, который будет перебирать записи по отысканию существующего IP-адреса
uint8_t i;
//проверим, может такой адрес уже есть в таблице ARP, а заодно и удалим оттуда просроченные записи
for(i=0;i<5;i++)
{
}
В тело цикла добавим условие
for(i=0;i<5;i++)
{
//Если записи уже более 12 часов, то удалим её
if ((clock_cnt-arp_rec[i].sec)>43200)
{
memset(arp_rec+(sizeof(arp_record_ptr)*i),0,sizeof(arp_record_ptr));
}
}
Из коментария вполне ясно, что мы здесь проделали. Мы забили нулями всю память отведённую под запись.
Теперь после этого условия добавим следующее, после выполнения тела условия (не цикла)
memset(arp_rec+(sizeof(arp_record_ptr)*i),0,sizeof(arp_record_ptr));
}
if (!memcmp(arp_rec[i].ipaddr,ip_addr,4))
{
return 0;
}
В теле данного условия перед тем, как нам выйти из функции с помощью return 0, мы посмотрим нашу таблицу
if (!memcmp(arp_rec[i].ipaddr,ip_addr,4))
{
//смотрим ARP-таблицу
for(i=0;i<5;i++)
{
sprintf(str1,"%d.%d.%d.%d - %02X:%02X:%02X:%02X:%02X:%02X - %lurn",
arp_rec[i].ipaddr[0],arp_rec[i].ipaddr[1],arp_rec[i].ipaddr[2],arp_rec[i].ipaddr[3],
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],
(unsigned long)arp_rec[i].sec);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
}
return 0;
}
Соберём код, прошьём контроллер и поиграмем с запросами
IP-адреса в таблице у нас больше не дублируются, на существующие адреса запросы не посылаются. 12 часов я ждать не стал, но я пробовал максимальное количество секунд в условии менять на значительно меньшее — устаревшие адреса удалялись. Так что всё работает.
Таким образом, мы создали теперь вполне полноценный ARP-клиент-сервер, который не только отзывается на ARP-запросы, но и умеет теперь их посылать.
Предыдущая часть Программирование МК STM32 Следующий урок
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Отладочную плату можно приобрести здесь STM32F103C8T6
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN
Переходник USB to TTL можно приобрести здесь USB to TTL ftdi ft232rl
Смотреть ВИДЕОУРОК (нажмите на картинку)
Интересно было бы увидеть уроки по работе этой микросхемы (или другой) через MII (или RMII) интерфейс без LWIP.