veiner.eu
ÚVODMCP2221IR PŘIJÍMAČOVLÁDÁNÍ ZVUKUSCHÉMA ZAPOJENÍHARDWARE
FIRMWARE
SOFTWARE

Dálkové ovládání PC - firmware

25.12.2018

Zdrojový kód zařízení, který je uvedený níže, je též možné si stáhnout. Kód byl vytvořen a přeložen užitím prostředí MPLAB X a překladače XC8.

Popis funkce zařízení

Na počátku proběhne inicializace. Jednak je nastavena pracovní frekvence 8 MHz a tudíž je 1 instrukce vykonána za 4/8000000 s = 0.5 us. Seriový port je nastaven na přenosovou rychlost 9600 a je povoleno přerušení při přijetí dat - toto přerušení je na počátku jako jediné aktivní. Nastaví se přerušení vyvolané při změně úrovně na vstupu pinu RA2. Časovač Timer0 je nastaven tak, že čítač přeteče každých 128 us. A nakonec se rozsvítí červená dioda na znamení, že proběhla inicializace.

Dále se čeká, než na vstup seriového portu přijde dvojice hodnot 0xAA, 0x55. Tyto hodnoty značí, že program v PC je připravený zpracovávat příchozí data. Jakmile zařízení tyto dva byty zaznamená, zakáže se přerušení na seriovém portu a naopak se povolí přerušení na pinu RA2 (výstup IR přijímače) při změně úrovně z vysoké na nízkou a přerušení generované časovačem Timer0. Taktéž RG dioda změní barvu z červené na zelenou, což signalizuje, že komunikace mezi zařízením a PC je funkční a jsou očekávána data z dálkového ovladače.

Po té, co jsou nastaveny hodnoty některých proměnných, je mikrokontroler uveden do režimu spánku, ze kterého může být aktivován přerušením na pinu RA2 (signál z IR přijímače).

Jakmile je detekována změna úrovně, je zaznamenán čas, po který daná úroveň trvala a režim detekce změny úrovně pomocí přerušení je změněn na opačný (pokud byla nyní detekována změna H->L, bude od teď detekována změna L->H ; a naopak). Je kontrolováno, aby čas, po který je úroveň na pinu RA2 nízká na počátku, byl mezi 8 a 10 ms (má být 9 ms). Pokud je tato podmínka splněna, je to považováno za začátek příchozích dat protokolu NEC.

Konec přenosu je detekován podmínkou, že úroveň signálu je vysoká po dobu delší než 2.56 ms a počet příchozích bitů je větší než 32. V tom případě jsou zakázána všechna přerušení, dioda krátce změní barvu ze zelené na červenou po dobu 50 ms a následně zpět na zelenou. Data jsou odeslána ve formě textového řetězce do PC. Po odeslání jsou všechna přerušení povolena a mikrokontroler je uveden do spánku, ze kterého opět může být probuzen přerušením vyvolaným změnou úrovně na výstupu IR přijímače.

Zdrojový kód

#include <xc.h>
#define _XTAL_FREQ 8000000

// CONFIG1
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF

// CONFIG2
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = OFF
//#pragma config BORV = LO
#pragma config LVP = OFF

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

void sendSerialString(BYTE *s)
{
  while (*s)
  {
    TXREG=*(s++);
    while (!TRMT)
      ;
  }
}

#define RED_LED_TRIS TRISCbits.TRISC1
#define RED_LED_LAT LATCbits.LATC1
#define GREEN_LED_TRIS TRISCbits.TRISC0
#define GREEN_LED_LAT LATCbits.LATC0

#define FALLING 0
#define RISING 1

volatile BYTE data;
volatile unsigned int c128us;
volatile BYTE edge;

volatile unsigned int times0[40];
volatile unsigned int times0count;
volatile unsigned int times1[40];
volatile unsigned int times1count;
unsigned int htime;

volatile BYTE dataready;

BYTE s[50];

volatile unsigned int d;

void main(void)
{
  OSCCON = 0b01110000;//8 MHz, 0.5 us / 1 instruction
  
  ANSELA=ANSELB=ANSELC=0;
  TRISA=TRISB=TRISC=0;
  LATA=LATB=LATC=0;
  
  RED_LED_TRIS=0;
  GREEN_LED_TRIS=0;
  
  //setup USART
  TRISBbits.TRISB5=1;//RX
  TRISBbits.TRISB7=0;//TX
  SPBRG=51;//9600->51.08
  TXSTAbits.BRGH=1;
  TXSTAbits.SYNC=0;
  RCSTAbits.SPEN=1;
  TXSTAbits.TXEN=1;
  RCSTAbits.CREN=1;
  INTCONbits.PEIE=1;
  
  //timer0
  OPTION_REGbits.TMR0CS=0;
  OPTION_REGbits.PSA=1;
  INTCONbits.TMR0IF=0;
  INTCONbits.TMR0IE=0;
  
  //interrupt pin RA2  
  TRISAbits.TRISA2=1;
  INTCONbits.INTF=0;
  INTCONbits.INTE=0;
  OPTION_REGbits.INTEDG=0;//interrupt pin RA2 rising edge = 1, falling edge = 0
  
  edge=FALLING;
  data=0;
  d=0;
  RED_LED_LAT=1;
  
  PIR1bits.RCIF=0;
  PIE1bits.RCIE=1;
  INTCONbits.GIE=1;
  
  while (d!=0xAA55)
    ;
  
  PIE1bits.RCIE=0;
  INTCONbits.GIE=0;
  
  RED_LED_LAT=0;
  GREEN_LED_LAT=1;
  
  c128us=0;
  times0count=0;
  times1count=0;
  dataready=0;
  
  BYTE x[4];
  
  __delay_ms(200);
  
  INTCONbits.TMR0IF=0;
  INTCONbits.TMR0IE=1;
  INTCONbits.INTF=0;
  INTCONbits.INTE=1;
  INTCONbits.GIE=1;
  
  SLEEP();
  
  while (1)
  {
    if (dataready)
    {
      INTCONbits.GIE=0;
      GREEN_LED_LAT=0;
      RED_LED_LAT=1;
      __delay_ms(50);
      RED_LED_LAT=0;
      GREEN_LED_LAT=1;
      
      if ((times0count>=33) && (times1count>=33))
      {
        memset(x,0,4);
        for (BYTE i=0;i<4;i++)
          for (BYTE j=0;j<8;j++)
          {
            if (times1[1+8*i+j]>1400)
              x[i]|=(1<<(7-j));
          }
        sprintf(s,"%02X%02X%02X%02X",x[0],x[1],x[2],x[3]);
        sendSerialString(s);
      }
      
      times0count=0;
      times1count=0;
      dataready=0;
      edge=FALLING;
      OPTION_REGbits.INTEDG=FALLING;
      //__delay_ms(1000);
      INTCONbits.INTF=0;
      INTCONbits.INTE=1;
      INTCONbits.TMR0IF=0;
      INTCONbits.TMR0IE=1;
      INTCONbits.GIE=1;
      SLEEP();
    }
  }
}

void interrupt isr(void)
{
  if (INTCONbits.TMR0IE && INTCONbits.TMR0IF)//timer0, 128 us passed
  {
    ++c128us;
    if ((edge==FALLING) && (times0count>=33) && (c128us>20))
    {
      INTCONbits.TMR0IE=0;
      INTCONbits.INTE=0;
      INTCONbits.INTF=0;
      dataready=1;
    }
    else if (c128us>1000)
    {
      times0count=0;
      times1count=0;
      dataready=0;
      edge=FALLING;
      OPTION_REGbits.INTEDG=FALLING;
    }
    INTCONbits.TMR0IF=0;
  }
  
  if (INTCONbits.INTE && INTCONbits.INTF)//interrupt pin RA2
  {
    if (edge==FALLING)
    {
      if (times0count>0)
      {
        times1[times1count++]=((unsigned int) (TMR0>>1))+(c128us<<7);
      }
      OPTION_REGbits.INTEDG=RISING;
      edge=RISING;
      TMR0=0;
      c128us=0;
    }
    else
    {
      htime=((unsigned int) (TMR0>>1))+(c128us<<7);
      if (times0count==0)
      {
        if ((htime>=8000) && (htime<=10000))
          times0[times0count++]=htime;
      }
      else
      {
        times0[times0count++]=htime;
      }
      OPTION_REGbits.INTEDG=FALLING;
      edge=FALLING;
      TMR0=0;
      c128us=0;
    }
    INTCONbits.INTF=0;
  }
  
  if (PIE1bits.RCIE && PIR1bits.RCIF)//UART receive
  {
    data=RCREG;
    switch (data)
    {
      case 0xAA:
      {
        if (d==0)
          d=(((unsigned int) data)<<8);
        else
          d=0;
        break;
      }
      case 0x55:
      {
        if (d==0xAA00)
          d|=((unsigned int) data);
        else
          d=0;
        break;
      }
      default:d=0;
    }
    PIR1bits.RCIF=0;
  }
}