|
Ламерские вопросы по Си, перехожу с асма |
|
|
|
May 19 2012, 06:34
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Добрый день. Копаю интернет, но все как-то безуспешно. Хочу написать функцию передачи строки по UART, работающую по прерыванию. Причем строка может быть как константа (храниться во флэш), так и переменная (храниться в СОЗУ). Код #define size_TX0 10 volatile static int8_t count_TX0;//кол-во непереданных символов в буфере UART0 volatile static int8_t index_TX0;//адресация в буфере передачи UART0 volatile static int8_t bufferTX0[size_TX0];//массив - буфер передачи UART0 /*==============================*/ void send_UART0(char *string) { int8_t i; for(i=0;*string!='\0';i++) { bufferTX0[i]=(*string); //сохраняем строку в буфер string++; count_TX0++; //считаем символы } index_TX0=0; //обнуляем адресацию в буфере sbi(UCSR0B,UDRIE0); //запускаем прерывание } /*==============================*/ ISR(USART0_UDRE_vect) { UDR0 = bufferTX0[index_TX0++]; // Берем данные из буффера. if(index_TX0==count_TX0) cbi(UCSR0B,UDRIE0); // Если все передали, то выкл.прерывание } /*==============================*/ Пишу send_UART0("12345");, но эти 12345 сохраняются в СОЗУ. Вопрос: как передать в функцию строку, чтобы она сохранилась во флэш? Спасибо.
Сообщение отредактировал Alt.F4 - May 19 2012, 06:36
|
|
|
|
|
May 19 2012, 07:54
|

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

|
Хм... Я не силён в терминологии разряда «цветовой дифференциации штанов», но в моём понимании «ламер» означает человека, который не просто не знает чего-то (все чего-то не знают *) ), а и не желает разобраться и при этом ещё пробует учить других. «ламерский вопрос» при этом редкость и обычно сам он своим уровнем хорошо показывает, что в предыдущих своих сообщениях человек с умным видом рассуждал о том, чего совершенно не знает. «нуб», в отличие от «ламера», хочет что-от узнать. Один и тот же вопрос может быть «ламерским» или «чайниковским» в зависимости от того, кто его задал (от предистории). Ну да не важно. Судя по тому, что соседний вопрос был по AVR Studio, используется avr-gcc. У него до версии 4.7 не поддерживаются разные пространства памяти и соответствующие модификаторы указателей (да и в 4.7, если я правильно понял, это есть в C frontend, но нет в C++). Так что нужно использовать атрибут размещения из avr/pgmspace.hУниверсальную функцию написать можно, сначала придумав, как передавать признак ОЗУ/флеш. Две специальных написать проще и результат не хуже. И это будет выглядеть как-то так: CODE #include <avr/pgmspace.h>
#define size_TX0 10
// беззнаковые счётчики и индексы дают код короче и быстрее volatile static unt8_t count_TX0; //кол-во непереданных символов в буфере UART0 volatile static unt8_t index_TX0; //адресация в буфере передачи UART0 // char так char, какая нам разница, какой размер и знак у символов volatile static char bufferTX0[size_TX0];//массив - буфер передачи UART0 ...
void wait_UART0() { while (UCSR0B & (1<<UDRIE0)) {} }
void start_UART0() { index_TX0 = 0; UCSR0B |= (1<<UDRIE0); }
// Строка из ОЗУ void send_UART0(char *string) { char *dst = buffer_TX0; char ch;
wait_UART0(); // надо ведь дождаться, пока уйдёт предыдущая строка
count_TX0 = 0;
// бесконечный цикл, тут прямо в условие действия затолкать для новичка возможно слишком уж нечитаемо выйдет for (;;) { ch = *string++; // если достигнут конец строки или уже нет местя в буфере — выходим if (ch == '\0' || ++count_TX0 >= size_TX0) break; *dst++ = ch; }
start_UART0(); }
... // Строка из флеша. // Увы, нормальной поддержки указатеелй во флеше нет, поэтому проще не обманывать себя void send_UART0_P(unsigned string_address) { char *dst = buffer_TX0; char ch;
wait_UART0();
count_TX0 = 0;
for (;;) { ch = pgm_read_byte(string_address); ++string_address; if ( ch == '\0' || ++count_TX0 >= size_TX0) break; *dst++ = ch; }
start_UART0(); }
... send_UART0_P( PSTR("Temperature: ") ); // эта строка не попадёт в ОЗУ char tmpbuf[size_TX0]; // к примеру, преобразовали в буфер число send_UART0( tmpbuf ); send_UART0_P( PSTR("\r\n") ); // эта строка не попадёт в ОЗУ
____________ *) Не знать — не стыдно. Стыдно не хотеть узнать.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
May 19 2012, 20:32
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(Dog Pawlowa @ May 19 2012, 10:19)  Но с универсальной функцией придется еще пободаться. да, можно её и так боднуть:-) http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gc...r-Builtins.htmlЦитата(ReAl @ May 19 2012, 10:54)  Универсальную функцию написать можно, сначала придумав, как передавать признак ОЗУ/флеш. А зачем передавать, можно на ходу макрос разворачивать и определять PGM_P она или нет.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
May 20 2012, 11:17
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Цитата Две специальных написать проще и результат не хуже. И это будет выглядеть как-то так: ... Спасибо большое!!! У себя реализовал так: Код void start_UART0_TX() { index_TX0 = 0; //обнуляем адресацию в буфере передачи sbi(UCSR0B,UDRIE0); //запускаем прерывание } /*--------------------------*/ void send_UART0(char *string) { u08 i; count_TX0 = 0; for(i=0;*string!='\0' && count_TX0<size_TX0;i++) { bufferTX0[i]=(*string++); //сохраняем строку в буфер count_TX0++; //считаем символы } start_UART0_TX(); //запускаем прерывание }
void send_UART0_P(const char *string) { u08 i; count_TX0 = 0; for(i=0;pgm_read_byte(string)!='\0' && count_TX0<size_TX0;i++) { bufferTX0[i]=pgm_read_byte(string++); //сохраняем строку в буфер count_TX0++; //считаем символы } start_UART0_TX(); //запускаем прерывание } /*--------------------------*/ ISR(USART0_UDRE_vect) { UDR0 = bufferTX0[index_TX0++]; // Берем данные из буффера. if(index_TX0>=count_TX0) cbi(UCSR0B,UDRIE0); // Если все передали, то выкл.прерывание }
Сообщение отредактировал Alt.F4 - May 20 2012, 11:21
|
|
|
|
|
May 21 2012, 00:33
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Строки наверное удобней выводить printf'ом. format str размещать во флеш, а %s - для вывода строк формируемых в run-time в ОЗУ... WinAVR поддерживает макросы с переменным числом аргументов Код #define pgm_printf(x, ...) do {\ static const char PROGMEM pgm_str[] = x;\ char pgm_data_buf[sizeof(pgm_str)];\ __pgm_strcpy( pgm_data_buf, pgm_str);\ printf( pgm_data_buf, __VA_ARGS__);\ } while(0)
void __pgm_strcpy(char *dst, const char PROGMEM *str) { do { *dst++ = *str; } while (*str++); }
примеры использования:
pgm_printf("strlen(%s) = %u\n", "hello", strlen("hello")); pgm_printf("x = %d\n", 123); pgm_printf("addr_0x%x\n", 0x1000);
|
|
|
|
|
May 23 2012, 03:11
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Возник новый вопрос. Прочел, что хорошим тоном является при написании макросов, в том числе многострочных, заключать их в do{...}while(0) Но вот беда, когда я заключаю в этот цикл while следующий макрос, он компилируется хрен знает каким образом. Исходный вариант: Код #define check_temp(X)\ if(temp==X) {index_RX0++;break;}\ goto start_RX0; В цикле while: Код #define check_temp(X)\ do{\ if(temp==X) {index_RX0++;break;}\ goto start_RX0;\ }while(0) Макрос вставляю в case инструкции switch. В дизасемблере второй макрос выполняет два раза index_RX0+, затем джамп и перед ret обнуляет index_RX0!!! Если do{...}while(0) убрать, то все работает как надо. Что это может быть? Спасибо.
Сообщение отредактировал Alt.F4 - May 23 2012, 03:12
|
|
|
|
|
May 23 2012, 05:48
|

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

|
QUOTE (Alt.F4 @ May 23 2012, 06:11)  Что это может быть? Во втором случае break выходит из do{}while(), а не из case(). P.S. на будущее - заключайте X внутри макроса в скобки. В месте вызова он может оказаться не переменной, а выражением.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jun 2 2012, 15:11
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Имеем массив в EEPROM и указатель на него: Код EEMEM char array[10]; unsigned int *addr_EE; addr_EE = array; <-- здесь компилятор ругается на несовпадение типов. EEAR = addr_EE; Подскажите пожалуйста, как правильно объявить указатель? Спасибо.
Сообщение отредактировал Alt.F4 - Jun 2 2012, 15:14
|
|
|
|
|
Jun 2 2012, 15:27
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Alt.F4 @ Jun 2 2012, 18:11)  Имеем массив в EEPROM и указатель на него: Код EEMEM char array[10]; unsigned int *addr_EE; addr_EE = array; <-- здесь компилятор ругается на несовпадение типов. EEAR = addr_EE; Подскажите пожалуйста, как правильно объявить указатель? Спасибо. Думаю что так: Код EEMEM char array[10]; EEMEM char *addr_EE; или так можно: Код unsigned int addr_EE; addr_EE = (unsigned int)array; EEAR = addr_EE;
|
|
|
|
|
Jun 2 2012, 15:32
|

Профессионал
    
Группа: Свой
Сообщений: 1 001
Регистрация: 27-06-06
Пользователь №: 18 409

|
Цитата(Alt.F4 @ Jun 2 2012, 18:11)  Имеем массив в EEPROM и указатель на него: Код EEMEM char array[10]; unsigned int *addr_EE; addr_EE = array; <-- здесь компилятор ругается на несовпадение типов. EEAR = addr_EE; Подскажите пожалуйста, как правильно объявить указатель? Спасибо. У Вас array объявлен как EEMEM char а указатель на тип данных int. Указатель должен соответсвовать типу данных или приводится к нему. в IAR прокатила бы такая такая запись Код __eeprom char array[10]; char __eeprom *addr_EE; addr_EE = array;
|
|
|
|
|
Jun 2 2012, 15:42
|
Профессионал
    
Группа: Свой
Сообщений: 1 468
Регистрация: 28-03-10
Из: Беларусь
Пользователь №: 56 256

|
Цитата или так можно: Код unsigned int addr_EE; addr_EE = (unsigned int)array; EEAR = addr_EE; Разницы от исходника компилятор не заметил, т.е. типы не совпадают. Цитата Думаю что так: Код EEMEM char array[10]; EEMEM char *addr_EE; Если делать так, то варнинг несовпадения типов выскакивает уже напротив: EEAR = addr_EE;Цитата Указатель должен соответсвовать типу данных или приводится к нему. Каким образом привести тип указателя, чтобы его значение можно было записать в EEAR и в тоже время в этот указатель можно записать адрес массива?
Сообщение отредактировал Alt.F4 - Jun 2 2012, 15:48
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|