|
|
  |
Указатель во флэш и IAR |
|
|
|
Dec 25 2011, 19:06
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 8-10-07
Пользователь №: 31 163

|
Доброго всем времени суток! С недавних пор начал разбираться с компилятором IAR, до этого писал на ассемблере. Начал простенький проектик с графическим дисплеем. Пытаюсь вывести на экран строку, расположенную в памяти программ. Строка, конечно, выводится, отображается как надо, но вот какими методами... В общем, есть у меня функция: Код void lcd_write_string(char __flash * string) { char i= * string++; while(i--!=0) { lcd_write_character(*string++); } } Принимает она указатель на flash, где переменная где у меня выглядит следующим образом: char __flash string[]={5,'A','B','C','D','E'}; 5 - число символов в строке, дальше символы. При компиляции всего этого начинаются для меня непонятные вещи: Код @00000057: lcd_write_string 46: void lcd_write_string(char __flash * string) 47: { +00000057: 940E00D2 CALL 0x000000D2 Call subroutine;тут компилятор сохраняет в программный стек регистры, хотя это и не нужно, ну да ладно +00000059: 01C8 MOVW R24,R16 Copy register pair;тут получили указатель, переданный через пару регистров R16:R17 48: char i= * string++; +0000005A: 01FC MOVW R30,R24 Copy register pair;но почему было сразу их не занести в ZH:ZL??? +0000005B: 91A4 LPM R26,Z Load program memory;если бы сразу занесли в ZH:ZL, можно бы было сделать LPM R26,Z+ +0000005C: 9601 ADIW R24,0x01 Add immediate to word;тогда эта строка лишняя +0000005D: C004 RJMP PC+0x0005 Relative jump;прыг +5 51: lcd_write_character(*string++); +0000005E: 01FC MOVW R30,R24 Copy register pair;опять переносим в Z указатель +0000005F: 9104 LPM R16,Z Load program memory +00000060: DFE9 RCALL PC-0x0016 Relative call subroutine;собственно, запись символа +00000061: 9601 ADIW R24,0x01 Add immediate to word;и почему-то увеличиваем указатель уже после выполнения функции 49: while(i--!=0) +00000062: 2F0A MOV R16,R26 Copy register;вот зачем дальше вся эта чехарда, когда можно просто проверить R26??? +00000063: 2FA0 MOV R26,R16 Copy register +00000064: 95AA DEC R26 Decrement +00000065: 2300 TST R16 Test for Zero or Minus +00000066: F7B9 BRNE PC-0x08 Branch if not equal;прыг на -8 53: } +00000067: E0E3 LDI R30,0x03 Load immediate +00000068: 940C00D6 JMP 0x000000D6 Jump Итак, вопрос: это такая работа компилятора, или это я чего-то не знаю? Как напрямую в регистр ZH:ZL скормить указатель и работать именно с этими регистрами? Нет, я могу то же самое написать на ассемблере, и ассемблерную библиотеку прикрутить, но зачем тогда мне компилятор?
|
|
|
|
|
Dec 25 2011, 21:19
|
Участник

Группа: Участник
Сообщений: 50
Регистрация: 5-07-08
Пользователь №: 38 757

|
Оптимизацию включать не пробовали?
Вы как-то странно строки инициализируете. Вы каждый раз вручную будете вычислять длину строки?
Почему нельзя записать
char __flash string[]="ABCDE";
И потом проверять ноль в конце строки? Ноль сам компилятор добавит.
Сообщение отредактировал Chameleon - Dec 25 2011, 21:29
|
|
|
|
|
Dec 26 2011, 01:45
|
Частый гость
 
Группа: Участник
Сообщений: 163
Регистрация: 10-10-05
Пользователь №: 9 463

|
Для двухстрочного LCD (взято с AVR Freaks) делал так: CODE #define LCD_Puts_FLASH(s) {static __flash unsigned char str[] = s; LCD_Putsf(str);} ...... void LCD_Putsf(unsigned char __flash *str) { unsigned char temp; while (temp=*str++) LCD_Data(temp, 1); } использование: LCD_Puts_FLASH("Start...");
|
|
|
|
|
Dec 26 2011, 07:04
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 8-10-07
Пользователь №: 31 163

|
Цитата(Chameleon @ Dec 26 2011, 00:19)  Оптимизацию включать не пробовали? Вы как-то странно строки инициализируете. Вы каждый раз вручную будете вычислять длину строки? Почему нельзя записать char __flash string[]="ABCDE"; И потом проверять ноль в конце строки? Ноль сам компилятор добавит. Полная оптимизация по скорости добавила таки команду LPM R16,Z+, счетчик теперь хранится только в R26, но хранение указателя в регистрах R25:R24 осталось. Наверное, есть какой-нибудь способ, чтобы жестко привязаться к регистрам ZH:ZL, если это все же предусмотрено компилятором. Будем искать... Код @00000054: lcd_write_string 46: void lcd_write_string(char __flash * string) 47: { +00000054: 93AA ST -Y,R26 Store indirect and predecrement +00000055: 939A ST -Y,R25 Store indirect and predecrement +00000056: 938A ST -Y,R24 Store indirect and predecrement 48: char i= * string++; +00000057: 01F8 MOVW R30,R16 Copy register pair +00000058: 91A5 LPM R26,Z+ Load program memory and postincrement +00000059: 01CF MOVW R24,R30 Copy register pair +0000005A: 23AA TST R26 Test for Zero or Minus +0000005B: F039 BREQ PC+0x08 Branch if equal +0000005C: 95AA DEC R26 Decrement 51: lcd_write_character(*string++); +0000005D: 01FC MOVW R30,R24 Copy register pair +0000005E: 9105 LPM R16,Z+ Load program memory and postincrement +0000005F: 01CF MOVW R24,R30 Copy register pair +00000060: DFE7 RCALL PC-0x0018 Relative call subroutine +00000061: 23AA TST R26 Test for Zero or Minus +00000062: F7C9 BRNE PC-0x06 Branch if not equal 53: } +00000063: 9189 LD R24,Y+ Load indirect and postincrement +00000064: 9199 LD R25,Y+ Load indirect and postincrement +00000065: 91A9 LD R26,Y+ Load indirect and postincrement +00000066: 9508 RET Subroutine return А по поводу хранения строки - это у меня после написания протокола modbus привычка осталась - значение, например, порта ввода-вывода любым может быть, и конец посылки по символу определять некорректно.
|
|
|
|
|
Dec 26 2011, 07:54
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 8-10-07
Пользователь №: 31 163

|
Нашел настройку компилятора --lock regs, а потом использовал __no_init __regvar char __flash * pointer @ 14; но она позволяет жестко выделить только регистры с R4 по R15...
|
|
|
|
|
Dec 26 2011, 13:39
|

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

|
QUOTE (Makki @ Dec 26 2011, 09:54)  Нашел настройку компилятора --lock regs В заголовочном файле pgmspace.h встречается квалификатор функции __x_z. Он как раз и задает передачу указателей в регистровых парах. Помню, что в документации какой-то из версий он (и несколько подобных) был задокументирован. Поищите его в документации к вашей версии или просто поэкспериментируйте. В интернете находится такая цитата из документации: QUOTE __x, __z __x_z, __z_x The compiler defines the function type attributes __x, __z __x_z, and __z_x which are used by parts of the runtime library. These attributes can give very efficient code in a local perspective, but should be used with care as they change the calling convention and may have a negative effect on the size of the entire application.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 29 2011, 09:08
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 8-10-07
Пользователь №: 31 163

|
Цитата(Сергей Борщ @ Dec 26 2011, 16:39)  В заголовочном файле pgmspace.h встречается квалификатор функции __x_z. __x The first pointer in the parameter list is placed in register X __z The first pointer in the parameter list is placed in register Z __x_z The first pointer in the parameter list is placed in register X and the second one in register Z __z_x The first pointer in the parameter list is placed in register Z and the second one in register X Почитал, попробовал, всё получилось, с одним маленьким исключением. Если в цикле функции, работающей с указателем ZH:ZL, есть вызов какой-либо другой функции, компилятор все равно сохраняет значение указателя во временные регистры. Поэтому для того, чтобы этого не было, все вызываемые функции пришлось "раскрыть". Странно, что компилятор не знает, что в вызываемых функциях ZH:ZL не изменяются... CODE //Запись символьной строки __z void lcd_write_string(char __flash * string) { char i= * string++; while(i--!=0) { SetBit(lcdctrl,rs); __delay_cycles(3); SetBit(lcdctrl,e); lcddata=*string++; __delay_cycles(3); ClearBit(lcdctrl,e); ClearBit(lcdctrl,rs); __delay_cycles(4); ddrlcddata=0x00; lcddata=0x00; SetBit(lcdctrl,rw); SetBit(lcdctrl,e); while((lcddatain&(1<<db7))!=0) { ; } ClearBit(lcdctrl,e); ClearBit(lcdctrl,rw); lcddata=0x00; ddrlcddata=0xff; } }
И, соответственно, результат компиляции. Тут всё работает так, как было бы написано мной на ассемблере, без оптимизации задержек, правда. CODE @0000004C: lcd_write_string 48: char i= * string++; +0000004C: 9115 LPM R17,Z+ Load program memory and postincrement +0000004D: 2311 TST R17 Test for Zero or Minus +0000004E: F0D9 BREQ PC+0x1C Branch if equal +0000004F: 951A DEC R17 Decrement 53: SetBit(lcdctrl,rs); +00000050: 9A38 SBI 0x07,0 Set bit in I/O register 54: __delay_cycles(3); +00000051: C000 RJMP PC+0x0001 Relative jump +00000052: 0000 NOP No operation 55: SetBit(lcdctrl,e); +00000053: 9A3A SBI 0x07,2 Set bit in I/O register 56: lcddata=*string++; +00000054: 9105 LPM R16,Z+ Load program memory and postincrement +00000055: BB05 OUT 0x15,R16 Out to I/O location 57: __delay_cycles(3); +00000056: C000 RJMP PC+0x0001 Relative jump +00000057: 0000 NOP No operation 58: ClearBit(lcdctrl,e); +00000058: 983A CBI 0x07,2 Clear bit in I/O register 59: ClearBit(lcdctrl,rs); +00000059: 9838 CBI 0x07,0 Clear bit in I/O register 60: __delay_cycles(4); +0000005A: C000 RJMP PC+0x0001 Relative jump +0000005B: C000 RJMP PC+0x0001 Relative jump 61: DDRC=0x00; +0000005C: E000 LDI R16,0x00 Load immediate +0000005D: BB04 OUT 0x14,R16 Out to I/O location 62: lcddata=0x00; +0000005E: BB05 OUT 0x15,R16 Out to I/O location 63: SetBit(lcdctrl,rw); +0000005F: 9A39 SBI 0x07,1 Set bit in I/O register 64: SetBit(lcdctrl,e); +00000060: 9A3A SBI 0x07,2 Set bit in I/O register 65: while((lcddatain&(1<<db7))!=0) +00000061: 999F SBIC 0x13,7 Skip if bit in I/O register cleared +00000062: CFFE RJMP PC-0x0001 Relative jump 69: ClearBit(lcdctrl,e); +00000063: 983A CBI 0x07,2 Clear bit in I/O register 70: ClearBit(lcdctrl,rw); +00000064: 9839 CBI 0x07,1 Clear bit in I/O register 71: lcddata=0x00; +00000065: BB05 OUT 0x15,R16 Out to I/O location 72: DDRC=0xff; +00000066: EF0F SER R16 Set Register +00000067: BB04 OUT 0x14,R16 Out to I/O location +00000068: 2311 TST R17 Test for Zero or Minus +00000069: F729 BRNE PC-0x1A Branch if not equal 74: } +0000006A: 9508 RET Subroutine return
|
|
|
|
|
Dec 29 2011, 10:23
|

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

|
QUOTE (Makki @ Dec 29 2011, 11:08)  Если в цикле функции, работающей с указателем ZH:ZL, есть вызов какой-либо другой функции, компилятор все равно сохраняет значение указателя во временные регистры. Да. читайте раздел документации про calling conventions. QUOTE (Makki @ Dec 29 2011, 11:08)  Поэтому для того, чтобы этого не было, все вызываемые функции пришлось "раскрыть". Нет  Изучайте квалификатор static для функций, изучайте квалификатор inline. QUOTE (Makki @ Dec 29 2011, 11:08)  Странно, что компилятор не знает, что в вызываемых функциях ZH:ZL не изменяются... Да, не знает. Потому что вызываемая функция может находиться в другой единице компиляции. Или может использоваться в другой единице компиляции. Если это не так, и функция объявлена в той же единице компиляции в которой и используется, используйте квалификатор static.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|