Apple Mbed Studio ready 15 Nov 2022

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 //This is the reference version
00002 
00003 
00004 /****************************** Apple TV Remote Decoder  and Preamp Controller V1.0 *************************/
00005 /*                                  Andrew C. Russell (c) August 2022                                  */
00006 /* This Apple TV Remote decoder works by reading in the IR stream from one of the serial port lines        */
00007 /* and saving the incoming stream into  an array called stream, after which it is decoded and   */
00008 /* the command executed.                                                */
00009 
00010 /* The following audio preamplifier facilities  are catered for:-                               */
00011 /* 1. Manual/Remote volume control adjustment via ALPS RK27 motorized potentiometer             */
00012 /* 2. Input select via rotary encoder                                                           */
00013 /* 3. Output mute via push button actuation                                                     */
00014 /* 4. Record loop via push button actuation                                                     */
00015 /* 5. Power ON output to drive the /standby input of a system power supply                      */
00016 /* Facilities 1,2,3 and 5 are supported by an Apple TV remote                                   */
00017 /* The controller pin definitions are set in Pindef1114.h file.                                 */
00018 
00019 
00020 // UPDATE 26 July 2018: tone functionality removed. The associated pin (dp25) has been sequestrated
00021 // for the standby output to drive a power amplifier. Pin 8 on J2 (Controller board)
00022 
00023 #include "mbed.h"
00024 #include "apple_codes.h"      // RC code definitions - in this case for Apple TV Remote
00025 #include "Pindef1114.h"    // all microcontroller I/O pin assignments defined here    
00026 
00027 #define TRUE 1
00028 #define FALSE 0
00029 #define HIGH 1
00030 #define LOW 0
00031 #define tick 280            // quarter bit time in us 
00032 #define tock  1120          // one bit time in us
00033 #define VUP_timeout 45      // defines max number of R/C cycles before the vol ctrl mtr drive stops
00034 #define VDWN_timeout 45     // as above but for volume decrease
00035 // Needed to ensure the motor is not burnt out
00036 #define DEBOUNCE 20000         // this is the switch debounce time
00037 
00038 // PHONO_   1               // these are the input assignments written out
00039 // CD       2               // on select_out - see thePindef1114.h file for details
00040 // TUN      4
00041 // MSERV    8
00042 // AUX      16
00043 // RECORDER 32
00044 /******************************************************************************************/
00045 //DigitalOut FWD1(dp1);       // these are the motor 'H' bridge drive signals
00046 //DigitalOut REV1(dp2);       // when the volume controll motor is not being driven
00047                             // they are all OFF
00048 
00049 //DigitalOut muteout(dp13);    // drives the mute relay via a mosfet or transistor
00050 //DigitalOut muteLED(dp14);
00051 //InterruptIn mute_int(dp11);  // mute p/button interrupt
00052 //DigitalIn mute(dp11);         // mute input from associated pushbutton
00053 
00054 //DigitalOut stby_pa(dp25);    //   power amplifier standby control which follows the premap
00055                             // but with suitable delays                       
00056                             
00057 //InterruptIn rc5int(dp17);    // this is the R/C interrupt triggered by the IRx data out
00058 //DigitalIn rc5dat(dp17);      // data is read in from here - its coming from the IRx data out
00059 
00060 
00061 //InterruptIn select_int(dp28); // select rotary encoder interrupt - we use the 'A' O/P to generate the interrupt
00062 //DigitalIn sela(dp28);         // select input rotary enc input A
00063 //DigitalIn selb(dp27);         // select input rotary enc input B
00064 
00065 //DigitalIn stdby(dp26);       // standby function p/button input
00066 //InterruptIn stdby_int(dp26); // standby p/button interrupt in
00067 
00068 
00069 //InterruptIn tone_pb(dp15);
00070 //DigitalIn tone(dp15);
00071 //DigitalOut tone(dp25);          // can only be turned on and off at this stage by the r/control
00072 
00073 //InterruptIn recloop_int(dp14);   //record loop interrupt
00074 //DigitalIn recloop_in(dp14);      // record loop p/button input
00075 //DigitalOut recloop_out(dp16);    // drives record loop LED
00076 //DigitalOut recloop_rly(dp10); 
00077 
00078 //BusOut select_drv(dp11,dp4, dp5, dp6, dp9, dp10);  //these are the select relay drivers
00079 
00080 /*******************************************************************************************/
00081 
00082 
00083 int startbit;
00084 int toggle;                 // this is the 3rd bit position in the input stream and checks for
00085 // subsequent button depresses from the r/control
00086 int toggle1;                // temorary storage in the volume UP and volume DOWN functions
00087 int toggle2;                // temprary storage of the PB in the mute function
00088 int toggle3;                // temp storage for the r/control tone in-out function
00089 int standby;
00090 int command = 0;
00091 int vendor_id = 0;
00092 int pair_command = 0;
00093 int address = 0;
00094 int stop_bit = 0;
00095 int recloop_status = 0;
00096 int FLAG1;                  // this is used in the remote control input processing
00097 int FLAG2;                  // this is used in the select input processing
00098 int FLAG3;                  // this is for the mute pushbutton
00099 int FLAG4;                  // this is for the standby pushbutton
00100 // int FLAG5;                  // this is the recloop flag
00101 int RCFLAG = FALSE;         // used to determine if the select command came via R/C
00102 int REPEATFLAG;             // repaet command flag used for volume control
00103 //int FLAGVOLUP;
00104 //int FLAGVOLDWN;
00105 //int FLAG7 = FALSE;          // thyis flag is set to TRUE if recloop is active
00106 int standbyflag;            // used to save the standby condition
00107 int RECLOOP1 = 16;          // this is the bus address 1 before the Recorder
00108 int RECLOOP2 = 32;          // this is the bus address for the Recorder input
00109 // and is used in the recloop service routine
00110 int muteflag = FALSE;        // use to control mute and mute indicatoe independently
00111 //int recloop_status = 32;    // this is the initial value. This variable is used
00112 // in the select_out routine to indicate when the
00113 // input select should wrap around dependent upon
00114 // whether the record loop has been activated.
00115 int relay;
00116 int key_press = 1;          // keeps track of key presses
00117 int REPEAT = 511;         // this is the repeat code for volup and voldown
00118 int COMSTORE = 0;           // store the previous command
00119 
00120 // delcarations below are all for the input select proceses
00121 int select = 0;
00122 int select_save = 2;        // we save the status of select drive here. Initial setting is for CD
00123 int select_rot;             // rotary encoder pulse counter
00124 
00125 // declare function prototypes here
00126 void select_out (void);     // writes selected input out to the select_drv bus
00127 void select_isr(void);
00128 void rc5isr(void);          // RC5 ISR for remote control
00129 void mute_isr(void);
00130 void mute_sel(void);        //mutes select relays for a few ms during select
00131 //void recloop_isr(void);
00132 void standby_out(void);
00133 
00134 /****************************** volume increase ***********************************/
00135 void vol_up (void)
00136 {
00137     if ((standbyflag == TRUE) && (key_press < VUP_timeout)) {
00138        FWD1 = HIGH;
00139        wait_us(100000);           //drive the motors for a short while
00140         } 
00141        FWD1 = LOW;
00142 
00143    // }
00144   //  if (toggle1 != toggle) {
00145   //      key_press = 0;      // user released the button, so reset counter
00146   //  } else if (toggle1 == toggle) {
00147   //      key_press++;        // button remained depressed, so increment counter
00148    //}
00149  ////   toggle1 = toggle;
00150     wait_us(1000);
00151 }
00152 
00153 /******************************* volume decrease **********************************/
00154 void vol_dwn (void)
00155 {
00156     if ((standbyflag == TRUE) && (key_press < VDWN_timeout)) {
00157         REV1 = HIGH; 
00158         wait_us(1000);           //drive the motors for a short while
00159         }
00160         REV1 = LOW;
00161  //   }
00162  //   if (toggle1 != toggle) {
00163  //       key_press = 0;      // user released the button, so reset counter
00164  //   } else if (toggle1 == toggle) {
00165  //       key_press++;        // button remained depressed, so increment counter
00166  //   }
00167   //  toggle1 = toggle;
00168     wait_us(1000);
00169 }
00170 
00171 /********************************** stdby_isr *************************************/
00172 void stdby_isr(void)
00173 {
00174     FLAG4 = TRUE;
00175 }
00176 
00177 /*********************************** standby **************************************/
00178 /* this will require supporting hardware functionality to power down the          */
00179 /* analog board, LED's etc. Best option here is to use regulators with a          */
00180 /* shutdown option. for now,  all the LED's are just turned off                   */
00181 /* and input relays and mute relayes disabled.                                    */
00182 
00183 void standby_out(void)      // both p/button and R/C come in here
00184 {
00185     __disable_irq();
00186     stdby_int.fall(NULL);   // on first power up cycle NO interrupts are accepted
00187     wait_us(DEBOUNCE);       // a very simple debounce
00188     do {                    // that waits for the depressed button to be released
00189         continue; //(1);a
00190     } while (stdby != 1);
00191 
00192     if (standbyflag == TRUE) {      // was ON so now turn it OFF
00193         stby_pa = HIGH;             // turn the trigger output OFF
00194         wait_us(1000000); //make sure power amp has powered down
00195          muteLED = HIGH;                   
00196         muteout = LOW;              // now mute the preamp
00197         wait_us(3000000);
00198         // turn off all interrupts except the standby and rc5int
00199         select_int.fall(NULL);
00200         select_save = select_drv;   // save the status of select_drv
00201         select_drv = 0;             // all input select relays are OFF
00202         standbyflag = FALSE;
00203         muteflag = FALSE;
00204        muteLED = LOW;
00205     }
00206 
00207 
00208     else if (standbyflag == FALSE) {// was OFF so we will turn it ON
00209 
00210         muteLED = HIGH;              // turn the mute indicator ON
00211         rc5int.rise(&rc5isr);       // trigger int on rising edge - go service it at rc5dat
00212         select_int.fall(&select_isr);   // input from rotary encoder or input select
00213         wait_us(100000);
00214         select_drv = select_save;   // recall the input select setting and write to output
00215         wait_us(2000000);                    // let things settle a bit
00216         muteout = HIGH;             // enable output
00217        muteflag = FALSE;
00218         muteLED = LOW;             // turn the mute indicator OFF
00219         standbyflag = TRUE;
00220         wait_us(3000000); // make sure preamp has settled before powering power amp ON
00221         stby_pa = LOW;             // now power up the amplifier
00222     }
00223     wait_us(500000);                   // let things settle a bit
00224     __enable_irq();
00225     stdby_int.fall(&stdby_isr);     // re-enable the standby interrupt
00226 
00227 }
00228 
00229 /************************************** mute  ************************************/
00230 void mute_out()
00231 {
00232     
00233     if (muteflag == FALSE) {    // mute was inactive so it will now get activated
00234         wait_us(100000);
00235         muteout = LOW;
00236         muteLED = HIGH;
00237         muteflag = TRUE;        // indicate its been activated
00238     }
00239 
00240     else if (muteflag == TRUE) {   //it was active, so it must be deactivated here
00241         wait_us(100000);
00242         muteout = HIGH;
00243         muteLED = LOW;
00244         muteflag = FALSE;
00245     }
00246 
00247     wait_us(800000);               // make sure relay state is settled
00248 
00249 }
00250 
00251 /************************************ rc5isr **************************************/
00252 /* Interrupt triggered by a rising edge on p21 which is R/C data in               */
00253 
00254 void rc5isr(void)
00255 {
00256     FLAG1 = TRUE;
00257     RCFLAG = TRUE;
00258   
00259 }
00260 
00261 /******************* save bit stream from remote controller ***********************/
00262 /* This function reads the input data on pin rc5dat at 1120us ('tock')intervals   */
00263 /* and saves the data into an array stream[i].                                    */
00264 
00265 void save_stream(void)
00266 {
00267     if (RCFLAG == TRUE) {
00268         wait_us(13500); // this is the AGC header - ignore
00269     }
00270 
00271     bool stream[32];// the array is initialized each time it is used and is local only
00272     int bitloop;    // number of bit positions
00273     int i = 0;      // counter
00274     int k = 0;      // temp storage
00275     vendor_id = 0;
00276     pair_command = 0;
00277     address = 0;
00278     command = 0;
00279     stop_bit = 0; //must always return a 1 to be valid, so reset it
00280     wait_us(tick);   // locate read point in middle of 1st half bit time of the 1st start bit
00281 
00282     for (bitloop = 0; bitloop <32; bitloop ++) {
00283 
00284         stream[bitloop] = rc5dat;  //read the data and save it to array position [i]
00285 //        bitstream = !bitstream; // RC bitstream moinitor on pin 14
00286         if (rc5dat == HIGH) {
00287             wait_us(tock);
00288         }
00289 
00290         wait_us(tock); //wait here until ready to read the next bit in
00291     }     // now have 31 bits loaded into stream[i]
00292 
00293     /* now put data in the array into the start, toggle, address and command variables - array counts from stream[0] */
00294 
00295     for (i=0; i<11; i++) {   // first 11 bit positions are vendor ID - always 043f for Apple; use for error checking later
00296 
00297         k = stream[i];      // k will hold the vendor ID
00298         vendor_id = (vendor_id << 1);
00299         vendor_id = vendor_id|k;
00300 
00301     }
00302 
00303     for (i = 11; i <16; i++) {        // command or pair
00304         k = stream[i];
00305         pair_command = (pair_command << 1);
00306         pair_command = pair_command|k;
00307     }
00308 
00309     for (i = 16; i <25; i++) {        // device pairing address
00310         k = stream[i];
00311         address = (address << 1);
00312         address = address|k;
00313     }
00314 
00315 
00316     for (i = 25; i <31; i++) {   // bit positions 25 to 30 are the command - 7 bit positions
00317         k = stream[i];
00318         command = (command << 1);
00319         command = command|k;
00320     }
00321     stop_bit = stream[31];
00322 
00323  //  printf("\n vendor_id = %d pair_command = %d address = %d command = %d  stop_bit = %d \r", vendor_id, pair_command, address, command, stop_bit);
00324 }
00325 
00326 /********************************* process_stream() *******************************/
00327 /* handles commands coming in from the remote controller only                     */
00328 
00329 void process_stream (void)
00330 {
00331     if ((RCFLAG == TRUE) && ((vendor_id == 479) || (vendor_id == 2047)) )  {     
00332     // basic error checking - must be preamp + startbit ok to get executed otherwise skip completly
00333  
00334  if (address == REPEAT) {
00335      address = COMSTORE; }
00336  
00337  
00338         switch (address) {
00339 
00340             case VUP:
00341                 if (standbyflag == TRUE) {
00342                 vol_up();} 
00343                  //FLAGVOLUP = TRUE;
00344                 break;
00345 
00346             case VDOWN:
00347                 if (standbyflag == TRUE) {
00348                     vol_dwn(); } 
00349                // FLAGVOLDWN = TRUE;
00350                 break;
00351 
00352             case MUTE:
00353                 if (standbyflag == TRUE) {mute_out();}
00354                 break;
00355 
00356             case SELECT_R:
00357                 if (standbyflag == TRUE) { select_out();}
00358                 wait_us(300000);
00359                 break;
00360 
00361             case SELECT_L:
00362                 if (standbyflag == TRUE) { select_out();}
00363                 wait_us(300000);
00364                 break;
00365 
00366             case STANDBY:
00367                 standby_out();
00368                 break;
00369 
00370         }
00371 COMSTORE = address; // save the just execued command
00372 
00373      }
00374     RCFLAG = FALSE;
00375 
00376 }
00377 /*********************************** select_isr ***********************************/
00378 
00379 void select_isr(void)
00380 {
00381     FLAG2 = TRUE;
00382 }
00383 
00384 /****************************** mute inter select*********************************/
00385 
00386 void mute_sel(void)
00387 {
00388     select_drv = 0;
00389     wait_us(2000);
00390 }
00391 
00392 /********************************* select_process *********************************/
00393 /* Used for selecting the input source.  This function is used by the             */
00394 /* rotary encoder only                                          */
00395 
00396 void select_process(void)
00397 {
00398 
00399     if (RCFLAG == FALSE) {  // if used R/C skip completely - extra safety check
00400         wait_us(5000); // debounce - very short for the rotary encoder
00401         select = 0; // flush select
00402 
00403         select = (select | sela) <<1; // read the two port lines associated with the select rotary encoder
00404         select = (select | selb);
00405 
00406 
00407         switch (select) {
00408             case 1:                 // select encoder is being rotated CW so increment select_rot
00409                 select_rot <<= 1;
00410                 if (select_rot > 32 ) {
00411                     select_rot = 1;   // wrap around to 1
00412                 }
00413 
00414                 break;
00415 
00416             case 0:
00417                 select_rot >>= 1;   // encoder is being rotated CCW so decrement select_rot
00418                 if (select_rot < 1) {
00419                     select_rot = 32; //wrap around to 32
00420                 }
00421 
00422                 break;
00423 
00424             case 2:
00425                 break;   // indeterminate fall through values - ignore
00426             case 3:
00427                 break;   // and do not change the output
00428         }
00429     }
00430 
00431     select_drv = select_rot;   // write the value out to the bus
00432 
00433 //    printf("\n RCFLAG %d    select_rot %d \r", RCFLAG, select_rot);
00434 }
00435 
00436 
00437 
00438 
00439 /********************************* select_out *********************************/
00440 // this is only  used by the IR remote
00441 
00442 void select_out (void)
00443 {
00444 
00445     if (address == SELECT_L) {
00446         select_rot >>= 1;
00447         if (select_rot <1) {
00448             select_rot = 32;
00449         }
00450     }
00451 
00452 
00453     if (address == SELECT_R) {
00454         select_rot <<= 1;
00455         if (select_rot >32) {
00456             select_rot = 1;
00457         }
00458 
00459     }
00460 
00461     select_drv = select_rot;   //write the selection out to the bus.
00462 
00463 // printf("\n select_rot = %d     select_drv = %d\r", select_rot, select_drv);
00464 
00465 }
00466 
00467 /************************************ main() ***************************************/
00468 int main(void)
00469 {
00470  //   Serial pc(USBTX, USBRX);
00471     __disable_irq();            // just to make sure we can set up correctly without problems
00472     stby_pa = HIGH;              // make sure the power aamp is OFF
00473                                  // make sure the power amp is OFF via the trigger output
00474     muteout = LOW;              //make sure the outputis muted from the get go
00475    muteLED  = HIGH;             //mute LED must be ON - power up preamble
00476     select_drv = 0;
00477    // bitstream = LOW;          // make sure the bitream monitor is LOW
00478     rc5dat.mode(PullUp);        // pin 17
00479     sela.mode(PullUp);          // pin 28
00480     selb.mode(PullUp);          // pin 27
00481     stdby.mode(PullUp);         // pin 26
00482     //recloop_in.mode(PullUp);    // pin 14
00483 
00484     wait_us(200000);
00485     FLAG1 = FALSE;
00486     FLAG2 = FALSE;
00487     FWD1=0;                     //make sure the volume control motor is OFF
00488     REV1=0;
00489 
00490     // set up the ISR's that will be used
00491     rc5int.fall(&rc5isr);               // trigger int on rising edge - go service it at rc5dat
00492     select_int.fall(&select_isr);       // input from rotary encoder or input select
00493     stdby_int.fall(&stdby_isr);         // the system power/standby switch
00494 
00495     //now disable them, leaving only the stand by p/button and rc5int interrupts active
00496     select_int.fall(NULL);
00497 
00498     standbyflag = TRUE;                 // preamp will be set-up first time for OFF
00499     standby_out();                      // set system up
00500     standbyflag = FALSE;
00501     select_save = 2;
00502     select_rot = select_save;          // CD will be selected when power is first turned on
00503     wait_us(1000000);
00504     muteLED = LOW;
00505     muteflag = FALSE;
00506     __enable_irq();
00507 
00508 // all ready and in standby from this point forward
00509 
00510 LOOP:                                   // this is the main operating loop
00511 
00512    __WFI();                            // wait here until interrupt
00513 
00514 
00515     if (FLAG1 == TRUE) {                // FLAG1 indicates remote control was used
00516         save_stream();
00517         process_stream();
00518 
00519         FLAG1 = FALSE;
00520     }
00521 
00522     if (FLAG2 == TRUE) {
00523         select_process();               //select process
00524         FLAG2 = FALSE;
00525     }
00526 
00527 
00528     if (FLAG4 == TRUE) {                // standby ON/OFF
00529         standby_out();
00530         FLAG4 = FALSE;
00531     }
00532 
00533 
00534     goto LOOP;
00535 
00536 }
00537 
00538 
00539 
00540