Skip to content

Instantly share code, notes, and snippets.

@spirilis
Created September 4, 2014 14:23
Show Gist options
  • Save spirilis/1d53a65c7ebc040f9d8d to your computer and use it in GitHub Desktop.
Save spirilis/1d53a65c7ebc040f9d8d to your computer and use it in GitHub Desktop.
Remote control illustrating Graycode incremental encoder interpretation
/* nRFDMX Remote Control implementation to send 3-letter RGB values based
* on a user-adjusted HSV colorspace system.
* Value adjusted by sliding potentiometer
* Hue adjusted by rotating a Rotary Encoder
* Saturation flipped between 1.0 and 0.1 by the rotary encoder pushbutton
*/
#include <msp430.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <string.h>
#include "msprf24.h"
#include "dmx512.h"
#include "packet_processor.h"
#include "misc.h"
#include "hsv2rgb.h"
volatile uint16_t sleep_counter;
volatile int16_t rotenc, rotenc_old;
volatile uint8_t gc_old, rotbtn;
volatile uint16_t pot, pot_old;
volatile float saturation;
int8_t graycode_interpret(uint8_t, uint8_t);
int main()
{
WDTCTL = WDTPW | WDTHOLD;
DCOCTL = CALDCO_16MHZ;
BCSCTL1 = CALBC1_16MHZ;
BCSCTL2 = DIVS_1; // SMCLK = 8MHz
BCSCTL3 = LFXT1S_2;
while (BCSCTL3 & LFXT1OF)
;
// Initialize Rotary Encoder
P2DIR &= ~(BIT4 | BIT5);
P2REN |= BIT4 | BIT5;
P2OUT |= BIT4 | BIT5;
__delay_cycles(100000); // Time for hardware debounce RC network to charge
P2IES = (P2IES & ~(BIT4 | BIT5)) | (P2IN & (BIT4 | BIT5)); /* Preload P2IES with current
* values since we can't interrupt
* on any change, but must track
* rising vs. falling transitions.
*/
P2IFG &= ~(BIT4 | BIT5);
gc_old = (P2IN & (BIT4 | BIT5)) >> 4;
P2IE |= BIT4 | BIT5;
// Initialize rotary encoder pushbutton
P2DIR &= ~BIT7;
P2SEL &= ~BIT7;
P2SEL2 &= ~BIT7;
P2REN |= BIT7;
P2OUT |= BIT7;
P2IES |= BIT7;
P2IFG &= ~BIT7;
P2IE |= BIT7;
rotbtn = 0;
// Initialize LEDs
P2OUT &= ~(LED_ROTGREEN | LED_ROTRED | LED_POT);
P2DIR |= LED_ROTGREEN | LED_ROTRED | LED_POT;
P2REN &= ~(LED_ROTGREEN | LED_ROTRED | LED_POT);
P2SEL &= ~(LED_ROTGREEN | LED_ROTRED | LED_POT);
P2SEL2 &= ~(LED_ROTGREEN | LED_ROTRED | LED_POT);
// Initialize S1+S2 address setting scheme
P2DIR &= ~(BIT0 | BIT1);
P2REN &= ~(BIT0 | BIT1); /* ^ No interrupts necessary since we just read P2IN for this information
* as we need it. Also no debounce circuitry; the switches go hard to Vcc or GND.
*/
// Initialize analog input port & VeREF+ output for potentiometer
P1DIR &= ~(BIT3 | BIT4);
P1REN &= ~(BIT3 | BIT4);
P1IE &= ~(BIT3 & BIT4);
pot = 0;
// Configure Nordic nRF24L01+
rf_crc = RF24_EN_CRC | RF24_CRCO; // CRC enabled, 16-bit
rf_addr_width = 5;
rf_speed_power = RF_SPEED | RF24_POWER_MAX; // RF_SPEED from tcsender.h
rf_channel = RF_CHANNEL; // This #define is located in tcsender.h
msprf24_init();
msprf24_open_pipe(0, 0);
msprf24_set_pipe_packetsize(0, 0);
packet_init_tasklist();
dmx512_init();
sleep_counter = ADC_SLEEP_INTERVAL;
WDTCTL = WDT_ADLY_16;
IE1 |= WDTIE;
saturation = 1.0;
while(1) {
// Handle RF acknowledgements
if (rf_irq & RF24_IRQ_FLAGGED) {
msprf24_get_irq_reason();
// Handle TX acknowledgements
if (rf_irq & RF24_IRQ_TX) {
// Acknowledge
msprf24_irq_clear(RF24_IRQ_TX);
msprf24_powerdown();
P2OUT &= ~LED_POT;
}
if (rf_irq & RF24_IRQ_TXFAILED) {
msprf24_irq_clear(RF24_IRQ_TXFAILED);
flush_tx();
msprf24_powerdown();
P2OUT &= ~LED_POT;
}
if (rf_irq & RF24_IRQ_RX) {
// Really no reason we should be receiving anything.
flush_rx();
msprf24_irq_clear(RF24_IRQ_RX);
} /* rf_irq & RF24_IRQ_RX */
} /* rf_irq & RF24_IRQ_FLAGGED */
if (rotbtn) {
// Process rotary encoder button press
if (saturation == 1.0)
saturation = 0.2;
else
saturation = 1.0;
// Do update
if (msprf24_queue_state() & RF24_QUEUE_TXEMPTY) {
HSV_to_RGB(dmx512_buffer, (float)rotenc, saturation, (float) (pot / POT_UPPER_VALUE) );
dmx512_output_channels(P2IN & 0x03, 1, dmx512_buffer, 3);
}
rotbtn = 0;
}
if (!sleep_counter) { // ADC conversion timer
ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFBURST | REFON | REFOUT | ADC10ON;
ADC10CTL1 = INCH_3 | ADC10DIV_1;
ADC10AE0 |= BIT3;
ADC10CTL0 |= ENC | ADC10SC;
while (ADC10CTL1 & ADC10BUSY)
;
pot = ADC10MEM >> 3;
ADC10CTL1 &= ~ENC;
ADC10CTL0 &= ~(ADC10ON | ADC10IFG);
if (pot != pot_old) {
// Do update
if (msprf24_queue_state() & RF24_QUEUE_TXEMPTY) {
HSV_to_RGB(dmx512_buffer, (float)rotenc, saturation, (float) (pot / POT_UPPER_VALUE) );
dmx512_output_channels(P2IN & 0x03, 1, dmx512_buffer, 3);
pot_old = pot;
}
}
sleep_counter = ADC_SLEEP_INTERVAL;
}
if (rotenc != rotenc_old) {
// Do update
if (msprf24_queue_state() & RF24_QUEUE_TXEMPTY) {
HSV_to_RGB(dmx512_buffer, (float)rotenc, saturation, (float) (pot / POT_UPPER_VALUE) );
dmx512_output_channels(P2IN & 0x03, 1, dmx512_buffer, 3);
rotenc_old = rotenc;
}
}
if (packet_task_next() != NULL) {
packet_process_txqueue();
P2OUT |= LED_POT;
}
LPM3;
} /* while(1) */
return 0; // Should never reach here
}
// WDT overflow/timer
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{
IFG1 &= ~WDTIFG;
if (sleep_counter)
sleep_counter--;
else
__bic_SR_register_on_exit(LPM3_bits);
}
// Port 2 ISR - Rotary Encoder, rotary encoder pushbutton
#pragma vector=PORT2_VECTOR
__interrupt void P2ISR(void)
{
uint8_t gc, p2in;
p2in = P2IN;
// Rotary encoder gray code
if (P2IFG & (BIT4 | BIT5)) {
gc = (p2in & (BIT4 | BIT5)) >> 4;
rotenc += graycode_interpret(gc, gc_old);
if (rotenc < 0)
rotenc = 359;
if (rotenc > 359)
rotenc = 0;
if (rotenc != rotenc_old)
__bic_SR_register_on_exit(LPM3_bits);
gc_old = gc;
P2IES = (P2IES & ~(BIT4 | BIT5)) | (p2in & (BIT4 | BIT5));
P2IFG &= ~(BIT4 | BIT5);
}
if (P2IFG & BIT7) {
P2IFG &= ~BIT7;
rotbtn = 1;
__bic_SR_register_on_exit(LPM3_bits); // Wake the CPU to process this one.
}
}
// LSB = A, MSB = B -- graycode lookup table
const uint8_t graycode_cw[4] = {2, 0, 3, 1};
const uint8_t graycode_ccw[4] = {1, 3, 0, 2};
int8_t graycode_interpret(uint8_t newgc, uint8_t oldgc)
{
if (graycode_cw[oldgc] == newgc)
return 1;
if (graycode_ccw[oldgc] == newgc)
return -1;
return 0; // should never get here, but, ya never know.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment