Перейти к содержанию
    

Микроконтроллеры для начинающих

В 01.04.2020 в 23:57, -=TRO=- сказал:

Правильно ли я понимаю из дискуссии, что если у меня настроено прерывание по фронту входного сигнала, и этот фронт случился во время запрета прерывания, то сразу по разрешению этого прерывания сразу произойдет прерывание? Т.е. событие я не потеряю, просто реакция на него будет позже.,?

Если не очистите флаг (к примеру, INFx в GIFR) прерывания вручную.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Еще вопрос, по похожему поводу, а именно PCINT. Если я поставил маску на срабатывание прерывания по фронту двух входов PCINT, фронт приходит на них одновременно, просто в разной комбинации (могут одновременно сработать два, а может и один). И вот допустим пришли фронты на два входа одновременно, я обработал прерывание, после выхода из прерывания я не попаду снова в прерывание? Ведь фронт был по двум входам? Я в том смысле что оно не запомнит что был фронт по второму входу что бы потом сделать снова прерывание. Извините если что невнятно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ответ в моем прошлом комментарии на самом деле. И в даташите. Что происходит, когда включено прерывание и срабатывает условие? Переферия возводит флаг прерывания в соответствующем регистре. Далее уже логика логика железа по обработке прерываний по завершению текущей инструкции проверяет этот флаг, также флаг глобального разрешения прерываний и меняет PC на вектор прерывания (переход по адресу без инструкции). При этом же снимается (отключается) бит глобальных прерываний и бит конкретного прерывания.

По выходу из прерывания инструкцией RETI глобальные прерывания разрешаются обратно. Всё это описано двумя небольшими параграфами в главе 6.7 даташита, например на atmega328p.

Посему если у вас прерывания разделяют один и тот же бит прерывания, и происходят в пределах до того, как железо зайдет в прерывание и сотрет бит прерывания (т.е. пока завершается текущая инструкция + 3 тика на установку бита прерывания и еще 4 тика (ответ в разделе 6.7.1 ДШ) для перехода на вектор прерывания), то повторного прерывания не будет. Если же второе прерывание произойдет уже после снятия бита - бит останется установленным и сразу после выхода из прерывания, оно будет повторено для 2го события.

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

А еще у той же Atmega328p биты прерываний разные, для разных PCINT. PCIF0..2 - 3 разных бита, для ног PCINT7..0, PCINT14..8, PCINT23..16 соотв. Поэтому если у вас включены скажем PCINT7 и PCINT8 и даже если событие изменения на ногах произойдет прям вот совсем одновременно, всё-равно выполнятся по очереди 2 прерывания. Если биты не очистить. Правда у них и векторы разные.

Изменено пользователем NStorm
опечатка незначительная была

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

NStormСпасибо большое, прям так ясно и подробно все по полкам разложили, все прочитал и понял с первого раза (пока вы писали я как раз грыз даташит по тем самым битам в регистрах). 

Изменено пользователем -=TRO=-

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Здравствуйте.

Вопрос по AVR, как реализовать такую задачу, первый светодиод мигает, второй начинает мигать при нажатии кнопки, а первый при этом продолжает мигать. У каждого светодиода должна независимо друг от друга прописываться своя частота мигания. Такое псевдо-параллельное исполнение двух действий. Не могу понять логику действий. Пробовал использовать два таймера, но при нажатии кнопки, частота первого диода почему то падает почти в два раза. Голову сломал, возможно ли вообще такое реализовать.

Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, Maxik777 said:

Голову сломал, возможно ли вообще такое реализовать.

Возможно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 minutes ago, Maxik777 said:

Пробовал использовать два таймера

Не надо, используйте один. Период мигания каждого из диодов задавайте через тики этого таймера, соответственно, по событию таймера решаете, нужно ли что зажечь/погасить в данный момент.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо! Просто озарение снизошло! Сразу всё понял, особенно после первого сообщения! Так подробно, а главное доходчиво!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, Maxik777 said:

 Сразу всё понял, особенно после первого сообщения!

Вы же сами так спросили. Возможно или нет. А не как. :wink3:

Хотя в вашем сообщении вообще не одного вопросительного знака.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне не понятен алгоритм, как сделать так, чтобы нажатие кнопки и условия работы второго светодиода, не влияли на частоту моргания первого? Ведь все действия в программе идет последовательно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

21 minutes ago, Maxik777 said:

Мне не понятен алгоритм, как сделать так, чтобы нажатие кнопки и условия работы второго светодиода, не влияли на частоту моргания первого? Ведь все действия в программе идет последовательно.

1. Настроили оба таймера.

2. Запустили только один таймер, по прерыванию которого мигает первый светодиод.

3. По прерыванию от кнопки запустили второй таймер.

 

Каким образом условие работы первого таймера меняются при нажатии кнопки, не видя вашего кода, понять невозможно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

#include <mega328p.h>

int z = 0;
int s = 0;

void Led ()
{
  if (TCNT0 == 255)
        {
          z++;
          TCNT0 = 0;
        }
        
        if (z == 40)
        {
        PORTD = 0b11111101;
        }
        
        if (z == 80)
        {
        PORTD = 0b11111111;
		}
		
		if (z > 80)
		{
			z = 0;
		}
}

void Key ()
{
if (PINB.0 == 0)
   {

   
      if (TCNT2 == 125)
        {
         s++;
          TCNT2 = 0;
        }
        
        if (s == 40)
        {
        PORTD = 0b11011111;
        }
        
        if (s == 80)
        {
        PORTD = 0b11111111;
		}
		
		if (s > 80)
		{
			s = 0;
		}
   }
}

void main(void)
{

#pragma optsize-
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif


DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0); 
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);
 
DDRD=0xFF; 
PORTD=0xFF;

TCCR0A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (0<<WGM21) | (0<<WGM20);
TCCR0B=(0<<WGM02) | (1<<CS02) | (0<<CS01) | (1<<CS00);
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;

TCCR2A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR2B=(0<<WGM22) | (1<<CS22) | (1<<CS21) | (1<<CS20);
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;


while (1)
      {
     Led ();
     Key (); 
      }
}

Вот всё, до чего я смог додуматься.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, Maxik777 said:

Вот всё, до чего я смог додуматься.

PORTD = 0b11111101; 

Так делать нельзя. Вы меняете состояние всего порта сразу. И, если какой-то бит порта (например 5-ый) был в нуле, то таким образом он станет в единицу.

Чтобы установить или сбросить бит надо применять чтение -> модификацию -> запись.

В коде это выглядит вот так

PORTD |= (1 << n); // n - номер бита (выдода) порта - установить бит
PORTD &= ~(1 << n); // n - номер бита (выдода) порта - сбросить бит
if (TCNT0 == 255)

Так делать небезопасно. В общем случае, пока программа выполняет другой кусок кода таймер за это время может прощелкать это значение и вы пропустите тот момент, когда TCNT0 был равен 255.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, всё работает правильно, я понял свою ошибку. А как это делается с одним таймером, как написал aaarrr ?

Если не сложно, подскажите пожалуйста.

Изменено пользователем Maxik777

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 минут назад, Maxik777 сказал:

как это делается с одним таймером

В основной программе ("основной цикл") всё обрабатываете, а в прерываниях от таймера лишь удлиняете его разрядность и общаетесь с периферией — читаете/пишете новые значения портов.

 

В частности, принятое в прерывании состояние кнопки требуется сперва очистить от дребезга её контактов, т.е. пропустить через цифровой фильтр в два-три прерывания длиной. А светодиодов, с персональным счётчиком у каждого, можно успевать сотни-тысячи обрабатывать, и все будут мигать индивидуально.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...