Skip to content

Instantly share code, notes, and snippets.

@dreamlayers
Created October 13, 2014 20:48
Show Gist options
  • Save dreamlayers/d0b2bd2318b83ed903fd to your computer and use it in GitHub Desktop.
Save dreamlayers/d0b2bd2318b83ed903fd to your computer and use it in GitHub Desktop.
NEC remote control protocol receiver for MSP430
/* mspremote - NEC remote control protocol receiver for MSP430 */
/* by Boris Gjenero <[email protected]> */
#include <stdbool.h>
#include <msp430.h>
#include <msp430g2252.h>
#define P1_LED1 0x01
#define P1_REMOTE 0x10
#define REMOTE_SHORT_MIN 400
#define REMOTE_SHORT_MAX 700
#define REMOTE_LONG_MIN 1500
#define REMOTE_LONG_MAX 1800
static unsigned char timer_overflow = 0;
static unsigned short last_transition = 0;
static unsigned char remote_bits;
static unsigned short remote_accumulator, remote_address, remote_command;
void static inline remote_abort(void)
{
remote_bits = 0;
}
bool static inline remote_burst(unsigned short len)
{
if (len > REMOTE_SHORT_MIN && len < REMOTE_SHORT_MAX) {
if (remote_bits == 16) {
remote_address = remote_accumulator;
} else if (remote_bits == 32) {
remote_command = remote_accumulator;
remote_bits = 0;
return true;
}
} else {
remote_abort();
}
return false;
}
void static inline remote_space(unsigned short len)
{
if (len > REMOTE_SHORT_MIN && len < REMOTE_SHORT_MAX) {
remote_bits++;
remote_accumulator >>= 1;
} else if (len > REMOTE_LONG_MIN && len < REMOTE_LONG_MAX) {
remote_bits++;
remote_accumulator >>= 1;
remote_accumulator |= 0x8000;
} else {
remote_abort();
}
}
// Timer 0 vectored interrupt handler
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A1_ISR(void)
{
switch (__even_in_range(TA0IV, TA0IV_TAIFG)) { // Use calculated branching
case TA0IV_TACCR2: {
unsigned short taccr = TA0CCR2, len;
unsigned char port = P1IN;
len = taccr - last_transition;
last_transition = taccr;
/* After a single overflow, unsigned subtraction works fine. After
* multiple overflows, length is unknown but certainly too long.
* COV and CCIFG check avoids situations where the pin changed
* again since the change which triggered the interrupt, and the
* port value may be misleading.
*/
if (timer_overflow < 2 && (TA0CCTL2 & (COV | CCIFG)) == 0) {
/* A space is high and a burst is low, but both are measured
* after they end, and the pin is in the opposite state. */
if (port & P1_REMOTE) {
if (remote_burst(len)) LPM0_EXIT;
} else {
remote_space(len);
}
} else {
TA0CCTL2 &= ~COV;
remote_abort();
}
timer_overflow = 0;
break;
}
case TA0IV_TAIFG:
if (timer_overflow == 0) {
timer_overflow = 1;
} else {
timer_overflow = 2;
}
break;
}
}
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
DCOCTL = 0x00; // Set DCOCLK to 1MHz
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
P1DIR = P1_LED1;
P1SEL = P1_REMOTE;
P1SEL2 = P1_REMOTE; // Required to route input to TA0
TA0CTL = TASSEL_2 | MC_2 | TACLR | TAIE;
TA0CCTL2 = CM_3 | SCS | CAP | CCIE;
__enable_interrupt();
while (1) {
LPM0;
P1OUT ^= P1_LED1;
}
//return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment