Jump to content

    

Помогите разобраться с PRINTF

Не хочется создавать большой кольцевой буфер для PRINTF. Поступил по - другому. С задержкой.

 

Посчитав что PRINTF работает через int fputc(int ch, FILE *f), в ней и написал:

 

int fputc(int ch, FILE *f)                  //Внутри функции реализована задержка заполнения
{                                            //буфера для уменьшения размера буфера.
static unsigned int StreamWaitCnt = 0;        //
    StreamWaitCnt++;                        //Счетчик числа передаваемых байт
    if (StreamWaitCnt > PC_TxBufSize-5)        //Если счеттчик достиг размера буфера
        {                                    //
            StreamWaitCnt = 0;                //Обнулить счетчик
            HAL_Delay(30);                    //Задержка пока буфер улетит в порт
        }                                    //
    return (PC_Putchar(ch));                 //
}                                            //

 

Т.е. когда буфер заполняется, происходит задержка для ожидания завершения передачи буфера в порт. Передача осуществляется через прерывание:

 

 

int PC_Putchar(int ch)                         //Передача. Функция помещает Ch в кольцевой буфер.
{                                             //Указатель головы кольцевого буфера перемещается на
    int tmphead = (PC_TxHead + 1) & PC_TX_BUFFER_MASK; //одну позицию, символ помещается в кольцевой буфер.
    PC_TxBuf[tmphead] = (char)ch;               //Если передача первая после опустошения
    PC_TxHead = tmphead;                    //буфера(переданы все символы), взводится флаг L86_Complete,
        if (PC_Complete==0)                    //Передаваемый символ помещается в передающий регистр порта
        {                                     //(L86_UsartTx();) и инициируется первая передача
            PC_Complete = 1;                //в цепочке - разрешением прерывания.
            PC_UsartTx();                     //Флаг начала цепочки передач
            PC_En_Tranfer_Interrupt;        //Положить первый символ в DR
        }                                     //
    return (ch);                            //
}                                             //

 

 

Само прерывание:

 

void USART2_IRQHandler(void)
{
    if (USART2->ISR & USART_ISR_TC)                 //Bit 6 TC: Transmission complete
    {                                             //
        PC_UsartTx();                             //
    }                                            //
    if (USART2->ISR & USART_ISR_RXNE)             //Bit 5 RXNE: Read data register not empty
    {                                             //
        PC_UsartRx();                            //Функция кладет байт из приёмного регистра в кольцевой буфер
    }                                             //
  HAL_UART_IRQHandler(&huart2);
}

 

 

void PC_UsartTx(void)                         //
{                                             //
    if (PC_TxHead != PC_TxTail)                //Check if all data is transmitted
    {                                         //
        PC_TxTail = (PC_TxTail + 1) & PC_TX_BUFFER_MASK;//Перемещение указателя по кольцевому буферу
        USART2->TDR = (int)PC_TxBuf[PC_TxTail];    //Start transmition
    }                                         //
    else                                    //
    {                                        //
        PC_Complete=0;                        //Цепочка передачи окончена
        PC_Dis_Tranfer_Interrupt;            //Запретить прерывание на передачу Bit 6 TCIE:
    }                                        //
}                                             //

 

Самой задержки (30мс) по идее должно хватать. Т.е. при скорости 115200 за 1 секунду улетает 10килобайт. Примерно. Значит за милисекунду - 10 байт. Итого - 300байт при буфере в 256.

 

Такой подход по идее должен разрешать иметь любой малый (в разумных пределах) буфер для передачи. На практике же это не работает - часть выводимой информации теряется. И только когда я делаю #define PC_TxBufSize 8192, передача осуществляется без потерь.

 

Где же я не прав, может кто подскажет?

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites

Я бился с такой проблемой, не решил. Сделал так. Отказался от передачи ТХ в прерывании, то есть перешёл на поллинг. В терминальной программе поставил задержку после передачи новой строки на 1 миллисекунду. Увеличение буфера до 8кБ помогает, но не всегда. Причём это в Виндоуз и в Линуксе. А файл 1кБ отлично передавался и с буфером 256 байт.

Share this post


Link to post
Share on other sites
часть выводимой информации теряется.

А вы уверены, что теряет не приемная сторона?

Share this post


Link to post
Share on other sites
А вы уверены, что теряет не приемная сторона?

Я делал USB-UART на STM32F103 и на CC2511. RX замкнут на TX. На CC2511 нормально работало до 1 МГц, на STM32F103 после 9600 были сбои на длинных файлах. Так и не разобрался.

Share this post


Link to post
Share on other sites
Я бился с такой проблемой, не решил. Сделал так. Отказался от передачи ТХ в прерывании, то есть перешёл на поллинг. В терминальной программе поставил задержку после передачи новой строки на 1 миллисекунду. Увеличение буфера до 8кБ помогает, но не всегда. Причём это в Виндоуз и в Линуксе. А файл 1кБ отлично передавался и с буфером 256 байт.

Вы правы. В случае поллинга все работает исправно.

 

USART2->TDR = ch;
if(UART_WaitOnFlagUntilTimeout(&huart2, UART_FLAG_TC, RESET, 100) != HAL_OK)
    {

    }

 

 

 

Но поллинг накладывает временные ограничения на выполнение. Я пишу код MBED TLS и в этом случае не проходит даже handshake. Либо в моем софте еще один глюк...

 

Видимо без буфера не обойтись.

 

Я делал USB-UART на STM32F103 и на CC2511. RX замкнут на TX. На CC2511 нормально работало до 1 МГц, на STM32F103 после 9600 были сбои на длинных файлах. Так и не разобрался.

У меня были сбои на 460800 пока я не перешел на кварц. Хотя на 9600 смещение частоты вряд ли скажется...

 

А вы уверены, что теряет не приемная сторона?

Уверен что приемная сторона нипричем. Сделал поллинг - вывод сработал исправно.

Share this post


Link to post
Share on other sites
на STM32F103 после 9600 были сбои на длинных файлах. Так и не разобрался.

Я при помощи STM32F103C8 гнал поток 2.25 Мбод (RX замкнут с TX). Ни один байт не потерялся за значительное время.

 

Share this post


Link to post
Share on other sites
Я при помощи STM32F103C8 гнал поток 2.25 Мбод (RX замкнут с TX). Ни один байт не потерялся за значительное время.

Конечно не будет теряться, если код написан правильно.

Я на 926000 тоже нормально гонял на другое устройство.

Share this post


Link to post
Share on other sites
Конечно не будет теряться, если код написан правильно.

Я писал так

void USART2_IRQHandler(void)
{
    if(USART2->ISR & (1 << USART_ISR_TXE))
    {
        if(con.tx_t != con.tx_b) USART2->TDR = bl_export.bl_sp_tx_pop(&con);
        else USART2->CR1 &= ~(1 << USART_CR1_TXEIE);
    }
    if(USART2->ISR & (1 << USART_ISR_ORE))
    {
        USART2->ICR = (1 << USART_ISR_ORE);
    }
    if(USART2->ISR & (1 << USART_ISR_RXNE))
    {
        con.rx_tm = 0;
        bl_export.bl_sp_rx_push(&con, USART2->RDR);
    }
}

push и pop помещают и извлекают байты из кольцевых буферов tx и rx.

Share this post


Link to post
Share on other sites
Самой задержки (30мс) по идее должно хватать.

Задержки - типичный ардуино-подход, дилетантский.

Для вывода в поток с помощью printf() никаких задержек вообще не нужно. И размер буфера может быть абсолютно любым, хоть 1 байт. Буфер только даёт возможность более эффективно использовать быстродействие CPU - в среднем уменьшает загрузку CPU.

Если у Вас что-то не работает из-за изменения размеров буфера или требует каких-то задержек - правьте консерваторию.

Share this post


Link to post
Share on other sites

Я делал совсем по простому

2 буфера линейных

ПДП

таймер

всё пузЫрилось на ура.

Share this post


Link to post
Share on other sites

Проверяйте в функции отправки сообщения статус возврата! Если ошибка — либо пропускайте (сообщение некритичное), либо ждите подтверждения отправки.

Соответственно, функция, заполняющая буфер просто будет возвращать OK если место есть и ошибку, если буфер полностью заполнен и надо подождать, пока "уедет".

 

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

Если буфер кольцевой, придется в обработчике прерывания TXE выполнять основную работу.

 

Но в любом случае ни в коем случае не надо лепить паузы! Это же не абдурина!!! Если буфер заполняется слишком быстро, либо повышайте скорость передачи, либо увеличивайте буфер.

Share this post


Link to post
Share on other sites

Очень может быть, что сбои (потери) байтов из-за плохой трассировки печатной платы. Изменение буферов, скоростей, поллинга-прерываний изменяют наводки от фронтов Rx-Tx и уменьшение скорости тогда единственный выход.

Share this post


Link to post
Share on other sites
Задержки - типичный ардуино-подход, дилетантский.

Для вывода в поток с помощью printf() никаких задержек вообще не нужно. И размер буфера может быть абсолютно любым, хоть 1 байт. Буфер только даёт возможность более эффективно использовать быстродействие CPU - в среднем уменьшает загрузку CPU.

Если у Вас что-то не работает из-за изменения размеров буфера или требует каких-то задержек - правьте консерваторию.

Интересная точка зрения. Ну допустим буфер 128 байт. Кольцевой. За пару милисекунд проц настрочил 5кб текста и выплюнул все это в кольцевой буфер. А с одним байтом будет еще прикольнее. Наверно только первый байт от 5кб и попадет в терминал. Мож и не первый...

Share this post


Link to post
Share on other sites
За пару милисекунд проц настрочил 5кб текста и выплюнул все это в кольцевой буфер.

Сам он это не выплюнет, он сделает так, как ему разработчик велел. И если разработчик не проверяет коды возврата из процедур, это его личные проблемы!

Если хочется использовать printf (хотя эта штука ну слишком уж жирная!), то она, если что, тоже возвращает количество принятых символов. И вместо фиксированного времени ожидания можно лишь подождать, пока буфер не освободится и закинуть в него критическое сообщение. Некритическое же вообще можно опустить (но для этого придется дописать простой макрос, возвращающий количество свободных символов в буфере).

Share this post


Link to post
Share on other sites
Сам он это не выплюнет, он сделает так, как ему разработчик велел. И если разработчик не проверяет коды возврата из процедур, это его личные проблемы!

Если хочется использовать printf (хотя эта штука ну слишком уж жирная!), то она, если что, тоже возвращает количество принятых символов. И вместо фиксированного времени ожидания можно лишь подождать, пока буфер не освободится и закинуть в него критическое сообщение. Некритическое же вообще можно опустить (но для этого придется дописать простой макрос, возвращающий количество свободных символов в буфере).

printf использует библиотека MBEDTLS, нужен форматирванный вывод.

 

Суть во всем это одна -

 

можно лишь подождать, пока буфер не освободится

 

Я так и делал, но другим способом - наблюдая за головой и хвостом.

 

В данном случае это ПОКА неприемлимо. Сервер выкидывает HandShake, следом сертификаты. Так вот. С выводом debug информации не получается успевать декодировать первое сообщение - его затирает втрое.

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this