Apple Mbed Studio ready 15 Nov 2022

Dependencies:   mbed

Revision:
0:3110d82c59ff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Nov 15 11:32:21 2022 +0000
@@ -0,0 +1,540 @@
+//This is the reference version
+
+
+/****************************** Apple TV Remote Decoder  and Preamp Controller V1.0 *************************/
+/*                                  Andrew C. Russell (c) August 2022                                  */
+/* This Apple TV Remote decoder works by reading in the IR 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.                                                */
+
+/* The following audio preamplifier facilities  are catered for:-                               */
+/* 1. Manual/Remote 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 Apple TV remote                                   */
+/* The controller pin definitions are set in Pindef1114.h file.                                 */
+
+
+// UPDATE 26 July 2018: tone functionality removed. The associated pin (dp25) has been sequestrated
+// for the standby output to drive a power amplifier. Pin 8 on J2 (Controller board)
+
+#include "mbed.h"
+#include "apple_codes.h"      // RC code definitions - in this case for Apple TV Remote
+#include "Pindef1114.h"    // all microcontroller I/O pin assignments defined here    
+
+#define TRUE 1
+#define FALSE 0
+#define HIGH 1
+#define LOW 0
+#define tick 280            // quarter bit time in us 
+#define tock  1120          // one bit time in us
+#define VUP_timeout 45      // defines max number of R/C cycles before the vol ctrl mtr drive stops
+#define VDWN_timeout 45     // as above but for volume decrease
+// Needed to ensure the motor is not burnt out
+#define DEBOUNCE 20000         // this is the switch debounce time
+
+// PHONO_   1               // these are the input assignments written out
+// CD       2               // on select_out - see thePindef1114.h file for details
+// TUN      4
+// MSERV    8
+// AUX      16
+// RECORDER 32
+/******************************************************************************************/
+//DigitalOut FWD1(dp1);       // these are the motor 'H' bridge drive signals
+//DigitalOut REV1(dp2);       // when the volume controll motor is not being driven
+                            // they are all OFF
+
+//DigitalOut muteout(dp13);    // drives the mute relay via a mosfet or transistor
+//DigitalOut muteLED(dp14);
+//InterruptIn mute_int(dp11);  // mute p/button interrupt
+//DigitalIn mute(dp11);         // mute input from associated pushbutton
+
+//DigitalOut stby_pa(dp25);    //   power amplifier standby control which follows the premap
+                            // but with suitable delays                       
+                            
+//InterruptIn rc5int(dp17);    // this is the R/C interrupt triggered by the IRx data out
+//DigitalIn rc5dat(dp17);      // data is read in from here - its coming from the IRx data out
+
+
+//InterruptIn select_int(dp28); // select rotary encoder interrupt - we use the 'A' O/P to generate the interrupt
+//DigitalIn sela(dp28);         // select input rotary enc input A
+//DigitalIn selb(dp27);         // select input rotary enc input B
+
+//DigitalIn stdby(dp26);       // standby function p/button input
+//InterruptIn stdby_int(dp26); // standby p/button interrupt in
+
+
+//InterruptIn tone_pb(dp15);
+//DigitalIn tone(dp15);
+//DigitalOut tone(dp25);          // can only be turned on and off at this stage by the r/control
+
+//InterruptIn recloop_int(dp14);   //record loop interrupt
+//DigitalIn recloop_in(dp14);      // record loop p/button input
+//DigitalOut recloop_out(dp16);    // drives record loop LED
+//DigitalOut recloop_rly(dp10); 
+
+//BusOut select_drv(dp11,dp4, dp5, dp6, dp9, dp10);  //these are the select relay drivers
+
+/*******************************************************************************************/
+
+
+int startbit;
+int toggle;                 // this is the 3rd bit position in the input stream and checks for
+// subsequent button depresses from the r/control
+int toggle1;                // temorary storage in the volume UP and volume DOWN functions
+int toggle2;                // temprary storage of the PB in the mute function
+int toggle3;                // temp storage for the r/control tone in-out function
+int standby;
+int command = 0;
+int vendor_id = 0;
+int pair_command = 0;
+int address = 0;
+int stop_bit = 0;
+int recloop_status = 0;
+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 REPEATFLAG;             // repaet command flag used for volume control
+//int FLAGVOLUP;
+//int FLAGVOLDWN;
+//int FLAG7 = FALSE;          // thyis flag is set to TRUE if recloop is active
+int standbyflag;            // used to save the standby condition
+int RECLOOP1 = 16;          // this is the bus address 1 before the Recorder
+int RECLOOP2 = 32;          // this is the bus address for the Recorder input
+// and is used in the recloop service routine
+int muteflag = FALSE;        // use to control mute and mute indicatoe independently
+//int recloop_status = 32;    // this is the initial value. This variable is used
+// in the select_out routine to indicate when the
+// input select should wrap around dependent upon
+// whether the record loop has been activated.
+int relay;
+int key_press = 1;          // keeps track of key presses
+int REPEAT = 511;         // this is the repeat code for volup and voldown
+int COMSTORE = 0;           // store the previous command
+
+// delcarations below are all for the input select proceses
+int select = 0;
+int select_save = 2;        // we save the status of select drive here. Initial setting is for CD
+int select_rot;             // rotary encoder pulse counter
+
+// declare function prototypes here
+void select_out (void);     // writes selected input out to the select_drv bus
+void select_isr(void);
+void rc5isr(void);          // RC5 ISR for remote control
+void mute_isr(void);
+void mute_sel(void);        //mutes select relays for a few ms during select
+//void recloop_isr(void);
+void standby_out(void);
+
+/****************************** volume increase ***********************************/
+void vol_up (void)
+{
+    if ((standbyflag == TRUE) && (key_press < VUP_timeout)) {
+       FWD1 = HIGH;
+       wait_us(100000);           //drive the motors for a short while
+        } 
+       FWD1 = 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_us(1000);
+}
+
+/******************************* volume decrease **********************************/
+void vol_dwn (void)
+{
+    if ((standbyflag == TRUE) && (key_press < VDWN_timeout)) {
+        REV1 = HIGH; 
+        wait_us(1000);           //drive the motors for a short while
+        }
+        REV1 = 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_us(1000);
+}
+
+/********************************** 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. for now,  all the LED's are just turned off                   */
+/* and input relays and mute relayes disabled.                                    */
+
+void standby_out(void)      // both p/button and R/C come in here
+{
+    __disable_irq();
+    stdby_int.fall(NULL);   // on first power up cycle NO interrupts are accepted
+    wait_us(DEBOUNCE);       // a very simple debounce
+    do {                    // that waits for the depressed button to be released
+        continue; //(1);a
+    } while (stdby != 1);
+
+    if (standbyflag == TRUE) {      // was ON so now turn it OFF
+        stby_pa = HIGH;             // turn the trigger output OFF
+        wait_us(1000000); //make sure power amp has powered down
+         muteLED = HIGH;                   
+        muteout = LOW;              // now mute the preamp
+        wait_us(3000000);
+        // turn off all interrupts except the standby and rc5int
+        select_int.fall(NULL);
+        select_save = select_drv;   // save the status of select_drv
+        select_drv = 0;             // all input select relays are OFF
+        standbyflag = FALSE;
+        muteflag = FALSE;
+       muteLED = LOW;
+    }
+
+
+    else if (standbyflag == FALSE) {// was OFF so we will turn it ON
+
+        muteLED = HIGH;              // turn the mute indicator ON
+        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
+        wait_us(100000);
+        select_drv = select_save;   // recall the input select setting and write to output
+        wait_us(2000000);                    // let things settle a bit
+        muteout = HIGH;             // enable output
+       muteflag = FALSE;
+        muteLED = LOW;             // turn the mute indicator OFF
+        standbyflag = TRUE;
+        wait_us(3000000); // make sure preamp has settled before powering power amp ON
+        stby_pa = LOW;             // now power up the amplifier
+    }
+    wait_us(500000);                   // let things settle a bit
+    __enable_irq();
+    stdby_int.fall(&stdby_isr);     // re-enable the standby interrupt
+
+}
+
+/************************************** mute  ************************************/
+void mute_out()
+{
+    
+    if (muteflag == FALSE) {    // mute was inactive so it will now get activated
+        wait_us(100000);
+        muteout = LOW;
+        muteLED = HIGH;
+        muteflag = TRUE;        // indicate its been activated
+    }
+
+    else if (muteflag == TRUE) {   //it was active, so it must be deactivated here
+        wait_us(100000);
+        muteout = HIGH;
+        muteLED = LOW;
+        muteflag = FALSE;
+    }
+
+    wait_us(800000);               // make sure relay state is settled
+
+}
+
+/************************************ rc5isr **************************************/
+/* Interrupt triggered by a rising edge on p21 which is R/C data in               */
+
+void rc5isr(void)
+{
+    FLAG1 = TRUE;
+    RCFLAG = TRUE;
+  
+}
+
+/******************* save bit stream from remote controller ***********************/
+/* This function reads the input data on pin rc5dat at 1120us ('tock')intervals   */
+/* and saves the data into an array stream[i].                                    */
+
+void save_stream(void)
+{
+    if (RCFLAG == TRUE) {
+        wait_us(13500); // this is the AGC header - ignore
+    }
+
+    bool stream[32];// the array is initialized each time it is used and is local only
+    int bitloop;    // number of bit positions
+    int i = 0;      // counter
+    int k = 0;      // temp storage
+    vendor_id = 0;
+    pair_command = 0;
+    address = 0;
+    command = 0;
+    stop_bit = 0; //must always return a 1 to be valid, so reset it
+    wait_us(tick);   // locate read point in middle of 1st half bit time of the 1st start bit
+
+    for (bitloop = 0; bitloop <32; bitloop ++) {
+
+        stream[bitloop] = rc5dat;  //read the data and save it to array position [i]
+//        bitstream = !bitstream; // RC bitstream moinitor on pin 14
+        if (rc5dat == HIGH) {
+            wait_us(tock);
+        }
+
+        wait_us(tock); //wait here until ready to read the next bit in
+    }     // now have 31 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<11; i++) {   // first 11 bit positions are vendor ID - always 043f for Apple; use for error checking later
+
+        k = stream[i];      // k will hold the vendor ID
+        vendor_id = (vendor_id << 1);
+        vendor_id = vendor_id|k;
+
+    }
+
+    for (i = 11; i <16; i++) {        // command or pair
+        k = stream[i];
+        pair_command = (pair_command << 1);
+        pair_command = pair_command|k;
+    }
+
+    for (i = 16; i <25; i++) {        // device pairing address
+        k = stream[i];
+        address = (address << 1);
+        address = address|k;
+    }
+
+
+    for (i = 25; i <31; i++) {   // bit positions 25 to 30 are the command - 7 bit positions
+        k = stream[i];
+        command = (command << 1);
+        command = command|k;
+    }
+    stop_bit = stream[31];
+
+ //  printf("\n vendor_id = %d pair_command = %d address = %d command = %d  stop_bit = %d \r", vendor_id, pair_command, address, command, stop_bit);
+}
+
+/********************************* process_stream() *******************************/
+/* handles commands coming in from the remote controller only                     */
+
+void process_stream (void)
+{
+    if ((RCFLAG == TRUE) && ((vendor_id == 479) || (vendor_id == 2047)) )  {     
+    // basic error checking - must be preamp + startbit ok to get executed otherwise skip completly
+ 
+ if (address == REPEAT) {
+     address = COMSTORE; }
+ 
+ 
+        switch (address) {
+
+            case VUP:
+                if (standbyflag == TRUE) {
+                vol_up();} 
+                 //FLAGVOLUP = TRUE;
+                break;
+
+            case VDOWN:
+                if (standbyflag == TRUE) {
+                    vol_dwn(); } 
+               // FLAGVOLDWN = TRUE;
+                break;
+
+            case MUTE:
+                if (standbyflag == TRUE) {mute_out();}
+                break;
+
+            case SELECT_R:
+                if (standbyflag == TRUE) { select_out();}
+                wait_us(300000);
+                break;
+
+            case SELECT_L:
+                if (standbyflag == TRUE) { select_out();}
+                wait_us(300000);
+                break;
+
+            case STANDBY:
+                standby_out();
+                break;
+
+        }
+COMSTORE = address; // save the just execued command
+
+     }
+    RCFLAG = FALSE;
+
+}
+/*********************************** select_isr ***********************************/
+
+void select_isr(void)
+{
+    FLAG2 = TRUE;
+}
+
+/****************************** mute inter select*********************************/
+
+void mute_sel(void)
+{
+    select_drv = 0;
+    wait_us(2000);
+}
+
+/********************************* select_process *********************************/
+/* Used for selecting the input source.  This function is used by the             */
+/* rotary encoder only                                          */
+
+void select_process(void)
+{
+
+    if (RCFLAG == FALSE) {  // if used R/C skip completely - extra safety check
+        wait_us(5000); // debounce - very short for the rotary encoder
+        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 so increment select_rot
+                select_rot <<= 1;
+                if (select_rot > 32 ) {
+                    select_rot = 1;   // wrap around to 1
+                }
+
+                break;
+
+            case 0:
+                select_rot >>= 1;   // encoder is being rotated CCW so decrement select_rot
+                if (select_rot < 1) {
+                    select_rot = 32; //wrap around to 32
+                }
+
+                break;
+
+            case 2:
+                break;   // indeterminate fall through values - ignore
+            case 3:
+                break;   // and do not change the output
+        }
+    }
+
+    select_drv = select_rot;   // write the value out to the bus
+
+//    printf("\n RCFLAG %d    select_rot %d \r", RCFLAG, select_rot);
+}
+
+
+
+
+/********************************* select_out *********************************/
+// this is only  used by the IR remote
+
+void select_out (void)
+{
+
+    if (address == SELECT_L) {
+        select_rot >>= 1;
+        if (select_rot <1) {
+            select_rot = 32;
+        }
+    }
+
+
+    if (address == SELECT_R) {
+        select_rot <<= 1;
+        if (select_rot >32) {
+            select_rot = 1;
+        }
+
+    }
+
+    select_drv = select_rot;   //write the selection out to the bus.
+
+// printf("\n select_rot = %d     select_drv = %d\r", select_rot, select_drv);
+
+}
+
+/************************************ main() ***************************************/
+int main(void)
+{
+ //   Serial pc(USBTX, USBRX);
+    __disable_irq();            // just to make sure we can set up correctly without problems
+    stby_pa = HIGH;              // make sure the power aamp is OFF
+                                 // make sure the power amp is OFF via the trigger output
+    muteout = LOW;              //make sure the outputis muted from the get go
+   muteLED  = HIGH;             //mute LED must be ON - power up preamble
+    select_drv = 0;
+   // bitstream = LOW;          // make sure the bitream monitor is LOW
+    rc5dat.mode(PullUp);        // pin 17
+    sela.mode(PullUp);          // pin 28
+    selb.mode(PullUp);          // pin 27
+    stdby.mode(PullUp);         // pin 26
+    //recloop_in.mode(PullUp);    // pin 14
+
+    wait_us(200000);
+    FLAG1 = FALSE;
+    FLAG2 = FALSE;
+    FWD1=0;                     //make sure the volume control motor is OFF
+    REV1=0;
+
+    // set up the ISR's that will be used
+    rc5int.fall(&rc5isr);               // trigger int on rising edge - go service it at rc5dat
+    select_int.fall(&select_isr);       // input from rotary encoder or input select
+    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);
+
+    standbyflag = TRUE;                 // preamp will be set-up first time for OFF
+    standby_out();                      // set system up
+    standbyflag = FALSE;
+    select_save = 2;
+    select_rot = select_save;          // CD will be selected when power is first turned on
+    wait_us(1000000);
+    muteLED = LOW;
+    muteflag = FALSE;
+    __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
+        save_stream();
+        process_stream();
+
+        FLAG1 = FALSE;
+    }
+
+    if (FLAG2 == TRUE) {
+        select_process();               //select process
+        FLAG2 = FALSE;
+    }
+
+
+    if (FLAG4 == TRUE) {                // standby ON/OFF
+        standby_out();
+        FLAG4 = FALSE;
+    }
+
+
+    goto LOOP;
+
+}
+
+
+
+