Урок 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 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Интересно было бы увидеть уроки по работе этой микросхемы (или другой) через MII (или RMII) интерфейс без LWIP.