An RC5 decoder and preamp controller. Written on the LPC11U24, Ported to LPC1114 and now 100% stable (January 2016)
Dependents: AppleRemoteController_copy_Production_Version AppleRemoteController_Reference_Only
Diff: main.cpp
- Revision:
- 0:83d4a20e7bc7
- Child:
- 1:bb881a434906
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Jun 16 11:22:55 2015 +0000 @@ -0,0 +1,431 @@ +/* **************************** RC5 Decoder and Preamp Controller V1.0 *************************/ +/* Andrew C. Russell (c) 2015 */ +/* This RC5 decoder works by reading in the RC5 stream from one of the serial port lines */ +/* and saving the incoming stream into an array called stream, after which it is decoded and */ +/* the command executed. A Marantz RC-68PM R/C was used to develop this program - it */ +/* should work with any controller complying with the Philips RC5 standard. */ +/* See the rc5codes.h header for the codes used. Note the address is 16 which is for a preamp. */ + +/* The following audio preamplifier facilities are catered for:- */ +/* 1. Manual volume control adjustment via ALPS RK27 motorized potentiometer */ +/* 2. Input select via rotary encoder */ +/* 3. Output mute via push button actuation */ +/* 4. Record loop via push button actuation */ +/* 5. Power ON output to drive the /standby input of a system power supply */ +/* Facilities 1,2,3 and 5 are supported by an RC5 compliant remote control for preamplifiers */ + +#include "mbed.h" +#include "rc5codes.h" // Philips RC5 code definitions +#include "Pindef1114.h" // all microcontroller I/O pin assignments defined here + +#define TRUE 1 +#define FALSE 0 +#define HIGH 1 +#define LOW 0 +#define rc5_bitcount 14 // number of RC5 bits +#define tick 444 // quarter bit time in us +#define tock 1778 // one bit time in us +#define VUP_timeout 10 // defines max number of R/C cycles before the vol ctrl mtr drive stops +#define VDWN_timeout 10 // as above but for volume decrease. Needed to ensure the motor is not burnt out + +//#define PHONO_IN 1 // these are the input assignments - not used in V1.0 of the +//#define CD_IN 2 // controller since the input select is just stepped +//#define TUN_IN 4 // through from PHONO_IN to REC_IN and back again +//#define AUX1_IN 8 +//#define MSERV_IN 16 +//define REC_IN 32 + +int startbit; +int toggle; +int toggle1; +int toggle2; +int standby; +int address; +int command; + +int FLAG1; // this is used in the remote control input processing +int FLAG2; // this is used in the select input processing +int FLAG3; // this is for the mute pushbutton +int FLAG4; // this is for the standby pushbutton +int FLAG5; // this is the recloop flag +int RCFLAG = FALSE; // used to determine if the select command came via R/C +int standbyflag; // used to save the standby condition + +int relay; +int key_press = 1; // keeps track of key presses +int toggle_press = 1; //stores value of toggle for key_press routine + +// delcarations below are all for the input select proceses +int select = 0; +int select_save = 1; // we save the status of select drive here. Initial value is 1 +int select_rot = 1; // rotary encoder pulse counter + +// declare function prototypes here +void select_out (void); +void rc5isr(void); +void mute_isr(void); +void recloop_isr(void); +void select_isr(void); +void standby_out(void); + +/****************************** volume increase ***********************************/ +void vol_up (void) +{ + if ((standbyflag == TRUE) && (key_press < VUP_timeout)) { + + FWD1 = HIGH; + // FWD2 = HIGH; + wait(.1); //drive the motors for a short while + FWD1 = LOW; + // FWD2 = LOW; + } + if (toggle1 != toggle) { + key_press = 0; // user released the button, so reset counter + } else if (toggle1 == toggle) { + key_press++; // button remained depressed, so increment counter + } + toggle1 = toggle; + wait_ms(1); +} + +/******************************* volume decrease **********************************/ +void vol_dwn (void) +{ + if ((standbyflag == TRUE) && (key_press < VDWN_timeout)) { + + REV1 = HIGH; + // REV2 = HIGH; + wait(.1); //drive the motors for a short while + REV1 = LOW; + // REV2 = LOW; + } + if (toggle1 != toggle) { + key_press = 0; // user released the button, so reset counter + } else if (toggle1 == toggle) { + key_press++; // button remained depressed, so increment counter + } + toggle1 = toggle; + wait_ms(1); +} +/********************************** stdby_isr *************************************/ +void stdby_isr (void) +{ + FLAG4 = TRUE; +} + +/*********************************** standby **************************************/ +/* this will require supporting hardware functionality to power down the */ +/* analog board, LED's etc. Best option here is to use regulators with a */ +/* shutdown option */ + +void standby_out(void) // both p/button and R/C come in here +{ + stdby_int.fall(NULL); // on first power up cycle NO interuppts are accepted + // and neither any while this function is executed in any case + wait_ms(20); // a very simple debounce + do { // that waits for the depressed button to be released + (1); + } while (stdby !=1); + + if (standbyflag == TRUE) { // was ON so we will turn it OFF + // turn off all interrupts except the standby and rc5int + select_int.fall(NULL); + mute_int.fall(NULL); + recloop_int.fall(NULL); + muteout = LOW; + wait(1); + recloop_out = LOW; + select_save = select_drv; // save the status of select_drv + select_drv = 0; // turn all input select realys OFF + wait(1); + power_ind = LOW; // this is the regulator shutdown control. HIGH = ON + standbyflag = FALSE; // set it up for the next power cycle + + } else if (standbyflag == FALSE) { // was OFF so we will turn it ON + power_ind = HIGH; + rc5int.rise(&rc5isr); // trigger int on rising edge - go service it at rc5dat + select_int.fall(&select_isr); // input from rotary encoder or input select + mute_int.fall(&mute_isr); + recloop_int.fall(&recloop_isr); + wait(2); + select_drv = select_save; // recall the input select setting + wait(2); // let things settle a bit + muteout = HIGH; + standbyflag = TRUE; + + } + wait_ms(5); + stdby_int.fall(&stdby_isr); // re-enable the standby interrupt +} + +/********************************** record loop isr *******************************/ + +void recloop_isr(void) +{ + FLAG5 = TRUE; +} +/************************** recloop - just a simple toggle ************************/ +void recloop() +{ + recloop_int.fall(NULL); // to prevent re-entrance when coming here from the R/C + wait_ms(20); // simple debounce for when mute is via the f/p p/b switch + do { + (1); // wait here until the button is released + } while (recloop_in!=1); + recloop_out = !recloop_out; + wait_ms(20); + recloop_int.fall(&recloop_isr); +} + +/************************************ mute_isr ************************************/ + +void mute_isr(void) +{ + FLAG3 = TRUE; + toggle2 = !toggle2; // so the p/button input is recognized in mute_out() +} +/*************************** mute - just a simple toggle **************************/ +void mute_out() +{ + mute_int.fall(NULL); // to prevent re-entance when coming here from the R/C + if ((standbyflag == TRUE) && (toggle != toggle2)) { // only toggle mute if the preamp is ON + wait_ms(20); //simple debounce for when mute is via the f/p p/b switch + do { + (1); //wait here until the button is released + } while (mute != 1); + muteout = !muteout; + wait_ms(20); + + } + toggle2 = toggle; + mute_int.fall(&mute_isr); +} + +/************************************ rc5isr **************************************/ +/* Interrupt triggered by a rising edge on p21 of the cont which is R/C data in */ + +void rc5isr(void) +{ + FLAG1 = TRUE; +} +/***************** save rc5 bit stream from remote controller *********************/ +/* This function reads the input data on pin rc5dat at 1778us ('tock')intervals */ +/* and saves the data into an array stream[i]. */ +/* This function only looks at the second half of the bit position, since if that */ +/* is a 1 then it is assumed the first half of the bit position is a zero. Note */ +/* that in Manchester encoding, you cannot (should not) have both the 0 half-bit */ +/* position and the 1 half-bit position both HIGH or LOW in the same bit time - */ +/* a simplification exploited in this function. */ + +void save_stream(void) +{ + bool stream[15];// the array is initialized each time its used and is local only + int bitloop; // number of bit positions + int i = 0; // counter + int k = 0; // temp storage + startbit = 0; + address = 0; + command = 0; + toggle = 0; + wait_us(tick); // locate read point in middle of 1st half bit time of the 1st start bit + for (bitloop = 0; bitloop <15; bitloop++) { + stream[bitloop] = rc5dat; //read the data and save it to array position [i] + wait_us(tock); //wait here until ready to read the next bit in + + // now have 14 bits loaded into stream[i] + } + /* now put data in the array into the start, toggle, address and command variables - array counts from stream[0] */ + + for (i=0; i<2; i++) { // first 2 bit positions are start bits = 3; will use this later for basic error checking + k = stream[i]; + startbit = (startbit << 1); + startbit = startbit|k; + } + + toggle = stream[2]; // 3rd bit position is the toggle bit - 1 bit + + for (i = 3; i <8; i++) { // bit positions 3 to 7 are the address (or 'system') - 5 bit positions in total + k = stream[i]; + address = (address << 1); + address = address|k; + } + + for (i = 8; i <14; i++) { // bit positions 8 to 13 are the command - 6 bit positions + k = stream[i]; + command = (command << 1); + command = command|k; + } + +} +/********************************* process_stream() *******************************/ +/* handles commands coming in from the remote controller only */ + +void process_stream (void) +{ + if ((address == PREAMP) && (startbit == 3)) { +// basic error checking - must be preamp + startbit ok to get executed otherwise skip completly + switch (command) { + + case VUP: + vol_up(); + break; + + case VDOWN: + vol_dwn(); + break; + + case MUTE: + mute_out(); + break; + + case SELECT_R: + select_out(); + break; + + case STANDBY: + standby_out(); + break; + } + } +} +/*********************************** select_isr ***********************************/ + +void select_isr(void) +{ + FLAG2 = TRUE; +} + +/********************************* select_process *********************************/ +/* used for selecting the input source. This function is used by both the */ +/* rotary encoder and the remote control */ + +void select_process(void) +{ + if (RCFLAG == FALSE) { // if used R/C skip to select below + wait_ms(5); // debounce + select = 0; // flush select + + select = (select | sela) <<1; // read the two port lines associated with the select rotary encoder + select = (select | selb); + } + + switch (select) { + case 1: // select encoder is being rotated CW + select_rot <<= 1; + if (select_rot > 32) { + select_rot = 1; + } + break; + + case 0: + select_rot >>= 1; // encoder is being rotated CCW + if (select_rot < 1) { + select_rot = 32; + } + break; + + case 2: + {} break; // indeterminate fall through values - ignore + case 3: + {} break; + } + select_drv = select_rot; // write the value out to the bus +} + + +/********************* input select from remote controller only *******************/ +void select_out (void) +{ + if (toggle != toggle1) { // if the R/C button is held down, skip the increment + RCFLAG = TRUE; // this indicates command came in through the remote + select = 1; + select_process(); + RCFLAG = FALSE; + } + toggle1 = toggle; +} +/************************************ main() ***************************************/ +int main(void) +{ + __disable_irq(); // just to make sure we can set up correctly without problems + muteout = LOW; // mute the output while we go through power-up sequence + recloop_out = LOW; // make sure initial recloop condition is delected + power_ind = LOW; // power control; HIGH = power up + wait(.2); + Serial pc(USBTX, USBRX); // for debuging only - comment out on production + FLAG1 = FALSE; + FLAG2 = FALSE; + FWD1=0; + // FWD2=0; + REV1=0; + // REV2=0; //make sure the volume control motor is OFF + + // set up the ISR's we will be using + rc5int.rise(&rc5isr); // trigger int on rising edge - go service it at rc5dat + select_int.fall(&select_isr); // input from rotary encoder or input select + mute_int.fall(&mute_isr); // mute push button interrupt + recloop_int.fall(&recloop_isr); // record loop push button interrupt + stdby_int.fall(&stdby_isr); // the system power/standby switch + + //now disable them, leaving only the stand by p/button and rc5int interrupts active + select_int.fall(NULL); + mute_int.fall(NULL); + recloop_int.fall(NULL); + + standbyflag = HIGH; // preamp will be set-up first time for OFF + standby_out(); // go through standby_out for initial set-up + select_save = 1; // phono will be selected when power is first turned on + __enable_irq(); + + // all ready and in standby from this point forward + +LOOP: // this is the main operating loop + + __WFI(); // wait here until interrupt + + if (FLAG1 == TRUE) { // FLAG1 indicates remote control was used + __disable_irq(); + save_stream(); + if (startbit == 3) { + process_stream(); + } + FLAG1 = FALSE; + __enable_irq(); + } + + if (FLAG2 == TRUE) { + __disable_irq(); + select_process(); //select process + FLAG2 = FALSE; + __enable_irq(); + } + + if (FLAG3 == TRUE) { + __disable_irq(); + mute_out(); //mute + FLAG3 = FALSE; + __enable_irq(); + } + + if (FLAG4 == TRUE) { + __disable_irq(); + standby_out(); // standby + FLAG4 = FALSE; + __enable_irq(); + } + + if (FLAG5 == TRUE) { + __disable_irq(); + recloop(); //recloop + FLAG5 = FALSE; + __enable_irq(); + } + + wait_us(5); + + goto LOOP; + +} + + +