Страница 1 из 1

Как правильно делать маски на порты?

Добавлено: Вс авг 30, 2020 19:25:45
Alek_von_German
Доброго здравия, господа.
Роясь в литературе всякой по AVR Atmel, в поисках ответа на всяческие вопросы, заметил, что везде на всяких форумах/книгах присутствует один и тот же сценарий,
Берем кнопку1 и цепляем её на пин0 порта В, далее берем светодиод и цепляем его на ножку порта С. Это понятно. НО, если мне нужно выделить часть порта на кнопки, а вторую часть порта будет иметь прерывание от таймера- как в таком случае быть? Стандартное DDRB|=0x0f; PORTB|=0xf0;- не работает, точнее из-за того, что двумя пинами дергает таймер, остальные не включаются как входы и не подтягиваются к плюсу, они просто висят. Как обойти такое?

Re: Как правильно делать маски на порты?

Добавлено: Вс авг 30, 2020 19:59:52
НАПАЛМ
В таких тонкостях архитектуры не силен, что таймер целый потр забивает, но если действительно так, что первый костыль, приходящий на ум, это дерганье ногами таймера в прерывании таймера, и всеми остальными уже как вам нужно.

Re: Как правильно делать маски на порты?

Добавлено: Вс авг 30, 2020 20:04:36
ARV
Alek_von_German писал(а):Стандартное DDRB|=0x0f; PORTB|=0xf0;- не работает, точнее из-за того, что двумя пинами дергает таймер, остальные не включаются как входы и не подтягиваются к плюсу, они просто висят.
все работает, если делать правильно. поскольку мы не видим, как делаете вы, вам придется самостотельно искать ошибку

Re: Как правильно делать маски на порты?

Добавлено: Вс авг 30, 2020 20:17:21
Dimon456
Полагаю речь идет о
Спойлер

Код: Выделить всё

#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
//пример: ClearBit(PORTB, 1); //сбросить 1-й бит PORTB

#define SetBit(reg, bit)          reg |= (1<<(bit))	
//пример: SetBit(PORTB, 3); //установить 3-й бит PORTB

#define BitIsClear(reg, bit)    ((reg & (1<<(bit))) == 0)
//пример: if (BitIsClear(PORTB,1)) {...} //если бит очищен

#define BitIsSet(reg, bit)       ((reg & (1<<(bit))) != 0)
//пример: if(BitIsSet(PORTB,2)) {...} //если бит установлен

#define InvBit(reg, bit)	  reg ^= (1<<(bit))
//пример: InvBit(PORTB, 1); //инвертировать 1-й бит PORTB

Re: Как правильно делать маски на порты?

Добавлено: Вс авг 30, 2020 21:25:31
Alek_von_German
[uquote="ARV",url="/forum/viewtopic.php?p=3885804#p3885804"]
Alek_von_German писал(а):Стандартное DDRB|=0x0f; PORTB|=0xf0;- не работает, точнее из-за того, что двумя пинами дергает таймер, остальные не включаются как входы и не подтягиваются к плюсу, они просто висят.
все работает, если делать правильно. поскольку мы не видим, как делаете вы, вам придется самостотельно искать ошибку[/uquote] Хочется делать всё правильно, но получается как получается...
вот куски кода
Спойлер

Код: Выделить всё

ISR (TIMER1_COMPA_vect)
{

    sec++;                          // увеличиваем счетчик секунд на 1
  
    if (sec>10)                     //если счетчик секунд перешел через 59
    {
        sec=0;                       // сбрасываем счетчик секунд в 0
                                 // увеличиваем счетчик минут
        display--;                      // уменьшаем значение в переменной дисплея
        if (display<0) display=0;       // если на дисплее 0-ниже не опускаемся
        if (display==0)
        {
        PORTD|=(1<<5);
        }
        else PORTD&=~(1<<5);
    }

}
Спойлер

Код: Выделить всё

void start (void)
{
    
    TCCR1B|= (1<<CS12) | (1<<CS10);      // деление частоты на 64 = 15625
    TIMSK1|= (1<<OCIE1A);                 // разрешаем прерывание при совпадении
    OCR1A=100;//
    TCNT1 = 0;
    TCCR1A |= (1<<WGM12);                // сброс при совпадении
    
    sei();//
    
}
Спойлер

Код: Выделить всё

ISR (TIMER1_COMPA_vect)
{

    sec++;                          // увеличиваем счетчик секунд на 1
  
    if (sec>10)                     //если счетчик секунд перешел через 59
    {
        sec=0;                       // сбрасываем счетчик секунд в 0
                                 
        display--;                      // уменьшаем значение в переменной дисплея
        if (display<0) display=0;       // если на дисплее 0-ниже не опускаемся
        if (display==0)
        {
        PORTD|=(1<<5);
        }
        else PORTD&=~(1<<5);
    }

}
Спойлер

Код: Выделить всё

ISR(TIMER2_OVF_vect)
{
    
    PORTB = 0xFF;                       // Гасим все разряды
    PORTC= (1 << segcounter);              // Выбираем следующий разряд
    
    
    switch(segcounter)
    {
        case 0:
        PORTB =~ (SEGMENTE[display % 100 / 10]);//десятки
        break;
        case 1:
        PORTB =~ (SEGMENTE[display % 10]);     //единицы
        break;
    }
    if(segcounter++ > 0) segcounter = 0;
}
И почему-то когда работает UART- тормозит таймер.

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 08:56:54
ARV
смотрел ваш код 5 секунд, и заметил вот это: в одном прерывании вы делаете правильно

Код: Выделить всё

        PORTD|=(1<<5);
        }
        else PORTD&=~(1<<5);
  
а в другом уже иначе

Код: Выделить всё

   PORTC= (1 << segcounter);              // Выбираем следующий разряд
в первом случае вы именно маскируете нужные разряды, а во втором обновляете значение порта целиком.

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 12:07:07
Alek_von_German
Угу. Я так уже думал. Но когда я делаю вот так PORTC|= (1 << segcounter); то на выходе обе ноги принимают значение 1-цы.

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 13:10:28
ARV
Alek_von_German писал(а):то на выходе обе ноги принимают значение 1-цы.
ну так это нормально... потому что вы неправильно делаете вообще. сначала надо подавить в ноль ту единицу, что сейчас, затем обновить порт с данными, затем вычислить номер единицы для новых данных, а уж затем активировать эту единицу.

Код: Выделить всё

ISR(TIMER2_OVF_vect)
{
   
    PORTB = 0xFF;                       // Гасим все разряды
    //PORTC= (1 << segcounter);              // Выбираем следующий разряд
   PORTC &= ~(1<<segcounter); // душим текущий разряд
   
    switch(segcounter)
    {
        case 0:
        PORTB =~ (SEGMENTE[display % 100 / 10]);//десятки
        break;
        case 1:
        PORTB =~ (SEGMENTE[display % 10]);     //единицы
        break;
    }
    segcounter ^= 1;
    PORTC |= 1<<segcounter; // активируем следующий разряд
}
Добавлено after 2 minutes 18 seconds:
но вообще говоря, сдвиг на переменное количество разрядов - это весьма затратная работа... лучше как-то обойтись без вот этого (1<<segcounter)

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 14:10:23
OKF
Терпеть не могу эти PORTB и т.п.! Конечно, это дело личное, но накой всё это, когда можно чуть выше:

Код: Выделить всё

void ledExe() {
  static uint8_t com;
  
  segment_off();                        //выключим всё
  com_off();
  keyExe();                             //опрашиваем кнопки
  switch (com) {                        //включим один общий
    case 0: com1_on(); break;
    case 1: com2_on(); break;
    case 2: com3_on(); break;
  }
  segment_on(segment[com]);             //включим сегменты
  if (++com >= COM_MAX)                 //следующая позиция  
    com = 0;                            
}
А остальное (аппаратнозависимое) выведи вне, макро (или инлайн):

Код: Выделить всё

#define segment_off()     ( DDRA  = B00000000, PORTA = B11111111)
#define segment_on(x)     ( DDRA  = B11111111, PORTA = ~(x)     )

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 14:37:02
BOB51
Иногда проще не состояние порта менять, а содержимое регистра направления.
Это ежли не требуется полный ток стекающий с 1...
:roll:

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 15:00:16
Alek_von_German
ARV писал(а):ну так это нормально... потому что вы неправильно делаете вообще. сначала надо подавить в ноль ту единицу, что сейчас, затем обновить порт с данными, затем вычислить номер единицы для новых данных, а уж затем активировать эту единицу.

Код: Выделить всё

ISR(TIMER2_OVF_vect)
{
   
    PORTB = 0xFF;                       // Гасим все разряды
    //PORTC= (1 << segcounter);              // Выбираем следующий разряд
   PORTC &= ~(1<<segcounter); // душим текущий разряд
   
    switch(segcounter)
    {
        case 0:
        PORTB =~ (SEGMENTE[display % 100 / 10]);//десятки
        break;
        case 1:
        PORTB =~ (SEGMENTE[display % 10]);     //единицы
        break;
    }
    segcounter ^= 1;
    PORTC |= 1<<segcounter; // активируем следующий разряд
}
Добавлено after 2 minutes 18 seconds:
но вообще говоря, сдвиг на переменное количество разрядов - это весьма затратная работа... лучше как-то обойтись без вот этого (1<<segcounter)
Да, это работает. Беру себе в коллекцию для изучения. Спасибо большое!!! :beer:

Добавлено after 3 minutes 25 seconds:
[uquote="BOB51",url="/forum/viewtopic.php?p=3886097#p3886097"]Иногда проще не состояние порта менять, а содержимое регистра направления.
Это ежли не требуется полный ток стекающий с 1...
:roll:[/uquote]
Т.Е., если я правильно вас понял, то прямо из функции можно менять направление порта? Теми же DDRx\PORTx?

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 15:16:50
NStorm
Alek_von_German, почитайте что-ли какие-то статьи хотя бы про AVR и C. Самые азы. И поймите. И тогда магия вокруг исчезнет, как придет понимание.
Эти вещи уже столько раз написаны во стольких местах. Хотя бы вот: http://easyelectronics.ru/avr-uchebnyj- ... ast-4.html
Т.Е., если я правильно вас понял, то прямо из функции можно менять направление порта? Теми же DDRx\PORTx?
Что-то неправильно вообще поняли. Направление порта меняется только через DDRx. А PORTx при это обычно 0 всегда. Так реализуется вывод с открытым стоком. Вместо того, чтобы дергать порт из низкого в высокий уровень, он дергается либо в низкий уровень, чтобы прижать линию к земле, либо в высокий, чтобы "отпустить" линию. При это подтяжка обычно внешняя ставится. Но это уже BOB51 ушел в другую "степь", развивая мысль дальше.

Re: Как правильно делать маски на порты?

Добавлено: Пн авг 31, 2020 15:31:42
Alek_von_German
NStorm, спасибо. Я-то читаю, литературы много есть, просто сразу всё охватить и запомнить тяжело. Про DDRx я знаю, правда зачем в коментарий я вплёл еще и PORTx- без понятия))