Andrew R
/
Apple_Mbed_Studio
Apple Mbed Studio ready 15 Nov 2022
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Nov 15 2022 11:35:22 by 1.7.2