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 2.0

Ethernet LAN Сетевой Модуль можно купить здесь (модуль SD SPI в подарок) ENC28J60 Ethernet LAN Сетевой Модуль.

 

 

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

 

AVR LAN. ENC28J60. ARP

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

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

*