|
|
  |
lwip с Rtos или без Rtos, Как лучше, правильнее, удобнее использовать стек с rtos или без |
|
|
|
Jan 29 2014, 11:53
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Понятно, какие еще есть открытые , на данный момент нашел два lwip и fnet и uIP. Второй кто нибудь использовал ? Еще вопрос по стеку Lwip в функции low_level_output в цикле заполняется dma и после отправляется фрейм. Если сразу отправлять, насколько это будет правильно? Код for(q = p; q != NULL; q = q->next) { заполняем dma(q->payload, q->len); запрос на передачу() }
|
|
|
|
|
Jan 29 2014, 12:45
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(sergey sva @ Jan 29 2014, 15:53)  Еще вопрос по стеку Lwip в функции low_level_output в цикле заполняется dma и после отправляется фрейм. Если сразу отправлять, насколько это будет правильно? Сразу отправлять - это zero-copy transmit что ли? Я, собственно, так и делаю. Естественно, есть ограничения: данные для отправки не могут лежать в флеше, т.к. STM32 Ethernet DMA не имеет к ней доступа. Кроме того, я жду окончания отправки пакета прямо там же, так как в противном случае есть вероятность того, что буфер будет затёрт до того, как пакет будет отправлен. Чтобы не быть голословным, вот код: CODE err_t low_level_output(struct netif *netif, struct pbuf *p) { int i; uint32_t mask;
if (!netif_is_link_up(netif)) { return ERR_OK; } while ((ETH_DMASR & 0x00700000) != 0x00600000) { /* wait until Tx DMA is suspended */ } mask = (1u << 31) /* OWN bit */ | (1 << 28); /* first segment */ i = -1; for (;;) { i++; if (i == TX_RING_SIZE) { /* not enough TX descriptors */ return ERR_MEM; } tx_desc[i].len[0] = p->len; tx_desc[i].ptr[0] = p->payload; p = p->next; if (p) { tx_desc[i].len[1] = p->len; tx_desc[i].ptr[1] = p->payload; p = p->next; if (p) { tx_desc[i].status = mask; mask = (1u << 31); /* OWN bit */ } else { break; } } else { tx_desc[i].len[1] = 0; break; } } mask |= (1 << 21) /* end of ring */ | (1 << 29); /* last segment */ tx_desc[i].status = mask; REGBIT(ETH_DMASR, 10) = 1; /* reset ETS flag in status register */ ETH_DMATPDR = 0; /* start transmission */ while (REGBIT(ETH_DMASR, 10) == 0) { /* wait for data to be copied into Tx FIFO */ } return ERR_OK; }
|
|
|
|
|
Jan 29 2014, 16:17
|
практикующий тех. волшебник
    
Группа: Участник
Сообщений: 1 190
Регистрация: 9-09-05
Пользователь №: 8 417

|
Цитата(sergey sva @ Jan 29 2014, 15:53)  .. по стеку Lwip..Если сразу отправлять, насколько это будет правильно?.. можно делать следующе: 1) завести обработчик прерывания на приём и передачу. 2) выкинуть нафик все мэмкопи. подумайте - у вас DMA и ему пофигу откуда кушать ваши данные!!! То же самое приёмник. 3) Если будете юзать ось - то там (благодаря обработчику) сокращается одна нитка. вообще то стэк основан на полинге а не на прерываниях - отсюда тормоза. Короче говоря надо доработать напильником
|
|
|
|
|
Jan 29 2014, 18:07
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Golikov A. @ Jan 29 2014, 19:29)  Какой тогда смысл ДМА, если в этот момент вы не можете делать ничего другого? Видимо ДМА справляется с этой задачей быстрее, чем ядро... Цитата(Golikov A. @ Jan 29 2014, 19:29)  вы не можете делать ничего другого? Могут выполнятся прерывания, и если шина свободна - ДМА отправит данные на передечу, а ядро нет. Правда выигрыш там накакой наверное будет...
|
|
|
|
|
Jan 29 2014, 18:32
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Мне кажется что memcpy быстрее скопирует данные из памяти в память, чем дма из памяти через мак в езернет.
потому выделить память, кинуть в нее данные, и отдать их на выдачу через ДМА по времени будет не дольше, а часто и быстрее чем в функции ждать окончания посылки. Из минусов только выделение памяти, зато можно много чего другого сделать, зарядить прочие ДМА пересылать данные дальше или собирать их, например....
я в чем то не прав?
еще есть вариант сделать свой кольцевой буфер, и запускать его через ДМА, в этом случае будет серьезный выигрыш.
|
|
|
|
|
Jan 29 2014, 19:08
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(Golikov A. @ Jan 29 2014, 21:29)  Простите, я может не так понял. Вы указываете ДМА где лежат данные, а потом ждете когда он их все перепихает наружу? Какой тогда смысл ДМА, если в этот момент вы не можете делать ничего другого? Смысл в том, я что я включил режим transmit store and forward, то есть DMA сразу копирует весь пакет в FIFO перед отправкой. Это происходит очень быстро (измерял), но точную цифру сейчас не помню. Альтернатива - не ждать, но тогда нужно серьёзно допиливать стек, чтобы освобождать буферы только после отправки пакета, при этом проблем возникнет больше, чем решится.
|
|
|
|
|
Jan 29 2014, 19:24
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Golikov A. @ Jan 29 2014, 20:32)  Из минусов только выделение памяти, зато можно много чего другого сделать, зарядить прочие ДМА пересылать данные дальше или собирать их, например.... я в чем то не прав? Да всё может и так, но scifi пишет: Цитата так исторически сложилось из-за дефицита ОЗУ
|
|
|
|
|
Jan 30 2014, 18:17
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Понятно видимо гдето ошибся. Добавили вывод в терминал при приеме и передачи пакетов, после подключения пакетов приходит много, а плата отправляет вначале 42байта и иногда по 64, попытался пинговать, узел не доступен. Может я не правильно указал функцию ввода ethernet_inpup? Подскажите на что обратить внимание? Код netif_add(netif_eth0, &my_ipaddr_data, &my_netmask_data, &my_gw_data, NULL, ethernetif_init, ethernet_input);
|
|
|
|
|
Jan 31 2014, 12:45
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Думаю что не хочет работать из за этой ошибки tcp_slowtmr: no active pcbs вот часть кода: Код void tcp_slowtmr(void) { struct tcp_pcb *pcb, *prev; u16_t eff_wnd; u8_t pcb_remove; /* flag if a PCB should be removed */ u8_t pcb_reset; /* flag if a RST should be sent when removing */ err_t err;
err = ERR_OK;
++tcp_ticks; ++tcp_timer_ctr;
tcp_slowtmr_start: /* Steps through all of the active PCBs. */ prev = NULL; pcb = tcp_active_pcbs; if (pcb == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); } while (pcb != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); Не разобрася до конца с этим стеком, предполагаю он не может найти интерфейс активный. После лов иннициализации нужно установить какойто бит? Если нет системы не получиться использовать сокет, если правильно понял.
|
|
|
|
|
Jan 31 2014, 14:01
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Быстрый ,медленый таймер дергае tcptmr(), может еще что то нужно сбрасывать или устанавливать. Код void lwip_init_user(void) { lwip_init();
IP4_ADDR(&my_ipaddr_data, 192, 168, 0, 17); IP4_ADDR(&my_netmask_data, 255, 255, 255, 0); IP4_ADDR(&my_gw_data, 0, 0, 0, 0);
netif_add(netif_eth0, &my_ipaddr_data, &my_netmask_data, &my_gw_data, NULL, ethernetif_init,ethernet_input );//ip_input
netif_set_default(netif_eth0);
netif_set_up(netif_eth0);
}
while (1) // repeat forever { if (karusel < 10) karusel++; else karusel = 0;
switch (karusel) { case 1: {
curTicks = systick_counter;
// process packets that arrive in the interface ethernetif_input(netif_eth0);
// functions that should call from times to times if (curTicks - last_arp_time > ARP_TMR_INTERVAL) { etharp_tmr(); last_arp_time = curTicks; }
if (curTicks - last_tcp_time > TCP_TMR_INTERVAL) { last_tcp_time = curTicks; tcp_tmr(); } break; } //1
|
|
|
|
|
Jan 31 2014, 14:03
|
Знающий
   
Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317

|
Цитата Если нет системы не получиться использовать сокет, если правильно понял. Да. Если у Вас пинга нету чего вы лезете к tcp_slowtmr ??? вам нужен только ARP, IP, ICMP. В изернете нельзя передавать пакеты меньшие 64 байт!!!, многие МАС не умеют дополнять пакет если он меньше 64 байт это надо будет сделать вам самим. STM32 умеет это делать но п омоему надо где то битик установить. P.S. Все по шагам. 1) Делаем функцию приема кадров изернет. Запускаем ее проверяем что она принимает правильно. Буфер расшифровываем в уме. 2) Делаем функцию передачи, создаем константный фрейм, засовываем его в функцию ловим ваершарком убеждаемся что все ок. 3) Прикручиваем эти функции к стеку. 4) Запускаем пинг. Смотрим ARP таблицу, если ваш МАС есть а девайс не пингается что с IP + ICMP, Если в таблице нету вашего МАСа копаем ARP. 5) Потом уже UDP + TCP Я использую FreeRTOS + LwIP примерно так у меня выглядят функции драйвера (прием вынесен в отдельную задачу) Прием: Код __task void EthernetDeamon(void *arg) { int32_t size; struct pbuf *p,*q; uint8_t* pkt; struct netif *ethif = (struct netif *)arg; for(;; ) { size = HwEMAC::RecivePacket((void**)&pkt, 1000); if ( size > 0 ) { p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); if (p != NULL) { for (q = p; q != NULL; q = q->next) { memcpy((u8_t*)q->payload, pkt, q->len); pkt += q->len; } HwEMAC::Recived(); if (tcpip_input(p,ethif) != ERR_OK) pbuf_free(p); } } else { LINKSTATUS_t ls = HwPHY::LinkStatus(PHY_ADDR); if ( ls == NOLINK ) { netif_set_link_down(ethif); HwEMAC::Stop(); } else { if (!netif_is_link_up(ethif)) { HwEMAC::Start(ls); netif_set_link_up(ethif); } } } } }; P.S. tcpip_input - используется если стек работает в режиме ОС (NO_SYS = 0), если без ос то фреймы нужно запихивать в ethernet_input передача: Цитата err_t EthernetOutput(struct netif *ethif, struct pbuf *p) { uint8_t* pkt; struct pbuf *q; if (HwEMAC::GetSendBuffer((void**)&pkt,20) != 0 ) return ERR_IF; for(q = p; q != NULL; q = q->next) { memcpy(pkt, q->payload, q->len); pkt += q->len; } HwEMAC::Send(p->tot_len); return ERR_OK; }
|
|
|
|
|
Feb 3 2014, 07:04
|
Знающий
   
Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317

|
Цитата Первый с копированием данных из pbuf и второй запись указателя в дескриптор на pbuf в dma, в первом скопировал стек продолжает работать. во втором передал указатель и ждем когда произойдет передача. Может еще есть варианты и какой из этих лучше? Вам следует хорошо изучить PBUF, в pbuf пакет может лежать не одним цельным, а кусками этот код собирает из кусков 1 целый буфер. Код for(q = p; q != NULL; q = q->next) {[quote][/quote] memcpy(pkt, q->payload, q->len); pkt += q->len; } Это можно конечно исправить, установив в настройках размер PBUF максимальному размеру фрейма. Но это очень большое расточительство памяти и никак не оправдывает себя. Есть маленький плюс позволяет сделать зеро копи драйвер. Но выигрыш очень мизерный. А памяти все будет жрать немерено. У меня сделано так. Для дма драйвера выделено 2-4 буфера на передачу размером в максимальный фрейм (1512 по моему). Есть указатель на текущий буфер. Я его беру туда копирую pbuf, запускаю передачу дма, а текущему указателю присваиваю адрес следующего буфера и так по кругу. С приемом также есть от 2 - 16 (буферов дма размером в фрейм), есть указатель на текущий. Так как я использую ОС нет необходимости для приема использовать прерывания, так как дма умеет по кругу писать в буфера. Буру текущий указатель проверяю есть ли новый фрейм если есть, копирую его в новый pbuf, этот мечу свободным передвигаю указатель на следующий. И так по кругу. У меня stm32f107 UDP - 70 Mbit/s TCP - 52 Mbit/s (4 буфера на передачу и 4 буфера на прием). #define PBUF_POOL_SIZE 24 #define PBUF_POOL_BUFSIZE 256 Цитата Понятно. Еще наверно будет проблема без ос, если потребуется управлять через веб интерфейс с нескольких компьютеров. Хотя наверно можно обойтись, слушаем порт 80 как присоединился клиент его апи сохраняем, делаем что то, тут присоединился компьютер с другим айпи ему отправиться последняя страница которую смотрели последний раз,как это лучше реализовать. С ос понятно каждому клиенту новый поток. Проблемы нету, это можно сделать. Но именно это меня и заставило перейти на ОС. Так под ОС это реализовывается очень просто на каждое соединение свой таск. Без ОС это столько всякого гемора. Особенно если надо передавать большие фалы устройству. Когда я это сделал без ОС я понял что у меня получилась своя собственная маленькая ос в программе и зачем это ?. Почему это так я могу рассказать но много писать. Могу сказать так что стек под ОС быстрее работает (следует понимать не скорость передачи,а больше ресурсов остается основной программе), меньше расходуется памяти, да да МЕНЬШЕ. Код становится хорошо читаемым и понимаемым, вместо громадных автоматов состояний. Ну и конечно же надо хорошо понимать много поточность, и как правильно писать под ОС и наче получите что то работающее очень плохо.
|
|
|
|
|
Feb 3 2014, 12:24
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Цитата максимальный фрейм (1512 по моему). наверно 1536 #define PBUF_POOL_SIZE 24 * #define PBUF_POOL_BUFSIZE 256 / 4 Пока тоже так сделал сейчас немного поэкспериментирую что получится. Как различаете соединения по портам если они все приходят на порт 80 и с одного айпи?
|
|
|
|
|
Feb 3 2014, 13:51
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
У меня вот так задается PBUF_POOL_BUFSIZE Код /** Calculate memory size for an aligned buffer - returns the next highest * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). */ #define PBUF_POOL_SIZE 16 #define MEM_ALIGNMENT 4 #ifndef LWIP_MEM_ALIGN_SIZE #define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) #endif
#define TCP_MSS 536 #define PBUF_LINK_HLEN 14 #define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) В DMA у меня по 2 буфера по 1536 каждый . Как лучше подобрать эти параметры?
|
|
|
|
|
Feb 3 2014, 14:00
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата(sergey sva @ Feb 3 2014, 16:24)  Как различаете соединения по портам если они все приходят на порт 80 и с одного айпи? Соединение может быть установлено только между двумя сокетами, и для каждой пары сокетов может существовать только одно соединение. Сокет - это тройка параметров 1. IP 2. ПОРТ 3. Тип соединения UDP/TCP ваша тройка параметров фиксирована IP, ПОРТ:80, TCP значит для создания разных соединений будут меняться тройки параметров подключаемого устройства, так как протокол и IP наверняка фиксированны, единственное что может быть изменено это - номер порта.
|
|
|
|
|
Feb 3 2014, 15:46
|
Знающий
   
Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317

|
Цитата Буквально на днях сам на этом обжёгся. lwip ожидает ... lwip ничего не ожидает, это ваше приложение может ожидать, можно сделать и один байт, главное что бы ваше приложение это учитывало и правильно собирало буфер. Цитата Как различаете соединения по портам если они все приходят на порт 80 и с одного айпи? Как вы работаете со стеком я не знаю. Но может быть 3 варианта 1) Работа без ОС, 2) Работа с ОС через netconn интерфейс, 3) работа через сокеты (они тоже на самом деле работают через netconn просто это скрыто от пользователя что бы пользователь мог пользоваться стандартным интерфейсом сокетов) в общем для первого варианта у вас есть функция tcp_accept(listen_pcb, callback_accept); которая устанавливает калбек функцию для нового соединения, у этой функции есть параметр struct tcp_pcb *pcb так вот этот pcb так вот этот pcb будет уникален для каждого соединения, следователь при записи или чтения использую эти pcb будете читать от того или другого клиента. Для 2 и 3 случая все ток же создается уникальная структур (сокет) для каждого соединения.
|
|
|
|
|
Feb 3 2014, 17:33
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
почитайте викизнание по LwIP
он сам сделает структуру для вас, и даст на нее указатель, надо его сохранить. А когда соединение закроется, структура пометиться как мертвая, и функцией которая вызывается при медленном таймере уничтожится.
вообще при работе с lwip, он хорошо следить за своей памятью, и если в процесс ручками не залезть, и ничего не напортить. И следовать стандартному алгоритму вызовов, с отметками освобождения буферов, все будет хорошо.
|
|
|
|
|
Feb 4 2014, 15:18
|
Гуру
     
Группа: Свой
Сообщений: 2 546
Регистрация: 23-05-07
Из: Самарская область Сызрань
Пользователь №: 27 923

|
Собрал по примерам исходников функцию которая должна показывать страницу. Принцип такой: как поступают данные в receive делаю парсинг и вызываю эту функцию: Код err_t ShowPage(void* arg,struct tcp_pcb *tpcb) { struct userstr *us = (struct userstr*) arg; char resp[150]; u16_t lenresp = 0; memset(resp,0,150); err_t err = ERR_OK; u16_t len = us->SizePage;
lenresp = sprintf(resp, "HTTP/1.1 200 OK\r\nServer: Mutniiserver/2014-02-04\r\nContent-Type: text/html\r\nContent-Length: %i\r\nConnection: close\r\n\r\n",us->SizePage); tcp_write(tpcb, resp, lenresp, 0);
UART_printf("send ... (%s) (%i)\n\r", resp,err);
do { LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
err = tcp_write(tpcb, us->PtrPageShow, len, 0);
if (err == ERR_MEM) { if ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) { /* no need to try smaller sizes */ len = 1; } else { len /= 2; }LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed, trying less (%d bytes)\n", len)); } } while ((err == ERR_MEM) && (len > 1));
UART_printf("post send ... (%i)\n\r",err);
if (err == ERR_OK) { LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); } else { LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); }
us->lenght = len; return err;
} // Но пока не хочет работать(
|
|
|
|
|
Feb 12 2014, 12:26
|
практикующий тех. волшебник
    
Группа: Участник
Сообщений: 1 190
Регистрация: 9-09-05
Пользователь №: 8 417

|
Цитата(sergey sva @ Feb 10 2014, 22:23)  Если увеличить скорость быстрого и медленного таймера? таймера служат для ограничений по времени. т.е. приняли некий пакет ликвидный по протоколу, сформировали ответ, запустили таймер(типа смысла ждать больше нет), и послали. заметьте - если прийдёт другой ответ от клиента следом, то будет другое состояние конечного автомата, таймер уже будет не нужен, и будет интерес к другому уже интервалу... т.е. таймера у вас не причина а страховка, я бы так сказал. ну типа нет ответа во время(читай не сошлась црц, потерялся один из пакетов на реассемблировании, сбой физики) и ага - таймер перепошлёт последний сегмент данных. отсюда следует, что править таймеры - это бороться с последствиями а не причиной. внимательно проверяйте анализатором пакеты, последовательные номера посылок, правильность формирования, адреса, чек суммы... где то что то не сходится...(если передача отработала. если нет - то ещё раньше смотрите. ставте бряк на это состояние - см. кейсы по коду на приёме - и ловите стэп бай стэп ситуацию). сразу скажу - есть состояния в данном протоколе, когда он тупо подвиснет. но это не ваш случай. напрямер - бяку нашёл на обрыве соединения, если другая сторона не пришлёт подтверждения, то там вечный переповтор посылки "сделан"...
Сообщение отредактировал kolobok0 - Feb 12 2014, 12:27
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|