diff --git a/pic/MDR/MDR.c b/pic/MDR/MDR.c index ef46cea..9c42e4d 100644 --- a/pic/MDR/MDR.c +++ b/pic/MDR/MDR.c @@ -47,6 +47,8 @@ /* variables */ static uint8_t switches; /* save the last switch state */ +#define START 208 /* starting timer 0 value to wait up to 12ms in start_timer*/ +static uint8_t code[3]; /* the received code (24 bits) */ /* configuration bits */ uint16_t __at(_CONFIG1) __CONFIG1 = _FCMEN_ON & /* enable fail-safe clock monitor */ @@ -59,9 +61,17 @@ uint16_t __at(_CONFIG1) __CONFIG1 = _FCMEN_ON & /* enable fail-safe clock monito _PWRTE_ON & /* use power-up timer */ _WDTE_OFF & /* disable watchdog */ _FOSC_XT; /* use external resonator */ +uint16_t __at(_CONFIG2) __CONFIG2 = _LVP_ON & /* enable low voltage programming */ + /* DEBUG is not present in library, but I don't use it */ + _BORV_LO & /* low borwn out voltage */ + _STVREN_ON & /* enable reset on stack overflow */ + _PLLEN_OFF & /* don't use 4X PLL, for longer timer */ + _WRT_OFF; /* no flash write protect */ + /* initialize micro-conroller */ void init (void) { + /* configure IO */ ANSELA = 0; /* all pins are digital */ ANSELB = 0; /* all pins are digital */ TRISA |= RADIO; /* radio signal is an input */ @@ -70,25 +80,67 @@ void init (void) { TRISB &= ~(LED|MEMORY_CLK|MEMORY_DATA); /* LED is I2C memory are outputs */ led_off(); /* starting state */ + /* configure switches input */ IOCIE = 1; /* enable interrupt on individual GPIO (typo in the lib) */ + IOCIF = 0; /* clear GPIO interrupt */ IOCBP |= (SWITCH1|SWITCH2); /* enable interrupt when switch is released */ IOCBN |= (SWITCH1|SWITCH2); /* enable interrupt when switch is pressed */ switches = PORTB; /* save current switch state */ -/* - T1CON = _T1CKPS1 | _T1CKPS0; // set prescaler to 8 - // 0 is set per default, but just be sure - TMR1ON = 0; // stop timer 1 - TMR1GE = 0; // enable timer 1 - TMR1CS = 0; // use internal clock / 4 (=1MHz) - TMR1IF = 0; // clear interrupt - PIE1 = 1; // enable timer 1 interrupt - PEIE = 1; // enable timer interrupt -*/ + /* use timer 0 to measure bitframes timing */ + TMR0CS = 1; /* disable timer 0 (T0CKI/RA4 is connected to ground) */ + TMR0SE = 0; /* increment timer0 on low to high edge ((T0CKI/RA4 is connected to ground) */ + PSA = 0; /* use presacler for timer 0 */ + PS0 = 1; /* use prescaler of 256 */ + PS1 = 1; /* use prescaler of 256 */ + PS2 = 1; /* use prescaler of 256 */ + TMR0IE = 1; /* enable timer 0 interrupt */ + TMR0IF = 0; /* clear timer 0 interrupt */ + /* use timer 2 to measure pulse durations (max 16.384ms) */ + TMR2ON = 1; /* stop timer 2 */ + T2CKPS0 = 1; /* use prescaler of 64 */ + T2CKPS1 = 1; /* use prescaler of 64 */ + T2OUTPS0 = 0; /* use postscale of 1 */ + T2OUTPS1 = 0; /* use postscale of 1 */ + T2OUTPS2 = 0; /* use postscale of 1 */ + T2OUTPS3 = 0; /* use postscale of 1 */ + PR2 = 0xff; /* set interrupt on overflow */ + TMR2IE = 1; /* enable timer 2 interrupt */ + TMR2IF = 0; /* clear timer 2 interrupt */ + + PEIE = 1; /* enable peripheral interrupt (for timer 2) */ GIE = 1; /* golablly enable interrupts */ } +/* start timer to measure bitframe + * returns the time (in 0.256ms steps) since the last start + * stops after 12ms (then it returns 0) + */ +uint8_t start_bit_timer(void) +{ + /* uses timer 0 + * speed is Fosc/4 with a prescaler of 256 + * each tick should be 1.0/((4000000/4)/256) = 0.0002560163850486431 seconds + * since there should be one pulse every 6ms, only wait up to 12ms + */ + uint8_t time = TMR0-START; /* remember the time passed */ + TMR0 = START; /* reset timer */ + TMR0CS = 0; /* enable timer 0 */ + return time; +} + +/* start timer to measure pulse length + * read the time from TMR2 + * step is 0.064ms + * stops at 0xff (16.384ms) + */ +void start_pulse_timer() +{ + TMR2 = 0; /* reset timer counter */ + TMR2ON = 1; /* start timer 2 */ +} + /* funcion called on interrupts */ /* interrupt 0 is only one on PIC16 */ static void interrupt(void) __interrupt 0 @@ -98,9 +150,9 @@ static void interrupt(void) __interrupt 0 switches ^= PORTB; /* figure out which switch changed */ if (switches&SWITCH1) { /* switch 1 changed */ if (PORTB&SWITCH1) { /* switch 1 released */ - led_off(); + led_off(); /* reset LED */ } else { /* switch 1 pressed */ - led_on(); + led_on(); /* test LED */ } } if (switches&SWITCH2) { /* switch 2 changed */ @@ -110,14 +162,68 @@ static void interrupt(void) __interrupt 0 } IOCIF = 0; /* clear GPIO interrupt */ } + if (TMR0IF) { /* timer 0 overflow. no bit within required time */ + TMR0CS = 1; /* stop timer 1 */ + TMR0 = START; /* reset timer */ + TMR0IF = 0; /* clear timer 0 interrupt */ + } + if (TMR2IF) { /* timer 2 overflow. long pulse */ + TMR2ON = 0; /* stop timer 2 */ + TMR2 = 0xff; /* set timer 2 count to maximum */ + TMR2IF = 0; /* clear timer 2 interrupt */ + } } void main (void) { + uint8_t rx = 0; /* are we receiving a pulse */ + uint8_t bit = 0; /* the current received bit */ + uint8_t time = 0; /* time since previous bitframe start, in 0.256ms steps */ + init(); /* configure micro-controller */ - led_on(); + while (1) { /* a microcontroller runs forever */ -// sleep(); /* sleep to save power */ + /* I can't go to sleep to safe power + * the RADIO is connected to RA0, but port A does not support interrupt on change + * the timers are off in sleep, except for timer 1 when externally driven, but this is not our case + * the watchdog can only wake up every 1ms, but that is not fast enough and a pulse could be missed + */ + if (rx==0 && (PORTA&RADIO)) { /* pulse detected */ + rx = 1; /* mark receiving start to wait until pulse is finished */ + start_pulse_timer(); + } else if (rx!=0 && !(PORTA&RADIO)) { /* end of pulse */ + rx = 0; + if (TMR2>=14) { /* only observe pulses >0.9ms */ + /* pulse should be 1ms, but 1.2ms are used in the field + + * the first transmission can sometimes be detected a 10ms, even with a 1ms pulse + * this is why the fallinf edge is used + */ + if (bit>0) { /* following pulses */ + time += start_bit_timer(); /* get time and start measure time for next pulse */ + if (time>=27 && time<=35) { /* pulse between 7 and 9 ms after previous bitframe start is a 0 */ + time = 8; /* pulse is 2ms after bitframe start */ + code[bit/8] &= ~(1<<(7-(bit%8))); /* store first bit=0 */ + bit++; /* wait for next bit */ + } else if (time>=40 && time<=47) { /* pulse between 10 and 12 ms after previous bitframe start is a 0 */ + time = 20; /* the sync pulse is 5ms after bitframe start */ + code[bit/8] |= 1<<(7-(bit%8)); /* store first bit=1 */ + bit++; /* wait for next bit */ + } else { /* unexpected pulse. code is broken */ + bit = 0; /* restart from beginning for new code */ + } + if (bit==24) { /* received all 24 bits */ + led_on(); + } + } + if (bit==0) { /* sync pulse (can be the current one it it is not a continuation */ + start_bit_timer(); /* measure time until next bit */ + time = 20; /* the sync pulse is 5ms after bitframe start */ + code[bit/8] |= 1<<(7-(bit%8)); /* store first bit=1 */ + bit++; /* wait for next bit */ + } + } + } } }