Форум по микроконтроллерам: pic12f629 b обработка прерывания - Форум по микроконтроллерам

Перейти к содержимому

Страница 1 из 1
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

pic12f629 b обработка прерывания

#1 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 18 Май 2017 - 13:21

Здравствуйте!
Пытаюсь освоить программирование МК. Написал первую прогу - помигал светодиодами, понажимал кнопками. Работает. Хочу написать прогу под следующую схему: нажатие кнопки - включение светодиода, дальнейшее нажатие с плавным увеличением яркости. Для этого необходимо использование прерываний и ШИМ. На прерывании у меня получилась первая затычка, не могу понять почему не работает.

#include <xc.h>
#include <pic.h>
#pragma config BOREN = OFF, MCLRE = OFF, PWRTE = ON, WDTE = OFF, FOSC = INTRCIO
#define _XTAL_FREQ 4000000
#define LEDG GPIO2
#define LEDR  GPIO1
#define knopka GPIO4
#define knopka2 GPIO5

unsigned char cnt=0;
void interrupt isr(void)
{ 
	if(GPIF)
	{
		cnt++;
		GPIF=0;
	}
}
void main() 
{
    GIE = 0;
	PEIE = 0;
    GPIE = 0;

    //TRISIO = 0x8;
    CMCON = 0x07;
    TRISIO = 0b111001;
    GPIO = 0;
    IOCB4 = 1;
    
    PEIE = 1;
	GIE  = 1;
    GPIE = 1;
    while (1)
    {
        if (cnt==2)
        {__delay_ms(100); LEDG=1; LEDR=0; __delay_ms(100);}
        if (cnt==4)
        {__delay_ms(100); LEDG=0; LEDR=1; __delay_ms(100); cnt=0;}
    }
}

-1

#2 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 888
  • Регистрация: 15 Февраль 11

Отправлено 18 Май 2017 - 23:58

Цитата

На прерывании у меня получилась первая затычка, не могу понять почему не работает
Какая затычка ? Что не работает ?
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#3 Пользователь офлайн   boatcall 

  • Знаток
  • PipPip
  • Группа: Пользователи
  • Сообщений: 70
  • Регистрация: 29 Октябрь 14
  • ГородКрасноярск

Отправлено 19 Май 2017 - 08:07

Смотрел по диагонали, но снимать флаг прерывания GPIO Change Interrupt у PIC надо по-другому, хотя-бы так:

Просмотр сообщенияyeye77 (18 Май 2017 - 13:21) писал:

...
void interrupt isr(void)
{ 
	if( GPIE && GPIF )
	{
		cnt++;
		GPIO;
	}
}
...


Т.е. нужно просто обратиться к порту, читать, записать, что угодно, в общем. В даташите они пишут:

Цитата

The user, in the Interrupt Service Routine, clears the interrupt by:
a) Any read or write of GPIO. This will end the mismatch condition, then,
b)Clear the flag bit GPIF.

Может в Microchip что-то поменяли, но на ранних PIC только чтением порта флаг прерывания снимался, писать GPIF=0 было вообще необязательно, как теперь в нынешних с этим обстоит, не знаю, может быть так теперь GPIF = 0 ; нужно писать обязательно, но чтение порта нужно делать по-любому.

Сообщение отредактировал boatcall: 19 Май 2017 - 08:17

0

#4 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 19 Май 2017 - 10:41

Спасибо за отклик.
Alex, под неработой подразумевал отсутствие реакции на нажатие кнопки.
boatcall, странно, видимо чтение флага перепутал с чтением портов, добавил то, что вы посоветовали, заработало. Но работает странно, доходит до определенного кол-ва нажатий кнопки и горит только один светодиод. Видимо нужно добавлять задержки. Можно ли их добавлять в подпрограмму обработки прерываний? Как лучше это решить? И вообще, правильно ли будет совместить обработку прерываний от кнопки и таймера с учетом поставленной задачи?

Еще принципиальный вопрос. В поиске решения читал много мнений, касающегося подпрограммы обработки прерываний. Считается, что в подпрограмме лучше ничего не делать кроме чтения/записи и сброса флагов, остальные вычисления/действия производить вне этой подпрограммы. Так ли это?
0

#5 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 888
  • Регистрация: 15 Февраль 11

Отправлено 19 Май 2017 - 11:26

попробуйте так :
        if (cnt>=4)
        {__delay_ms(100); LEDG=0; LEDR=1; __delay_ms(100); cnt=0;}
        else if (cnt>=2)
        {__delay_ms(100); LEDG=1; LEDR=0; __delay_ms(100);}

Возможно, значение счётчика перескакивает за значение 4 до того, как программа доходит до условия.
И на будущее, примите за правило вставлять в условия сравнения операторы "больше" или "меньше", если не нужно явное сравнение на равенство.
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#6 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 19 Май 2017 - 12:31

Интересно. Для четкой фиксации нажатий кнопок я использовал след.конструкцию:
if (knopka==1 && cnt==0)
        {__delay_ms(20); LEDG=1; __delay_ms(20);  LEDR=0; 
        __delay_ms(20); while(knopka);
        cnt++;
        }


Работает как надо. Но состыковать это с ШИМ у меня не получилось (ШИМ честно содрал с раб.кода. Просто ШИМ без обработки кнопок работает). При первом нажатии диод загорается. Однако согласно задумке, инкрементации яркости и гашения светодиода не происходит. Ниже листинг:

#include <xc.h>
#pragma config BOREN = OFF, MCLRE = OFF, PWRTE = ON, WDTE = OFF, FOSC = INTRCIO
#define _XTAL_FREQ 4000000
#define LEDG GPIO2
#define LEDR  GPIO1
#define knopka GPIO4

unsigned int t1value = 65535;
unsigned char cnt;
float ff;
// Interrupt service routine
void interrupt isr(void)
{ 
if(T0IF)
	{
		TMR0 = 217;
		TMR1H = (t1value>>8)&0xFF;
		TMR1L = (t1value&0xFF);
		LEDG = 1;	// Pin on
		TMR1ON = 1;
		T0IF = 0;
	}
	if(TMR1IF)
	{
		TMR1ON = 0;
		LEDG = 0;	// pin off
		TMR1IF = 0;
	}
}
void pwm_init(void)
{
	// Timer 0
	OPTION_REG = 0xD7;		// 0b 1101 0111  T0CS=0, PSA=0, 1:256
	TMR0 = 217;		// (Fosc/4)/(256*39) = 100Hz
	T0IF = 0;
	T0IE = 1;

	// Timer 1
	T1CON = 0x00;			// 0b 0000 0000
	TMR1H = (t1value>>8)&0xFF;	//  Timer for 10KHz multiples
	TMR1L = (t1value&0xFF);
	TMR1IF = 0;
	TMR1IE = 1;
}
void pwm_on(void)
{
	PEIE = 1;
	GIE  = 1;
}
void pwm_off(void)
{
GIE = 0;
PEIE = 0;
}

void main() 
{
    TRISIO = 0b111001;
    GPIO = 0;
    //LEDR=1;
    int prir=600;
    //float ff;
    //int l;
    cnt=0;
    pwm_init();
    while (1)
    {
        if (knopka==1 && cnt==0)
          {__delay_ms(20);
           pwm_on();
          __delay_ms(20);
          while(knopka);
          cnt++;
        }
        if (knopka==1 && cnt>1)    
          {__delay_ms(20);
          ff=cnt%10;
          if (ff==0){
            LEDR=1;
            __delay_ms(500); 
            LEDR=0;
          }
          while(knopka);
          __delay_ms(20);
          cnt++;
        }
        if (cnt==6)
        {    LEDG=0;
             pwm_off();
        }
        t1value = 65535-(prir*cnt);
        __delay_ms(100);
    }
}



Поэтому пытаюсь кнопки через обработчик прерываний.

Сообщение отредактировал yeye77: 19 Май 2017 - 14:29

0

#7 Пользователь офлайн   boatcall 

  • Знаток
  • PipPip
  • Группа: Пользователи
  • Сообщений: 70
  • Регистрация: 29 Октябрь 14
  • ГородКрасноярск

Отправлено 22 Май 2017 - 05:38

Читать полностью лениво, но всё-же, для начала опишите обработчик прерываний подобным образом:

...
void interrupt isr(void)
{
if(T0IE && T0IF)
        {
...
        }
        if(TMR1IE && TMR1IF)
        {
...
        }
}
...


Не забывайте, что при отпускании кнопки, так-же происходит "дребезг":
if (knopka==1 && cnt==0)
        {
        __delay_ms(20); // Кнопка нажата
        ...
        while(knopka);
        __delay_ms(20); // Кнопка отпущена
        ...
        cnt++;
        }


Чтобы добиться самому полного понимания алгоритма программы, нарисуйте блок-схему программы, не упуская такие детали типа "дребезг", прерывания и.т.д.

Сообщение отредактировал boatcall: 22 Май 2017 - 05:52

1

#8 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 22 Май 2017 - 15:41

Спасибо за помощь!
Сделал, вроде получилось, и даже работает как задумал. Нажатие включает диод, далее увеличение яркости до 10 нажатий, затем плавное уменьшение до 20 нажатий, больше выключение. Обработку прерываний использовал только для ШИМ (нажатие кнопки обрабатывается без него). Код, в общем, тот же.
Теперь хочу слегка изменить задачу: долгое нажатие отключает/включает диод, короткое - инкрементирует/декрементирует. Как лучше реализовать? Использовать таймер, но оба таймера отведены для ШИМ?
0

#9 Пользователь офлайн   boatcall 

  • Знаток
  • PipPip
  • Группа: Пользователи
  • Сообщений: 70
  • Регистрация: 29 Октябрь 14
  • ГородКрасноярск

Отправлено 22 Май 2017 - 16:15

Просмотр сообщенияyeye77 (22 Май 2017 - 15:41) писал:

...Использовать таймер, но оба таймера отведены для ШИМ?

Использовать TMR0, разумеется. Я в своё время делал как-то так. TMR0 был настроен на 250мс ( непринципиально, пересчитаете под себя ). В прерывании инкрементировалась глобальная переменная pass. По её значению можно было судить о количестве прерываний, а следовательно о времени после обнуления, при pass=1 прошло 250мс, при pass=2 прошло 500мс, при pass=3 прошло 750мс и.т.д. Выбиралось условное время "длительного" нажатия, к примеру 2с ( pass=8 ). Тогда в главном цикле будет что-то такое:
...
void interrupt isr(void)
{
if(T0IE && T0IF)
        {
...
        pass ++ ; // Прошли очередные 250мс
        }
        if(TMR1IE && TMR1IF)
        {
...
        }
}
...

if (knopka==1 && cnt==0)
        {
        pass = 0; // Обнуляем количество проходов
        __delay_ms(20); // Кнопка нажата
        ...
        while( knopka && pass <= 8 );// Проверка опускания кнопки, или "длительного нажатия". 
                                     // При pass >= 8 выход 
                                     // из цикла
        if( pass >= 8 )
        {
        ...// было длительное нажатие, делаем что-то
        }
        // Иначе, отпускание кнопки до таймаута
        __delay_ms(20); // Кнопка отпущена
        ...
        }
...

Полагаю, основную мысль я пояснил. Не сомневаюсь, что есть ещё варианты, я делал так, потому-что у меня тоже других таймеров уже не было. Такой подход в целом удобен тем, что из TMR0 делается некий генератор системных тиков. Если сделать его период, к примеру 1мс, то очень многие вещи из "человеческого диапазона", легко реализуются, т.к. таких переменных, как pass, может быть несколько, но дискрета будет единой, зато предсказуемой.

Сообщение отредактировал boatcall: 22 Май 2017 - 16:16

1

#10 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 22 Май 2017 - 17:28

Спасибо за оперативный ответ. Мысль понятна.
До Вашего ответа хотел испробовать вариант со счетчиком без таймера, такой:

cnt2=0;
while(knopka)
{
cnt2++;
}


и далее расчет/угадывание его приемлемого значения. Но Ваш вариант кажется более удобным.

while( knopka && pass <= 8 ); - здесь включение pass в условие разве не лишнее, т.к. его наращивание в таймере не зависит от длительности удержания кнопки?

Пока оба варианта не заработали :)

Сообщение отредактировал yeye77: 22 Май 2017 - 17:29

0

#11 Пользователь офлайн   mf323 

  • Знаток
  • PipPip
  • Группа: Пользователи
  • Сообщений: 83
  • Регистрация: 27 Январь 13
  • ГородУфа

Отправлено 22 Май 2017 - 19:49

Опрос кнопки с частотой в 20ms и никакого дребезга никогда не будет. Всегда использую один и тот же принцип. По факту изменения (нажатия/отжатия) затираешь таймер (свою переменную). А сам таймер работает всегда и независимо от чего-либо, то есть тупо инкрементируется раз в 20ms (и скажем на FF самоостанавливается), после него проверяешь нажата или отжата, считай разветляешься на две области, и в каждой из них сравниваешь на совпадение текущее значение таймера и твоей константы. Совпало, делаешь дело, или проходишь мимо(через 20ms следующая такая проверка). То есть всё просто, один таймер считает сколько нажата и он же сколько отжата. Правда я не знаю как это будет выглядеть на си, поскольку пишу на асме, но главное просто осознать сам принцип и отбросить ненужное.
1

#12 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 31 Май 2017 - 18:08

При компиляции выдает следующую ошибку (setRGBsequence - это процедура, может ссылатся и на _main, если что-то изменить):

(1358) no space for _setRGBsequence temps

В описании компилятора эта ошибка поясняется так:
"The code generator was unable to find a space large enough to hold the temporary
variables (scratch variables) for this program."

Что имеется в виду - не хватает места для врем.переменных для данного чипа? И как выйти из такого положения?
0

#13 Пользователь офлайн   Alex 

  • Убиватель МК
  • PipPipPipPip
  • Группа: Пользователи
  • Сообщений: 1 888
  • Регистрация: 15 Февраль 11

Отправлено 31 Май 2017 - 23:17

Цитата

не хватает места для врем.переменных для данного чипа?
Да, не хватает памяти.

Цитата

И как выйти из такого положения?
Или оптимизировать свой код, или брать чип с бОльшим объёмом памяти.
Не говорите что мне делать, и я не скажу куда Вам идти !
0

#14 Пользователь офлайн   yeye77 

  • Новичок
  • Pip
  • Группа: Пользователи
  • Сообщений: 8
  • Регистрация: 17 Май 17

Отправлено 05 Июнь 2017 - 11:58

Реализовал короткое и долгое нажатие. При коротком вкл/выкл, долгое - нарастание/убывание по кругу. Добавил еще двойной клик, по задумке нарастание яркости до 50%, но работает некорректно - при двойном нажатии может пройти через выкл/вкл. Как лучше это реализовать?

/*
 Short button press turn on/off LED, long press increment/decrement LED light
 Double click sets the value of LED light to 50%
 */
#include <xc.h>
#pragma config BOREN = OFF, MCLRE = OFF, PWRTE = ON, WDTE = OFF, FOSC = INTRCIO
#define _XTAL_FREQ 4000000
#define LEDG GPIO2 
#define LEDR  GPIO1  
#define knopka GPIO4 
//#define TIME_SHORT_PRESS 25 // 250 msec ( 25 * 10us )
//#define TIME_DOUBLE_PRESS 65
#define TIME_SHORT_PRESS 35 // 350 msec ( 35 * 10us )
#define TIME_DOUBLE_PRESS 85
#define prir 60

unsigned int t1value = 65535;
unsigned char cnt, dbclk;
unsigned char BUTTON_FIRST_PRESSED = 0;
volatile unsigned short cnt2, cnt3;

// Interrupt service routine
void interrupt isr(void)
{ 
if(T0IE && T0IF)
	{
		TMR0 = 217;
		TMR1H = (t1value>>8)&0xFF;
		TMR1L = (t1value&0xFF);
        cnt2++;
        //if (dbclk==1)
        cnt3++;
        if (BUTTON_FIRST_PRESSED==1){
            LEDG = 1;	// Pin on
		}   
        TMR1ON = 1;
		T0IF = 0;
	}
	if(TMR1IE && TMR1IF)
	{
		TMR1ON = 0;
		LEDG = 0;	// pin off
		TMR1IF = 0;
	}
}
void pwm_init(void)
{
	// Timer 0
	//OPTION_REG = 0xD7;		// 0b 1101 0111  T0CS=0, PSA=0, 1:256
	//OPTION_REG = 0b00000111;
    OPTION_REG = 0b11010111;
    TMR0 = 217;		// (Fosc/4)/(256*39) = 100Hz
    T0IF = 0;
	T0IE = 1;

	// Timer 1
	T1CON = 0x00;			// 0b 0000 0000
	TMR1H = (t1value>>8)&0xFF;	//  Timer for 10KHz multiples
	TMR1L = (t1value&0xFF);
	TMR1IF = 0;
	TMR1IE = 1;
}
void pwm_on(void)
{
	PEIE = 1;
	GIE  = 1;
}
void pwm_off(void)
{
    GIE = 0;
    PEIE = 0;
}
void set_off(void)
{
    cnt=0;
    dbclk=0;
    t1value=65535;
    LEDG=0;
    BUTTON_FIRST_PRESSED=0;
}
void dimm(void)
{
        if (cnt<11)
        {
            t1value = t1value-(prir*cnt);
            // t1value = t1value-(prir*cnt*cnt);
        }
        else
        {
            t1value = t1value +(prir*(21-cnt));
            if (cnt==20) 
                cnt=1;
            //t1value = t1value +(prir*(21-cnt)*(21-cnt));
        }
}
void main() 
{
    TRISIO = 0b111001;
    GPIO = 0;
    CMCON = 0x07;
    cnt=0;
    dbclk=0;
    pwm_init();
    pwm_on();
    while (1)
    {
        if (knopka==1){
            cnt2=0;
            __delay_ms(20);
            while(knopka)
            {
                if (BUTTON_FIRST_PRESSED==1) 
                {
                    if (cnt2>TIME_SHORT_PRESS){
                        cnt++;
                        dimm();
                        __delay_ms(150);
                    }
                }
            };            
            if (cnt2<=TIME_SHORT_PRESS){
                if (BUTTON_FIRST_PRESSED==0){
                    BUTTON_FIRST_PRESSED=1;
                    dbclk=1;
                    dimm();
                    cnt3=0;
                }
                else
                {   
                    if (cnt3<=TIME_DOUBLE_PRESS && dbclk==1)
                    {
                        dbclk=0;
                        cnt=5;
                        dimm();
                        cnt3=0;
                    } else    
                    {   
                        if (cnt3<=TIME_DOUBLE_PRESS)
                        {cnt=5;
                        dimm();
                        cnt3=0;}
                        else
                        set_off();
                    }
                    //set_off(); 
                }
            } 
            __delay_ms(20);
            }
        __delay_ms(5);
    }
}




0

Поделиться темой:


Страница 1 из 1
  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

1 человек читают эту тему
0 пользователей, 1 гостей, 0 скрытых пользователей