Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: прерывания PIC
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
HarieR
Помогите нубу с прерываниями, плиз. По нажатию кнопки RB0 (pic16f887) должно измениться состояние светодиодов на порту PORTD. Все вроде должно работать, ан-нет. Прошу помощи у PIC-гуру smile.gif . Ниже код:
Код
unsigned short counter;
void interrupt()
{
  if(INTCON.INTF)
  {
      counter++;
        if (counter > 8)
        {
          PORTD=~PORTD; //PORTD - меняем состояние на противопол  
          counter=0;
        }
    INTCON.INTF=0;
   }
}// interrupt

void Init()
{
   TRISB=0b00000001; //RB0 - вход
   TRISD=0b00000000; //PORTD - все выходы
   PORTD=0b00000000; //PORTD - не горят          
   OPTION_REG = 0x87;  
   INTCON.INTE=1; //включаем прерыв на порту RB0    
   INTCON.GIE=1;   //разрешаем все прерывания  
}// Init

void main()
{
   Init();
   while(1)
  {          
   }
}
Vlad27
Перепишите строку PORTD=~PORTD; на PORTD=^0xFF;
Не забывайте что при обращении к порту выполняется операция <чтение-модификация-запись>.
Как вариант, добавить переменную static volatile unsigned char temp=0;
temp ^= 0xFF;
PORTD = temp;
Ну, надеюсь, мысль понятна.
xemul
Устранение дребезга
Или Вы предполагали, что counter этим и занимается?
HarieR
переписал с учетом советов:
Код
static volatile unsigned char temp=0;
void interrupt()
{
     if(INTCON.INTF)
     {
      if (PORTB.F0==1)
      {
       delay_ms(20);
       if(PORTB.F0==1)
       {
        temp = 0xFF;
        PORTD = temp; // инверсия уровней на выводах PORTD
       }
      }
      INTCON.INTF=0;
     }
}// interrupt
void Init()
{
     TRISB=0b00000001;
     TRISD=0b00000000;
     PORTD=0b00000001;          
     OPTION_REG = 0x87;  
     INTCON.INTE=1;    
     INTCON.GIE=1;      
}// Init
void main()
{
     Init();
     while(1) {}
}

не работает laughing.gif
xemul
Цитата(HarieR @ May 5 2010, 12:42) *
переписал с учетом советов:
...
не работает laughing.gif

Где и какие советы Вы учли, не понял.
Ваш код может сделать только PORTD = 0xFF, но не наоборот. Уберите temp за ненадобностью (PORTD и так объявлен volatile) и напишите PORTD ^= 0xFF, как советовали, или PORTD = ~PORTD - в Вашем случае (TRISD=0b00000000;) без разницы.
delay_ms() в прерывании (да и вообще) - имхо, моветон.
Vlad27
Не делайте таких обработчиков прерываний, прерывания должны обрабатывться по-возможности быстро.
Лучше устанавливайте флаг, а задержку реализуйте в main.
Код
static volatile unsigned char temp=0;
static unsigned char flag;
void interrupt()
{
     if(INTCON.INTF)
     {
         temp ^= 0xFF;
         PORTD = temp; // инверсия уровней на выводах PORTD
         flag = 1;
         INTCON.INTF=0;
     }
}// interrupt

void main() {
   Init();
   while(1) {
     if (flag == 1) {
       flag = 0;
       delay_ms(20);
     }
   }
}
xemul
Буфер для вывода имеет смысл вводить, если требуется какая-либо синхронизация вывода или TRIS меняется в процессе. Здесь же достаточно:
Код
#define TRISD_MASK 0b01010101 // для разнообразия
#define PORTD_INVERSION_MASK (0b00001010 & ~TRISD_MASK) // инвертируем 1 и 3 биты

PORTD ^= PORTD_INVERSION_MASK;


static unsigned char flag; должен быть volatile, иначе if (flag == 1) { flag = 0; ... } в примере будет соптимизировано напрочь.
HarieR
сделал без флагов, обработки дребезга и т.д.
Код
void interrupt()
{
     if(INTCON.INTF)
     {
        
         PORTD=~PORTD;
    
         INTCON.INTF=0;
     }
}// interrupt
void Init()
{
     TRISB=0b00000001;
     TRISD=0b00000000;
     OPTION_REG = 0x87;  
     INTCON.INTE=1;      
     INTCON.GIE=1;      
}// Init
void main() {

   Init();
   while(1) { }
   }
}

результат 0
xemul
Цитата(HarieR @ May 5 2010, 13:51) *
сделал без флагов, обработки дребезга и т.д.
...
результат 0

Сразу не заметил. Вы уверены, что
void interrupt()
компилятору достаточно, чтобы сообразить, что это подпрограмма обработки прерываний?
HarieR
да, с прерыванием по TOIF все работает
Vlad27
Цитата(HarieR @ May 5 2010, 13:03) *
да, с прерыванием по TOIF все работает

То есть, прерывание INT не возникает? В таком случае тупой вопрос, как кнопка подключена?
xemul
Цитата(HarieR @ May 5 2010, 14:03) *
да, с прерыванием по TOIF все работает

Гуманный компилятор - экономит пальцы и клавиатуру.
Если проверяете в железе, то остаётся вариант - очень_чётная_кнопка, которая принципиально выдаёт только чётное число импульсов дребезга.
Ой, ещё одно (странное) предположение - на RB0 нет резистора привязки к gnd или vcc, но тогда прерывания должны легко генериться наводкой от пальца.
Не пробовали проверить в симуляторе?
HarieR
подключена нормально, на минус через резистор. Тестовые программы работают, эта - нет
xemul
Цитата(HarieR @ May 5 2010, 14:37) *
подключена нормально, на минус через резистор. Тестовые программы работают, эта - нет

"Нормально" в этом случае будет: vcc - R - RB0 - btn - gnd, а не RB0 - R - btn - gnd.
Или включите внутреннюю привязку для RB0 (RBPU = 0 в OPTION, WPUB = 1).
HarieR
добавил две волшебные строчки, программа ожила:
ANSEL = 0;
ANSELH = 0;
теперь рабочий код такой:
Код
void interrupt()
{
     if(INTCON.INTF)
     {

         PORTD=~PORTD;

         INTCON.INTF=0;
     }
}// interrupt
void Init()
{
     ANSEL  = 0;
      ANSELH = 0;
     TRISB=0b00000001;
     TRISD=0b00000000;
     OPTION_REG = 0x87;
     INTCON.INTE=1;
     INTCON.GIE=1;
}// Init
void main() {

   Init();
   while(1) { }
   }

подтяжка RB0 к gnd через 10k и внутренняя подтяжка PORTB отключена: /RPBU=1.
Вопрос: каким образом могли повлиять на работу регистры ANSEL и ANSELH?
@Ark
Цитата
Вопрос: каким образом могли повлиять на работу регистры ANSEL и ANSELH?

Непосредственным. Эти регистры задают режимы работы портов - аналоговый или цифровой. После старта (сброса) - устанавливается аналоговый режим. Нужно правильно настраивать порты, прежде чем ими пользоваться.
HarieR
Спасибо большое всем за помощь!


вешаю кнопку на RB4 и меняю программу, чтобы прерывание срабатывало с нее:
Код
void interrupt()
{
     if(INTCON.RBIF)
     {

         PORTD=~PORTD;

         INTCON.RBIF=0;
     }
}// interrupt
void Init()
{
     ANSEL  = 0;
     ANSELH = 0;
     TRISB=0b00010000;
     TRISD=0b00000000;
     OPTION_REG = 0b10000111;
     INTCON.RBIE=1;
     INTCON.GIE=1;
}// Init
void main() {

   Init();
   while(1) { }
   }

не работает! в чем ошибка, пните плиз
evc
Посмотрите регистр IOCB. Стр. 49/ds
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.