Урок 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-запросы из терминальной программы на различные адреса, которые мы знаем в своей сети
Как мы видим, таблица соответствия адресов у нас нормально заполняется, только вот если мы посылаем запрос на существующий IP он у нас добаляется ещё раз. Это непорядок.
В следующей части нашего занятия мы уберём запись в таблицу ARP уже существующих адресов, а также напишем код удаления оттуда просроченных записей адресов.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Приобрести плату Atmega 328p Pro Mini можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий