|
Массив портов ввода PINx, вопрос к знатокам GCC |
|
|
|
Nov 27 2006, 21:57
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Контроллеры от ATmega8 до Atmega128, и т.д. Есть такой массив: Код BYTE PIN_[3]={ (BYTE)(&PINB), (BYTE)(&PINF), (BYTE)(&PING) }; Доступ к нему присходит вот так: Код PIN_tmp=_SFR_IO8(PIN_[0]-0x20); ...... PIN_tmp=_SFR_IO8(PIN_[2]-0x20); Все работает, более того мне даже нравится код который генерит при этом GCC, НО, осадок остается Собствено вопросы: 1. Есть ли в GCC другие варианты доступа к массиву PINx, без использования _SFR_IO8 ? 2. Насколько такой вариант портируем на более старые/новые реализации GCC AVR ? 3. 0x20 режет глаз, а что с этим делать ?
|
|
|
|
|
Nov 27 2006, 23:06
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Во первых посмотрите в avr-libc-user-manual: FAQ - 7.3.13 "How do I pass an IO port as a parameter to a function?" Масив я объявил так: volatile uint8_t *ports[2] = {&PINB, &PIND}; Если не хотите инициализировать при объявении, то инициализируете так: ports[0] = &PINB; ports[1] = &PIND; Доступ к порту: uint8_t test; test = *ports[0]; Ассемблерный код: Цитата uint8_t test; test = *ports[0]; 482: e0 91 88 00 lds r30, 0x0088 486: f0 91 89 00 lds r31, 0x0089 48a: 80 81 ld r24, Z Вроде работает правильно, но перепроверьте. Анатолий.
|
|
|
|
|
Nov 27 2006, 23:45
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(aesok @ Nov 27 2006, 23:06)  Во первых посмотрите в avr-libc-user-manual: FAQ - 7.3.13 "How do I pass an IO port as a parameter to a function?" Ну это я посмотрел прежде чем задавать вопрос Там про то как записать значение в порт. Цитата Масив я объявил так: volatile uint8_t *ports[2] = {&PINB, &PIND};
Доступ к порту: uint8_t test; test = *ports[0]; Да, так оно работает правильно! Cпасибо. ----------------- НО, На самом деле я конечно не очень точно задал свой вопрос. Есть такой массив: Код BYTE PIN_[3][2]={ {(BYTE)(&PINB), 1<<PINB5}, {(BYTE)(&PINF), 1<<PINF2}, {(BYTE)(&PING), 1<<PING1} }; Доступ к нему присходит вот так: Код PIN_tmp=_SFR_IO8(PIN_[0][0]-0x20); tmp=PIN_tmp & PIN_[0][1]; ...... PIN_tmp=_SFR_IO8(PIN_[2]-0x20); ...... Как правильно написать доступ в таком варианте ?
|
|
|
|
|
Nov 28 2006, 00:06
|
Участник

Группа: Участник
Сообщений: 37
Регистрация: 20-03-05
Пользователь №: 3 533

|
сделайте структуру Код typedef struct { unsigned char *pinr; unsigned char *port; unsigned char *ddr; unsigned char pinNumb : 3; } PIN;
PIN pins[2] = {{&PINA, &PORTA, &DDRA, 2}, {&PINB, &PORTB, &DDRB, 7}}; А обращаться можно, например, так: Код for(unsigned char index = 0; index < 2; index++) { *pins[index].port |= (1 << pins[index].pinNumb); *pins[index].port |= (1 << pins[index].pinNumb); }
|
|
|
|
|
Nov 28 2006, 00:35
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(Lem @ Nov 28 2006, 01:06)  сделайте структуру [code] typedef struct { unsigned char *pinr; unsigned char *port; unsigned char *ddr; unsigned char pinNumb : 3; } PIN;
PIN pins[2] = {{&PINA, &PORTA, &DDRA, 2}, {&PINB, &PORTB, &DDRB, 7}}; НЕВЕРНО. pinr, port и ddr - ДОЛЖНЫ обязательно быть volatile!!! В противном случае на высоких уровнях оптимизации компиллятор оптимизирует следующий код: *pins[index].port = 0xAA; *pins[index].port = 0x55; Он выкинит первую запись в порт. Оставит только вторую. Для обычных переменых это правильная оптимизация, но доступ к портам оптимизироваться не должен. Для 16-битных портов все намного страшнее - бутет неправильнай порядок обращения байтам. Анатолий.
|
|
|
|
|
Nov 28 2006, 01:04
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(singlskv @ Nov 28 2006, 01:55)  Цитата(aesok @ Nov 28 2006, 00:35) 
НЕВЕРНО. pinr - ДОЛЖНЫ обязательно быть volatile!!!
В противном случае на высоких уровнях оптимизации компиллятор оптимизирует следующий код: *pins[index].port = 0xAA; *pins[index].port = 0x55;
Он выкинит первую запись в порт. Оставит только вторую. Для обычных переменых это правильная оптимизация, но доступ к портам оптимизироваться не должен.
а должно ли PINx быть volatile ? ??? avr/iom128.h ... /* Input Pins, Port D */ #define PIND _SFR_IO8(0x10) ... /* Timer/Counter 3 Control Register C */ #define TCCR3C _SFR_MEM8(0x8C) ... avr/port_def.h .... #define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr)) #define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr)) ..... ... #define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr) #define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr) #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + 0x20) #define _SFR_IO16(io_addr) _MMIO_WORD((io_addr) + 0x20) ..... Как видете все SFR обявлены как volatile. Анатолий.
|
|
|
|
|
Nov 28 2006, 02:06
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(singlskv @ Nov 28 2006, 02:12)  чтение и запись(volatile переменных) - это две большие разницы чтение и запись volatile и не volatile переменых ничем не отличаеться. Модификатор volatile делает только одно - он указывае компилятору не при каких условиях и ни на каких уровнвх оптимизации не оптимизировать доступ к этой переменной!! Другими словами, везде где в тексте программы встречаеться запись или чтение из этой переменой отавлять их без изменения. Пример: int i; i = 1; i = 0; Оптимизатор компилятора оптимизирует этот код и выкидывает первое присваивание переменной i (=1), потомучто это значение нигде не используеться и оставляет только второе i = 0. А теперь представте что i это выходной порт и вам надо надо вывести вначале 1 затем 0. Вас устроит что компилятор удалит операцию вывода 1. НЕТ. Вот для этого и используеться volatile. volatile int i; i = 1; i = 0; Все теперь оба обращения к переменной i останутся в скомпилированом коде. Те кто не понимают значение модификатора volatile обрекают себя на то что их программы работают только при выключеной оптимизации, а всех других на чтение топиков со словами "Глюкавый компилятор .....". Анатолий. PS: Если быть совсем точным то ассемблерный код для 16-битных volatile и не volatile переменных в AVR-GCC различаеться, но обьяснение этого выходит за рамки этого топика.
|
|
|
|
|
Nov 28 2006, 10:38
|
Участник

Группа: Участник
Сообщений: 37
Регистрация: 20-03-05
Пользователь №: 3 533

|
Да нет, в процедуре доступа (обработки) обращения портов лучше прагмой компилятору указывать на отсутствие оптимизации в данном куске кода (для каждого компилятора по-разному, я так понял). а с волатайл вы замучаетесь явно типы преобразовывать.
+ такое определение, как я указал выше, есть компромисс между удобством и универсальностью, с одной стороны, и произовдительностью, с другой. То есть, при разыменовывании указателей к регистрам доступ осуществляется с помощью обычных команд обращения к памяти (через общее адресное пространство), а не с помощью специальных команд для регистров ввода/вывода, что требует, если я не ошибаюсь, в два раза больше времени. Так что, работать с такими конструкциями надо осторожно, именно, в специально созданных для этого и проверенных (контролируемых на дизассемблере) функциях.
|
|
|
|
|
Nov 28 2006, 13:33
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(Lem @ Nov 28 2006, 11:38)  Да нет, в процедуре доступа (обработки) обращения портов лучше прагмой компилятору указывать на отсутствие оптимизации в данном куске кода (для каждого компилятора по-разному, я так понял). а с волатайл вы замучаетесь явно типы преобразовывать. Зачем отключать оптимизацию для всей функции (или куска кода) если volatile только и предназначена для того чтобы отключить оптимизацию но только для одной переменной???? Анатолий.
|
|
|
|
|
Nov 28 2006, 14:40
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(aesok @ Nov 28 2006, 00:04)  Я бы сделал чтото вроде этого:
1. Объявил структуру. struct __port { volatile uint8_t *port; unsigned char pin; }; Такая структура занимает 3 байта. Адрес порта <0x100, поэтому достаточно 2 байт. Если массив будет большим, это серьезная экономия. Вот еще вопросики появились: почему Код PIN_tmp=_SFR_MEM8(PIN_[0][0]); при компиляции выдает warning, а Код PIN_tmp=_SFR_MEM8(PIN_[0][0]+0); не выдает ? при компиляции этого кода получаем: Код +0000020A: 91800109 LDS R24,0x0109 Load direct from data space +0000020C: 2FE8 MOV R30,R24 Copy register +0000020D: 27FF CLR R31 Clear Register +0000020E: 8180 LDD R24,Z+0 Load indirect with displacement кто-нибудь может обьяснить зачем в lds используется r24 почему не так: Код LDS R30,0x0109 CLR R31 LDD R24,Z+0 ???
|
|
|
|
|
Nov 28 2006, 16:56
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
На первый вопрос отвечу сам себе Цитата(singlskv @ Nov 28 2006, 14:40)  почему Код PIN_tmp=_SFR_MEM8(PIN_[0][0]); при компиляции выдает warning, а Код PIN_tmp=_SFR_MEM8(PIN_[0][0]+0); не выдает ? а потому что _SFR_MEM8(PIN_[0][0]+0) эквивалентно _SFR_MEM8((WORD)PIN_[0][0]) . А вот со вопросом про LDS R24,... непонятно
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|