Подключение микроконтроллера к локальной сети: UDP-сервер

Краткое содержание:
- Полезные штуки
- Стек протоколов
- Протокол Ethernet
- ARP
- IP
- ICMP
- UDP
- Пишем приложение
- Заключение
Что пригодится
При написании сетевых приложений для МК накосячить просто, а найти баг — не всегда. Так что, есть смысл для отладки использовать различные прикольные штуки, отладку общегчающие:
- JTAG-адаптер для AVR'ок — поможет заглянуть в память и регистры МК, потрейсить прошивку. Поддерживается AVR Studio 4. Жутко тормозной, но найти многие баги с ним всё же намного быстрее, чем без него.
- Wireshark — хороший кроссплатформенный сниффер. Покажет данные, передаваемые через сетевой адаптер компа. Умеет анализировать протоколы. Покажет в каком месте пакета что-то не так (битая чексумма, неверная длина пакета, etc.). Настраивается методом научного тыка)
- netcat — удобная утилитка, которая поможет отправлять и принимать данные по сети. Под Windows можно взять нативную сборку, либо установить из cygwin'а.
Стек протоколов
Вот так будет выглядеть стек протоколов, который мы реализуем:

При прохождении пакета вниз по стеку, к нему прикрепляются заголовки протоколов. Например, вот так будет выглядеть UDP-пакет при передаче по сети Ethernet.

В соответствии с моделью OSI, уровни должны быть изолированы друг от друга. Но у нас будет не совсем так, все протоколы будут работать с одним и тем же пакетом, но обращаться к своим заголовкам. Что позволит сэкономить память и такты микроконтроллера. Для такого простого стека это вполне нормально.
Неприятная особенность IP-стека — все поля пакетов всех протоколов закодированы в этом чудовищном атавизме, big endian'е. Придётся перекодировать в нормальный формат и обратно. Для этого пригодятся макросы:
// Перекодирование word'а
#define htons(a) ((((a)>>8)&0xff)|(((a)<<8)&0xff00))
#define ntohs(a) htons(a)
// Перекодирование dword'а
#define htonl(a) ( (((a)>>24)&0xff) | (((a)>>8)&0xff00) |\
(((a)<<8)&0xff0000) | (((a)<<24)&0xff000000) )
#define ntohl(a) htonl(a)
Для работы стека, устройству нужно задать MAC-адрес и IP-адрес.
// Макрос для IP-адреса
#define inet_addr(a,b,c,d) ( ((uint32_t)a) | ((uint32_t)b << 8) |\
((uint32_t)c << 16) | ((uint32_t)d << 24) )
// MAC-адрес
uint8_t mac_addr[6] = {0x00,0x13,0x37,0x01,0x23,0x45};
// IP-адрес
uint32_t ip_addr = inet_addr(192,168,0,222);
Ethernet
Вот формат Ethernet-фрейма:

Контрольная сумма рассчитывается и проверяется ENC28J60, так что для нас остяются видимы только 4 поля:
- Адрес получателя. Тут может находиться конкретный MAC-адрес, широковещательный адрес ff:ff:ff:ff:ff:ff или Multicast-адрес. В Multicast-адресе установлен бит 40 (01:00:00:00:00:00). Обрати на это внимание. Если ты установишь своему девайсу адрес, например, 01:23:45:67:89:ab, получишь кучу проблем, источник которых вовсе не очевиден. Когда будешь придумывать MAC-адрес, лучше вообще обнули старший байт.
- Адрес отправителя. Тут MAC-адрес, с которого отправлен фрейм.
- По стандарту, здесь может находиться длина поля данных фрейма (без выравнивания), либо идентификатор протокола. Если бит 8 (0x0800) установлен, поле идентифицирует протокол. В случае протоколов IP-стека (IP, ARP, etc.), здесь находится именно идентификатор протокола и ничего другого.
- Поле данных. Здесь будет полезная нагрузка, например, IP-пакет. В нормальном фрейме данных должно быть от 60 до 1500 байт. Если реальных данных меньше, поле выравнивается нулями до 60 байт.
Вот пример работы с Ethernet-фреймом:
#define ETH_TYPE_ARP htons(0x0806)
#define ETH_TYPE_IP htons(0x0800)
// Ethernet-фрейм
typedef struct eth_frame {
uint8_t to_addr[6]; // адрес получателя
uint8_t from_addr[6]; // адрес отправителя
uint16_t type; // протокол
uint8_t data[];
} eth_frame_t;
// Отправка ответа на Ethernet-фрейм
// (подходит для серверного приложения -
// получили запрос, обменяли местами адрес отправителя и получателя,
// отправили назад)
void eth_reply(eth_frame_t *frame, uint16_t len)
{
memcpy(frame->to_addr, frame->from_addr, 6);
memcpy(frame->from_addr, mac_addr, 6);
enc28j60_send_packet((void*)frame, len +
sizeof(eth_frame_t));
}
// Обработчик получаемых Ethernet-фреймов
void eth_filter(eth_frame_t *frame, uint16_t len)
{
// Проверяем длину фрейма
// Проверять адрес получателя не будем,
// положимся на фильтр пакетов ENC28J60
if(len >= sizeof(eth_frame_t))
{
switch(frame->type)
{
// Получен ARP-пакет, вызываем обработчик ARP-пакетов
case ETH_TYPE_ARP:
arp_filter(frame, len - sizeof(eth_frame_t));
break;
// Получен IP-пакет, вызываем обработчик IP-пакетов
case ETH_TYPE_IP:
ip_filter(frame, len - sizeof(eth_frame_t));
break;
}
}
}
ARP
ARP (Address Resolution Protocol) — вспомогательный протокол, позволяющий получить MAC-адрес узла по IP-адресу.
Узел, желающий получать IP-пакеты, должен отвечать на ARP-запросы, чтобы другие узлы могли найти его в локальной сети по IP-адресу.
ARP работет следующим образом:
- Узел, который хочет узнать MAC-адрес другого узла, посылает широковещательный запрос с IP-адресом искомого узла.
- Узел с указанным IP-адресом отсылает ответ первому узлу.
- Первый узел добавляет адрес найденного узла в ARP-кэш, затем время от времени посылает новые запросы, чтобы убедиться, что второй узел никуда не делся.
Пример кода:
#define ARP_HW_TYPE_ETH htons(0x0001)
#define ARP_PROTO_TYPE_IP htons(0x0800)
#define ARP_TYPE_REQUEST htons(1)
#define ARP_TYPE_RESPONSE htons(2)
// ARP-пакет
typedef struct arp_message {
uint16_t hw_type; // протокол канального уровня (Ethernet)
uint16_t proto_type; // протокол сетевого уровня (IP)
uint8_t hw_addr_len; // длина MAC-адреса =6
uint8_t proto_addr_len; // длина IP-адреса =4
uint16_t type; // тип сообщения (запрос/ответ)
uint8_t mac_addr_from[6]; // MAC-адрес отправителя
uint32_t ip_addr_from; // IP-адрес отправителя
uint8_t mac_addr_to[6]; // MAC-адрес получателя, нули если неизвестен
uint32_t ip_addr_to; // IP-адрес получателя
} arp_message_t;
// Обработчик ARP-пакетов
void arp_filter(eth_frame_t *frame, uint16_t len)
{
arp_message_t *msg = (void*)(frame->data);
// Проверяем длину пакета
if(len >= sizeof(arp_message_t))
{
// Проверяем тип протокола
if( (msg->hw_type == ARP_HW_TYPE_ETH) &&
(msg->proto_type == ARP_PROTO_TYPE_IP) )
{
// ARP-запрос и наш IP-адрес?
if( (msg->type == ARP_TYPE_REQUEST) &&
(msg->ip_addr_to == ip_addr) )
{
// Отправляем ответ
msg->type = ARP_TYPE_RESPONSE;
memcpy(msg->mac_addr_to, msg->mac_addr_from, 6);
memcpy(msg->mac_addr_from, mac_addr, 6);
msg->ip_addr_to = msg->ip_addr_from;
msg->ip_addr_from = ip_addr;
eth_reply(frame, sizeof(arp_message_t));
}
}
}
}
IP
IP (Internet Protocol) — протокол сетевого уровня. А заодно и сердце наших интернетов. Служит он для абстрагирования от технологии локальной сети, для маршрутизации пакетов между сетями, etc.
Формат IP-пакета:

- Версия — 4 для IPv4.
- Длина заголовка — 5 (dword'ов) для стандартного пакета.
- Длина пакета — суммарная длина заголовка и поля данных. Важна, поскольку точный размер пакета мы не знаем из-за выравнивания Ethernet-фреймов.
- Время жизни — это поле уменьшается после каждого хопа (прохождения пакета через роутер). Если оно доходит до нуля, пакет прибивается. Нужно для отлавливания «заблудившихся» пакетов. При отпраке пакета пишем в него, например, 64.
- Принцип рассчёта контрольной суммы описан ниже, в примере кода. При рассчёте контрольной суммы заголовка в данном поле должен быть 0.
Для каждой локальной сети определён MTU (Maximum Transmission Unit) — максимальный размер пакета, который она может протащить через себя. Например, для Ethernet — 1500 байт, максимальный размер поля данных в фрейме. Если пакет переходит в сеть, через которую он не может пролезть целиком, IP фрагментирует пакет. В каждом фрагменте устанавливается идентификатор и смещение фрагмента. Это значит, что, теоретически, пакет может прийти нам по кусочкам.
Впрочем, минимально допустимый MTU для IP — 576 байт. Пакеты до такого размера фрагментироваться не будут. Если мы не собираемся посылать и принимать пакеты больше минимального MTU, на фрагментирование можно просто забить.
Пример кода:
// Коды протоколов
#define IP_PROTOCOL_ICMP 1
#define IP_PROTOCOL_TCP 6
#define IP_PROTOCOL_UDP 17
// IP-пакет
typedef struct ip_packet {
uint8_t ver_head_len; // версия и длина заголовка =0x45
uint8_t tos; //тип сервиса
uint16_t total_len; //длина всего пакета
uint16_t fragment_id; //идентификатор фрагмента
uint16_t flags_framgent_offset; //смещение фрагмента
uint8_t ttl; //TTL
uint8_t protocol; //код протокола
uint16_t cksum; //контрольная сумма заголовка
uint32_t from_addr; //IP-адрес отправителя
uint32_t to_addr; //IP-адрес получателя
uint8_t data[];
} ip_packet_t;
// Отправка IP-пакета в ответ - для сервера
void ip_reply(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *packet = (void*)(frame->data);
// Заполняем заголовок
packet->total_len = htons(len + sizeof(ip_packet_t));
packet->fragment_id = 0;
packet->flags_framgent_offset = 0;
packet->ttl = IP_PACKET_TTL;
packet->cksum = 0;
packet->to_addr = packet->from_addr;
packet->from_addr = ip_addr;
packet->cksum = ip_cksum(0, (void*)packet, sizeof(ip_packet_t));
// Заворачиваем в Ethernet-фрейм и отправляем
eth_reply((void*)frame, len + sizeof(ip_packet_t));
}
// Обработчик IP-пакетов
void ip_filter(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *packet = (void*)(frame->data);
// Можно не проверять - минимальное поле данных Ethernet-фрейма
// всегда больше размера IP-заголовка
//if(len >= sizeof(ip_packet_t))
//{
// Проверяем версию протокола и адрес получателя
if( (packet->ver_head_len == 0x45) &&
(packet->to_addr == ip_addr) )
{
// Вычисляем длину поля данных
len = ntohs(packet->total_len) -
sizeof(ip_packet_t);
switch(packet->protocol)
{
// Протокол = ICMP, вызываем обработчик пакетов
case IP_PROTOCOL_ICMP:
icmp_filter(frame, len);
break;
// Протокол = UDP, вызываем обработчик пакетов
case IP_PROTOCOL_UDP:
udp_filter(frame, len);
break;
}
}
//}
}
// Рассчёт контрольной суммы для IP (и других протоколов)
uint16_t ip_cksum(uint32_t sum, uint8_t *buf, size_t len)
{
// Рассчитываем сумму word'ов блока (big endian)
// (блок выравнивается на word нулём)
while(len >= 2)
{
sum += ((uint16_t)*buf << 8) | *(buf+1);
buf += 2;
len -= 2;
}
if(len)
sum += (uint16_t)*buf << 8;
// Складываем старший и младший word суммы
// пока не получим число, влезающее в word
while(sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
// Снова конвертируем в big endian и берём дополнение
return ~htons((uint16_t)sum);
}
ICMP
ICMP (Internet Control Message Protocol) — вспомогательный протокол сетевого уровня, работающий «рядом» с IP. ICMP служит, в том числе, и для диагностики сети. Всем известная утилита ping использует ICMP Echo-запросы. Если мы хотим, чтобы девайс пинговался, можно добавить поддержку ICMP Echo-запросов.
ICMP-сообщение заворачивается в IP-пакет. Echo-запрос и ответ выглядят вот так:

- Тип пакета. Запрос (8) или ответ (0).
- Код пакета — 0 для Echo-запроса и ответа.
- Контрольная сумма заголовка рассчитыватся также, как и для заголовка IP-пакета.
Остальные поля устанавливаются на усмотрение хоста. Ответ должен содержать те же значения.
Пример кода:
// Тип пакета
#define ICMP_TYPE_ECHO_RQ 8
#define ICMP_TYPE_ECHO_RPLY 0
// ICMP Echo-пакет
typedef struct icmp_echo_packet {
uint8_t type;
uint8_t code;
uint16_t cksum;
uint16_t id;
uint16_t seq;
uint8_t data[];
} icmp_echo_packet_t;
// Обработчик ICMP-пакета
void icmp_filter(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *packet = (void*)frame->data;
icmp_echo_packet_t *icmp = (void*)packet->data;
// Проверяем длину пакета
if(len >= sizeof(icmp_echo_packet_t) )
{
// Получили Echo-запрос
if(icmp->type == ICMP_TYPE_ECHO_RQ)
{
// Меняем тип пакета на ответ
icmp->type = ICMP_TYPE_ECHO_RPLY;
// Обновляем контрольную сумму,
// мы изменили только одно поле в пакете,
// так что пересчитывать полностью не обязательно
icmp->cksum += 8;
// Отправляем пакет назад
ip_reply(frame, len);
}
}
}
UDP
UDP (User Datagram Protocol) — простейший протокол транспортного уровня. UDP позволяет узлам обмениваться небольшими сообщениями, называемыми датаграммами.
Тут мы реализуем UDP-сервер — информация будет отправляться только в ответ на запрос.
Чтобы датаграмма точно пролезла в MTU сети без фрагментации IP-пакета, количество полезных данных в ней не должно превышать 512 байт.
UDP называется ненадёжным протоколом — при потере IP-пакета, датаграмма теряется. Впрочем, современные сети вполне себе надёжны — в хорошо работающей локальной сети пакеты не теряются почти никогда. Но, естественно, приложение должно адекватно реагировать, если датаграмма всё же потеряется.
UDP отлично подходит для передачи данных в реальном времени, нечувствительных к потерям. Скажем, с помощью UDP удобно забирать показания каких-нибудь датчиков или отправлять инфомрацию на дисплей.
UDP-пакет заворачивается в IP-пакет. Формат UDP-пакета:

Контрольная сумма тут рассчитывается необычно. Не от самого пакета, а от пакета с псевдозаголовком, который выглядит вот так:

Часть полей берётся из заголовка IP-пакета (который, кстати, и сам с контрольной суммой). Мда)
Рассчитаная от этой штуки контрольная сумма записывается уже в нормальный UDP-пакет.
Пример кода:
// UDP-пакет
typedef struct udp_packet {
uint16_t from_port;
uint16_t to_port;
uint16_t len;
uint16_t cksum;
uint8_t data[];
} udp_packet_t;
// Обработчик UDP-пакета
void udp_filter(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *ip = (void*)(frame->data);
udp_packet_t *udp = (void*)(ip->data);
// Проверяем длину заголовка
if(len >= sizeof(udp_packet_t))
{
// Отдаём пакет приложению
udp_packet(frame, ntohs(udp->len) -
sizeof(udp_packet_t));
}
}
// Ответ на UDP-пакет
void udp_reply(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *ip = (void*)(frame->data);
udp_packet_t *udp = (void*)(ip->data);
uint16_t temp;
// Рассчитываем длину всего пакета
len += sizeof(udp_packet_t);
// Меняем местами порт отправителя и получателя
temp = udp->from_port;
udp->from_port = udp->to_port;
udp->to_port = temp;
// Длина пакета
udp->len = htons(len);
// Рассчитываем контрольную сумму от псведозаголовка + данных
// Псведозаголовок = длина пакета+протокол+IP адреса+нормальный udp-заголовок
// длину пакета+протокол передаём как начальное значение для
// рассчёта контрольной суммы
// ip адреса берём из заголовка IP-пакета (udp-пакет - 8)
udp->cksum = 0;
udp->cksum = ip_cksum(len + IP_PROTOCOL_UDP,
(uint8_t*)udp-8, len+8);
ip_reply(frame, len);
}
Приложение
Чтобы придать всему этому смысл, напишем простенькое приложение, работающее по UDP. Например, «переходник» Ethernet-UART.
// Цепляем библиотеку для буферизованной работы с UART
#include "buart.h"
// При приёме UDP-пакета
void udp_packet(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *ip = (void*)(frame->data);
udp_packet_t *udp = (void*)(ip->data);
uint8_t *data = udp->data;
uint8_t i, count;
// Отпавляем данные в UART
for(i = 0; i < len; ++i)
uart_write(data[i]);
// Возвращаем компу то, что наприходило
// с момента последнего обмена
count = uart_rx_count();
if(count)
{
for(i = 0; i < count; ++i)
data[i] = uart_read();
udp_reply(frame, count);
}
}
int main()
{
uint8_t len;
static uint8_t net_buf[576];
eth_frame_t *frame = (void*)net_buf;
// Инициализируем ENC28J60
enc28j60_init(mac_addr);
// Инициализируем UART
uart_init();
sei();
// Ловим приходящие пакеты
while(1)
{
if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
eth_filter(frame, len);
}
return 0;
}
Работать с переходником просто. Чтобы вывести данные в UART, отправляем девайсу UDP-пакет на любой порт. Если с момента последнего обмена на UART приходили данные, девайс возвращает пакет с этими данными. Чтобы постоянно получать данные, комп должен с некоторым интервалом посылать девайсу пакеты (хотя бы пустые). В принципе, типичный способ общения с сервером.

Девайс этот полезен тем, что позволяет подключить к сети другие, уже сделанные девайсы. Ну и поэкспериментировать с сетью.
Заключение
Взять проект целиком можно здесь.
В этой части мы научились отвечать на UDP-пакеты. В следующей узнаем как самим отпрвить пакет, чтобы получить данные с другого сервера.
зы. Нужна ли статья по общению с сетевыми девайсами со стороны компа (ну и «выводе» девайсов в интернет, etc.) или это всё и так понятно?

Все статьи цикла
- Подключение микроконтроллера к локальной сети: Теория
- Подключение микроконтроллера к локальной сети: работаем с ENC28J60
- Подключение микроконтроллера к локальной сети: UDP-сервер
- Подключение микроконтроллера к локальной сети: UDP-клиент
- Подключение микроконтроллера к локальной сети: Широковещательные сообщения и DHCP
- Подключение микроконтроллера к локальной сети: TCP-клиент
- Подключение микроконтроллера к локальной сети: HTTP и CGI
- Подключение микроконтроллера к локальной сети: TCP и HTTP (продолжение)
- Подключение микроконтроллера к локальной сети: HTTP и CGI (заключение)
- Подключение микроконтроллера к локальной сети: тесты производительности и краткое описание API стека
- Подключение микроконтроллера к локальной сети: Заключение
- Веб сервер на Tiny2313. Чисто ради лулзов
- +23
- 31 марта 2011, 01:47
- Lifelover
" Нужна ли статья по общению с сетевыми девайсами со стороны компа (ну и «выводе» девайсов в интернет, etc.) или это всё и так понятно?"
Конечно нужна. Хотелось бы знать от и до.
Спасибо за информацию, за труды.
Конечно нужна. Хотелось бы знать от и до.
Спасибо за информацию, за труды.
Вообще-то, инфы по этой части, в том числе и на русском — дофига. Искать по «сетевое программирование» и «сокеты» на программерских ресурсах.
Спасибо за статью. Сеть довольно прикольная штука. Вот сворганил небольшое продолжение UDP сервера под IAR + UDP клиент acvarif.info/progmk/progmk6.html Может кому пригодится.
Нужна ли статья по общению с сетевыми девайсами со стороны компа (ну и «выводе» девайсов в интернет, etc.)А кстати, какая здесь связь? Девайсы, в принципе, уже готовы к выходу в инет — достаточно их подключить к нему, это задача сисадминская и изрядно зависит от железа. Правда, прежде, чем их выставлять голой жопой в инет — стоит позаботиться о защите (и, возможно, веб-морде) — но это все со стороны МК.
А вот общение со стороны компа — задача чисто программерская и довольно простая, благо стек в ОС уже полностью готов и используется через вполне стандартный интерфейс.
То есть, на мой взгляд это довольно разные вопросы.
Не а, это один но очень разноплановый вопрос. Чтобы сделать готовый продукт надо вникнуть во все нюансы. Как МК будет взаимодействовать с компьютером или как компьютер будет взаимодействовать с МК. МК и интернет, интернет и МК. Чем больше взаимосвязанной инфы будет в одном месте, тем проще будет вникнуть в суть вопроса. Особенно начинающим.
В принципе, при использовании протокола IP — это одно и то же. Нужно только провесить канал до инета и выделить IP девайсу. Ну или спрятать за NAT, выделив порт.
А вот HTTP — он, хотя и не сложный, но зато текстовый (причем не W 10 20 45 как конфигурирование всяких девайсов по COM), так что нужно достаточно много и ОЗУ (под разбираемые тексты), и ПЗУ (под контент).
А вот HTTP — он, хотя и не сложный, но зато текстовый (причем не W 10 20 45 как конфигурирование всяких девайсов по COM), так что нужно достаточно много и ОЗУ (под разбираемые тексты), и ПЗУ (под контент).
Классная подача материал. С удовольствием прочитал. Для полного счасть не хватает CGI сценариев и думаю, Ваши статьи дадут толчек многим к разработке множества интересных модулей на МК.
Большое СПАСИБО.
Большое СПАСИБО.
- TracerrecarT
- 01 апреля 2011, 00:04
- ↓
Проект настоился и откомпилился (Build succeeded with 0 Warnings…
) под mega128 (AvrStudio 4.13 Build 528). Но при попытке симуляции застревает на void enc28j60_soft_reset() В железе наверняка работать на будет. Оригинальный проект (mega16) симулируется легко. Не пойму, что не так
) под mega128 (AvrStudio 4.13 Build 528). Но при попытке симуляции застревает на void enc28j60_soft_reset() В железе наверняка работать на будет. Оригинальный проект (mega16) симулируется легко. Не пойму, что не так
Ой, наверняка вечная трабла со SPI. Ножка SS (PB0 в м128) в режиме мастера должна быть или настроена на выход (DDRB |= 1<<PB0) или подтянута снаружи к земле.
Оригинальный проект (mega16) симулируется легко.И даже пакеты принимает и отправляет?)
дефайны для меги128
Потом PB0 всеравно инициализируется на выход
Теперь симулируется легко (имеется ввиду симулятор, а не jtag), но железо вроде работает но на запросы не отвечает.
На железе:
ss — отрицательные импульсы длительность 4мкс период 15мкс
sck — по две ВЧ пачки (по положению внутри ss) период между пачками как и ss
mosi — по 2 отрицательных импулься (по положению внутри ss) период тот же
#define ENC28J60_SPI_DDR DDRB
#define ENC28J60_SPI_PORT PORTB
#define ENC28J60_SPI_CS (1<<PB0)
#define ENC28J60_SPI_MOSI (1<<PB2)
#define ENC28J60_SPI_MISO (1<<PB3)
#define ENC28J60_SPI_SCK (1<<PB1)
Потом PB0 всеравно инициализируется на выход
// Initialize SPI
ENC28J60_SPI_DDR |= ENC28J60_SPI_CS|ENC28J60_SPI_MOSI|ENC28J60_SPI_SCK;
Теперь симулируется легко (имеется ввиду симулятор, а не jtag), но железо вроде работает но на запросы не отвечает.
На железе:
ss — отрицательные импульсы длительность 4мкс период 15мкс
sck — по две ВЧ пачки (по положению внутри ss) период между пачками как и ss
mosi — по 2 отрицательных импулься (по положению внутри ss) период тот же
Пока пытаюсь на AvrStudio.
Я так понимаю, что железо работает правильно (судя по импульсам), тоесть ожидает пакеты?
Похоже я не допираю как на enc28 подать пакет с сообщением.
Подключил все к внутренней сети (у нас сетка 192.168.2.1, себе выставил в проекте IP 192.168.2.15). Запускаю утилиту nc и пишу nc -u 192.168.2.15 12345 test
Я так понимаю, что железо должно поймать это и перетранслировать на ком. Но ком гад молчит как рыба об лед.
Я так понимаю, что железо работает правильно (судя по импульсам), тоесть ожидает пакеты?
Похоже я не допираю как на enc28 подать пакет с сообщением.
Подключил все к внутренней сети (у нас сетка 192.168.2.1, себе выставил в проекте IP 192.168.2.15). Запускаю утилиту nc и пишу nc -u 192.168.2.15 12345 test
Я так понимаю, что железо должно поймать это и перетранслировать на ком. Но ком гад молчит как рыба об лед.
Как все же правильно подключить к сети данный UDP сервер?
Через хаб или к сетевухе компа. Если к сетевой карте компа то как ее настроить? Проект в железе вроде крутится, а увидеть это воочию не удается.
Как в проекте сделать признак, что enc28… проинициализировалась правильно (все регистры получили, что надо)?
Как можно увидеть, что утилита nc посылает пакет на нужный IP?
Как в проекте сделать какой либо признак о том, что пакет принят?
Вопросы вроде простые, но небольшой опыт работы с сеткой не дает ответа на эти вопросы. Ответьте кому не лень…
Через хаб или к сетевухе компа. Если к сетевой карте компа то как ее настроить? Проект в железе вроде крутится, а увидеть это воочию не удается.
Как в проекте сделать признак, что enc28… проинициализировалась правильно (все регистры получили, что надо)?
Как можно увидеть, что утилита nc посылает пакет на нужный IP?
Как в проекте сделать какой либо признак о том, что пакет принят?
Вопросы вроде простые, но небольшой опыт работы с сеткой не дает ответа на эти вопросы. Ответьте кому не лень…
Сделал пока так:
Если enc принимает пакеты (любые) то на PC1 будут появляться 1мс импульсы. Это действительно происходит, поскольку в сети всегда чего нибудь твориться. Кроме того, поскольку импульсы имеются, значит enc работает и что-то принимает. Я прав?
Теперь сделал так:
Если принят пакет и в нем длина и IP что надо то появится 1 мс импульс на PC0. У меня после команды nc -u 192.168.2.15 12345 test ничего не происходит. Не допираю в чем секрет… как еще кроме nc можно посылать UDP пакеты? Может как-то можно это делать в цикле? С помощью чего?
void lan_poll()
{
uint16_t len = 0;
eth_frame_t *frame = (void*)net_buf;
while((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
{
DDRC |= (1 << DDC1);
PORTC |= (1 << PC1);
_delay_ms(1);
PORTC &= ~(1 << PC1);
eth_filter(frame, len);
}
}
Если enc принимает пакеты (любые) то на PC1 будут появляться 1мс импульсы. Это действительно происходит, поскольку в сети всегда чего нибудь твориться. Кроме того, поскольку импульсы имеются, значит enc работает и что-то принимает. Я прав?
Теперь сделал так:
void ip_filter(eth_frame_t *frame, uint16_t len)
{
ip_packet_t *packet = (void*)(frame->data);
//if(len >= sizeof(ip_packet_t))
//{
if( (packet->ver_head_len == 0x45) &&
(packet->to_addr == ip_addr) )
{
len = ntohs(packet->total_len) -
sizeof(ip_packet_t);
switch(packet->protocol)
{
#ifdef WITH_ICMP
case IP_PROTOCOL_ICMP:
icmp_filter(frame, len);
break;
#endif
case IP_PROTOCOL_UDP:
udp_filter(frame, len);
break;
}
DDRC |= (1 << DDC0);
PORTC |= (1 << PC0);
_delay_ms(1);
PORTC &= ~(1 << PC0);
}
//}
}
Если принят пакет и в нем длина и IP что надо то появится 1 мс импульс на PC0. У меня после команды nc -u 192.168.2.15 12345 test ничего не происходит. Не допираю в чем секрет… как еще кроме nc можно посылать UDP пакеты? Может как-то можно это делать в цикле? С помощью чего?
Наконец заработало. Применил это www.hwgroup.cz/products/hercules/index_en.html
Согласование уровней для меги128а вроде не нужно. Похоже и так все понимает.
Спасибо автору.
Согласование уровней для меги128а вроде не нужно. Похоже и так все понимает.
Спасибо автору.
Перенес код на STM32F100
Сделал так:
Основной шлюз 192.168.1.1
IP 192.168.1.100
В проекте поставил 192.168.1.101
Сделал так:
while(1)
{
if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
{
eth_filter(frame, len);
}
}
Отправляю с ПК, через дабагер смотрю что заходит у функцию eth_filter(frame, len); дальше case ETH_TYPE_ARP:
arp_filter(frame, len - sizeof(eth_frame_t));
и вот на проверкеif( (msg->type == ARP_TYPE_REQUEST) && (msg->ip_addr_to == ip_addr) )
ip_addr_to =0 как такое может быть? Основной шлюз 192.168.1.1
IP 192.168.1.100
В проекте поставил 192.168.1.101
прежде всего спасибо за отличный цикл статей! Просто все супер!
Но и не без вопроса конечно… Я натолкнулся на странную ситуацию. При реализации PING получаю следующее все вроде работает но ответы пинга я вижу только в сниффере а сама команда на них не реагирует… ПОЧТИ! Ладно б совсем не реагировала! Но времени от времени она ловит мой ответ. Но крайне редко и с огромной задержкой. При этом сниффер реагирует мгновенно. Может кто что подскажет? Пингую из под Windows
Но и не без вопроса конечно… Я натолкнулся на странную ситуацию. При реализации PING получаю следующее все вроде работает но ответы пинга я вижу только в сниффере а сама команда на них не реагирует… ПОЧТИ! Ладно б совсем не реагировала! Но времени от времени она ловит мой ответ. Но крайне редко и с огромной задержкой. При этом сниффер реагирует мгновенно. Может кто что подскажет? Пингую из под Windows
хочу поделится своим опытом доработки данного примера под STM32:
1) в виду проблем с паковкой все структуры данных следует упаковывать, иначе когда по структуре начинаешь «бегать» относительно ее начала попадаешь мимо полей нужных, т.к. компилятор выравнивает все структуры по dword? запретить ему это делать надо вот как:
2)причину тут я не понял но видимо из за тормознутости enc28j60 на высоких скоростях без задержек у меня ничего не заработало, вернее работало только в дебагере, когда код выполнялся медленно, проблема решилась когда я перед обработкой очередного фрейма делал небольшую задержку, примерно 2 мсек:
1) в виду проблем с паковкой все структуры данных следует упаковывать, иначе когда по структуре начинаешь «бегать» относительно ее начала попадаешь мимо полей нужных, т.к. компилятор выравнивает все структуры по dword? запретить ему это делать надо вот как:
typedef struct arp_message {
uint16_t hw_type;
uint16_t proto_type;
uint8_t hw_addr_len;
uint8_t proto_addr_len;
uint16_t type;
uint8_t mac_addr_from[6];
uint32_t ip_addr_from;
uint8_t mac_addr_to[6];
uint32_t ip_addr_to;
} __attribute__ ((__packed__)) arp_message_t;
2)причину тут я не понял но видимо из за тормознутости enc28j60 на высоких скоростях без задержек у меня ничего не заработало, вернее работало только в дебагере, когда код выполнялся медленно, проблема решилась когда я перед обработкой очередного фрейма делал небольшую задержку, примерно 2 мсек:
void eth_filter(eth_frame_t *frame, uint16_t len)
{
if(len >= sizeof(eth_frame_t))
{
Delay_ms(2); // ВОТ ТУТ СТАВИМ ЗАДЕРЖКУ И ВУАЛЯ!!!!
switch(frame->type)
{
case ETH_TYPE_ARP:
arp_filter(frame, len - sizeof(eth_frame_t));
break;
case ETH_TYPE_IP:
ip_filter(frame, len - sizeof(eth_frame_t));
break;
}
}
}
Привет zwitch! Тисни название покупного модуля Ethernet и где его можно купить. И если не очень в лом дай пошире свой упомянутый опыт. Заранее благодарен.
Я тоже такую штуку непрофессионально заюзал, брал на ебае (ENC28J60 Ethernet LAN Network Module). Трудно поддаётся понимание материала, но чую структурированность кода (тяжело в ученье — легко на работе). Закурил я eth_232 код на Атмеге[email protected], со второго пинка пинг завёлся, чему я был несказанно рад, мерси за это автору. Размер данных больше 466 байтов не проходит — нет ответа (может памяти в кристалле мало) при этом задержка 35-40мс, пробовал заводить 5 пингов на енку по 466 байтов каждый, задержка увеличилась и стала нестабильной, так и должно быть.
Простите, что не совсем топик, но всё же.
Загрузил исходники по ссылке выше, создал проект в AVR Studio 4. Пытаюсь сделать build, но он пишет, что не может найти файл eth_232.elf. Я начинающий в деле программирования. Если можете, то подскажите в чем проблема и что мне надо поменять в коде или makefile'e?
Заранее спасибо
Загрузил исходники по ссылке выше, создал проект в AVR Studio 4. Пытаюсь сделать build, но он пишет, что не может найти файл eth_232.elf. Я начинающий в деле программирования. Если можете, то подскажите в чем проблема и что мне надо поменять в коде или makefile'e?
Заранее спасибо
Скачал проект из статьи, скомпилировал под внутренний кварц 8МГц, контроллер тот же, операционка XP. Скачал Netcat по первой ссылке (нативную сборку), закинул в system32.
Пишу в командной строке: nc -u 192.168.0.222 12345 test; в ответ получаю: invalid port test: NO_DATA. Как правильно пользоваться NetCat в данном случае?
Пишу в командной строке: nc -u 192.168.0.222 12345 test; в ответ получаю: invalid port test: NO_DATA. Как правильно пользоваться NetCat в данном случае?
- Electronik
- 11 марта 2013, 18:20
- ↓
Подключил через свитч. Устройство в сети не появилось. Соответственно, на ping не отвечает: PING: сбой передачи. General failure. Но в то же время ENC пересылает контроллеру все сетевые пакеты от других приложений(браузер, антивирус, …)
- Electronik
- 14 марта 2013, 15:44
- ↑
- ↓
Лампочка, надо полагать, на свитче горит?
поддержку ICMP включили, UPD настроили? Какая скорость передачи на SPI?
поддержку ICMP включили, UPD настроили? Какая скорость передачи на SPI?
Да, лампочка горит. hex брал у DI в статье Pinboard II. Ethernet модуль на базе ENC28J60. Контроллер ATMega16, частота 8MHz. Проект даже не компилил. ICMP и UDP в проекте настроены. Скорость передачи по SPI 4Mbit.
- Electronik
- 14 марта 2013, 17:28
- ↑
- ↓
Подключал через свитч (т.к. в наличии провода только с прямой обжимкой) просто к одному компу (192.168.7.100).
- Electronik
- 15 марта 2013, 11:14
- ↑
- ↓
адрес компа поменял на 192.168.2.100, не подумал, спасибо. Только компьютер пишет то же самое: узел недоступен.
- Electronik
- 15 марта 2013, 11:57
- ↑
- ↓
Пробовал на XP и семерке: результат один. Насчет «эта хр… на не работает...» есть сомнения, т.к. ENC ловила пакеты от оперы и вебера. Думаю подключить лог. анализатор и проверить все пакеты с ENC. Может там собака зарыта.
- Electronik
- 15 марта 2013, 12:11
- ↑
- ↓
Вот сейчас мне мысль пришла — взял я и выдернул кабель сетевой из компа и попробовал пингануть несуществующий хост — выдало «аппаратный сбой». Если кабель не вытаскивать и пингануть несуществующий узел — будет ответ «заданный узел недоступен». Чуешь к чему это я веду?
Пробовал выдернуть кабель, комп написал то же самое: заданный узел недоступен. К чему ведёшь не чую :)
- Electronik
- 15 марта 2013, 12:26
- ↑
- ↓
слушай, путаешься в показаниях — в начале у тебя был аппаратный сбой, теперь узел недоступен — ошибки разные и причины разные. Прошу срочно определиться!!!
Ошибки разные потому, что с разных компов. Где узел недоступен там комп с ХР, где general failure там семерка. Сегодняшние попытки были на ХР, сейчас сделал все тоже самое на семерке (настроил IP и маску) и все заработало. Почему на ХР не работает непонятно, может настройки где, а может и глючная винда.
- Electronik
- 15 марта 2013, 12:59
- ↑
- ↓
Как я выяснил на этом компе (с ХР) стоит специальная программа для работы, которая блокирует все чуждые сетевые соединения, удалить или заблокировать её не получается, а на семёрке всё ок. Вообщем подытожим: проблема была в неправильных настройках сетевых адресов компа.
- Electronik
- 15 марта 2013, 14:33
- ↑
- ↓
Чет не могу понять. С приемом пакетов все нормально. На пинги отвечает, ARP тоже нормально резолвится. А вот с отправкой пакетов UDP — косяк. Шарк ругается на контрольную сумму IP пакета.
Вот ответ железки (отправляю 16 символов «1»)
А шарк говорит, что Header checksum: 0xb92b [incorrect, should be 0x062c (may be caused by «IP checksum offload»?)]
Вот ответ железки (отправляю 16 символов «1»)
0000 00 19 5b ea 7b ae 00 13 37 01 23 45 08 00 45 00 ..[.{... 7.#E..E.
0010 00 2d 00 00 00 00 40 11 <strong>b9 2b</strong> c0 a8 79 de c0 a8 [email protected] .+..y...
0020 79 65 4d bc fd 99 00 18 b6 49 31 31 31 31 31 31 yeM..... .I111111
0030 31 31 31 31 31 31 31 31 31 31 00 00 11111111 11..
А шарк говорит, что Header checksum: 0xb92b [incorrect, should be 0x062c (may be caused by «IP checksum offload»?)]
Приветствую. Ну во-первых спасибо автору за статью, как раз то, что я искал. Заранее извиняюсь за дилетантские вопросы. Вот пытаюсь использовать этот проект для подключения enc28j60 к pic18f2455. Если я правильно понял, в папке проекта eth_232 все блоками, так как в основной файле buart.c, lan.c и enc28j60.c не присоединены. Попытался компилировать, ошибки полезли. Сначала регистры исправил, потом типы данных изменил (uint8_t на unsigned char, uint16_t на unsigned int и uint32_t на unsigned long). Да, и еще data заменил на Data, и code на Code, так как она в этой проге как-то не так воспринимаются. Вот теперь вылазит ошибка, никак не могу понять, чего надо исправить, так как с программированием у меня туго. Подскажите, кто может. Буду очень благодарен. Вот фото ошибки:

Во первых, не рекомендую mikroC. Я невысокого мнения о качестве их компилятора. Да и из всех доступных он похоже наименее соответсвует стандартам С.
Во вторых, что значит «присоединены»? Это С, никакого присоединения тут нет. Файлы компилируются независимо и затем линкуются в единую программу, причем ты сам должен указать из каких именно файлов нужно собирать программу. В современных средах это делается добавлением требуемых .c файлов в проект.
И в третьих, стек написан для компилятора AVR-GCC. Рекомендую в нем и работать, т.к. не похоже что ты обладаешь достаточными знаниями для портирования на другой компилятор.
В четвертых, если ты хочешь, чтобы кто-то таки ковырялся с твоим вариантом на mikroC — выложи весь проект на нем. Кто знает, что ты там наменял, на скрине все нормально.
Во вторых, что значит «присоединены»? Это С, никакого присоединения тут нет. Файлы компилируются независимо и затем линкуются в единую программу, причем ты сам должен указать из каких именно файлов нужно собирать программу. В современных средах это делается добавлением требуемых .c файлов в проект.
И в третьих, стек написан для компилятора AVR-GCC. Рекомендую в нем и работать, т.к. не похоже что ты обладаешь достаточными знаниями для портирования на другой компилятор.
В четвертых, если ты хочешь, чтобы кто-то таки ковырялся с твоим вариантом на mikroC — выложи весь проект на нем. Кто знает, что ты там наменял, на скрине все нормально.
Понятно. Ну переходить с pic на avr не хотелось бы, так как с ним я хоть более менее освоился. А вот по поводу проги, если mikroC так плох, что лучше использовать?
А, это еще и пик. Тогда вообще никаких гаранти2й, нужно портировать с пониманием того, как работает стек, МК и компилятор и в чем различия между платформами (angel5a вон, например, успешно портировал этот стек на STM8, рекомендую почитать его опыт).
По поводу компиляторов для PIC почти ничего не скажу. Для них вообще на удивление мало С-компилеров. Хорошим вроде считается HI-TECH PIC C для младших семейств, для старших обычно применяются C18/C30/C32 от самого микрочипа. Еще есть новый комплект компиляторов от микрочипа — XC8, XC16, XC32 (вроде так), из комплекта MPLAB X, но я ничего не знаю о их качестве. Вот только они все поголовно платные, в бесплатных версиях (если есть) как правило отключена оптимизация.
Ну и современные версии поделок микроэлектроники вроде уже не так лажают и косячат, как первые.
По поводу компиляторов для PIC почти ничего не скажу. Для них вообще на удивление мало С-компилеров. Хорошим вроде считается HI-TECH PIC C для младших семейств, для старших обычно применяются C18/C30/C32 от самого микрочипа. Еще есть новый комплект компиляторов от микрочипа — XC8, XC16, XC32 (вроде так), из комплекта MPLAB X, но я ничего не знаю о их качестве. Вот только они все поголовно платные, в бесплатных версиях (если есть) как правило отключена оптимизация.
Ну и современные версии поделок микроэлектроники вроде уже не так лажают и косячат, как первые.
Добрый день!
У меня есть предложение по ускорению расчета контрольной суммы заголовка)
Дело в том, что во время суммирования не обязательно переводить из Big-endian в Little-endian как у Вас:
У меня есть предложение по ускорению расчета контрольной суммы заголовка)
Дело в том, что во время суммирования не обязательно переводить из Big-endian в Little-endian как у Вас:
Я прошу прощения за неполное сообщение, что-то пошло не так когда я писал пост, коммент отправился после нажатия Ctrl+V.
Добрый день!
У меня есть предложение по ускорению расчета контрольной суммы заголовка)
Дело в том, что во время суммирования не обязательно переводить из Big-endian в Little-endian как у Вас:
вот так:
Добрый день!
У меня есть предложение по ускорению расчета контрольной суммы заголовка)
Дело в том, что во время суммирования не обязательно переводить из Big-endian в Little-endian как у Вас:
<code>sum += ((uint16_t)*buf << 8) | *(buf+1);</code>Можно просто посчитать сумму(с поразярдным дополнением) 16-битных чисел заголовка в том порядке байтов, в котором они лежат в структуре ip_packet, поскольку в конце, перед записью в поле контрольной суммы заголовка его и так надо записывать в big-endian.
вот так:
<code>// Расчёт контрольной суммы для IP (и других протоколов) uint16_t ip_cksum(uint32_t sum, uint16_t *buf, size_t len) { // Рассчитываем сумму word'ов блока while(len >= 2) { sum += *buf; buf ++; len -= 2; } if(len) sum += *buf; // Складываем старший и младший word суммы // пока не получим число, влезающее в word while(sum >> 16) sum = (sum & 0xffff) + (sum >> 16); //Берём дополнение до единицы return ~((uint16_t)sum); }</code>
Автору огромное спасибо за статьи, сижу прорабатываю)))) Соответственно к сожалению вопрос,
щас гоняю под протеусом, все нормально, пинг идет на ура, из уарта в удп передается все нормально,
а вот обратно из удп в уарт идти-то идет но почему-то сообщения дублируются, то есть если мы посылаем
удп пакет с содержимым 1 то в уарте мы увидим 11 причем и при передаче из уарта в удп и удпэшный
(отправленный в удп) пакет и уартовский(отправленный до этого по уарту) дублируются оба в уарт.
Это мне одному не повезло с протеусом или это на самом деле?
щас гоняю под протеусом, все нормально, пинг идет на ура, из уарта в удп передается все нормально,
а вот обратно из удп в уарт идти-то идет но почему-то сообщения дублируются, то есть если мы посылаем
удп пакет с содержимым 1 то в уарте мы увидим 11 причем и при передаче из уарта в удп и удпэшный
(отправленный в удп) пакет и уартовский(отправленный до этого по уарту) дублируются оба в уарт.
Это мне одному не повезло с протеусом или это на самом деле?
Большое спасибо Автору за статью! Во всем инете даже нечего рядом поставить.
Можно разъяснить ситуацию, когда udp-server стоит дома, а я хочу получить от него данные в другом городе.
Какой IP нужно и можно назначить, так чтобы его издалека увидеть?
Может где почитать об этом?
Можно разъяснить ситуацию, когда udp-server стоит дома, а я хочу получить от него данные в другом городе.
Какой IP нужно и можно назначить, так чтобы его издалека увидеть?
Может где почитать об этом?
IP выдается провайдером. Подключаешь к прову так же, как и любой другой девайс (т.е. как правило через роутер), при необходимости прокидываешь порты на роутере.
Если адрес динамический — можно использовать динамический DNS (noip.com, например). Если адрес за NAT'ом… Ну, тогда придется изучать вопрос покупки у провайдера более белого айпи, либо отказываться от сервера на девайсе (сервер можно расположить на хостинге в инете, а девайс будет сам периодически связываться с этим сервером и обмениваться данными).
Если адрес динамический — можно использовать динамический DNS (noip.com, например). Если адрес за NAT'ом… Ну, тогда придется изучать вопрос покупки у провайдера более белого айпи, либо отказываться от сервера на девайсе (сервер можно расположить на хостинге в инете, а девайс будет сам периодически связываться с этим сервером и обмениваться данными).
Люди добрые и знающие, подскажите.
1) if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
Данное выражение означает, если >0
2) У меня есть возможность посмотреть любую переменную в микроконтроллере находу и нет uart. Как восстановить функцию udp_packet(), чтоб она просто этот самый udp принимала.
3) Никак не могу понять, в какой переменной мне посмотреть принятый udp_packet. Вот отправил я к примеру цифру 4 с компа, где мне ее увидеть?
4) ЧТо задать со стороны компа в настройках IP, маски подсети и основного шлюза? Я же так понимаю тут комп главный и задавать надо вручную
Не уверен, что все работает верно, но считать из регистра ENC записанный при инициализации MAC адрес удалось и он верный. Следовательно SPI работает правильно.
Заранее спасибо. Извиняюсь за дилетантский уровень вопросов
1) if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
Данное выражение означает, если >0
2) У меня есть возможность посмотреть любую переменную в микроконтроллере находу и нет uart. Как восстановить функцию udp_packet(), чтоб она просто этот самый udp принимала.
3) Никак не могу понять, в какой переменной мне посмотреть принятый udp_packet. Вот отправил я к примеру цифру 4 с компа, где мне ее увидеть?
4) ЧТо задать со стороны компа в настройках IP, маски подсети и основного шлюза? Я же так понимаю тут комп главный и задавать надо вручную
Не уверен, что все работает верно, но считать из регистра ENC записанный при инициализации MAC адрес удалось и он верный. Следовательно SPI работает правильно.
Заранее спасибо. Извиняюсь за дилетантский уровень вопросов
1) Данное выражение присваивает переменной len значение, возвращенное функцией enc28j60_recv_packet и проверяет его на неравенство нулю.
2) Убрать все содержимое функции udp_packet, начиная с "// Отпавляем данные в UART". Правда, тогда она вообще ничего полезного делать не будет и оптимизатор скорее всего выкинет все не нужные более действия вроде помещения указателя на payload в переменную data. Можно просто вернуть пакет как есть, заменив удаленное строчкой udp_reply(frame, len).
3) Полезные данные (payload) пакета можно посмотреть в переменной uint8_t *data в функции udp_packet.
4) Зависит от конфигурации твоей сети.
2) Убрать все содержимое функции udp_packet, начиная с "// Отпавляем данные в UART". Правда, тогда она вообще ничего полезного делать не будет и оптимизатор скорее всего выкинет все не нужные более действия вроде помещения указателя на payload в переменную data. Можно просто вернуть пакет как есть, заменив удаленное строчкой udp_reply(frame, len).
3) Полезные данные (payload) пакета можно посмотреть в переменной uint8_t *data в функции udp_packet.
4) Зависит от конфигурации твоей сети.
Спасибо за ответ, надеюсь найти его на данные простые для знающих вопросы.
1) Функция udp_reply(), как я понял, отправляет принятый UDP пакет обратно отправителю. А зачем это нужно в данном случае, когда пакет приходит в комп по uart. Или я понял чего не правильно. Зачем вообще эта функция нужна?
2) Данное объявление меня немного смущает
eth_frame_t *frame = (void*)net_buf;
То есть мы объявляем указатель типа eth_frame_t, в котором куча переменных и вкидываем в него просто массив net_buf. Как так они стыкуются. Просто по порядку? А что не влезло? Можно пояснить
3) Приведение к типу указателя (void*) какое имеет тут значение, зачем и почему применяется?
Заранее спасибо
1) Функция udp_reply(), как я понял, отправляет принятый UDP пакет обратно отправителю. А зачем это нужно в данном случае, когда пакет приходит в комп по uart. Или я понял чего не правильно. Зачем вообще эта функция нужна?
2) Данное объявление меня немного смущает
eth_frame_t *frame = (void*)net_buf;
То есть мы объявляем указатель типа eth_frame_t, в котором куча переменных и вкидываем в него просто массив net_buf. Как так они стыкуются. Просто по порядку? А что не влезло? Можно пояснить
3) Приведение к типу указателя (void*) какое имеет тут значение, зачем и почему применяется?
Заранее спасибо
возможно, ошибка:
int main()
{
uint8_t len;
static uint8_t net_buf[576];
eth_frame_t *frame = (void*)net_buf;
…
…
// Ловим приходящие пакеты
while(1)
{
if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
eth_filter(frame, len);
}
…
разве len uint8_t? Ведь функция enc28j60_recv_packet возвращает uint16_t.
Спасибо за статьи, они очень помогают!
int main()
{
uint8_t len;
static uint8_t net_buf[576];
eth_frame_t *frame = (void*)net_buf;
…
…
// Ловим приходящие пакеты
while(1)
{
if((len = enc28j60_recv_packet(net_buf, sizeof(net_buf))))
eth_filter(frame, len);
}
…
разве len uint8_t? Ведь функция enc28j60_recv_packet возвращает uint16_t.
Спасибо за статьи, они очень помогают!
Если ктото использует IAR и stm32 для этого проекта
для типов используйте __packed например:
и для указателей на поля данных eth, ip, udp используйте отдельные указатели uint8_t
придется добавить по строчке в каждый уровень например
для типов используйте __packed например:
typedef __packed struct eth_frame {
uint8_t to_addr[6];
uint8_t from_addr[6];
uint16_t type;
//uint8_t *data;
} eth_frame_t;
и для указателей на поля данных eth, ip, udp используйте отдельные указатели uint8_t
придется добавить по строчке в каждый уровень например
void lan_poll()
{
uint16_t len;
eth_frame_t *frame = (eth_frame_t*)net_buf;
eth_data_p = net_buf + sizeof(eth_frame_t);
while((len = enc.recv_packet(net_buf, sizeof(net_buf))))
{
eth_filter(frame, len);
}
}
- megannnn98
- 17 февраля 2017, 12:25
- ↓
Комментарии (105)
RSS свернуть / развернуть