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