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

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;
+
+}
+
+
+