AVR Урок 41. LAN. ENC28J60. ARP. Часть 2



 

Урок 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.

 

 

Давайте соберём код, прошьём контроллер и попробуем что-то ввести в терминальную программу

 

image00

 

Как видим, у нас всё получилось. Мы вводим на данный момент 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-адреса в числовое и отобразили его в терминальной программе.

Давайте это проверим, собрав код и прошив контроллер и затем введя такую же строку, какую и вводили, в терминальной программе

 

image01

 

В следующей части нашего занятия мы напишем и проверим функцию для отправки запроса ARP, а также отделим в нашем коде запросы ARP от ответов.

 

 

Предыдущая часть Программирование МК AVR Следующая часть

 

 

Техническая документация:

Документация на микросхему ENC28J60

Перечень ошибок ENC28J60 (Errata)

 

 

Приобрести плату Atmega 328p Pro Mini можно здесь.

Приобрести программатор USBASP USBISP с адаптером можно здесь USBASP USBISP 3.3 с адаптером

Ethernet LAN Сетевой Модуль можно купить здесь ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

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

AVR LAN. ENC28J60. ARP

 

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

AVR LAN. ENC28J60. ARP

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

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

*