Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

мултифункционален бутон + PWM
http://mcu-bg.com/mcu_site/viewtopic.php?f=7&t=8966
Страница 1 от 5

Автор:  pachuka [ Сря Юни 22, 2011 12:06 am ]
Заглавие:  мултифункционален бутон + PWM

от няколко седмици се боря да направя един контролер за 2 RGB лампи и една и една обикновенна :)

за целата ми трябва да направя бутон който да различва click, double click and hold работа с MPLAB под С

това е кода до който стигнах последно но така не работи бутона изобщо не засича. Тръгнал съм да го правя със инттерупт защото искам да вкарам и функция която да превърта плавно цветовете .
Код:
#include <htc.h>
#include <pic.h>
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

unsigned char but;
unsigned char mode;

#define BUTTON RB5
#define MAX_DB_CNT 20/2

volatile unsigned char PB_dbstate;
volatile unsigned char PB_change;

void main()
{
TRISA = 0b00000000;
TRISB = 0b11110000;
PORTA = 0b10000000;
PORTB = 0b00000000;
CMCON = 7;
T0CS=1;
PSA=1;
PB_dbstate=1;
PB_change=0;
mode=1;
but=1;
T0IE = 1;
ei();
   for(;;)
   {
   if (PB_change==1)
      {
      but++;
      PB_change=0;
      if(but==6){but=1;}
      }
   if (PB_change==2)
      {
      mode++;
      PB_change=0;
      if (mode==4){mode=1;}
      }
   if (PB_change==3)
      {
      PB_change=0;
      if(RA7==1){RA7=0;}else{RA7=1;}
      }

       if (mode==1){                   
      switch(but)
      {

         case 1:// if screen == 1 run this code!
         PORTB = 0b00101110;
         break;

         case 2:// if screen == 2 run this code!
         PORTB = 0b00101100;
         break;

         case 3:// if screen == 2 run this code!
         PORTB = 0b00101000;
         break;
         case 4:// if screen == 2 run this code!
         PORTB = 0b00100010;
         break;
         case 5:// if screen == 2 run this code!
         PORTB = 0b00100110;
         break;
   
      }
         }
  if (mode==2){                   
      switch(but)
      {

         case 1:// if screen == 1 run this code!
         PORTA = 0b00001100;
         RB0=1;
         break;

         case 2:// if screen == 2 run this code!
         PORTA = 0b00001100;
         RB0=0;
         break;

         case 3:// if screen == 2 run this code!
         PORTA = 0b00001000;
         RB0=0;
         break;
         case 4:// if screen == 2 run this code!
         PORTA = 0b00000000;
         RB0=1;
         break;
         case 5:// if screen == 2 run this code!
         PORTB = 0b00000100;
         RB0=1;
         break;
   
      }
         }
         
   }   
}

void interrupt isr(void)
{
volatile unsigned char t0_cnt=0;
volatile unsigned char db_cnt=0;
volatile unsigned char long_cnt=0;
volatile unsigned char double_cnt=0;
volatile unsigned char flag=0;

TMR0 += 256-250+3;
T0IF =0;

++t0_cnt;
if (++t0_cnt==2000/250)
   {
   t0_cnt= 0;
   ++double_cnt;
   if (BUTTON == PB_dbstate)
      {
      t0_cnt=0;
      long_cnt=0;
      }
   else
      {
      ++db_cnt;
      ++long_cnt;
      
      if (db_cnt== MAX_DB_CNT)
         {
         PB_dbstate = !PB_dbstate;
         db_cnt=0;
         PB_change=1;
         ++flag;
         if(flag==2&&double_cnt<250)
            {
            flag=0;
            double_cnt=0;
            PB_change=2;
            }
         if(flag==2)
            {
            flag=0;
            double_cnt=0;
            }
         }
      if (long_cnt==200)
         {
         long_cnt=0;
         PB_change=3;
         }
      
      }
   }
}









много съм го закъсал , вече направих кутия за трафа и за контролера , направих и дистанционно което да го ползвам за бутон, направих и водоустоичивите лампи. Мислех че кода няма да ми е толквоа проблем но уви.... та моля някой да даде едно рамо , че немошах да си намеря протеус зада ги тествам направо на машината и трябва постоянно да свалям и закачам което направо вече ме побърква. Чипа е 16f627a

Автор:  michev [ Сря Юни 22, 2011 12:20 am ]
Заглавие: 

Не съм ти гледал целия код, и не съм се мъчил да разбирам какво си правил, но на пръв поглед виждам в прекъсването, че
Код:
t0_cnt
никога няма да стане повече от 2, и съответно няма да влезе в нито едно условие. На всяко викане на прекъсването t0_cnt = 0. Ако я направиш статична въпросната променлива, може и да има някакъв шанс.

Автор:  pachuka [ Сря Юни 22, 2011 12:42 am ]
Заглавие: 

даа бе защо съм ги оставил там а не при другите , още утре ще пробвам как ще е като ги извадя от там.

Но изобщо в правилна насока ли съм , някой правил ли е енещо подобно, по много техники се наблюдават такива бутони :)

Автор:  bkulev [ Сря Юни 22, 2011 9:31 am ]
Заглавие: 

Колега, тази тема е за "Академия".

И аз не съм ти гледал подробно кода, но мога да ти дам някои насоки, които така да се каже са общи при работа с бутони:
- Трябва да разпознаваш натискането и отпускането на бутона, т.е. преходите от едното в другото състояние, обикновенно се постига с прости логически функции приложени към текущото и предишното състояние
- Трябва да приложиш техника против трепкане на контактите, защото всички бутони повече или по малко го правят
- Има различни техники в прекъсване или не, но ако приложението ти не е сложно, не е необходимо да правиш прекъсване. В този случай имай предвид, че сканирането на бутоните се прави с ниска честота, т.е. рядко, защото не е необходимо да е бързо, все пак това е човешки интерфейс

Автор:  pachuka [ Съб Юни 25, 2011 4:54 pm ]
Заглавие: 

на теория ми е ясно как трябва да стане , но писането на кода ми куца.

Автор:  tolstolob [ Нед Юни 26, 2011 12:48 am ]
Заглавие: 

Koпи & пейст (с допълнението за double-click) от друга подобна тема. Не гарантирам тръгване от раз, щото е нахвърляно набързо.
Само трябва да измислиш как да викаш OneButtonTask() циклично.
Код:
// Таймерите: 0 = спрян, > 0 = работещ

#define TASK_PERIOD_MS                  10      // Период на OneButtonTask().
#define DEBOUNCE_TIME                   (50 / TASK_PERIOD_MS)    // Дебаунс време
#define DOUBLE_CLICK_TIME               (500 / TASK_PERIOD_MS)   // Време между две натискания, което минава за double-click
#define BTN_PRESSED                     0       // или 1, зависи как е вързан бутона
#define pBUTTON                         pA2     // тук си дефинираш пина, на който си вързал бутона.


typedef enum {
    IDLE,           // бутона е отпуснат
    DEBOUNCING,     // бутона е натиснат и тече дебаунс
    DEBOUNCED,      // бутона е натиснат, дебаунс времето е изтекло, състоянието на бутона е потвърдено
    DOUBLE_CLICKED,
    CANCELLED,      // бутона е отпуснат преди да е изтекло дебаунс времето
    RELEASING       // чакаме бутона да се отпусне
} TStates;


static TStates      еState = IDLE;

void OneButtonTask(void)
{
    static UInt16      debounceTimer = 0;
    static UInt16      doubleClickTimer = 0;

    switch (еState)
    {
    case IDLE:
        {
            if (BTN_PRESSED == pBUTTON)
            {
                // Стартираме дебаунс
                debounceTimer = DEBOUNCE_TIME;
                еState = DEBOUNCING;
            }

            break;
        }

    case DEBOUNCING:
        {
            // Натиснат ли е все още бутона?
            if (BTN_PRESSED == pBUTTON)
            {
                // Да. Дебаунс времето изтекло ли е?
                if (0 == debounceTimer)
                {
                    // Да, имаме натискане
                    еState = DEBOUNCED;
                }
            }
            else
            {
                // Бутона е отпуснат по време на дебаунс
                еState = CANCELLED;
            }

            break;
        }

    case DEBOUNCED:
        {
            // Тук можеш да свършиш каквото трябва при еднократно натискане на бутона
            // .....
            //
           
            // Стартираме таймера за double-click, само ако е изтекъл doubleClickTimer.
            if (0 == doubleClickTimer)
            {
                еState = RELEASING;
                doubleClickTimer = DOUBLE_CLICK_TIME;
            }
            else
            {
                еState = DOUBLE_CLICKED;
                doubleClickTimer = 0;
            }

            break;
        }

    case DOUBLE_CLICKED:
        {
            // Тук си при двоен клик.
            // .....
            //
           
            еState = RELEASING;
            break;
        }

    case CANCELLED:
        {
            // Тук можеш да свършиш нещо в случай, че бутона се отпусне преждевременно. Удобно е при дълъг дебаунс
            // .....

            еState = RELEASING;
            break;
        }

    case RELEASING:
        {
            // Бутона отпуснат ли е?
            if (BTN_PRESSED != pBUTTON)
            {
                // Да, почваме отначало
                еState = IDLE;
            }
           
            break;
        }

    default:
        {
            // По принцип тук не трябва да се влиза никога, но кой знае... :)
            еState = IDLE;
            break;
        }
    }

    // Накрая придвижваме таймерите
    if (0 < debounceTimer)
    {
        debounceTimer--;
    }

    if (0 < doubleClickTimer)
    {
        doubleClickTimer--;
    }
}


edit: Опс, намерих грешка. По-добре е таймерите в случая да са с 2 стейта.

Автор:  relsys [ Нед Юни 26, 2011 10:38 am ]
Заглавие: 

@tolostolob +1

Все едно аз съм го писал тоя код. Като покажа на някой обаче код написан в такъв стил, нещо не могат да го разберат. Толкова ли е сложно и неразбираемо? Според мен, стейт машините са най-разбираемия начин за описване на дадена задача.

Автор:  tolstolob [ Нед Юни 26, 2011 1:19 pm ]
Заглавие: 

Е, точно в тоя му вид не бих го ползвал в мой код, но за илюстрация - става.
@relsys, виждам, че работиш с пикове основно, ако искаш добави разните му там честоти, инициализации, фюзове и т.н. и да стикнем темата, че като гледам половината въпроси тук са за бутони :)

Автор:  pachuka [ Вто Юни 28, 2011 2:13 am ]
Заглавие: 

@tolstolob благодаря много ако го бях видял по рано твоя отговор щях да си спестя доста нерви. Но успях да се спрява и сам с бутона :) с което съм горд. ето го и целяи код може на някой да е полезен


Код:
#include <htc.h>
#include <pic16f627a.h>

/***** CONFIGURATION *****/
// ext reset, no code or data protect, no brownout detect,
// no watchdog, power-up timer enabled, 4MHz int clock


// Pin assignments
#define rele 7 // releto na  RA7
/***** GLOBAL VARIABLES *****/
unsigned char sGPB; // shadow copy of GPIO
volatile unsigned char flag=0;
volatile unsigned char but=0;
volatile unsigned char mode=0;
/***** MAIN PROGRAM *****/
void main()
{
// Initialisation
// setup ports
PORTA= 0;
PORTB = 0; // start with LED off
sGPB = 0; // update shadow
TRISB = 0b11110000; // configure LED pin
TRISA = 0b00000000;
CMCON =7;
mode=3;
but=1;
OPTION_REG = 0b11010111;
// configure Timer0:
//--0----- timer mode (T0CS = 0)
//----0--- prescaler assigned to Timer0 (PSA = 0)
//-----111 prescale = 256 (PS = 111)
// -> increment every 256 us
// configure interrupts
T0IE = 1; // enable Timer0 interrupt
ei(); // enable global interrupts
// Main loop
for (;;) {
// continually copy shadow GPB to port
RA7 = sGPB;
if (mode>=4){mode=1;}
if (mode==1){                   
  switch(but)
  {

   case 1:// if screen == 1 run this code!
   PORTB = 0b00101110;
   break;

   case 2:// if screen == 2 run this code!
   PORTB = 0b00101100;
   break;

   case 3:// if screen == 2 run this code!
   PORTB = 0b00101000;
   break;
   case 4:// if screen == 2 run this code!
   PORTB = 0b00100010;
   break;
   case 5:// if screen == 2 run this code!
   PORTB = 0b00100110;
   break;
   case 6:// if screen == 2 run this code!
   but=1;
   break;

  }
   }
  if (mode==2){                   
  switch(but)
  {

   case 1:// if screen == 1 run this code!
   PORTA = 0b00001100;
   RB0=1;
   break;

   case 2:// if screen == 2 run this code!
   PORTA = 0b00001100;
   RB0=0;
   break;

   case 3:// if screen == 2 run this code!
   PORTA = 0b00001000;
   RB0=0;
   break;
   case 4:// if screen == 2 run this code!
   PORTA = 0b00000000;
   RB0=1;
   break;
   case 5:// if screen == 2 run this code!
   PORTA = 0b00000100;
   RB0=1;
   break;
    case 6:// if screen == 2 run this code!
   but=1;
   break;

  }
   }
   if (mode==3)
   {
////////////////////////////////////////////////////////////////
/////////ТУК ДА PWM ФУНКЦИЯТА ЗА РЕДУВАНЕ НА ЦВЕТОВЕТЕ/////////   
//////////////////////////////////////////////////////////////
   }


} // repeat forever
}


/***** INTERRUPT SERVICE ROUTINE *****/
void interrupt isr(void)
{
static unsigned int t0_cnt=0;
static unsigned int dc_cnt=0;
static unsigned int hold_cnt=0;
// handle Timer0 interrupt
T0IF = 0;
   dc_cnt++;
if(dc_cnt==32){dc_cnt=31;}
if (RB5==0)
   {
   t0_cnt++;
   hold_cnt++;
   }
else if (RB5==1)
   {
   if(hold_cnt>=20)
      {
      flag=0;
      sGPB ^= 1;
      hold_cnt=t0_cnt=dc_cnt=0;
      }   

   else if(t0_cnt>=3 && flag==1 && dc_cnt<10)
      {
      flag=0;
      mode++;
      hold_cnt=t0_cnt=dc_cnt=0;
      }
   else if(t0_cnt>=3&&dc_cnt>10)
      {
      flag=1;
      hold_cnt=t0_cnt=dc_cnt=0;
      }
   if (flag==1&&dc_cnt>10)
      {
      flag=0;
      but++;
      hold_cnt=t0_cnt=dc_cnt=0;
      }
   else
      {

      hold_cnt=t0_cnt=0;
      }
   }

}



НО сега ми идва другия проблем. За да е всичко готово ми се иска там където е "mode==3" да се пускат да се връткат всички възможни цветове едновременно и на двата RGBta
демек
RA2=RB1= зелено
RA3=RB2= червено
RB0=RB3 = синьо

та моляви дайте едно бързо рамо че съм си дал срок до петък да е готово всичко и да ги монтирам :)
PIC а е 16f627a

че това със пулса не съм го рпавил досега и тези настройки са доста мътни
пробвах следния код но промлема е че така само светва плавно свето диода , а неиска да се изгася плавно


Код:
   while( 1 )                  // Loop forever...
   {
   GPIO2 = 1;               // Start pulse
   for(pulse = 0; pulse < duty; pulse++);      // Lengthen pulse if < duty
   GPIO2 = 0;               // End pulse
   for(pulse = 0; pulse < (freq - duty); pulse++);   // Continue until frequency
   duty++;                  // Increment Duty cycle
   }

Автор:  pachuka [ Сря Юни 29, 2011 3:52 am ]
Заглавие: 

Изображение

искам да постигна този ефект. някой можели поне да разясни малко как трябва да стане защото миголяма мешавица в главата .

вече имам прекъсване на с него мисля да увеличвама честотата, как да задам тази стойност да светка постепенно или да изгася постепенно даден пин.

Автор:  ToHu [ Сря Юни 29, 2011 7:38 am ]
Заглавие: 

Мммм какво конкретно те мъчи, трябват ти 3 PWM, аз не бих изпрал точно пик за целта но при тебе вече е факт. Остава ти въпроса с преливането на цветовете което може да стане по два начина, по сложния да измислиш алгоритъм който да мени едновременно трите PWM в желаната от теб комнинация, другия по лесен ако си решил да имаш примерно 64 стъпки на промяна да си зададеш табличните стойности и да ги въртиш. може и нещо междинно, да се движиш по таблични стойности, но да апроксимираш между тях, така ще имаш повече стъпки и плавност. Тук апроксимацията трябва да с епомисли дали винаги ще е линейна, или по скоро дали винаги ще е един тип функция без значение линейна или не.
Като знам колко много флаш имат тия пикове може и да не ти стигне :)

Автор:  valioman [ Сря Юни 29, 2011 9:22 am ]
Заглавие: 

Що бе Тони ако иска точно това от картинката .. ми то се вижда алгоритъма ..колко флаш ще иде за пускане на един таймер и в прекъсването му според зададените стойности да клатиш 3 те крака .
Май Цецо преди беше се бъзикал с един пикльо 12-ка на тази тема.
Е ако имаше 3 PWM щеше да е прекалено лесно ама нали за това сме пикоборци .. да ни е трудно :D

Автор:  RM [ Сря Юни 29, 2011 9:34 am ]
Заглавие: 

Правих и аз нещо за RGB ... ама не се сещам вече за какво беше и как изглеждаше.
Спомням си че плавно се смесваха цветовете, и имаше условие да не с включват трите диода заедно, нещо пазене на батерииката ... . Мисля че основното беше това :
Код:
#include "D:\3_led\main.h"
char flag,tmr1,i,j;
#bit rtmr=flag.0
#bit gtmr=flag.1
#bit btmr=flag.2
#bit xxxx=flag.3
//#use fast_io(ALL)   
#int_RTCC
void  RTCC_isr(void)
{
set_RTCC(200);
tmr1++;
if(tmr1==1){
   output_low(PIN_A2);
   if(rtmr)output_high(PIN_A0);
   output_low(PIN_A1);
            };
if(tmr1==2){
   output_low(PIN_A0);
   if(gtmr)output_high(PIN_A1);
   output_low(PIN_A2);
            };
if(tmr1>=3){
   output_low(PIN_A1);
   if(btmr)output_high(PIN_A2);
   output_low(PIN_A0);
   tmr1=0;
         };
}

#int_TIMER1
void  TIMER1_isr(void)
{
set_timer1(0);
//rtmr=1,gtmr=1,btmr=1;
}



void main()
{

   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);setup_wdt(WDT_18MS);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   setup_comparator(NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_RTCC);
//   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!
while(1){
restart_wdt();
rtmr=1,gtmr=0,btmr=0;
delay_ms(500);
for(i=1,j=15;i<=15;i++,j--){
   gtmr=1;
   rtmr=0;
   delay_ms(i);
   rtmr=1;
   gtmr=0;
   delay_ms(j);
};
rtmr=0,gtmr=1,btmr=0;
delay_ms(500);
for(i=1,j=15;i<=15;i++,j--){
   gtmr=0;
   btmr=1;
   delay_ms(i);
   btmr=0;
   gtmr=1;
   delay_ms(j);
};
rtmr=0,gtmr=0,btmr=1;
delay_ms(500);
for(i=1,j=15;i<=15;i++,j--){
   btmr=0;
   rtmr=1;
   delay_ms(i);
   btmr=1;
   rtmr=0;
   delay_ms(j);
};
};
}


Прикачени файлове:
3_led.rar [32.55 KiB]
139 пъти

Автор:  ToHu [ Сря Юни 29, 2011 12:41 pm ]
Заглавие: 

valioman написа:
Що бе Тони ако иска точно това от картинката .. ми то се вижда алгоритъма ..колко флаш ще иде за пускане на един таймер и в прекъсването му според зададените стойности да клатиш 3 те крака .
Май Цецо преди беше се бъзикал с един пикльо 12-ка на тази тема.
Е ако имаше 3 PWM щеше да е прекалено лесно ама нали за това сме пикоборци .. да ни е трудно :D


Е ако иска точно това от картинката нещата са прости, там се мени само едният ШИМ, но не знам какво преливане би се получило, иначе по тоя начин може д астане мног опо плавно значително по лесно, може и 10 битов да е ШИМ.

Автор:  pachuka [ Сря Юни 29, 2011 4:25 pm ]
Заглавие: 

точно това искам от катинката, преливане само най голямата яркост на цветовете. гледам че му викат дъга или rainbow само къде аз искам да има и бяло. във youtybe е пълно със готови таквиа проекти с пикчета 12фххх
таймера ми е на лице не е проблем да вкарам в него една променлива "x" която да се мени от 0до 255 за желано от мен време.

но как да задам на самия пин тая стойност ако напиша RB1=255; и RB2=120; свети само с максимална мощност RB2
та май това ми е проблема алгоритъма ще го измисля за връткането, но дори и таблично да го направя както предлага тони пак незнам как да задам да свети примерно наполовина единия лед.

Страница 1 от 5 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/