LWIP ECHO SERVER: Как увеличить размер буфера в функции itoa? - PullRequest
0 голосов
/ 26 октября 2018

Я работаю с дизайном Xilinx Ethernetlite (LWIP).Я могу передавать данные с платы KC на ПК (Hercules) через Ethernet, только если buf = 32.Но мой фактический размер буфера равен 1024. Как увеличить размер буфера с 32 до 1024

Я не могу убедиться, что ошибка в коде или в геркулесе.Чтобы прочитать значения (целое число) в геркулесе, я делаю эту функцию.

Первоначально, от Геркулеса я отправлю команду Hello в Board, затем Board с тем, чтобы принять этот запрос.После этого плата выведет данные (целочисленные значения) в Геркулес.

C-код для itoa

char* itoa(int val, int base)
{
static char buf[32] = {0};          //buf size 
int i = 30;

for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
}

Модифицированный код

    #define  DAQ_FIFO_DEPTH  128

  int transfer_data() 
  {
  return 0;
  }

  err_t tcp_write_u32_string(struct tcp_pcb *pcb, unsigned char   prefix, u32_t value)
   {
    unsigned char  buf[11]; /* enough room for prefix and value. */
    err_t          result;
    u16_t          len;
    unsigned char *p = buf + sizeof buf;
  do {
    /* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
    *(--p) = 48 + (value % 10u);
    value /= 10;
     } while (value);
     if (prefix)
    *(--p) = prefix;
 len = buf + sizeof buf - p;
  if (tcp_sndbuf(pcb) < len) 
     {
    result = tcp_output(pcb);
    if (result != ERR_OK)
        return result;
    }
 return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
  }

   err_t send_list(struct tcp_pcb *pcb, const u32_t data[], u16_t len)
  {
  static const char  newline[2] = { 13, 10 }; /* ASCII \r\n */
  err_t              result;

    if (len > 0) {
     u16_t  i;


      result = tcp_write_u32_string(pcb, 0, data[0]);
      if (result != ERR_OK)
        return result;
    for (i = 1; i < len; i++) 
   {
        /* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
        result = tcp_write_u32_string(pcb, 44, data[i]);
        if (result != ERR_OK)
            return result;
     }
   }
   result = tcp_write(pcb, newline, 2, 0);
    if (result)
    return result; 
   return tcp_output(pcb);
  }
 int application_connection(void *arg, struct tcp_pcb *conn, err_t err)
 {
  struct netif *netif = arg; /* Because of tcp_arg(, netif). */
  u32_t         data[DAQ_FIFO_DEPTH];
  u32_t         i, n;
 if (err != ERR_OK) {
    tcp_abort(conn);
    return ERR_ABRT;
   }
  err = daq_setup();
  if (err != ERR_OK) 
  {
    tcp_abort(conn);
    return ERR_ABRT;
 }
 while (1) 
    {
    xemacif_input(netif);
    tcp_tmr();
    tcp_output(conn);
    n = daq_acquire(data, DAQ_FIFO_DEPTH);
    if (n > DAQ_FIFO_DEPTH)
        break;
    if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
        break;
     }
// daq_close();

/* Close the TCP connection. */
    if (tcp_close(conn) == ERR_OK)
     return ERR_OK;

/* Close failed. Abort it, then. */
    tcp_abort(conn);
    return ERR_ABRT;
    }
  int application_main(struct netif *netif, unsigned int port)
  {
   struct tcp_pcb *pcb;
   err_t           err;
   pcb = tcp_new();
   if (!pcb) {
    /* Out of memory error */
    return -1;
      }
    err = tcp_bind(pcb, IP_ADDR_ANY, port);
    if (err != ERR_OK) {
    /* TCP error */
    return -1;
     }
   pcb = tcp_listen_with_backlog(pcb, 1);
  if (!pcb) {
    /* Out of memory. */
    return -1;
    }
  tcp_arg(pcb, netif); 
  tcp_accept(pcb, application_connection);
  while (1)
  xemacif_input(netif);
  }

Выход Hercules введите описание изображения здесь

1 Ответ

0 голосов
/ 26 октября 2018

Итак, это продолжение обсуждения на форумах Xilinx ?

Функция itoa() преобразует целое число без знака (сохраненное в int) в первые около 30 символов в буфере buf.

Функция recv_callback() делает малобез смысла.

Звонок на aurora_rx_main() задокументирован как «FUNCTION CALL», что довольно мало чем полезно (потому что мы понятия не имеем, что он делает), и даже его возвращаемое значение полностью игнорируется..

Первый цикл for выводит содержимое первых 100 u32 в DestinationBuffer[] для целей отладки, поэтому код не связан с поставленной задачей.Однако мы не знаем, кто или что заполнило DestinationBuffer.Он мог или не мог быть заполнен вызовом aurora_rx_main();нам не сообщают в любом случае.

(кажется, что функции tcp_*() соответствуют API, описанному в lwIP Wiki в Викия.)

Если pпараметр имеет значение NULL, затем вызывается tcp_close(tcpb), после чего следует вызов tcp_recv(tcpb, NULL).Это имеет наименьший смысл из всех: зачем пытаться получить что-либо (и почему параметр NULL) после закрытия?

Следующая часть также сбивает с толку.Похоже, что тест if проверяет, превышает ли размер буфера отправки TCP 1024 байта.Если нет, буфер p освобождается.В противном случае цикл for пытается преобразовать каждый u32 в DestinationBuffer в строку, записать эту строку в буфер TCP;однако вместо правильных флагов API он использует константу 1 и даже не проверяет, работает ли добавление в буфер отправки TCP.

В итоге это выглядит как куча вставленного при копировании кодаэто ничего толкового не делает.Увеличение размера буфера в функции itoa не только не нужно (u32, даже при преобразовании в int, всегда будет помещаться в пределах 12 символов (исключая либо знак минус, либо нулевой байт в конце, поэтому сделайте так, чтобы 13)всего символов), но совершенно не имеет отношения к проблеме, которую предполагается исправить.

Корень проблемы в том, что код ужасен. Изменить его - все равно, что положить наполнитель кузова поверх кусочка старой жевательной резинки впопытка «исправить» это. Правильное исправление состоит в том, чтобы полностью удалить этот ненужный код и вместо этого использовать что-то лучшее.

Редактировать: ОП заявляет, что они - новый программист, поэтому комментарии выше должны бытьвоспринимается как прямое, честное мнение о показанном коде, а не о самом OP. Давайте посмотрим, сможем ли мы помочь OP в создании лучшего кода.


Во-первых, показанная функция itoa() глупа.Намерение действительно состоит в том, чтобы отправить обратно u32_t в DestinationBuffer в виде десятичных строк, гораздо лучше реализовать вспомогательную функцию для выполненияпреобразование.Поскольку значению должно предшествовать запятая (или какой-либо другой разделитель), мы можем добавить это также тривиально.Так как он будет отправлен с использованием tcp_write(), мы объединим функциональность:

err_t tcp_write_u32_string(struct tcp_pcb *pcb,
                           unsigned char   prefix, /* 0 for none */
                           u32_t           value)
{
    /* Because 0 <= u32_t <= 4294967295, the value itself is at most 10 digits long. */
    unsigned char  buf[11]; /* enough room for prefix and value. */
    err_t          result;
    u16_t          len;
    unsigned char *p = buf + sizeof buf;

    /* Construct the value first, from right to left. */
    do {
        /* ASCII encoding: '0' = 48, '1' = 49, ..., '9' = 57. */
        *(--p) = 48 + (value % 10u);
        value /= 10;
    } while (value);

    /* Prepend the prefix, if any. */
    if (prefix)
        *(--p) = prefix;

    /* Calculate the length of this part. */
    len = buf + sizeof buf - p;

    /* If the TCP buffer does not have enough free space, flush it. */
    if (tcp_sendbuf(pcb) < len) {
        result = tcp_output(pcb);
        if (result != ERR_OK)
            return result;
    }

    /* Append the buffer to the TCP send buffer.
       We also assume the packet is not done yet. */
    return tcp_write(pcb, p, len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE);
}

, чтобы отправлять num u32_t s из указанного массива в виде десятичных строк с новой строкой в ​​конце, вы можете использовать

err_t send_list(struct tcp_pcb *pcb,
                const u32_t data[],
                u16_t len)
{
    static const char  newline[2] = { 13, 10 }; /* ASCII \r\n */
    err_t              result;

    if (len > 0) {
        u16_t  i;

        /* The first number has no prefix. */
        result = tcp_write_u32_string(pcb, 0, data[0]);
        if (result != ERR_OK)
            return result;

        /* The following numbers have a comma prefix. */
        for (i = 1; i < len; i++) {
            /* ASCII comma is code 44. (Use 32 for space, or 9 for tab.) */
            result = tcp_write_u32_string(pcb, 44, data[i]);
            if (result != ERR_OK)
                return result;
        }
    }

    /* We add a final newline.
       Note that this one can be referenced,
       and it does complete what we wanted to send thus far. */
    result = tcp_write(pcb, newline, 2, 0);
    if (result)
        return result;

    /* and flush the buffer, so the packet gets sent right now. */
    return tcp_output(pcb);
}

Теперь я не написал C для Xilinx и не использовал стек lwIP вообще, поэтому приведенный выше код написан вслепую.Тем не менее, я вполне уверен, что это работает (за исключением каких-либо опечаток или идей; если вы их найдете, сообщите о них в комментарии, а я проверю и исправлю).

Два буфера (buf иnewline) объявлены static, так что, хотя они видны только в своих соответствующих функциях, их значение допустимо в глобальной области действия.

Поскольку TCP является потоковым протоколом, нет необходимостиподгонять каждый ответ к одному пакету.Кроме 11-символьных (для каждого номера и его префиксного) и 2-символьных (новой строки) буферов, единственный большой буфер, который вам нужен, это буфер отправки TCP (максимальная единица передачи или максимальный размер сегмента , поскольку я не уверен, как lwIP использует буфер внутри), обычно от 536 до 1518 байт.

Две вышеупомянутые функции пытаются разделить пакеты между числами, но это только потому, что это проще, чем пытаться заполнить каждый пакет точно.Если следующее (запятая и) значение помещается в буфер, то оно добавляется в буфер;в противном случае буфер сбрасывается первым, а затем к нему добавляется следующее (запятая и) значение.

Со стороны получателя вы должны получить красивый, читаемый поток, используя, например, netcat .(Я понятия не имею, является ли Hercules приложением или просто именем вашего локального компьютера.) Поскольку TCP является потоковым протоколом, получатель не может (надежно) определить, где были границы пакета (в отличие, скажем, от дейтаграмм UDP).На практике TCP-соединение - это всего лишь два потока данных, каждый из которых идет в одну сторону, а разделение на пакеты - это просто подробности протокола, о которых программистам не нужно беспокоиться.Для lwIP, потому что это такая низкоуровневая библиотека, нужно немного позаботиться, но, как видно из приведенного выше кода, это действительно не так уж много.


Вкомментарий, OP объяснил, что они не очень опытны, и что общее намерение состоит в том, чтобы устройство приняло соединение TCP и передавало данные (выборки, полученные отдельной платой сбора данных) как 32-разрядные целые числа без знака через соединение.

Поскольку я хотел бы иметь одну из этих плат ПЛИС (у меня есть несколько задач, которые я мог бы увидеть, могу ли я разгрузить на ПЛИС), но нет ресурсов для их получения, я постараюсь изложить здесь все приложение.Обращаем ваше внимание, что единственная информация, на которую мне нужно перейти, - это версия документа Xilinx для ОС и библиотек 2018 года (UG643) ( PDF ).Похоже, что OP хочет использовать необработанный API для высокой производительности.

Преобразование примеров в текст глупо, особенно если требуется высокая производительность.Мы должны просто использовать сырой двоичный файл и любой порядок байтов, который использует KC705.(Я не видел это с первого взгляда из документации, но я подозреваю, что это little-endian).

Согласно документации, сырой API main() выглядит примерно так:

int main(void)
{
    /* MAC address. Use an unique one. */
    unsigned char  mac[6] = { 0x00, 0x0A, 0x35, 0x00, 0x01, 0x02 };

    struct netif  *netif = NULL;
    ip_addr_t      ipaddr, netmask, gateway;

    /* Define IP address, netmask, and gateway. */
    IP4_ADDR(&ipaddr,  192, 168,   1,  1);
    IP4_ADDR(&netmask, 255, 255, 255,  0);
    IP4_ADDR(&gateway,   0,   0,   0,  0);

    /* Initialize lwIP networking stack. */
    lwip_init();

    /* Add this networking interface, and make it the default one */
    if (!xemac_add(netif, &ipaddr, &netmask, &gateway, mac, EMAC_BASEADDR)) {
        printf("Error adding network interface\n\r");
        return -1;
    }
    netif_set_default(netif);

    platform_enable_interrupts();

    /* Bring the network interface up (activate it) */
    netif_set_up(netif);

    /* Our application listens on port 7. */
    return application_main(netif, 7);
}

В примерах документации вместо return application_main(netif); вы увидите вызовstart_application(), а затем бесконечный цикл, который регулярно вызывает xemacif_input(netif).Это просто означает, что application_main() должен регулярно звонить xemacif_input(netif), чтобы иметь возможность получать данные.(В документации lwIP говорится, что мы также должны вызывать sys_check_timeouts() или tcp_tmr() через равные промежутки времени.)

Обратите внимание, что я пропустил сообщение об ошибке printfs, и вместо того, чтобы корректно восстанавливаться после ошибок, это простовозврат (с main());Я не уверен, вызывает ли это перезапуск KC705 или что.

int application_main(struct netif *netif, unsigned int port)
{
    struct tcp_pcb *pcb;
    err_t           err;

    pcb = tcp_new();
    if (!pcb) {
        /* Out of memory error */
        return -1;
    }

    /* Listen for incoming connections on the specified port. */
    err = tcp_bind(pcb, IP_ADDR_ANY, port);
    if (err != ERR_OK) {
        /* TCP error */
        return -1;
    }
    pcb = tcp_listen_with_backlog(pcb, 1);
    if (!pcb) {
        /* Out of memory. */
        return -1;
    }

    /* The accept callback function gets the network interface
       structure as the extra parameter. */
    tcp_arg(pcb, netif);

    /* For each incoming connection, call application_connection(). */
    tcp_accept(pcb, application_connection);

    /* In the mean time, process incoming data. */
    while (1)
        xemacif_input(netif);
}

Для каждого TCP-соединения с портом мы получаем вызов application_connection().Это функция, которая устанавливает плату сбора данных и передает данные так долго, как этого хочет получатель.

/* How many DAQ samples to process in each batch.
 * Should be around the DAQ FIFO depth or so, I think. */
#define  DAQ_FIFO_DEPTH  128

err_t application_connection(void *arg, struct tcp_pcb *conn, err_t err)
{
    struct netif *netif = arg; /* Because of tcp_arg(, netif). */
    u32_t         data[DAQ_FIFO_DEPTH];
    u32_t         i, n;

    /* Drop the connection if there was an error. */
    if (err != ERR_OK) {
        tcp_abort(conn);
        return ERR_ABRT;
    }

    /* Setup the data aquisition. */
    err = daq_setup();
    if (err != ERR_OK) {
        tcp_abort(conn);
        return ERR_ABRT;
    }

    /* Data acquisition to TCP loop. */
    while (1) {

        /* Keep the networking stack running. */
        xemacif_input(netif);
        tcp_tmr();

        /* Tell the networking stack to output what it can. */
        tcp_output(conn);

        /* Acquire up to DAQ_FIFO_DEPTH samples. */
        n = daq_acquire(data, DAQ_FIFO_DEPTH);
        if (n > DAQ_FIFO_DEPTH)
            break;

        /* Write data as-is to the tcp buffer. */
        if (tcp_write(conn, data, n * sizeof data[0], TCP_WRITE_FLAG_COPY) != ERR_OK)
            break;
    }

    /* Stop data acquisition. */
    daq_close();

    /* Close the TCP connection. */
    if (tcp_close(conn) == ERR_OK)
        return ERR_OK;

    /* Close failed. Abort it, then. */
    tcp_abort(conn);
    return ERR_ABRT;
}

Существует еще три функции для реализации: daq_setup(), которая должна настроитьсбор данных и FIFO;daq_acquire(u32_t *data, u32_t count), который хранит до count сэмплов в data[] и возвращает фактическое количество сохраненных сэмплов - было бы лучше, если бы он просто опустошил FIFO, а не ждал прибытия новых сэмплов - и, наконец,daq_close(), это останавливает сбор данных.

Я считаю, что они должны быть примерно такими:

XLlFifo         daq_fifo;

err_t daq_setup(void)
{
    XLlFifo_Config *config = NULL;

    config = XLlFifo_LookupConfig(DAQ_FIFO_ID);
    if (!config)
        return ERR_RTE;

    if (XLlFifo_CfgInitialize(&daq_fifo, config, config->BaseAddress) != XST_SUCCESS)
        return ERR_RTE;
}

u32_t daq_acquire(u32_t *data, u32_t max)
{
    u32_t len, have;

    have = XLlFifo_iRxGetLen(&daq_fifo);
    if (have < 1)
        return 0;
    else
    if (have < max)
        max = have;

    for (len = 0; len < max; len++)
        data[len] = XLlFifo_RxGetWork(&daq_fifo);

    return len;
}

err_t daq_close(void)
{
    /* How to stop the FIFO? Do we need to? */
}

Вот и все.

...