Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Оператор "printf" и функция "putchar" для работы с символьным LCD
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Буратино
не понимаю, что за беда с выводом.
Прописал "putchar" в виде:
Код
void putchar( char c ) {
    if ( !c )
    return( 0 );
    Transmit_Byte( c );  /*функция отправки сообщения в последовательный порт*/
}


LCD оснащен контроллером, который принимает данные по последовательному порту и выводит на экран дисплея 16*02
Не могу разобраться почему:

printf("%a", 30.0) выводит "???"
printf("%.2A", 30.0) выводит "???"
printf("%c", 'a') ничего выводит
printf("<%3c|%-3c>", 'a', 'b') выводит "< крокозябра >"
и т.д. совсем другими словами не то что я ожидаю.

printf("Hello World!") выводит "Hello World!", но если использовать длинную строку или несколько операторов "printf" со строками, то выводятся куски строк в хаотичном порядке.
размер Data Stack в компиляторе IAR установил в 0x300.

Мне нужно выводить на экран простые конструкции состоящие из текста и числовых данных, что делать? smile.gif
barabek
Цитата(Буратино @ Mar 19 2010, 21:47) *
printf("%a", 30.0) выводит "???"
printf("%.2A", 30.0) выводит "?

А если попробовать : printf("%f", 30.0); или
printf("%.2f", 30.0);

Кстати, касательно терминологии printf() тоже функция , а не оператор.
Буратино
printf("%f", 30.0); выводит "0.000000"
printf("%.2f", 30.0); выводит "0.00"
zltigo
Цитата(Буратино @ Mar 19 2010, 14:47) *
Прописал "putchar" в виде:
void putchar( char c )

Для начала эта функция НЕ putchar(), ибо ну зря продали букварь Карабасу smile.gif - буквари надо читать sad.gif. Ну а дальше по нарастающей....
Буратино
А почему эта функция не putchar()?

---
Буратино продал букварь НЕ Карабасу.
zltigo
Цитата(Буратино @ Mar 19 2010, 15:43) *
А почему эта функция не putchar()?

А вот об этом написано в Букваре:
Цитата
#include <stdio.h>
int putchar( int c );

Цитата
Буратино продал букварь НЕ Карабасу.

Главное, что продали НЕ читая sad.gif
Ладно, если Букварь не читали, то может, хоть форум почитаете? Вопрос дежурный sad.gif
Буратино
Цитата(zltigo @ Mar 19 2010, 15:46) *
А вот об этом написано в Букваре:


Главное, что продали НЕ читая sad.gif
Ладно, если Букварь не читали, то может, хоть форум почитаете? Вопрос дежурный sad.gif


Да. Вы правы. Сейчас посмотрю повнимательнее.
Буратино
Пересмотрел форум, и понял ,что функция printf() занимает много места в ОЗУ. Я установил в опциях ИАР минимальную конфигурацию для нее (print formatter - Small), менял размер области памяти под стек, но ничего не получается. Вместо того, что я ожидаю получаю абракадабруsad.gif
Идея форматированого вывода на LCD дисплей идет ко дну, вместе с моей уверенностью в своих собственных силах.
Мне не хочется вникать в тонкости работы компилятора, я хочу сделать устройство для полива цветов на окне. Пожалуйста подскажите, что не так, куда копать?

Вот реализация "putchar"
Код
int putchar( int c ) {
    Transmit_Byte( c );  /*выводит байт в UART*/
   return c;
}



да и вот еще что: дело в том, что компилятор ругается на printf() и вот что пишет:

Warning[w6]: Type conflict for external/entry "printf", in module main against external/entry in module ?printf; prototyped function
vs K&R function
/* In module main: */
/* K&R Function, args 0, attr 0 */
int (__version_3 printf)();
/* In module ?printf: */
/* Function, args 2, attr 0 */
int (__version_3 printf)(char const *,
...);
zltigo
Цитата(Буратино @ Mar 19 2010, 18:36) *
Я установил в опциях ИАР минимальную конфигурацию для нее (print formatter - Small)

Ага, и решили попечатать float smile.gif
Цитата
Мне не хочется вникать в...

Ну тогда займитесь чем-нибудь другим по жизни. Тем более, что тонкостей-то и нет sad.gif. Налицо какая-то безалаберность, например, ну никак не мог, если его не задушить до полного безразличия, не ругаться компилятор на void putchar( char ). Что и куда Вы сейчас подсунули - тоже неведомо никому, как и того, что и как делает дивная функция Transmit_Byte(), как настроен, и как должен быть настроен UART....
Если Вам "не хочется вникать в", то от других тогда тоже не ожидайте особого желания вникать, и уж тем более вытягивать из Вас информацию.
one_eight_seven
А в контроллер уже зашиты ASCII коды?

Может, я не прав, но всё, что мне встричалось, программировалось по SPI.
Обязательно объявлялись константы ASCII кодов, в массив заносился текст с помощью sprintf() или memcpy(), и уже этот массив отправлялся по SPI
Буратино
zltigo ,почему Вы такой злой? Ну нет желания помочь - ну и не нужно! В конце-концов весь мир не вокруг одного Вашего мизинчика вращается.
Я достаточно обеспеченный человек, чтобы позволить себе заниматься электроникой в качестве хобби, и достаточно самостоятельный человек, чтобы разобраться, чем мне в жизни увлекаться.

Уарт настроен на baud rate: 115200, char size: 8 bit, parity: Disabled
Вот код функций:
Код
#define UART_TX_BUFFER_SIZE0 64
#define UART_TX_BUFFER_MASK0 ( UART_TX_BUFFER_SIZE0 - 1 )

static unsigned char UART_TxBuf0[UART_TX_BUFFER_SIZE0];
static volatile unsigned char UART_TxHead0;
static volatile unsigned char UART_TxTail0;

void Transmit_Byte( unsigned char data0 ) {
    unsigned char tmphead0;

    tmphead0 = ( UART_TxHead0 + 1 ) & UART_TX_BUFFER_MASK0;
    while ( tmphead0 == UART_TxTail0 );
    UART_TxBuf0[tmphead0] = data0;
    UART_TxHead0 = tmphead0;
    UCSR0B |= (1<<UDRIE0);
}


#pragma vector=USART_UDRE_vect
__interrupt void USART0_TX_interrupt( void ) {
    unsigned char tmptail0;

    if ( UART_TxHead0 != UART_TxTail0 ) {
        tmptail0 = ( UART_TxTail0 + 1 ) & UART_TX_BUFFER_MASK0;
    UART_TxTail0 = tmptail0;
    UDR0=UART_TxBuf0[tmptail0];
    }
    else {
    UCSR0B &= ~(1<<UDRIE0);
    }
}


Повторяю, функция printf() выводит короткий текст типа "Hello World!", что само по себе свидетельство правильной работы UART, ведь данные были переданы и приняты процессорами.
zltigo
Цитата(Буратино @ Mar 19 2010, 18:59) *
zltigo ,почему Вы такой злой?

Что-бы больше разбирались сами, и меньше загадывали шарады.
Буратино
Все, разобрался (хотя и остались некоторые непонятки):
Не включил в проект #include <stdio.h> rolleyes.gif
---
zltigo, прошу прощения, что наорал на Вас, злой очень был.
Все никак не решусь на новый виток в образовании, вот если бы ногу или руку сломать (на крайний случай ветрянка сойдет), чтоб дома в кровати валяться и книги умные читать..
zltigo
Цитата(Буратино @ Mar 19 2010, 21:20) *
Все, разобрался (хотя и остались некоторые непонятки):

Некошерно смотрится дергание бита разрешения прерывания sad.gif. Ну и volatile для UART_TxHead0 лишнее - только warning вызывает. tmptail0 лишняя сущность... Про имена помолчу.... Прототипы своих функций тоже игнорируем sad.gif
Буратино
Цитата(zltigo @ Mar 19 2010, 21:37) *
Некошерно смотрится дергание бита разрешения прерывания sad.gif. Ну и volatile для UART_TxHead0 лишнее - только warning вызывает. tmptail0 лишняя сущность... Про имена помолчу....


Я тут на сайте нашел документ "Стандарт кодирования на СИ" Вы его еще чихвостили в той ветке не слабо, но мне понравилось.
Планирую переписывать все что буду писать в соответствии с этим стандартом, включая форматирование и соглашение об именах.
Старый код пока не буду трогать.
Что касается разрешения прерывания, то пересмотрю источник (не помню какая апликуха атмела) возможно я уже что-то напакостил в тексте. Справедливости ради хочу заметить что этот код уже гигибайта три прокачалsmile.gif
volatile ,как и все остальные косяки в работе с ИАР буду стараться исправить, многого конечно не получится, но с варнингами точно "поквитаюсь":)

Все, спасибо всем!
zltigo
Цитата(Буратино @ Mar 19 2010, 21:45) *
Что касается разрешения прерывания, то пересмотрю источник (не помню какая апликуха атмела) возможно я уже что-то на пакостил в тексте. Справедливости ради хочу заметить что этот код уже гигибайта три прокачалsmile.gif

Я сказал не кошерно, а не не халяльно smile.gif. Другими словами просто не красиво(полезно) sad.gif
Сергей Борщ
Цитата(zltigo @ Mar 19 2010, 20:37) *
Некошерно смотрится дергание бита разрешения прерывания sad.gif
Нормально смотрится. Нужны прервания - разрешаем, не нужны - запрещаем. Флаг-то аппаратно торчит всегда, когда свободен буфер передачи. Это вам не '550.
Цитата(zltigo @ Mar 19 2010, 20:37) *
Ну и volatile для UART_TxHead0 лишнее - только warning вызывает.
Формально - не лишняя. Переменная меняется в другой относительно прерывания нити.
Цитата(zltigo @ Mar 19 2010, 20:37) *
tmptail0 лишняя сущность...
Вовсе нет. Без нее получаем предупреждение компилятора на неопределенный порядок чтения двух volatile в сравнении.
zltigo
Цитата(Сергей Борщ @ Mar 20 2010, 00:20) *
Нормально смотрится. Нужны прервания - разрешаем, не нужны - запрещаем. Флаг-то аппаратно торчит всегда, когда свободен буфер передачи.

Свободен сдвиговый регистр - записываем прямо в UART, не свободен в буфер и по прерыванию уже вычитываем, либо по анализу указателей, либо флаг можно завести.
Цитата
Формально - не лишняя. Переменная меняется в другой относительно прерывания нити.

Абсолютно лишняя, ибо пока выполняется обработчик она измениться за его пределами не может. для Tail volatile нужен, Head - нет.
Цитата
Вовсе нет. Без нее получаем предупреждение компилятора на неопределенный порядок чтения двух volatile в сравнении.

Два volatile, повторяю, ну совсем не нужны, а warning, гарантировано при if ( UART_TxHead0 != UART_TxTail0 ) вылезет хоть есть tmptail0, хоть нет.
Сергей Борщ
Цитата(zltigo @ Mar 19 2010, 23:53) *
Свободен сдвиговый регистр - записываем прямо в UART, не свободен в буфер и по прерыванию уже вычитываем, либо по анализу указателей, либо флаг можно завести.
И все эти телодвижения только ради того, чтобы не запрещать/разрешать прерывание? Нафиг. Хорошо, пусть даже идем вашим путем. В прерывании вычитываем из буфера. Буфер пуст - все данные переданы. Что делать дальше? Флаг прерывания сбросить невозможно - для этого нужно что-то начать передавать. Если выйдем из прерывания не запретив его - тут же попадем в это же прерывание снова. Что мы можем сделать кроме как запретить это прерывание? Еще раз повторяю - это не '550, тут не получается начать генерить прерывания положив первый байт в передатчик. И такая реализация дает более компактный код, без всяких лишних проверок "свободен/не свободен сдвиговый регистр".
Цитата(zltigo @ Mar 19 2010, 23:53) *
Абсолютно лишняя, ибо пока выполняется обработчик она измениться за его пределами не может.
Ну хорошо, тут соглашусь.
Цитата(zltigo @ Mar 19 2010, 23:53) *
Два volatile, повторяю, ну совсем не нужны, а warning, гарантировано при if ( UART_TxHead0 != UART_TxTail0 ) вылезет хоть есть tmptail0, хоть нет.
Тюю... Невнимательно посмотрел исходник. Рассмотрел только TransmitByte, увидел, что она очень похожа на один из вариантов используемого мной кода и экстраполировал этот вывод и на обработчик прерывания. Виноват. У автора ветки действительно лишний.
Код
void uart_t::send(uint8_t byte)
{
    uint8_t Tmp = (TxHead + 1) & (UART_TX_BUFF_SIZE - 1);
    while(Tmp == TxTail)
       ;       // Buffer full
    TxBuffer[ TxHead ] = byte;
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
}

inline void  uart_t::UDRE_handler(void)
{
    uint8_t Tmp = TxTail;
    UDR = TxBuffer[ Tmp ];
    TxTail = Tmp = (Tmp + 1) & (UART_TX_BUFF_SIZE - 1);
    if(Tmp == TxHead)
        UCSRB &= ~(1<<UDRIE);
}
zltigo
Цитата(Сергей Борщ @ Mar 20 2010, 12:54) *
И все эти телодвижения....

Это не все, это на вскидку три разных варианта. Каждый из 3x не сложнее суммарного дергания периферии с целью разрешения/запрещения прерывания в функции передачи и обработчике.
Цитата
И такая реализация дает более компактный код, без всяких лишних проверок "свободен/не свободен сдвиговый регистр".

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

Цитата
У автора ветки действительно лишний.
Код
inline void  uart_t::UDRE_handler(void)
{
    uint8_t Tmp = TxTail;
    UDR = TxBuffer[ Tmp ];
    TxTail = Tmp = (Tmp + 1) & (UART_TX_BUFF_SIZE - 1);
    if(Tmp == TxHead)
        UCSRB &= ~(1<<UDRIE);
}

Ну вообще-то и у тебя лучше обойтись smile.gif без.

Код
union {
volatile uint8_t TxTail;
uint8_t TxTail_int;
};

inline void  uart_t::UDRE_handler(void)
{
    UDR = TxBuffer[ TxTail_int ];
    TxTail_int = (TxTail_int + 1) & (UART_TX_BUFF_SIZE - 1);
    if(TxTail_int == TxHead)
        UCSRB &= ~(1<<UDRIE);
}

Меньше указивок, меньше думать и больше свободы компилятору.
P.S.
Кстати, в таких случаях я предпочитаю НЕ накладывать маску на индексы при их наращивании - пусть крутятся по полному циклу, а маска накладывается при использовании индекса. В этом случае одними действием вычитания индексов можно узнать сколько байтов в буфере - бывает нужно при пакетной передаче.
Сергей Борщ
Цитата(zltigo @ Mar 20 2010, 12:34) *
Очень очень зависит от конкретики и железа. Компактность кода, тоже не всегда синоним скорости - для тех контроллеров, у которых периферия находится на медленной шине безусловное действие прочитать-изменить-записать флаг в при каждом вызове передачи байта катострофически проигрывает по времени, например, анализу софтового флага.
В данном случае код был под конкретный контроллер - AVR. В котором, во-первых, периферия висит на быстрой шине (других просто нет), а во-вторых - без сброса разрешения прерывания просто не получится.
Цитата(zltigo @ Mar 20 2010, 12:34) *
Кстати, в таких случаях я предпочитаю НЕ накладывать маску на индексы при их наращивании - пусть крутятся по полному циклу, а маска накладывается при использовании индекса. В этом случае одними действием вычитания индексов можно узнать сколько байтов в буфере - бывает нужно при пакетной передаче.
Так я же написал - это один из вариатнтов. Опять же, речь идет об AVR, у которого FIFO нет (двойная буферизация не считается), и толку от знания "сколько конкретно байт в буфере" нет никакого.
Код
void uart_t::send(uint8_t byte)
{
    uint8_t Tmp = TxHead;
    while((uint8_t)(Tmp - TxTail) >=  (uint8_t) UART_TX_BUFF_SIZE)
    ; // Buffer full
    TxBuffer[ Tmp++ & (UART_TX_BUFF_SIZE - 1) ] = byte;
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
}

inline void uart_t::UDRE_Handler()
{
    uint8_t Tmp = TxTail;
    UDR = TxBuffer[ Tmp++ & (UART_TX_BUFF_SIZE - 1) ];
    TxTail = Tmp;
    if(Tmp == TxHead)
        UCSRB &= ~(1<<UDRIE);
}
Но результирующий код получается длинее на 6 байт. И чего я на него перешел? Надо вернуться к первому - память кода может пригодиться.

Цитата(zltigo @ Mar 20 2010, 12:34) *
Меньше указивок, меньше думать и больше свободы компилятору.
Фу. union в таком применении - грязный хак. Как раз таки и не дающий никакой дополнительной свободы компилятору, но чреватый какими-нибудь граблями если компилятор окажется черезчур умным. А думать надо всегда smile.gif

Впрочем, мы уклонились от темы ветки.
zltigo
Цитата(Сергей Борщ @ Mar 20 2010, 14:08) *
Опять же, речь идет об AVR, у которого FIFO нет (двойная буферизация не считается), и толку от знания "сколько конкретно байт в буфере" нет никакого.

А причем тут наличие или отсутствие FIFO и AVR??? Это уже конкретные проблемы верхнего уровня - надо ему знать сколько есть места в софтовом буфере передачи, или нет. При необходимости поместить в буфер за один вызов функции не один байт, а фрейм - весьма не лишняя возможность.
Цитата
Но результирующий код получается длинее на 6 байт.

Это уже конкретные заморочки компилятора sad.gif, кстати, возможно, и из-за неоптимальных volatile. То,что я описал, это просто тот вариант работы с индексами который я реализовывал "для начала". В случае каких-то дополнительных/особых требований, условий - естественно никаких догм.
sonycman
Кстати, можно вопрос по поводу UARTов микроконтроллеров?

Мне пока доводилось поработать с двумя реализациями их передатчиков:
1. как только сдвиговый регистр передатчика становится пуст - устанавливается соответствующее прерывание.
Сбросить его невозможно до тех пор, пока передатчик пуст.

Тут получается весьма простой обработчик - надо передавать данные - разрешаем прерывания и в обработчике происходит загрузка.
Данные закончились - запрещаем прерывание.

2. в LPC1768 другая система (так называемый 550-ый?) - триггер прерывания "передатчик пуст" устанавливается только после завершения передачи данных (а не присутствует постоянно).
То есть изначально его нет (хотя передачтик пуст!), и после вызова обработчика он сбрасывается.

Здесь уже погеморройнее - загрузку FIFO приходится делать как в обработчике прерывания, так и снаружи (для инициирования передачи).
Причём дополнительно есть какой-то механизм откладывания прерывания:

The UARTn THRE interrupt (UnIIR[3:1] = 001) is a third level interrupt and is activated
when the UARTn THR FIFO is empty provided certain initialization conditions have been
met. These initialization conditions are intended to give the UARTn THR FIFO a chance to
fill up with data to eliminate many THRE interrupts from occurring at system start-up. The
initialization conditions implement a one character delay minus the stop bit whenever
THRE = 1 and there have not been at least two characters in the UnTHR at one time
since the last THRE = 1 event. This delay is provided to give the CPU time to write data to
UnTHR without a THRE interrupt to decode and service. A THRE interrupt is set
immediately if the UARTn THR FIFO has held two or more characters at one time and
currently, the UnTHR is empty. The THRE interrupt is reset when a UnTHR write occurs or
a read of the UnIIR occurs and the THRE is the highest interrupt (UnIIR[3:1] = 001).


Здесь я до конца не понял конкретику, лишь то, что флаг THRE может устанавливаться не сразу по опустошению передатчика, а спустя какое-то время.

То есть проанализировав в регистре статуса бит Transmitter Empty (TEMT), в случае, если он установлен, нельзя однозначно понять, был ли уже вызов прерывания, или только ещё будет - из-за one character delay?

В общем, некоторый гиморрой получается sad.gif

Может, кто нибудь более популярно разъяснит механику работы LPCшного UART?
zltigo
Цитата(sonycman @ Mar 20 2010, 15:57) *
В общем, некоторый гиморрой получается sad.gif

Пальцем покажите:
Код
//----------------------------------------------------------------------------
void boutchar( char ch )
{
    if(    status_word & STW_THR_BUSY )
    {    tbuf.buf[tbuf.tail & TBUF_SIZE_MSK] = ch;
              tbuf.tail++;
    }
    else
    {
        status_word |= STW_THR_BUSY;
        U0THR = ch;
    }
}
//----------------------------------------------------------------------------
__irq __arm  void uart_isr(void)
{
bint ch;

    while( !((ch = (U0IIR&IIR_MASK)) & IIR_IP) )        // Check Pending Bit
    {
        // What caused the interrupt?
        switch( ch )
        {

        case IIR_THRE:    // The THRE+TEMPT+FIFO is empty. If there is another
                        //    characters in the TX buffer, load its now to FIFO->THRE.
            if( tbuf.tail != tbuf.head )
            {   for( int ii=0; ( tbuf.tail != tbuf.head )&&( ii < TR_FIFO_SIZE ); ii++ )
                    U0THR = tbuf.buf[(tbuf.head++) & TBUF_SIZE_MSK];
            }
            else
                status_word &= (~STW_THR_BUSY);
            break;

.......


Цитата
Тут получается весьма простой обработчик - надо передавать данные - разрешаем прерывания и в обработчике происходит загрузка.
Данные закончились - запрещаем прерывание.

Если выкинуть работу с FIFO отсутствующим у AVR, то получаем вот такой "сложный" обработчик прерывания для LPC:
Код
if( tbuf.tail != tbuf.head )
                    U0THR = tbuf.buf[(tbuf.head++) & TBUF_SIZE_MSK];
            else
                status_word &= (~STW_THR_BUSY);

И такой "простой" ( © Сергея ) для AVR:
Код
    
    uint8_t Tmp = TxTail;
    UDR = TxBuffer[ Tmp++ & (UART_TX_BUFF_SIZE - 1) ];
    TxTail = Tmp;
    if(Tmp == TxHead)
        UCSRB &= ~(1<<UDRIE);
sonycman
Цитата(zltigo @ Mar 20 2010, 17:10) *
Пальцем покажите:

Спасибо за пример.
У Вас используется софтовый флаг (STW_THR_BUSY) для исключения коллизии при загрузке данных.
На AVR, благодаря "упрощённому" железу он совсем не нужен.

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

Цитата(zltigo @ Mar 20 2010, 17:10) *
void boutchar( char ch )
{
if( status_word & STW_THR_BUSY )
{ tbuf.buf[tbuf.tail & TBUF_SIZE_MSK] = ch;
tbuf.tail++;
}
else
{
status_word |= STW_THR_BUSY;
U0THR = ch;
}
}

Кстати, у Вас в этой функции потенциальная проблема с опросом флага status_word.
Для корректной работы необходимо запрещать прерывание THRE перед обращением к status_word.
Жаль, не хотелось бы этого...
zltigo
Цитата(sonycman @ Mar 20 2010, 17:00) *
У Вас используется софтовый флаг (STW_THR_BUSY) для исключения коллизии при загрузке данных.

Прежде всего из-за много более медленного доступа к железным флагам.
Цитата
Кстати...

Да, в реальности я байтовое занесение даже в буфер печати не использую - просто болталась неиспользуемая функция sad.gif.
Используется свой xprintf() в котором в том числе оптимизирован контроль за флагом, который контролируется только в конце заполнения буфера.
Сергей Борщ
Цитата(zltigo @ Mar 20 2010, 13:14) *
При необходимости поместить в буфер за один вызов функции не один байт, а фрейм - весьма не лишняя возможность.
А если весь фрейм не лезет, что будем делать? Ждать, пока влезет или все же запихаем сколько сможем, чтобы избежать пауз в потоке? Будет кошерный вариант для AVR без дерганья флага?
Цитата(zltigo @ Mar 20 2010, 13:14) *
Это уже конкретные заморочки компилятора sad.gif
С чего бы это? В одном случае сравниваются две переменные, во втором - их разность сравнивается с константой. Очевидно, что второй вариант требует больше команд.
zltigo
Цитата(Сергей Борщ @ Mar 20 2010, 18:08) *
А если весь фрейм не лезет, что будем делать?

Лично у меня в большинстве случаев буфера разумно-большие и отсутствие места для фрейма это аварийное состояние. Предпринимаются разборки, торможение уровней и встечной стороны... Если это просто отладочная печать захлебывается, то тем более, ждать нечего - как мы начали идти до жизни такой уже видно в начале буфера, остальное в мусорник.
Цитата
..чтобы избежать пауз в потоке?

Понятие фрейма само по себе не завязано на непрерывность потока. Иногда даже наоборот
Цитата
С чего бы это? В одном случае сравнисаются две переменные, во втором - их разность сравнивается с константой. Очевидно, что второй ввариант требует больше команд.

На ШЕСТЬ байт больше??? Кроме вышеуказанного сравнения остальные действия достаточно равнозначны, даже для AVR различие в 6 байт это проблемы компилятора sad.gif. Для ARM, полагаю, как минимум, что в лоб, что по лбу.


P.S.
Глянул для AVR IAR.
"Плохой" вариант с константой
Код
     82          void send1( BYTE ch )
   \                     send1:
     83          {
   \   00000000   ....               LDI     R30, LOW(TxTail)
   \   00000002   ....               LDI     R31, (TxTail) >> 8
   \   00000004   8121               LDD     R18, Z+1
     84              while( (BYTE)(TxHead - TxTail) >=  UART_TX_BUFF_SIZE)
   \                     ??send1_0:
   \   00000006   8130               LD      R19, Z
   \   00000008   2F12               MOV     R17, R18
   \   0000000A   1B13               SUB     R17, R19
   \   0000000C   3410               CPI     R17, 64
   \   0000000E   F7D8               BRCC    ??send1_0
     85; // Buffer full
     86          
     87              TxBuffer[ TxTail++ & (UART_TX_BUFF_SIZE - 1) ] = ch;
   \   00000010   8120               LD      R18, Z
   \   00000012   2F12               MOV     R17, R18
   \   00000014   9513               INC     R17
   \   00000016   8310               ST      Z, R17
   \   00000018   E0F0               LDI     R31, 0
   \   0000001A   732F               ANDI    R18, 0x3F
   \   0000001C   2FE2               MOV     R30, R18
   \   0000001E   ....               SUBI    R30, LOW((-(TxBuffer) & 0xFFFF))
   \   00000020   ....               SBCI    R31, (-(TxBuffer) & 0xFFFF) >> 8
   \   00000022   8300               ST      Z, R16
     88          }
   \   00000024   9508               RET



"Хороший" вариант:
Код
     91          void send2( BYTE ch )
   \                     send2:
     92          {
   \   00000000   2F3B               MOV     R19, R27
   \   00000002   2F4A               MOV     R20, R26
     93          BYTE Tmp = ( TxTail + 1 )&(UART_TX_BUFF_SIZE - 1);    
   \   00000004   ....               LDI     R30, LOW(TxTail)
   \   00000006   ....               LDI     R31, (TxTail) >> 8
   \   00000008   8110               LD      R17, Z
   \   0000000A   9513               INC     R17
   \   0000000C   731F               ANDI    R17, 0x3F
   \   0000000E   8121               LDD     R18, Z+1
     94          
     95              while( TxHead == Tmp )
   \                     ??send2_0:
   \   00000010   1721               CP      R18, R17
   \   00000012   F3F1               BREQ    ??send2_0
     96; // Buffer full
     97          
     98              TxBuffer[ TxTail ] = ch;
   \   00000014   81A0               LD      R26, Z
   \   00000016   E0B0               LDI     R27, 0
   \   00000018   ....               SUBI    R26, LOW((-(TxBuffer) & 0xFFFF))
   \   0000001A   ....               SBCI    R27, (-(TxBuffer) & 0xFFFF) >> 8
   \   0000001C   930C               ST      X, R16
     99              TxTail = Tmp;
   \   0000001E   8310               ST      Z, R17
    100          }
   \   00000020   2FA4               MOV     R26, R20
   \   00000022   2FB3               MOV     R27, R19
   \   00000024   9508               RET


Где 6 байт проигрыша?

P.P.S.
Это была оптимизация по скорости. При оптимизации по размеру "Плохой" выиграл два байта у "Хорошего"
Код
     82          void send1( BYTE ch )
   \                     send1:
     83          {
   \   00000000   ....               LDI     R30, LOW(TxTail)
   \   00000002   ....               LDI     R31, (TxTail) >> 8
     84              while( (BYTE)(TxHead - TxTail) >=  UART_TX_BUFF_SIZE)
   \                     ??send1_0:
   \   00000004   8111               LDD     R17, Z+1
   \   00000006   8120               LD      R18, Z
   \   00000008   1B12               SUB     R17, R18
   \   0000000A   3410               CPI     R17, 64
   \   0000000C   F7D8               BRCC    ??send1_0
     85        ; // Buffer full
     86          
     87              TxBuffer[ TxTail++ & (UART_TX_BUFF_SIZE - 1) ] = ch;
   \   0000000E   8120               LD      R18, Z
   \   00000010   2F12               MOV     R17, R18
   \   00000012   9513               INC     R17
   \   00000014   8310               ST      Z, R17
   \   00000016   E0F0               LDI     R31, 0
   \   00000018   732F               ANDI    R18, 0x3F
   \   0000001A   2FE2               MOV     R30, R18
   \   0000001C   ....               SUBI    R30, LOW((-(TxBuffer) & 0xFFFF))
   \   0000001E   ....               SBCI    R31, (-(TxBuffer) & 0xFFFF) >> 8
   \   00000020   8300               ST      Z, R16
     88          }
   \   00000022   9508               RET


Код
     91          void send2( BYTE ch )
   \                     send2:
     92          {
   \   00000000   2F3B               MOV     R19, R27
   \   00000002   2F4A               MOV     R20, R26
     93          BYTE Tmp = ( TxTail + 1 )&(UART_TX_BUFF_SIZE - 1);    
   \   00000004   ....               LDI     R30, LOW(TxTail)
   \   00000006   ....               LDI     R31, (TxTail) >> 8
   \   00000008   8110               LD      R17, Z
   \   0000000A   9513               INC     R17
   \   0000000C   731F               ANDI    R17, 0x3F
     94          
     95              while( TxHead == Tmp )
   \                     ??send2_0:
   \   0000000E   8121               LDD     R18, Z+1
   \   00000010   1721               CP      R18, R17
   \   00000012   F3E9               BREQ    ??send2_0
     96        ; // Buffer full
     97          
     98              TxBuffer[ TxTail ] = ch;
   \   00000014   81A0               LD      R26, Z
   \   00000016   E0B0               LDI     R27, 0
   \   00000018   ....               SUBI    R26, LOW((-(TxBuffer) & 0xFFFF))
   \   0000001A   ....               SBCI    R27, (-(TxBuffer) & 0xFFFF) >> 8
   \   0000001C   930C               ST      X, R16
     99              TxTail = Tmp;
   \   0000001E   8310               ST      Z, R17
    100          }
   \   00000020   2FA4               MOV     R26, R20
   \   00000022   2FB3               MOV     R27, R19
   \   00000024   9508               RET

В общем, как и ожидалось, веских причин генерировать сильно отличающийся по размеру код у компиляторов нет.
Буратино
blink.gif
zltigo
Цитата(Буратино @ Mar 20 2010, 19:49) *
blink.gif

А что? Просто обмен опытом и жизненными наблюдениями.
ReAl
Цитата(Сергей Борщ @ Mar 20 2010, 13:11) *
Фу. union в таком применении - грязный хак.
продолжая уклоняться - если уж всё равно плюсы, то можно попробовать http://electronix.ru/forum/index.php?showt...mp;#entry535341
zltigo
Цитата(Сергей Борщ @ Mar 20 2010, 14:11) *
Фу. union в таком применении - грязный хак.

Сразу не заметил sad.gif этой реплики - никакого хака, это его прямое предназначение.
Цитата
Как раз таки и не дающий никакой дополнительной свободы компилятору

напротив, разрешающий компилятору в безопасных местах, в данном случае в обработчике прерывания, не заниматься подстилкой соломки и пользоваться всеми возможностями оптимизации.
Цитата
..чреватый какими-нибудь граблями если компилятор окажется черезчур умным.

Нималейших прав у него нету использовать какую-либо информацию о других членах union.
Сергей Борщ
Цитата(zltigo @ Mar 20 2010, 01:08) *
Абсолютно лишняя, ибо пока выполняется обработчик она измениться за его пределами не может. для Tail volatile нужен, Head - нет.
Вспомнил, зачем Head должен быть volatile. Долго же в мозгу зрело, не давало покоя - ну не совсем же сдуру я его делал volatile! Если компилятор достаточно умный и решит встроить uart_t::send (например, если он вызывается только из одной точки программы), то без volatile он имеет полное право закешировать в основном коде Head в регистре и обработчик вместо одного байта будет каждый раз выстреливать весь буфер от начала до конца.

Кстати, и буфер тоже должен быть volatile. В противном случае компилятор имеет полное право перенести запись в буфер после изменения Head и разрешения прерывания.
zltigo
Цитата(Сергей Борщ @ Apr 21 2010, 00:00) *
то без volatile он имеет полное право закешировать в основном коде Head в регистре...

Это по любому глобальная переменная. Даже при встраивании функции область работы с этой переменной и область cуществования локальной переменной Tmp четко определена телом бывшей функции и при выходе за ее пределы должна быть синхронизирована с памятью. Если этого не будет сделано, то компилятор не умный, а вовсе даже с ошибками.
Даже допустим, что компилятор плюет на все и всех, но даже в этом случае uart_t::send должна встроися в некий вечный цикл, причем ничего особо кроме вызова send() не делающий, дабы была куча неиспользуемых регистров и компилятор мог ими разбрасываться. Что-то за уши притянутое.
Цитата
Кстати, и буфер тоже должен быть volatile. В противном случае компилятор имеет полное право перенести запись в буфер после изменения Head и разрешения прерывания.

Ну это вообще понесло sad.gif
Вместо:
Код
  TxBuffer[ TxHead ] = byte;
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);

Сделать
Код
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
  TxBuffer[ TxHead ] = byte;

Может он тогда имеет право и вообще генерить любые побочные эффекты и точки следования ему не указ?
Сергей Борщ
Цитата(zltigo @ Apr 21 2010, 01:28) *
Это по любому глобальная переменная. Даже при встраивании функции область работы с этой переменной и область cуществования локальной переменной Tmp четко определена телом бывшей функции и при выходе за ее пределы должна быть синхронизирована с памятью. Если этого не будет сделано, то компилятор не умный, а вовсе даже с ошибками.
Переменная не volatile и компилятор имеет право делать с ней что угодно, если это не меняет observable behaviour. Глобальность и время жизни переменной тут совершенно не при чем. Стандарт не нарушен. Компилятор не виноват.
Цитата(zltigo @ Apr 21 2010, 01:28) *
Что-то за уши притянутое.
Ну и что? "Никто не знает, что может прийти в голову этим пчелам" (с). Имеются ввиду и программисты и компилятор. Потенциально бага есть. Предположенное мной поведение компилятора не противоречит стандарту. Значит отмахиваться от его возможности только потому, что "сегодняшний мой компилятор в моих программах так не делает" - это положить самому себе граблю. Не факт, что он не будет так делать никогда. И все ради чего? Ради экономии пары volatile и одной временной переменной? На сегодняшнем результате компиляции это никак не скажется, это только увеличит исходный текст. Но сделает его безопасным.
Цитата(zltigo @ Apr 21 2010, 01:28) *
Ну это вообще понесло sad.gif
Вместо:
Код
  TxBuffer[ TxHead ] = byte;
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);

Сделать
Код
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
  TxBuffer[ TxHead ] = byte;
Нет. Такого он не имеет права делать, ибо TxHead, как мы договорились, volatile. А вот такое - вполне:
Код
  uint8_t * ptr = &TxBuffer[ TxHead ];
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
    *ptr = byte;
И уже делает. Не в этом конкретно случае, но подобные оптимизации встречал.
Цитата(zltigo @ Apr 21 2010, 01:28) *
Может он тогда имеет право и вообще генерить любые побочные эффекты и точки следования ему не указ?
К точке следования должны закончиться побочные эффекты. TxBuffer не volatile, значит запись в нее не дает побочных эффектов. Компилятор имеет полное право момент записи отложить. При кроссфайловой оптимизации запись вообще может быть выкинута, ибо компилятор может увидеть, что чтения из этой переменной в этом потоке выполнения нет.

В чем я не прав?
ReAl
Цитата(zltigo @ Apr 21 2010, 01:28) *
Это по любому глобальная переменная. Даже при встраивании функции область работы с этой переменной и область cуществования локальной переменной Tmp четко определена телом бывшей функции и при выходе за ее пределы должна быть синхронизирована с памятью. Если этого не будет сделано, то компилятор не умный, а вовсе даже с ошибками.

Код
#include <avr/io.h>
uint8_t i;
inline void ppi() {
        uint8_t temp = i + 1;
        i = temp;
}

void foo(uint8_t n) {
        while(n--) {
                ppi();
                PORTB = 0;
        }
}

Код
foo:
    tst r24
    breq .L6
    lds r18,i
    mov r25,r24
.L5:
    out 56-32,__zero_reg__
    subi r25,lo8(-(-1))
    brne .L5
    add r18,r24
    sts i,r18
.L6:
    ret

Всегда считал, что тут всё правильно. Хотя тут нет ни намёка на работу с i "в пределах тела бывшей функции".
Аналогично при встраивании uart::send при отправке строки вполне может на первом байте разрешить прерывания (записать в UDR - не важно), но обновить head в глобальной переменной только в конце строки.

Цитата(zltigo @ Apr 21 2010, 01:28) *
Вместо:
Код
  TxBuffer[ TxHead ] = byte;
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);

Сделать
Код
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
  TxBuffer[ TxHead ] = byte;

Может он тогда имеет право и вообще генерить любые побочные эффекты и точки следования ему не указ?
Ну аж так нельзя, так как у массива индекс выходит другой. А со сгенерированным компилятором временным указателем или индексом, сохраняющем предыдущее значение - вполне.
Точки следования не к этому. Вон в примере выше от точек следования в ppi() вообще ничего не осталось. И инвариантный код из цикла оптимизатором выносится с полным правом это делать, хоть при этом точки следования и меняются местами - от _независимых_ операций.
Там в стандарте есть такое слово "observable behavior", такая перестановка
Код
  uint8_t * ptr = &TxBuffer[ TxHead ];
    TxHead = Tmp;
    UCSRB |= (1<<UDRIE);
    *ptr = byte;
для не-volatile TxBuffer "видимое поведение" не меняет. А если ещё и TxHead не-volatile, то можно и так:
Код
    UCSRB |= (1<<UDRIE);
    TxBuffer[ TxHead ] = byte;
    TxHead = Tmp;
и тут даже внтренняя переменная для индекса/адреса не нужна. И в этом смысле TxHead должен быть volatile как у Сергея, так и в 11-том сообщении автора темы.

Как-то очередной версией копилятора в scmRTOS
Код
class TCritSect
{
public:
    TCritSect () : StatusReg( SREG ) { cli(); }
    ~TCritSect() { SREG = StatusReg; }
private:
    TStatusReg StatusReg;
};

    INLINE inline dword GetTickCount() { TCritSect cs; return Kernel.SysTickCount; }
при не-volatile на тот момент SysTickCount начало компилироваться не как
Код
   dword tmp;
   cs.StatusReg = SREG;
   cli();
   tmp = Kernel.SysTickCount;
   SREG = cs.StatusReg;
   return tmp;
а как
Код
   cs.StatusReg = SREG;
   cli();
   SREG = cs.StatusReg;
   return Kernel.SysTickCount;

Тут http://forum.sources.ru/index.php?showtopic=201904 эти все перестановки кода обсуждались. Тему неплохо почистили от ругни, но суть оталась :-)
Уже нужно было где-то процитировать - тогда нашлась в последнем сообщении опечатка, исправлялсь, поэтому дата у него оторванная от остальных.
zltigo
Цитата(Сергей Борщ @ Apr 21 2010, 00:14) *
Вспомнил, зачем Head должен быть volatile.
Кстати, и буфер тоже должен быть volatile.
....

Можешь попытаться заставить любой из компиляторов такое сделать? Я не стебаюсь, просто мне никак заставить сделать такое не удалось, а поисследовать поведение и пути обхода без огульного применения volatile, очень хочется.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.