Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Передача в качестве параметра порта ввода-вывода
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
AlexMad
Есть набор функций работы с однопроводными устройствами,вроде
Код
#define ONEPIN       PINC // PIN register
#define ONEDDR       DDRC // Data Direction Register
#define ONEPORT      PORTC// PORT

unsigned char OneWireReset(unsigned char mask)
{
ONEPORT &= ~(mask);                 // Normal input no pull up
if (ONEPIN == 0) return 0;        // detect 0V on buss error
ONEDDR |= mask;                        // out at 0
  delay_us(500);
ONEDDR &= ~(mask);                  // Set to input
  delay_us(70);
if ((ONEPIN & mask) == 0)
   {
     delay_us(500);
   return mask;
   }
mask&=~ONEPIN;  
  delay_us(500);
return mask;
}

Работают замечательно, НО в железе есть восемь датчиков DS18b20, подключенных к одному порту (PORTC) и еще один датчик, подключенный к другому порту. Пробовал переделать функции так, чтобы рабочий порт можно было передавать, как параметр в функцию, но либо я чего-то не допер, либо это невозможно. На текущий момент предыдущим разработчиком просто сделан второй набор функций, где они работают с другим портом. На мой взгляд это не совсем правильно.
rezident
Почему это по-вашему неправильно? У вас нехватка памяти или какая-то другая причина извращаться с оптимизацией кода?
AlexMad
Цитата(rezident @ Oct 7 2007, 21:55) *
Почему это по-вашему неправильно? У вас нехватка памяти или какая-то другая причина извращаться с оптимизацией кода?

Неправильно - потому, что сделано несколько подобных устройств и хочется не то, чтобы оптимизировать, а скорее унифицировать код - то есть, сделать одинаковые функции для разных проектов. Потому что потом приходится путаться, какая функция из какого именно порта читает.
rezident
Цитата(AlexMad @ Oct 8 2007, 00:18) *
Неправильно - потому, что сделано несколько подобных устройств и хочется не то, чтобы оптимизировать, а скорее унифицировать код - то есть, сделать одинаковые функции для разных проектов. Потому что потом приходится путаться, какая функция из какого именно порта читает.

Дык если функция работает одинаково с портами, то чтобы не переписывать обе функции можно писать одну, но при включении ее в текст исходника манипулировать адресами портов с помощью макросов.
В противном случае в функцию придется передавать все три адреса. Либо передавать номер "набора" из адресов портов, но при этом добавить кучу ветвлений для проверки и вызова той или иной операции внутри вашей функции. Или вы считаете что куча ветвлений, лучше линейного кода?
AlexMad
Цитата(rezident @ Oct 7 2007, 22:40) *
Дык если функция работает одинаково с портами, то чтобы не переписывать обе функции можно писать одну, но при включении ее в текст исходника манипулировать адресами портов с помощью макросов.
В противном случае в функцию придется передавать все три адреса. Либо передавать номер "набора" из адресов портов, но при этом добавить кучу ветвлений для проверки и вызова той или иной операции внутри вашей функции. Или вы считаете что куча ветвлений, лучше линейного кода?

Так я в вопросе и писал про то, что хочу передать как параметры все три адреса (порт, пин, ддр). Проблема в том и заключается, что сами порты не передать, а если передавать условные значения, то придется делать кучу ветвлений. Просто хотелось сделать красиво - одна функция на все порты, только в параметре указывать, какие именно.
ReAl
В avr-gcc нормально передаются указатели на порты, думаю, что и у IAR должны передаваться, причём не удивлюсь, если там найдётся какое-то "волшебное слово" по типу __flash, которое ещё и делает это более удобным.

Для большинства портов идёт единый порядок PIN-DDR-PORT (не выполняется это, например, для порта F у меги64, который в наследство от меги103 достался как порт толькона чтение и его DDRF, PORTF улетели в memory-mapped IO, включаемое при стёртом фьюзе M103).

Учитывая это, можно (но осторожно) обойтись одним указателем.

Код
#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>

typedef struct {
    uint8_t pin;
    uint8_t ddr;
    uint8_t port;
} port_t;


uint8_t OneWireReset(volatile port_t *pport, uint8_t mask)
{
    pport->port &= ~mask;
    if( (pport->pin & mask) == 0) return 0;        // detect 0V on buss error
    pport->ddr |= mask;                        // out at 0
    _delay_ms(0.5);
    pport->ddr &= ~mask;                  // Set to input
    _delay_us(70);
    if( (pport->pin & mask) == 0) {
        _delay_ms(0.5);
        return mask;
    }
    mask &= ~pport->pin;
    _delay_ms(0.5);
    return mask;
}

uint8_t test(void) {
    return OneWireReset( (volatile port_t*)&PINB, 0x02);
}
prottoss
Всем известно (надеюсь), что адреса портов находятся в пространстве памяти данных SRAM - следовательно к ним можно обращаться либо напрямую, как к ячейке памяти, либо через указатель на ячейку памяти
AlexMad
Цитата(ReAl @ Oct 7 2007, 23:58) *
Учитывая это, можно (но осторожно) обойтись одним указателем.

Код
#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>

typedef struct {
    uint8_t pin;
    uint8_t ddr;
    uint8_t port;
} port_t;

}

Вот тут уже ближе. Я с ходу именно так и сделал, но, толи кодевижен это не хочет понимать, толи это все-таки не получится. У меня при инициализации
Код
pin=PINB
, а также по остальным двум портам матерно ругается, что PINB должно быть константой, указатель на PINB тоже не принимает, говорит порты нельзя передать sad.gif После этого я свой вопрос и задал.

А вот насчет обращения, как к памяти это интересно, попробую.
ReAl
Цитата(AlexMad @ Oct 8 2007, 06:48) *
Я с ходу именно так и сделал, но, толи кодевижен это не хочет понимать,
...
А вот насчет обращения, как к памяти это интересно, попробую.

Ну или выбрось каку, или пробуй вручную адрес писать - как адрес памяти, из скобок в register summary.
В моём примере как раз и сделано через "обращения к памяти"

Код
#define MEGA8_PINB_ADR 0x36
uint8_t test(void) {
    return OneWireReset( (volatile port_t*)MEGA8_PINB_ADDR, 0x02);
}


У avr-gcc в h-файлах адреса портов как адреса памяти сразу и прописаны, а уж он потом смотрит - если попадает в достижимое по in/out или sbi/cbi - то оптимизирует до коротких команд. И если в приведенном мной примере объявить OneWireReset как inline - то он её по месту вставит именно с командами sbi/cbi/sbis/sbic даже если передать (volatile port_t*)0x36
prottoss
Цитата(AlexMad @ Oct 8 2007, 12:48) *
А вот насчет обращения, как к памяти это интересно, попробую.


В ИАРе прокатывает вот такой код

Код


char* pPORTA = &PORTA;
*pPORTA = 0x55;
То бишь, при присвоении указателю адреса порта происходит прибавление к адресу IO 0х20
defunct
В общем случае я бы вероятно сделал так:
создал бы структуру "канал", в которую бы вошли 3 указателя на функции ввода вывода (чтение конкретного бита из конкретного PIN / запись конкретного бита в конкретный PORT / и запись конкретного бита в конкретный DDR).
И сделал бы таблицу во флеш - массив "каналов". Ну а дальше тривиально - в функцию передается указатель на "канал", и там все требуемые функции ввода/вывода. Расширять можно как угодно.
rezident
2 ReAl, в сообщении #6 вызывает сомнение, должен ли быть только указатель на структуру port_t типом volatile или все-таки все члены структуры должны быть volatile?
ИМХО более правильно было бы
Код
typedef struct {
    volatile uint8_t pin;
    volatile uint8_t ddr;
    volatile uint8_t port;
} port_t;
ReAl
Цитата(rezident @ Oct 8 2007, 15:20) *
должен ли быть только указатель на структуру port_t типом volatile или все-таки все члены структуры должны быть volatile?
volatile там не указатель, а тот объект, на который он указывает.
Ведь не
port_t * volatile pport;
написано.
Т.е. вся структура (все её поля) - volatile. Так же, как все они const, если вся структура const.
Вместо этого приписывать кавлификатор персонально к полям имеет смысл только тогда, когда часть полей volatile, часть - нет.

Собственно, квалификатор нужно было вообще в typedef включить и везде использовать просто port_t *

Код
typedef volatile struct {
    uint8_t pin;
    uint8_t ddr;
    uint8_t port;
} port_t;
singlskv
Цитата(ReAl @ Oct 8 2007, 17:38) *
Собственно, квалификатор нужно было вообще в typedef включить и везде использовать просто port_t *

Код
typedef volatile struct {
    uint8_t pin;
    uint8_t ddr;
    uint8_t port;
} port_t;

Объясните мне пожалуйста, зачем эту структуру содержащую адреса регистров портов нужно
объявлять volatile ? Недопонимаю...
rezident
Цитата(singlskv @ Oct 8 2007, 22:19) *
Объясните мне пожалуйста, зачем эту структуру содержащую адреса регистров портов нужно
объявлять volatile ? Недопонимаю...

Потому что порт это аппаратура, "железка" т.с., а не переменная. И оптимизировать обращение к порту компилятор не имеет права. В хидерах описаний аппаратных регистров volatile, может и неявно, но обычно всегда присутствует.
singlskv
Цитата(rezident @ Oct 8 2007, 20:42) *
Потому что порт это аппаратура, "железка" т.с., а не переменная. И оптимизировать обращение к порту компилятор не имеет права.
Проблемма в том что я обращения к портам еще не увидел smile.gif
А читать структру содержащую адреса портов можно и через volatile, тока зачем ?
ReAl
Цитата(singlskv @ Oct 8 2007, 18:19) *
Объясните мне пожалуйста, зачем эту структуру содержащую адреса регистров портов нужно объявлять volatile ? Недопонимаю...
В данном конкретном случае - может и не нужно. Но рано или поздно захочется написать
while( pport->pin & mask ) { }
и тут уже в полный рост встанет то, что оптимизатор компилятора не имеет права делать предположения типа "раз только что прочитано это значение, то ещё раз его читать не нужно".

Цитата(singlskv @ Oct 8 2007, 18:51) *
Проблемма в том что я обращения к портам еще не увидел smile.gif
А читать структру содержащую адреса портов можно и через volatile, тока зачем ?

Да не адреса портов в структуре!
Вся структура "мапится" на пространство ввода-вывода, у нас указатель на структуру, мы говорим, что она лежит по такому-то адресу, а там не память, а порты.
singlskv
Цитата(ReAl @ Oct 8 2007, 20:54) *
Да не адреса портов в структуре!
Вся структура "мапится" на пространство ввода-вывода, у нас указатель на структуру, мы говорим, что она лежит по такому-то адресу, а там не память, а порты.

В таком варианте, согласен, тока я не понял как предполагается реализация того что спрашивал
автор поста, те по сути, нужно некоторое количество например входов, которые "произвольным"
образом размазаны по портам и их нужно опрашивать.
ReAl
Цитата(singlskv @ Oct 8 2007, 19:14) *
В таком варианте, согласен, тока я не понял как предполагается реализация того что спрашивал автор поста, те по сути, нужно некоторое количество например входов, которые "произвольным" образом размазаны по портам и их нужно опрашивать.
Ну это у топикстартера спрашивать надо. По-видимому,

Код
ResetAll() {
    OneWireReset(channel1_pport, channel1_mask);
    OneWireReset(channel2_pport, channel2_mask);
    OneWireReset(channel3_pport, channel3_mask);
}
AlexMad
Цитата(ReAl @ Oct 9 2007, 00:01) *
Ну это у топикстартера спрашивать надо. По-видимому,

Код
ResetAll() {
    OneWireReset(channel1_pport, channel1_mask);
    OneWireReset(channel2_pport, channel2_mask);
    OneWireReset(channel3_pport, channel3_mask);
}

Именно так и есть. ReAl - спасибо, внимательно изучил ваш код и нашел грабли у себя. Теперь все сделано именно так.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.