Урок 41
Часть 2
LAN. ENC28J60. ARP
В предыдущей части нашего занятия мы подправили некоторые ошибки прошлого занятия и вынесли функции реализации протокола ARP в отдельный модуль.
Теперь будем обдумывать то, как мы будем посылать ARP-запросы, то есть за счёт чего будет контроллер вообще получать такую команду. Но так как у нас уже подключен модуль USART, то давайте и будем его использовать, не будем пока подключать никаких дисплеев и кнопок. То есть воспользуемся функцией приёма данных по USART и будем эти команды посылать из ПК с помощью терминальной программы. Когда-то мы это уже делали, но всё забылось. Вот давайте заодно и повторим, как это делается.
Поэтому перейдём теперь в файл usart.h и создадим там некоторую структуру со свойствами
void USART_TX (uint8_t *str1, uint8_t cnt);
//--------------------------------------------------
typedef struct USART_prop{
uint8_t usart_buf[20];
uint8_t usart_cnt;
uint8_t is_ip;
} USART_prop_ptr;
//--------------------------------------------------
Используя тип нашей структуры создадим глобальную переменную файле usart.c
#include "usart.h"
//---------------------------------------------------------------
USART_prop_ptr usartprop;
Теперь проинициализируем заранее свойства в функции инициализации USART
//1 стоп-бит (USBS=0), 8-бит посылка (UCSZ01=1 и UCSZ00=1)
usartprop.usart_buf[0]=0;
usartprop.usart_cnt=0;
usartprop.is_ip=0;
}
Так как необходимые прерывания в инициализации USART, а также глобальные прерывания у нас уже включены, то нам достаточно просто добавить функцию-обработчик прерывания по окончанию приёма байта в наш USART, в которой мы сразу запишем принятый байт в переменную
//----------------------------------------
ISR(USART_RX_vect)
{
uint8_t b;
b = UDR0;
}
//----------------------------------------
теперь начнём обрабатывать результат, своевременно меняя все статусы
b = UDR0;
//если вдруг случайно превысим длину буфера
if (usartprop.usart_cnt>20)
{
usartprop.usart_cnt=0;
}
else if (b == 'a')
{
usartprop.is_ip=1;//статус отрпавки ARP-запроса
}
else
{
usartprop.usart_buf[usartprop.usart_cnt] = b;
usartprop.usart_cnt++;
}
}
Я думаю, тут и объяснять нечего, так всё ясно. То что строк много, это ничего, я просто везде использовал фигурные скобки независимо от количества строк кода, так как пока мы работаем только с ARP, а в последствии, я думаю подтянутся другие протоколы, соответственно появятся статусы и нам не придется ничего добавлять, то есть для простоты написания будущего кода. А символ «a» мы отслеживаем для того, что введенную строку мы будем им заканчивать, чтобы как-то отследить окончание данной строки. То есть мы будем в терминале вводить IP-адрес устройства, MAC-адрес которого мы хотим узнать.
Теперь перейдём в файл net.c и подключим нашу переменную свойств USART туда
extern uint8_t macaddr[6];
extern USART_prop_ptr usartprop;
//--------------------------------------------------
Теперь отловим изменение статуса в функции net_poll, создав в ней для начала локальную переменную
uint16_t len;
uint32_t ip=0;
Далее после приёма очередного пакета мы в этой функции начнём следить за статусом
eth_read(frame,len);
}
if (usartprop.is_ip==1) //статус отправки ARP-запроса
{
USART_TX(usartprop.usart_buf,usartprop.usart_cnt);
USART_TX((uint8_t*)"rn",2);
usartprop.is_ip = 0;
usartprop.usart_cnt=0;
}
}
Заодно мы здесь выведем в терминал строку, принятую из него же и опять обнулим все наши статусы для того, чтобы нормально ждать следующей строки из USART.
Давайте соберём код, прошьём контроллер и попробуем что-то ввести в терминальную программу
Как видим, у нас всё получилось. Мы вводим на данный момент ip-адрес нашего ПК или какой-то другой и получаем его в нашей программе в виде строки.
Но запрос ARP посылать мы пока не сможем, так как нам теперь нужно из строки преобразовать данный адрес в 32-битное число.
Для этого добавим в этом же модуле соответствующую функцию где-то над функцией eth_read, добавив в неё сразу несколько локальных переменных и возврат результата
//--------------------------------------------------
//функция преобразования строкового значения IP в 32-битное числовое
uint32_t ip_extract(char* ip_str,uint8_t len)
{
uint32_t ip = 0;
uint8_t offset = 0;
uint8_t i;
char ss2[5] = {0};
char *ss1;
int ch = '.';
return ip;
}
//--------------------------------------------------
Теперь пробежим по функции на предмет появления точек
int ch = '.';
for(i=0;i<3;i++)
{
ss1 = strchr(ip_str,ch);
}
Опять мы используем новую интересную функцию, которая следит за вхождением символа в строке, добавленной в качестве первого аргумента, а затем возвращает адрес в памяти вхождения данного символа. Только нам удобнее работать не с адресом а со смещением относительно начала строки с IP-адресом, поэтому вычислим это смещение
ss1 = strchr(ip_str,ch);
offset = ss1-ip_str+1;
Затем мы скопируем ещё в одну строку часть нашей строки до точки, а затем закончи её нулём, как того просит функция, которую мы будем использовать далее
offset = ss1-ip_str+1;
strncpy(ss2,ip_str,offset);
ss2[offset]=0;
А далее мы, преобразовав вышесозданную строку в число, запишем его в соответствующий байт нашей возвращаемой впоследствии 32-битной величины IP-адреса
ss2[offset]=0;
ip |= ((uint32_t) atoi(ss2))<<(i*8);
Далее мы соответствующим образом сдвинем указатели нашей строки с IP-адресом и длины этой строки, а затем выйдем из цикла for
ip |= ((uint32_t) atoi(ss2))<<(i*8);
ip_str+=offset;
len-=offset;
}
Теперь, выйдя из цикла мы уже работаем с последним байтом IP-адреса, после которого уже точки нет и также записываем его в соответствующий байт возвращаемого IP
len-=offset;
}
strncpy(ss2,ip_str,len);
ss2[len]=0;
ip |= ((uint32_t) atoi(ss2))<<24;
return ip;
}
Продолжим писать тело функции net_pool, используя вышенаписанную функцию, вставив код в соответствующее место в этой функции
USART_TX((uint8_t*)"rn",2);
ip=ip_extract((char*)usartprop.usart_buf,usartprop.usart_cnt);
sprintf(str1,"%lu",ip);
USART_TX((uint8_t*)str1,strlen(str1));
USART_TX((uint8_t*)"rn",2);
usartprop.is_ip = 0;
Тем самым мы преробразовали строковое значение IP-адреса в числовое и отобразили его в терминальной программе.
Давайте это проверим, собрав код и прошив контроллер и затем введя такую же строку, какую и вводили, в терминальной программе
В следующей части нашего занятия мы напишем и проверим функцию для отправки запроса ARP, а также отделим в нашем коде запросы ARP от ответов.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация:
Документация на микросхему ENC28J60
Перечень ошибок ENC28J60 (Errata)
Приобрести плату Atmega 328p Pro Mini можно здесь.
Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером
Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий