IR receiver works, next up decoding
authorJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 8 Mar 2021 20:09:12 +0000 (22:09 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 8 Mar 2021 20:09:12 +0000 (22:09 +0200)
src/main.c

index 2584292..995149e 100644 (file)
@@ -8,10 +8,12 @@
 #include "i2c.h"
 
 /* TODO
- * IR decoding, connected to PCINT0
+ * IR decoding, connected to PD2
  * enable/disable with switch on PCINT1
  * some of the buttons multiplexed on ADC0
  */
+#define PD_IR  2
+#define PD_ENSW        3
 
 #define EVQ_SIZE       64
 uint16_t evq[EVQ_SIZE];
@@ -40,6 +42,10 @@ static uint16_t dump_addr;
 static int dump_count;
 static int dbgmode;
 
+static volatile unsigned char pending;
+static volatile unsigned char enable, prev_enable;
+static volatile uint16_t ir_input;
+
 static void proc_cmd(char *input);
 static void printstat(void);
 static void printdump(void);
@@ -47,7 +53,7 @@ static void printdump(void);
 
 int main(void)
 {
-       uint16_t val;
+       unsigned char pend;
 
        /* tri-state everything and disable pullups */
        DDRB = 1;       /* B0: activity LED */
@@ -55,7 +61,23 @@ int main(void)
        DDRD = 0;
        PORTB = 0;
        PORTC = 0;
-       PORTD = 0;
+       PORTD = 1 << PD_ENSW;
+
+       /* setup external interrupts */
+       EICRA = 0x6;    /* INT0 falling edge, INT1 both */
+       EIMSK = 3;              /* enable INT0 and INT1 */
+
+       /* we'll use the timer 0 compare interrupt for sampling the IR input pulses
+        * we need to sample every 889us or 1124.86 Hz
+        * interrupt frequency = F_CPU / (prescaler * (1 - OCRnx))
+        * 14745600 / (64.0 * (1.0 + 204)) -> 1123.9 Hz
+        */
+       TCCR0A = 2;             /* clear on compare match */
+       TCCR0B = 3;             /* clock select: ioclock / 64 <- prescaler */
+       OCR0A = 192;    /* count top */
+
+       /* read initial enable switch state */
+       enable = (~PIND >> PD_ENSW) & 1;
 
        i2c_init();
 
@@ -67,6 +89,18 @@ int main(void)
        for(;;) {
                i2c_check_async();
 
+               cli();
+               pend = pending;
+               pending = 0;
+               sei();
+               if(pend & 1) {
+                       printf("IR: %04x (%s)\n", (unsigned int)ir_input & 0x7fff, ir_input & 0x8000 ? "err" : "ok");
+               }
+               if(pend & 2 && enable != prev_enable) {
+                       printf("enable: %d\n", enable);
+                       prev_enable = enable;
+               }
+
                if(have_input()) {
                        int c = getchar();
                        putchar(c);
@@ -82,6 +116,8 @@ int main(void)
                }
 
                /* read from queue and send over the serial port */
+               /*
+               uint16_t val;
                val = 0xffff;
                cli();
                if(evq_wr != evq_rd) {
@@ -94,6 +130,7 @@ int main(void)
                        unsigned char ev = val >> 8;
                        printf("%02x: %02x\n", ev, val & 0xff);
                }
+               */
        }
        return 0;
 }
@@ -213,3 +250,58 @@ static void printdump(void)
                putchar('\n');
        }
 }
+
+static unsigned char nsamples;
+static uint32_t samples;
+
+ISR(INT0_vect)
+{
+       /* ignore interrupts while a previous input is pending */
+       if(pending & 1) return;
+
+       /* IR going low */
+       ir_input = 0;
+       nsamples = 1;   /* we're starting in the middle of the first bit which should be 01 (=1) */
+       samples = 0;
+       EIMSK &= 0xfe;  /* disable further interrupts while decoding IR code */
+
+       TCNT0 = 96;     /* reset the counter to half the range so it'll trigger in the middle of the current pulse */
+       TIFR0 |= 1 << OCF0A;    /* clear pending interrupts */
+       TIMSK0 = 1 << OCIE0A;   /* enable output compare interrupt */
+
+       //PORTB ^= 1;
+}
+
+ISR(TIMER0_COMPA_vect)
+{
+       static unsigned char err;
+
+       PORTB ^= 1;
+
+       samples = (samples << 1) | (~(PIND >> PD_IR) & 1);
+       if((++nsamples & 1) == 0) {
+               if((samples & 3) == 0 || (samples & 3) == 3) {
+                       /* 00 or 11 are invalid sequences, we lost sync */
+                       err = 1;
+               }
+
+               /* 01->1, 10->0 */
+               ir_input = (ir_input << 1) | (samples & 1);
+       }
+
+       if(nsamples >= 28) {
+               if(err) {
+                       ir_input |= 0x8000;
+               }
+               pending |= 1;
+               err = 0;
+               TIMSK0 &= ~(1 << OCIE0A);       /* disable the sampling interrupt */
+               EIMSK |= 1;             /* re-enable the edge-detect interrupt for the next input */
+       }
+}
+
+ISR(INT1_vect)
+{
+       enable = (~PIND >> PD_ENSW) & 1;
+       pending |= 2;
+}