fundamental.cz

Elektronika

Další minutka

31.12.2017

Tato verze stojí opět na mikrokontroleru PIC16F88. Minutka dokonce obsahuje ten samý kus, který byl v původním zařízení.

Rozdíly oproti předchozí verzi

Rozdíly spadají do tří kategorií - zapojení, kód, vnější vzhled zařízení.

  1. Zapojení
    • Chybí červená LED, která signalizovala zapnutý stav zařízení. Tato LED je nadbytečná, protože stav zařízení je zřejmý ze stavu displeje: zapnuto - display svítí, vypnuto - display nesvítí.
    • Došlo ke změnám v přiřazení pinů PICu k periferiím. To je dáno potřebou využití přerušení na vstupu, použití PWM a též samozřejmě praktickými potřebami při fyzické realizaci zapojení.
    • Jinak je zapojení s drobnými odchylkami provedeno obdobným způsobem, jak je vidět v předchozím článku. Nové schéma se mi nechtělo kreslit, ale přiřazení pinů a funkcí je zřejmé ze zdrojového kódu, který uvádím níže.
  2. Kód
    • Kód v jazyce C jsem napsal celý znovu. Nejpodstatnější změna se týká použití přerušení pro indikaci stisku resetovacího tlačítka a pro signalizaci přetečení čítače časovače. Časovač je použit jednak při čekání na stisk číselných tlačítek a jednak při samotném odpočtu.
    • Dále je použitý PWM výstup pro buzení piezzo speakru.
  3. Vzhled
    • K výrobě jsem použil materiál a nástroje, které jsem měl k dispozici. Výsledek není nijak zvlášť hezký na pohled, ale zařízení funguje spolehlivě a přesně podle očekávání. Rozměry jsou menší než u původní verze (zhruba 90 x 60 x 30 mm).

Zdrojový kód

Kód je krátký, v paměti PICu zaberou data jen malou část.

Dále uvádím celý zdrojový kód minutky tak, jak byl po přeložení nahrán do PICu. Program by se jistě dal napsat lépe - sám v něm teď vidím například nějaké zbytečné proměnné apod. Ovšem do kódu jsem už nezasáhl, protože je odzkoušený a úpravou bych do něho mohl vnést chyby.

#define _XTAL_FREQ 2000000

// CONFIG1
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CCPMX = RB3
#pragma config CP = OFF

// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF

#include ‹stdio.h›
#include ‹stdlib.h›
#include ‹string.h›

#include ‹xc.h›

#define BT0RESET RB0
#define BT1 RB2
#define BT2 RB1
#define BT3 RA5
#define BT4 RA4
#define BT5 RA3

#define SOUND RB3

//  _   7
// |_|  9 10 6
// |_|. 1 2 4 5
#define LED1 RA6
#define LED2 RA7
#define LED4 RA0
#define LED5 RA1
#define LED6 RB7
#define LED7 RB6
#define LED9 RB5
#define LED10 RB4

#define LED_2_DIGITS RA2

typedef unsigned char BYTE;

void SetTwoDigitsLED(BYTE mode)
{
  LED_2_DIGITS=mode;
}

void SetLEDDecimalPoint(BYTE on)
{
  if (on==1)
    LED5=0;
  else
    LED5=1;
}

void SetLEDNumber(BYTE number)
{
  switch (number)
  {
    case 0:
    {
      LED1=0;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=0;
      LED10=1;
    }break;
    case 1:
    {
      LED1=1;
      LED2=1;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=1;
      LED9=1;
      LED10=1;
    }break;
    case 2:
    {
      LED1=0;
      LED2=0;
      LED4=1;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=1;
      LED10=0;
    }break;
    case 3:
    {
      LED1=1;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=1;
      LED10=0;
    }break;
    case 4:
    {
      LED1=1;
      LED2=1;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=1;
      LED9=0;
      LED10=0;
    }break;
    case 5:
    {
      LED1=1;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=1;
      LED7=0;
      LED9=0;
      LED10=0;
    }break;
    case 6:
    {
      LED1=0;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=1;
      LED7=0;
      LED9=0;
      LED10=0;
    }break;
    case 7:
    {
      LED1=1;
      LED2=1;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=1;
      LED10=1;
    }break;
    case 8:
    {
      LED1=0;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=0;
      LED10=0;
    }break;
    case 9:
    {
      LED1=1;
      LED2=0;
      LED4=0;
      LED5=1;
      LED6=0;
      LED7=0;
      LED9=0;
      LED10=0;
    }break;
  }
}

BYTE remaining_minutes_count;
BYTE digit1;
BYTE digit2;

BYTE button_number_pressed;

BYTE increment_digit2;
BYTE increment_digit1;

void ResetAll(void)
{
  remaining_minutes_count=0;
  digit1=0;
  digit2=0;
  increment_digit2=0;
  increment_digit1=0;
  SetTwoDigitsLED(0);
  SetLEDDecimalPoint(0);
  SetLEDNumber(0);
  TMR2ON=0;
  CCP1CON=0;//PWM off
}

BYTE ButtonPressTest(void)
{
  if (BT1==1)
    return(1);
  if (BT2==1)
    return(2);
  if (BT3==1)
    return(3);
  if (BT4==1)
    return(4);
  if (BT5==1)
    return(5);
  return(0);
}

BYTE mode;

BYTE counter;
unsigned int counter2;

void main(void)
{
  OPTION_REG=0;
  OPTION_REGbits.INTEDG=1;//RB0 interrupt rising edge
  OPTION_REGbits.PS2=1;//timer0 prescaler 111=1:256 -› 4*256*256/2000000=0.131072 s
  OPTION_REGbits.PS1=1;
  OPTION_REGbits.PS0=1;
  OSCCON=0b01011000;//2 MHz
  
  ANSEL=0x0;
  TRISA=0x0;
  TRISB=0x0;
  PORTA=0x0;
  PORTB=0x0;
  
  SetLEDNumber(0);
  
  TRISA3=1;
  TRISA4=1;
  TRISA5=1;
  
  TRISB0=1;
  TRISB1=1;
  TRISB2=1;

#define MODE_IDLE 1
#define MODE_SETTING_DIGIT_2 2
#define MODE_SETTING_DIGIT_1 3  
#define MODE_COUNTDOWN 4
#define MODE_RESET 5
  
  mode=MODE_IDLE;
  
  ResetAll();
  
  T2CON=0b00000000;//prescaler 1:1, TMR2ON=0, max 4*256*1/2000000=0.000512 s (1953.125 Hz)
  PR2=255;
  CCPR1L=0x80;//50 % = 512
  
  INT0IF=0;
  INT0IE=1;
  TMR0IF=0;
  TMR0IE=1;
  PEIE=1;
  GIE=1;
  
  while (1)
  {
    switch (mode)
    {
      case MODE_IDLE:
      {
        if ((button_number_pressed=ButtonPressTest())›0)
        {
          digit2=button_number_pressed;
          SetLEDNumber(digit2);
          counter=0;
          mode=MODE_SETTING_DIGIT_2;
          __delay_ms(300);
        }
      }break;
      case MODE_SETTING_DIGIT_2:
      {
        if (increment_digit2==1)
        {
          increment_digit2=0;
          if (digit2‹9)
            digit2++;
          else
            digit2=0;
          SetLEDNumber(digit2);
          __delay_ms(300);
          INT0IF=0;
          INT0IE=1;
          GIE=1;
          counter=0;
        }
        if ((button_number_pressed=ButtonPressTest())›0)
        {
          digit1=button_number_pressed;
          SetLEDNumber(digit1);
          SetLEDDecimalPoint(1);
          SetTwoDigitsLED(1);
          counter2=0;
          mode=MODE_SETTING_DIGIT_1;
          __delay_ms(300);
        }
        if (counter==15)
        {
          counter=0;
          if (digit2›0)
          {
            mode=MODE_COUNTDOWN;
            remaining_minutes_count=10*digit1+digit2;
            TMR0=0;
          }
        }
      }break;
      case MODE_SETTING_DIGIT_1:
      {
        if (increment_digit1==1)
        {
          increment_digit1=0;
          if (digit1‹9)
            digit1++;
          else
            digit1=0;
          SetLEDNumber(digit1);
          SetLEDDecimalPoint(1);
          __delay_ms(300);
          INT0IF=0;
          INT0IE=1;
          GIE=1;
          counter=0;
        }
        if (counter==15)
        {
          counter2=0;
          if ((digit1›0) || (digit2›0))
          {
            mode=MODE_COUNTDOWN;
            BYTE pom=digit1;
            digit1=digit2;
            digit2=pom;
            remaining_minutes_count=10*digit1+digit2;
            TMR0=0;
          }
        }
      }break;
      case MODE_COUNTDOWN:
      {
        if ((counter2%14)==0)
        {
          SetLEDNumber(digit2);
          SetLEDDecimalPoint(1);
        }
        else if((counter2%7)==0)
        {
          if (remaining_minutes_count>9)
          {
            SetLEDNumber(digit1);
          }
          SetLEDDecimalPoint(0);
        } 
        if (counter2==458)
        {
          counter2=0;
          if (--remaining_minutes_count›0)
          {
            digit1=remaining_minutes_count/10;
            digit2=remaining_minutes_count%10;
            if (remaining_minutes_count‹10)
            {
              SetTwoDigitsLED(0);
            }
          }
          else
          {
            SetLEDNumber(0);
            TMR2ON=1;
            while (mode==MODE_COUNTDOWN)
            {
              CCP1CON=0b00001100;//PWM on
              __delay_ms(50);
              CCP1CON=0;
              __delay_ms(50);
            }
          }
        }
      }break;
      case MODE_RESET:
      {
        ResetAll();
        mode=MODE_IDLE;
        INT0IF=0;
        INT0IE=1;
        GIE=1;
      }break;
    }
  }
  
  return;
}

void interrupt isr(void)
{
  if ((INT0IE==1) && (INT0IF==1))
  {
    switch (mode)
    {
      case MODE_SETTING_DIGIT_2:
      {
        INT0IE=0;
        GIE=0;
        increment_digit2=1;
      }break;
      case MODE_SETTING_DIGIT_1:
      {
        INT0IE=0;
        GIE=0;
        increment_digit1=1;
      }break;
      case MODE_COUNTDOWN:mode=MODE_RESET;break;
      default:INT0IF=0;break;
    }
  }
  
  if ((TMR0IE==1) && (TMR0IF==1))
  {
    switch (mode)
    {
      case MODE_SETTING_DIGIT_2:
      case MODE_SETTING_DIGIT_1:counter++;break;
      case MODE_COUNTDOWN:counter2++;break;
      default:break;
    }
    TMR0IF=0;
  }
}