Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Массив портов ввода PINx
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
singlskv
Контроллеры от 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,
НО, осадок остается sad.gif smile.gif

Собствено вопросы:

1. Есть ли в GCC другие варианты доступа к массиву PINx, без использования _SFR_IO8 ?

2. Насколько такой вариант портируем на более старые/новые реализации GCC AVR ?

3. 0x20 режет глаз, а что с этим делать ?
aesok
Во первых посмотрите в 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


Вроде работает правильно, но перепроверьте.

Анатолий.
singlskv
Цитата(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?"

Ну это я посмотрел прежде чем задавать вопрос smile.gif
Там про то как записать значение в порт.
Цитата
Масив я объявил так:
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);
  ......


Как правильно написать доступ в таком варианте ?
aesok
Я бы сделал чтото вроде этого:

1. Объявил структуру.
struct __port {
volatile uint8_t *port;
unsigned char pin;
};

2. Масив:
struct __port ports[3] = {
{&PINB, 1<<PINB5},
{&PINF, 1<<PINF2},
{&PING, 1<<PING1}
};

3. Доступ:
PIN_tmp=*(ports[0].port);
tmp=PIN_tmp & ports[0].pin;


Код я не тестировал, привожу только как идею!!!

Анатолий.
Lem
сделайте структуру
Код
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);
}
aesok
Цитата(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-битных портов все намного страшнее - бутет неправильнай порядок обращения байтам.

Анатолий.
singlskv
Цитата(aesok @ Nov 28 2006, 00:35) *
НЕВЕРНО. pinr - ДОЛЖНЫ обязательно быть volatile!!!

В противном случае на высоких уровнях оптимизации компиллятор оптимизирует следующий код:
*pins[index].port = 0xAA;
*pins[index].port = 0x55;

Он выкинит первую запись в порт. Оставит только вторую. Для обычных переменых это правильная оптимизация, но доступ к портам оптимизироваться не должен.

а должно ли PINx быть volatile ?
aesok
Цитата(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.

Анатолий.
singlskv
чтение и запись(volatile переменных) - это две большие разницы
aesok
Цитата(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 различаеться, но обьяснение этого выходит за рамки этого топика.
Lem
Да нет, в процедуре доступа (обработки) обращения портов лучше прагмой компилятору указывать на отсутствие оптимизации в данном куске кода (для каждого компилятора по-разному, я так понял). а с волатайл вы замучаетесь явно типы преобразовывать.

+ такое определение, как я указал выше, есть компромисс между удобством и универсальностью, с одной стороны, и произовдительностью, с другой. То есть, при разыменовывании указателей к регистрам доступ осуществляется с помощью обычных команд обращения к памяти (через общее адресное пространство), а не с помощью специальных команд для регистров ввода/вывода, что требует, если я не ошибаюсь, в два раза больше времени. Так что, работать с такими конструкциями надо осторожно, именно, в специально созданных для этого и проверенных (контролируемых на дизассемблере) функциях.
aesok
Цитата(Lem @ Nov 28 2006, 11:38) *
Да нет, в процедуре доступа (обработки) обращения портов лучше прагмой компилятору указывать на отсутствие оптимизации в данном куске кода (для каждого компилятора по-разному, я так понял). а с волатайл вы замучаетесь явно типы преобразовывать.


Зачем отключать оптимизацию для всей функции (или куска кода) если volatile только и предназначена для того чтобы отключить оптимизацию но только для одной переменной????

Анатолий.
IgorKossak
Цитата(Lem @ Nov 28 2006, 09:38) *
Да нет, в процедуре доступа (обработки) обращения портов лучше прагмой компилятору указывать на отсутствие оптимизации в данном куске кода (для каждого компилятора по-разному, я так понял). а с волатайл вы замучаетесь явно типы преобразовывать.

Ещё более геморройное и непереносимое занятие, нежели использование стандартного средства vilatile.
С volatile как раз не нужно делать никаких преобразований типов (если написано грамотно), т. к. volatile вообще не относится к типам.
singlskv
Цитата(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

???
singlskv
На первый вопрос отвечу сам себе
Цитата(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,... непонятно sad.gif
singlskv
Цитата(aesok @ Nov 28 2006, 00:35) *
Цитата(Lem @ Nov 28 2006, 01:06) *

сделайте структуру
Код
typedef struct
{
   unsigned char *pinr;
  .....
} PIN;

НЕВЕРНО. pinr, port и ddr - ДОЛЖНЫ обязательно быть volatile!!!


Цитата(aesok @ Nov 28 2006, 01:04) *
Цитата(singlskv @ Nov 28 2006, 01:55) *

Цитата(aesok @ Nov 28 2006, 00:35) *

Он выкинит первую запись в порт. Оставит только вторую. Для обычных переменых это правильная оптимизация, но доступ к портам оптимизироваться не должен.

а должно ли PINx быть volatile ?

???
avr/port_def.h
....
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
.....

Как видете все SFR обявлены как volatile.

Обявлять структуру или массив в которых хранятся адреса портов volatile НЕТ никакой необходимости.
И даже более того вредно.
Нужно обявлять доступ к порту как volatile, то есть так как сделано в _SFR...
(*(volatile uint8_t *)(mem_addr))
IgorKossak
Цитата(singlskv @ Nov 28 2006, 13:40) *
Цитата(aesok @ Nov 28 2006, 00:04) *

Я бы сделал чтото вроде этого:

1. Объявил структуру.
struct __port {
volatile uint8_t *port;
unsigned char pin;
};

Такая структура занимает 3 байта.
Адрес порта <0x100, поэтому достаточно 2 байт.
Если массив будет большим, это серьезная экономия.

Чтобы указатель был однобайтовым, он должен указывать на tiny память.
В IAR это __tiny, а как в GCC не знаю.
aesok
Цитата(singlskv @ Nov 28 2006, 18:25) *
Обявлять структуру или массив в которых хранятся адреса портов volatile НЕТ никакой необходимости.
И даже более того вредно.



В чем вред? Пример.

ADD:
Не правильно прочитал вашу фразу.

Я не предлагал обьявлять структуру как volatile, а только те члены структуры в которых храняться ссылки на порты.


Цитата
Нужно обявлять доступ к порту как volatile, то есть так как сделано в _SFR...
(*(volatile uint8_t *)(mem_addr))


Главное чтобы весь доступ к портам был помечен как 'volatile'. А как - это без разницы.

Анатолий.
singlskv
Цитата(aesok @ Nov 28 2006, 18:10) *
Цитата(singlskv @ Nov 28 2006, 18:25) *

Обявлять структуру или массив в которых хранятся адреса портов volatile НЕТ никакой необходимости.
И даже более того вредно.

В чем вред? Пример.

Вам какой результат компиляции нравится больше ?

Вот этот, с volatile при объявлении массива
Код
volatile BYTE PIN_[2]={
    (BYTE)(&PINB),
    (BYTE)(&PIND)
  };

  tmp1= *(volatile BYTE *)((WORD)PIN_[0]);
  tmp2= *(volatile BYTE *)((WORD)PIN_[0]);

результат компиляции
Код
160:        tmp1= *(volatile BYTE *)((WORD)PIN_[0]);
+0000020A:   91800100    LDS     R24,0x0100       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
161:        tmp2= *(volatile BYTE *)((WORD)PIN_[0]);
+0000020F:   91800100    LDS     R24,0x0100       Load direct from data space
+00000211:   2FE8        MOV     R30,R24          Copy register
+00000212:   27FF        CLR     R31              Clear Register
+00000213:   8180        LDD     R24,Z+0          Load indirect with displacement


или этот, без volatile ?
Код
BYTE PIN_[2]={
    (BYTE)(&PINB),
    (BYTE)(&PIND)
  };

  tmp1= *(volatile BYTE *)((WORD)PIN_[0]);
  tmp2= *(volatile BYTE *)((WORD)PIN_[0]);

результат компиляции
Код
160:        tmp1= *(volatile BYTE *)((WORD)PIN_[0]);
+0000020A:   91800100    LDS     R24,0x0100       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
161:        tmp2= *(volatile BYTE *)((WORD)PIN_[0]);
+0000020F:   8180        LDD     R24,Z+0          Load indirect with displacement
aesok
Созрело 2 решения:

1. Ваше.
BYTE PIN_[2]={
(BYTE)(&PINB),
(BYTE)(&PIND)
};

tmp1= *(volatile BYTE *)((WORD)PIN_[0]);
tmp2= *(volatile BYTE *)((WORD)PIN_[0]);

Минусы: Не забывать писать "*(volatile..." при каждом обращении к порту!!!
Плюсы: Экономия 1 байта RAM на каждий порт.

2. Моё.

struct __port {
volatile uint8_t *port;
unsigned char pin;
};


struct __port ports[3] = {
{&PINB, 1<<PINB5},
{&PINF, 1<<PINF2},
{&PING, 1<<PING1}
};


PIN_tmp=*(ports[0].port);
tmp=PIN_tmp & ports[0].pin;

Минусы: Потеря 1 байта RAM на каждий порт.
Плюсы: Более понятная и постая запись. (моё личное мнение)



Анатолий.
singlskv
Цитата(aesok @ Nov 28 2006, 18:49) *
Созрело 2 решения:
2. Моё.

struct __port {
volatile uint8_t *port;
unsigned char pin;
};


struct __port ports[3] = {
{&PINB, 1<<PINB5},
{&PINF, 1<<PINF2},
{&PING, 1<<PING1}
};


PIN_tmp=*(ports[0].port);
tmp=PIN_tmp & ports[0].pin;

Минусы: Потеря 1 байта RAM на каждий порт.
Плюсы: Более понятная и постая запись. (моё личное мнение)

к Вашему варианту добавим еще один минусик
при компиляции:
PIN_tmp1=*(ports[0].port);
PIN_tmp2=*(ports[0].port);
будем опять же иметь лишний код
а если доступ к одному и тому же порту будет в цикле,
будем иметь кучу лишнего кода sad.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.