|
Оператор "printf" и функция "putchar" для работы с символьным LCD |
|
|
|
Mar 19 2010, 11:47
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
не понимаю, что за беда с выводом. Прописал "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. Мне нужно выводить на экран простые конструкции состоящие из текста и числовых данных, что делать?
Сообщение отредактировал Буратино - Mar 19 2010, 12:46
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 36)
|
Mar 19 2010, 12:09
|
Знающий
   
Группа: Свой
Сообщений: 540
Регистрация: 16-08-07
Из: Владивосток
Пользователь №: 29 831

|
Цитата(Буратино @ Mar 19 2010, 21:47)  printf("%a", 30.0) выводит "???" printf("%.2A", 30.0) выводит "? А если попробовать : printf("%f", 30.0); или printf("%.2f", 30.0); Кстати, касательно терминологии printf() тоже функция , а не оператор.
|
|
|
|
|
Mar 19 2010, 12:46
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Буратино @ Mar 19 2010, 15:43)  А почему эта функция не putchar()? А вот об этом написано в Букваре: Цитата #include <stdio.h> int putchar( int c ); Цитата Буратино продал букварь НЕ Карабасу. Главное, что продали НЕ читая  Ладно, если Букварь не читали, то может, хоть форум почитаете? Вопрос дежурный
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 19 2010, 15:36
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Пересмотрел форум, и понял ,что функция printf() занимает много места в ОЗУ. Я установил в опциях ИАР минимальную конфигурацию для нее (print formatter - Small), менял размер области памяти под стек, но ничего не получается. Вместо того, что я ожидаю получаю абракадабру  Идея форматированого вывода на 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 *, ...);
Сообщение отредактировал Буратино - Mar 19 2010, 15:42
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Mar 19 2010, 15:49
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Буратино @ Mar 19 2010, 18:36)  Я установил в опциях ИАР минимальную конфигурацию для нее (print formatter - Small) Ага, и решили попечатать float  Цитата Мне не хочется вникать в... Ну тогда займитесь чем-нибудь другим по жизни. Тем более, что тонкостей-то и нет  . Налицо какая-то безалаберность, например, ну никак не мог, если его не задушить до полного безразличия, не ругаться компилятор на void putchar( char ). Что и куда Вы сейчас подсунули - тоже неведомо никому, как и того, что и как делает дивная функция Transmit_Byte(), как настроен, и как должен быть настроен UART.... Если Вам "не хочется вникать в", то от других тогда тоже не ожидайте особого желания вникать, и уж тем более вытягивать из Вас информацию.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 19 2010, 15:59
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
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, ведь данные были переданы и приняты процессорами.
Сообщение отредактировал Буратино - Mar 19 2010, 16:13
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Mar 19 2010, 18:20
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Все, разобрался (хотя и остались некоторые непонятки): Не включил в проект #include <stdio.h> --- zltigo, прошу прощения, что наорал на Вас, злой очень был. Все никак не решусь на новый виток в образовании, вот если бы ногу или руку сломать (на крайний случай ветрянка сойдет), чтоб дома в кровати валяться и книги умные читать..
Сообщение отредактировал Буратино - Mar 19 2010, 18:28
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Mar 19 2010, 18:45
|

Профессионал
    
Группа: Свой
Сообщений: 1 433
Регистрация: 27-10-08
Из: Украина, Киев
Пользователь №: 41 215

|
Цитата(zltigo @ Mar 19 2010, 21:37)  Некошерно смотрится дергание бита разрешения прерывания  . Ну и volatile для UART_TxHead0 лишнее - только warning вызывает. tmptail0 лишняя сущность... Про имена помолчу.... Я тут на сайте нашел документ "Стандарт кодирования на СИ" Вы его еще чихвостили в той ветке не слабо, но мне понравилось. Планирую переписывать все что буду писать в соответствии с этим стандартом, включая форматирование и соглашение об именах. Старый код пока не буду трогать. Что касается разрешения прерывания, то пересмотрю источник (не помню какая апликуха атмела) возможно я уже что-то напакостил в тексте. Справедливости ради хочу заметить что этот код уже гигибайта три прокачал  volatile ,как и все остальные косяки в работе с ИАР буду стараться исправить, многого конечно не получится, но с варнингами точно "поквитаюсь":) Все, спасибо всем!
Сообщение отредактировал Буратино - Mar 19 2010, 19:00
--------------------
Брак - это такой вид отношений, в которых один всегда прав, - а другой - муж.
|
|
|
|
|
Mar 19 2010, 21:20
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(zltigo @ Mar 19 2010, 20:37)  Некошерно смотрится дергание бита разрешения прерывания  Нормально смотрится. Нужны прервания - разрешаем, не нужны - запрещаем. Флаг-то аппаратно торчит всегда, когда свободен буфер передачи. Это вам не '550. Цитата(zltigo @ Mar 19 2010, 20:37)  Ну и volatile для UART_TxHead0 лишнее - только warning вызывает. Формально - не лишняя. Переменная меняется в другой относительно прерывания нити. Цитата(zltigo @ Mar 19 2010, 20:37)  tmptail0 лишняя сущность... Вовсе нет. Без нее получаем предупреждение компилятора на неопределенный порядок чтения двух volatile в сравнении.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 19 2010, 21:53
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Mar 20 2010, 00:20)  Нормально смотрится. Нужны прервания - разрешаем, не нужны - запрещаем. Флаг-то аппаратно торчит всегда, когда свободен буфер передачи. Свободен сдвиговый регистр - записываем прямо в UART, не свободен в буфер и по прерыванию уже вычитываем, либо по анализу указателей, либо флаг можно завести. Цитата Формально - не лишняя. Переменная меняется в другой относительно прерывания нити. Абсолютно лишняя, ибо пока выполняется обработчик она измениться за его пределами не может. для Tail volatile нужен, Head - нет. Цитата Вовсе нет. Без нее получаем предупреждение компилятора на неопределенный порядок чтения двух volatile в сравнении. Два volatile, повторяю, ну совсем не нужны, а warning, гарантировано при if ( UART_TxHead0 != UART_TxTail0 ) вылезет хоть есть tmptail0, хоть нет.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 09:54
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(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); }
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 20 2010, 10:34
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ 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); } Ну вообще-то и у тебя лучше обойтись  без. Код 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. Кстати, в таких случаях я предпочитаю НЕ накладывать маску на индексы при их наращивании - пусть крутятся по полному циклу, а маска накладывается при использовании индекса. В этом случае одними действием вычитания индексов можно узнать сколько байтов в буфере - бывает нужно при пакетной передаче.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 11:11
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(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 в таком применении - грязный хак. Как раз таки и не дающий никакой дополнительной свободы компилятору, но чреватый какими-нибудь граблями если компилятор окажется черезчур умным. А думать надо всегда  Впрочем, мы уклонились от темы ветки.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 20 2010, 11:14
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Mar 20 2010, 14:08)  Опять же, речь идет об AVR, у которого FIFO нет (двойная буферизация не считается), и толку от знания "сколько конкретно байт в буфере" нет никакого. А причем тут наличие или отсутствие FIFO и AVR??? Это уже конкретные проблемы верхнего уровня - надо ему знать сколько есть места в софтовом буфере передачи, или нет. При необходимости поместить в буфер за один вызов функции не один байт, а фрейм - весьма не лишняя возможность. Цитата Но результирующий код получается длинее на 6 байт. Это уже конкретные заморочки компилятора  , кстати, возможно, и из-за неоптимальных volatile. То,что я описал, это просто тот вариант работы с индексами который я реализовывал "для начала". В случае каких-то дополнительных/особых требований, условий - естественно никаких догм.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 12:57
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Кстати, можно вопрос по поводу 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? В общем, некоторый гиморрой получается  Может, кто нибудь более популярно разъяснит механику работы LPCшного UART?
|
|
|
|
|
Mar 20 2010, 13:10
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(sonycman @ Mar 20 2010, 15:57)  В общем, некоторый гиморрой получается  Пальцем покажите: Код //---------------------------------------------------------------------------- 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);
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 14:00
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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. Жаль, не хотелось бы этого...
|
|
|
|
|
Mar 20 2010, 14:41
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(sonycman @ Mar 20 2010, 17:00)  У Вас используется софтовый флаг (STW_THR_BUSY) для исключения коллизии при загрузке данных. Прежде всего из-за много более медленного доступа к железным флагам. Цитата Кстати... Да, в реальности я байтовое занесение даже в буфер печати не использую - просто болталась неиспользуемая функция  . Используется свой xprintf() в котором в том числе оптимизирован контроль за флагом, который контролируется только в конце заполнения буфера.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 15:08
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(zltigo @ Mar 20 2010, 13:14)  При необходимости поместить в буфер за один вызов функции не один байт, а фрейм - весьма не лишняя возможность. А если весь фрейм не лезет, что будем делать? Ждать, пока влезет или все же запихаем сколько сможем, чтобы избежать пауз в потоке? Будет кошерный вариант для AVR без дерганья флага? Цитата(zltigo @ Mar 20 2010, 13:14)  Это уже конкретные заморочки компилятора  С чего бы это? В одном случае сравниваются две переменные, во втором - их разность сравнивается с константой. Очевидно, что второй вариант требует больше команд.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 20 2010, 15:35
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Mar 20 2010, 18:08)  А если весь фрейм не лезет, что будем делать? Лично у меня в большинстве случаев буфера разумно-большие и отсутствие места для фрейма это аварийное состояние. Предпринимаются разборки, торможение уровней и встечной стороны... Если это просто отладочная печать захлебывается, то тем более, ждать нечего - как мы начали идти до жизни такой уже видно в начале буфера, остальное в мусорник. Цитата ..чтобы избежать пауз в потоке? Понятие фрейма само по себе не завязано на непрерывность потока. Иногда даже наоборот Цитата С чего бы это? В одном случае сравнисаются две переменные, во втором - их разность сравнивается с константой. Очевидно, что второй ввариант требует больше команд. На ШЕСТЬ байт больше??? Кроме вышеуказанного сравнения остальные действия достаточно равнозначны, даже для AVR различие в 6 байт это проблемы компилятора  . Для 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 В общем, как и ожидалось, веских причин генерировать сильно отличающийся по размеру код у компиляторов нет.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Mar 20 2010, 18:51
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Mar 20 2010, 14:11)  Фу. union в таком применении - грязный хак. Сразу не заметил  этой реплики - никакого хака, это его прямое предназначение. Цитата Как раз таки и не дающий никакой дополнительной свободы компилятору напротив, разрешающий компилятору в безопасных местах, в данном случае в обработчике прерывания, не заниматься подстилкой соломки и пользоваться всеми возможностями оптимизации. Цитата ..чреватый какими-нибудь граблями если компилятор окажется черезчур умным. Нималейших прав у него нету использовать какую-либо информацию о других членах union.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Apr 20 2010, 21:59
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(zltigo @ Mar 20 2010, 01:08)  Абсолютно лишняя, ибо пока выполняется обработчик она измениться за его пределами не может. для Tail volatile нужен, Head - нет. Вспомнил, зачем Head должен быть volatile. Долго же в мозгу зрело, не давало покоя - ну не совсем же сдуру я его делал volatile! Если компилятор достаточно умный и решит встроить uart_t::send (например, если он вызывается только из одной точки программы), то без volatile он имеет полное право закешировать в основном коде Head в регистре и обработчик вместо одного байта будет каждый раз выстреливать весь буфер от начала до конца. Кстати, и буфер тоже должен быть volatile. В противном случае компилятор имеет полное право перенести запись в буфер после изменения Head и разрешения прерывания.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 20 2010, 22:13
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Apr 21 2010, 00:00)  то без volatile он имеет полное право закешировать в основном коде Head в регистре... Это по любому глобальная переменная. Даже при встраивании функции область работы с этой переменной и область cуществования локальной переменной Tmp четко определена телом бывшей функции и при выходе за ее пределы должна быть синхронизирована с памятью. Если этого не будет сделано, то компилятор не умный, а вовсе даже с ошибками. Даже допустим, что компилятор плюет на все и всех, но даже в этом случае uart_t::send должна встроися в некий вечный цикл, причем ничего особо кроме вызова send() не делающий, дабы была куча неиспользуемых регистров и компилятор мог ими разбрасываться. Что-то за уши притянутое. Цитата Кстати, и буфер тоже должен быть volatile. В противном случае компилятор имеет полное право перенести запись в буфер после изменения Head и разрешения прерывания. Ну это вообще понесло  Вместо: Код TxBuffer[ TxHead ] = byte; TxHead = Tmp; UCSRB |= (1<<UDRIE); Сделать Код TxHead = Tmp; UCSRB |= (1<<UDRIE); TxBuffer[ TxHead ] = byte; Может он тогда имеет право и вообще генерить любые побочные эффекты и точки следования ему не указ?
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Apr 21 2010, 07:31
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(zltigo @ Apr 21 2010, 01:28)  Это по любому глобальная переменная. Даже при встраивании функции область работы с этой переменной и область cуществования локальной переменной Tmp четко определена телом бывшей функции и при выходе за ее пределы должна быть синхронизирована с памятью. Если этого не будет сделано, то компилятор не умный, а вовсе даже с ошибками. Переменная не volatile и компилятор имеет право делать с ней что угодно, если это не меняет observable behaviour. Глобальность и время жизни переменной тут совершенно не при чем. Стандарт не нарушен. Компилятор не виноват. Цитата(zltigo @ Apr 21 2010, 01:28)  Что-то за уши притянутое. Ну и что? "Никто не знает, что может прийти в голову этим пчелам" (с). Имеются ввиду и программисты и компилятор. Потенциально бага есть. Предположенное мной поведение компилятора не противоречит стандарту. Значит отмахиваться от его возможности только потому, что "сегодняшний мой компилятор в моих программах так не делает" - это положить самому себе граблю. Не факт, что он не будет так делать никогда. И все ради чего? Ради экономии пары volatile и одной временной переменной? На сегодняшнем результате компиляции это никак не скажется, это только увеличит исходный текст. Но сделает его безопасным. Цитата(zltigo @ Apr 21 2010, 01:28)  Ну это вообще понесло  Вместо: Код 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, значит запись в нее не дает побочных эффектов. Компилятор имеет полное право момент записи отложить. При кроссфайловой оптимизации запись вообще может быть выкинута, ибо компилятор может увидеть, что чтения из этой переменной в этом потоке выполнения нет. В чем я не прав?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 24 2010, 21:28
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(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 эти все перестановки кода обсуждались. Тему неплохо почистили от ругни, но суть оталась :-) Уже нужно было где-то процитировать - тогда нашлась в последнем сообщении опечатка, исправлялсь, поэтому дата у него оторванная от остальных.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Apr 24 2010, 22:39
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Apr 21 2010, 00:14)  Вспомнил, зачем Head должен быть volatile. Кстати, и буфер тоже должен быть volatile. .... Можешь попытаться заставить любой из компиляторов такое сделать? Я не стебаюсь, просто мне никак заставить сделать такое не удалось, а поисследовать поведение и пути обхода без огульного применения volatile, очень хочется.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|