Andrew R
/
Power_Sequencer_Nov_10_2022
Power control and management
main.cpp@3:92148e16d530, 22 months ago (annotated)
- Committer:
- andrewcrussell
- Date:
- Fri Nov 18 15:57:25 2022 +0000
- Revision:
- 3:92148e16d530
- Parent:
- 2:c449cfb7752c
regen;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
andrewcrussell | 0:36494271e31a | 1 | /****************************** Power Sequencer and Controller V1.0 *************************/ |
andrewcrussell | 0:36494271e31a | 2 | /* Andrew C. Russell (c) 2015 */ |
andrewcrussell | 0:36494271e31a | 3 | /* The controller pin definitions are set in Pindef1114.h file. */ |
andrewcrussell | 0:36494271e31a | 4 | /* The main loop is driven by an interrupt on ACDET1 - 16.66ms for 60 Hz and 20ms for 50 Hz */ |
andrewcrussell | 2:c449cfb7752c | 5 | /* The WDT is set for a 25ms timeout. If AC power is removed, the MCU resets in 25ms */ |
andrewcrussell | 2:c449cfb7752c | 6 | /* and all relays de-energized. If the ERROR input goes LOW after power-up cycle, the */ |
andrewcrussell | 0:36494271e31a | 7 | /* program disconnects the speaker. During the power up cycle - 150 mains cycles for */ |
andrewcrussell | 2:c449cfb7752c | 8 | /* pre-charge and 250 mains cycles for the output to DC settle - and whenever an error */ |
andrewcrussell | 0:36494271e31a | 9 | /* condition is encountered, the power LED is flashed at 0.5 Hz */ |
andrewcrussell | 0:36494271e31a | 10 | /* The latency between the loss of ACDET1 and the SPKR being disengaged is ~25ms */ |
andrewcrussell | 2:c449cfb7752c | 11 | /* Provision for two 10k NTC thermistor sensors is also provided. The NTC is in the lower */ |
andrewcrussell | 2:c449cfb7752c | 12 | /* leg of a divider with the upper leg a 10k resistor. The trip temp (TRIPON), */ |
andrewcrussell | 2:c449cfb7752c | 13 | /* reset temp (TRIPOFF) and the temperature alarm (TEMPHIGH) are directly set in Volts */ |
andrewcrussell | 2:c449cfb7752c | 14 | /* A low on the clip input will cause the clip LED to flash on for 1s. Note CLIP has to */ |
andrewcrussell | 2:c449cfb7752c | 15 | /* recycle HIGH and then LOW again to retrigger the CLIP output - it does not flash */ |
andrewcrussell | 2:c449cfb7752c | 16 | /* continuosly if held LOW */ |
andrewcrussell | 2:c449cfb7752c | 17 | /* Note: if the amp clips for too long, the DC offset interuppt will be triggered because */ |
andrewcrussell | 2:c449cfb7752c | 18 | /* the clip input and the DC offset detect inputs are connected. */ |
andrewcrussell | 0:36494271e31a | 19 | |
andrewcrussell | 0:36494271e31a | 20 | #include "mbed.h" |
andrewcrussell | 2:c449cfb7752c | 21 | #include "WDT.h" // Watch Dog Timer |
andrewcrussell | 0:36494271e31a | 22 | #include "Pindef1114.h" // all microcontroller I/O pin assignments defined here |
andrewcrussell | 2:c449cfb7752c | 23 | #include "AnalogIn.h" // standard mbed supplied analog driver header file |
andrewcrussell | 2:c449cfb7752c | 24 | #include "math.h" |
andrewcrussell | 2:c449cfb7752c | 25 | |
andrewcrussell | 0:36494271e31a | 26 | #define AC_LOSS 0.025 // this is the wdt set value in seconds |
andrewcrussell | 0:36494271e31a | 27 | #define TRUE 1 |
andrewcrussell | 0:36494271e31a | 28 | #define FALSE 0 |
andrewcrussell | 0:36494271e31a | 29 | #define HIGH 1 |
andrewcrussell | 0:36494271e31a | 30 | #define LOW 0 |
andrewcrussell | 2:c449cfb7752c | 31 | #define INDIC 25 // the number of mains cycles between LED illumination flip |
andrewcrussell | 2:c449cfb7752c | 32 | #define inrush_t 200 //# of mains cycles to wait before bypassing the NTC |
andrewcrussell | 0:36494271e31a | 33 | #define amp_settle 300 //# of mains cycles to let amp module OP settle |
andrewcrussell | 2:c449cfb7752c | 34 | #define CLIP_ON 20 // the clip LED goes on for n ACDET1 periods |
andrewcrussell | 2:c449cfb7752c | 35 | #define TEMPHIGH 65 // temperature alarm in Volts - 65 deg C |
andrewcrussell | 2:c449cfb7752c | 36 | #define TRIPON 70 // this is the trip temperature in volts - 70 deg C |
andrewcrussell | 2:c449cfb7752c | 37 | #define TRIPOFF 62 // this is the lower re-activate amplifier temperature in volts - 64 deg C |
andrewcrussell | 2:c449cfb7752c | 38 | |
andrewcrussell | 2:c449cfb7752c | 39 | //Following used in the calculation of temp in deg C from an NTC |
andrewcrussell | 2:c449cfb7752c | 40 | #define Vref 3.31 // this is the A-D reference voltage |
andrewcrussell | 2:c449cfb7752c | 41 | #define Ref 10000 // the upper reference resistor from VREF to the NTC [was 2.2k] |
andrewcrussell | 2:c449cfb7752c | 42 | #define To 298 // coefficiants required for Steinhart-Hart equation |
andrewcrussell | 2:c449cfb7752c | 43 | #define B 3900 // from NTC data sheet |
andrewcrussell | 2:c449cfb7752c | 44 | #define Ro 10000 |
andrewcrussell | 2:c449cfb7752c | 45 | #define cal65 3.3 // cal out any error at 65deg C - determined by applying |
andrewcrussell | 2:c449cfb7752c | 46 | // required offset at 65 deg C - so at 65C the temp is accurate |
andrewcrussell | 2:c449cfb7752c | 47 | |
andrewcrussell | 2:c449cfb7752c | 48 | #define e 2.718281828459 |
andrewcrussell | 2:c449cfb7752c | 49 | |
andrewcrussell | 2:c449cfb7752c | 50 | int F1; // test flag used in trip() TRUE if overtemp has tripped |
andrewcrussell | 0:36494271e31a | 51 | int mains_cnt; // mains cycle counter |
andrewcrussell | 2:c449cfb7752c | 52 | int FLAG1; // TRUE if power-up process is completed otherwise FALSE |
andrewcrussell | 0:36494271e31a | 53 | int indi_flip; // counts mains cycles - used in indicator status |
andrewcrussell | 0:36494271e31a | 54 | int clip_cnt; // ACDET1 int derived counter for the clip indicator |
andrewcrussell | 2:c449cfb7752c | 55 | float templ; // left and right channel temperature read in by A-D in deg C |
andrewcrussell | 0:36494271e31a | 56 | float tempr; |
andrewcrussell | 2:c449cfb7752c | 57 | float t_left; |
andrewcrussell | 2:c449cfb7752c | 58 | float t_right; |
andrewcrussell | 0:36494271e31a | 59 | |
andrewcrussell | 2:c449cfb7752c | 60 | Ticker flipper; // this is the ACDET1 WDT ticker timer |
andrewcrussell | 2:c449cfb7752c | 61 | |
andrewcrussell | 0:36494271e31a | 62 | // declare function prototypes here |
andrewcrussell | 2:c449cfb7752c | 63 | void trip(void); // assess what actions to take based on temperature |
andrewcrussell | 2:c449cfb7752c | 64 | void temp_measure(void); // measure the temparature |
andrewcrussell | 2:c449cfb7752c | 65 | void output_clip(void); // clip indicator |
andrewcrussell | 2:c449cfb7752c | 66 | void indi_flash (void); // universal indicator flash |
andrewcrussell | 2:c449cfb7752c | 67 | void temp_trip (void); // action to be taken when overtemp |
andrewcrussell | 2:c449cfb7752c | 68 | void acdetect(void); // triggers the counter |
andrewcrussell | 0:36494271e31a | 69 | |
andrewcrussell | 0:36494271e31a | 70 | /************************************ analog_input *********************************/ |
andrewcrussell | 2:c449cfb7752c | 71 | void trip(void) { |
andrewcrussell | 0:36494271e31a | 72 | |
andrewcrussell | 2:c449cfb7752c | 73 | |
andrewcrussell | 2:c449cfb7752c | 74 | // temp alarm on either input |
andrewcrussell | 2:c449cfb7752c | 75 | if ((t_left >= TEMPHIGH) || (t_right >= TEMPHIGH)) { |
andrewcrussell | 2:c449cfb7752c | 76 | clip = HIGH; } |
andrewcrussell | 0:36494271e31a | 77 | |
andrewcrussell | 0:36494271e31a | 78 | |
andrewcrussell | 2:c449cfb7752c | 79 | if ((t_left < TEMPHIGH) && (t_right < TEMPHIGH) && (FLAG1 == TRUE)) { |
andrewcrussell | 2:c449cfb7752c | 80 | SPKR = HIGH; } |
andrewcrussell | 2:c449cfb7752c | 81 | |
andrewcrussell | 2:c449cfb7752c | 82 | if ((t_left > TRIPON) || (t_right > TRIPON)) { //temp trip |
andrewcrussell | 2:c449cfb7752c | 83 | SPKR = LOW; |
andrewcrussell | 2:c449cfb7752c | 84 | F1 = TRUE; } |
andrewcrussell | 2:c449cfb7752c | 85 | |
andrewcrussell | 2:c449cfb7752c | 86 | // below is to turn speaker relay back ON if the temperature has fallen BELOW TRIPOFF |
andrewcrussell | 2:c449cfb7752c | 87 | |
andrewcrussell | 2:c449cfb7752c | 88 | if (((t_left < TRIPOFF) && (t_right < TRIPOFF)) && (FLAG1 == TRUE)){ |
andrewcrussell | 2:c449cfb7752c | 89 | SPKR = HIGH; |
andrewcrussell | 2:c449cfb7752c | 90 | clip = LOW; |
andrewcrussell | 2:c449cfb7752c | 91 | F1 = FALSE; } |
andrewcrussell | 2:c449cfb7752c | 92 | |
andrewcrussell | 2:c449cfb7752c | 93 | |
andrewcrussell | 2:c449cfb7752c | 94 | } |
andrewcrussell | 0:36494271e31a | 95 | /*********************************** ACDET1 Interrupt ******************************/ |
andrewcrussell | 0:36494271e31a | 96 | void acdetect(void) |
andrewcrussell | 0:36494271e31a | 97 | { |
andrewcrussell | 2:c449cfb7752c | 98 | ACDET1.rise(NULL); // prevent accidental re-entry |
andrewcrussell | 2:c449cfb7752c | 99 | |
andrewcrussell | 0:36494271e31a | 100 | if (mains_cnt < amp_settle) { |
andrewcrussell | 0:36494271e31a | 101 | mains_cnt++; |
andrewcrussell | 2:c449cfb7752c | 102 | FLAG1 = FALSE; // FLAG1 only becomes TRUE after power up process is complete |
andrewcrussell | 0:36494271e31a | 103 | } |
andrewcrussell | 0:36494271e31a | 104 | |
andrewcrussell | 0:36494271e31a | 105 | else if (mains_cnt >= amp_settle) { |
andrewcrussell | 2:c449cfb7752c | 106 | FLAG1 = TRUE; // fully powered up at this point |
andrewcrussell | 2:c449cfb7752c | 107 | mains_cnt = amp_settle; // after power up mains_cnt is held to amp_settle |
andrewcrussell | 2:c449cfb7752c | 108 | } |
andrewcrussell | 2:c449cfb7752c | 109 | |
andrewcrussell | 2:c449cfb7752c | 110 | ACDET1.rise(&acdetect); // reinstate the interrupt |
andrewcrussell | 0:36494271e31a | 111 | } |
andrewcrussell | 0:36494271e31a | 112 | /******************************** output clip ****************************************/ |
andrewcrussell | 0:36494271e31a | 113 | |
andrewcrussell | 0:36494271e31a | 114 | void output_clip(void) { |
andrewcrussell | 2:c449cfb7752c | 115 | clip_cnt = CLIP_ON; } |
andrewcrussell | 0:36494271e31a | 116 | |
andrewcrussell | 0:36494271e31a | 117 | /******************************** indi_flash ***************************************/ |
andrewcrussell | 2:c449cfb7752c | 118 | // this just flashes the power LED to indicate amp is in power-up cycle |
andrewcrussell | 2:c449cfb7752c | 119 | // or a temperature shutdown |
andrewcrussell | 0:36494271e31a | 120 | |
andrewcrussell | 0:36494271e31a | 121 | void indi_flash(void) |
andrewcrussell | 0:36494271e31a | 122 | { |
andrewcrussell | 0:36494271e31a | 123 | if (indi_flip > INDIC) { |
andrewcrussell | 0:36494271e31a | 124 | INDI = !INDI; |
andrewcrussell | 0:36494271e31a | 125 | indi_flip = 0; |
andrewcrussell | 0:36494271e31a | 126 | } |
andrewcrussell | 0:36494271e31a | 127 | } |
andrewcrussell | 2:c449cfb7752c | 128 | /************************************ temp_measure **********************************/ |
andrewcrussell | 2:c449cfb7752c | 129 | /* This routine fetches the NTC resistance as measured at the A-D inputs left_temp */ |
andrewcrussell | 2:c449cfb7752c | 130 | /* and right Temp. It uses the Steinhart-Hart NTC R>T equation to compute the */ |
andrewcrussell | 2:c449cfb7752c | 131 | /* temperature.left_temp and right_temp are A-D inputs defined in the pindef.h file */ |
andrewcrussell | 0:36494271e31a | 132 | |
andrewcrussell | 2:c449cfb7752c | 133 | void temp_measure(void) { |
andrewcrussell | 2:c449cfb7752c | 134 | |
andrewcrussell | 2:c449cfb7752c | 135 | float vo_left; |
andrewcrussell | 2:c449cfb7752c | 136 | float vo_right; |
andrewcrussell | 2:c449cfb7752c | 137 | float r_left; |
andrewcrussell | 2:c449cfb7752c | 138 | float r_right; |
andrewcrussell | 2:c449cfb7752c | 139 | float l_roh; |
andrewcrussell | 2:c449cfb7752c | 140 | float r_roh; |
andrewcrussell | 2:c449cfb7752c | 141 | |
andrewcrussell | 2:c449cfb7752c | 142 | // first, fetch the NTC resistances |
andrewcrussell | 2:c449cfb7752c | 143 | |
andrewcrussell | 2:c449cfb7752c | 144 | vo_left = left_temp*Vref; //scale the A-D reading for Vref = 3.3V |
andrewcrussell | 2:c449cfb7752c | 145 | vo_right = right_temp*Vref; |
andrewcrussell | 2:c449cfb7752c | 146 | |
andrewcrussell | 2:c449cfb7752c | 147 | r_left = vo_left/((Vref-vo_left)/Ref); |
andrewcrussell | 2:c449cfb7752c | 148 | r_right = vo_right/((Vref-vo_right)/Ref); |
andrewcrussell | 2:c449cfb7752c | 149 | |
andrewcrussell | 2:c449cfb7752c | 150 | // from the NTC resistance, calculate the temperature in Kelvin using the |
andrewcrussell | 2:c449cfb7752c | 151 | // simplified Steinhart-Hart Equation. |
andrewcrussell | 2:c449cfb7752c | 152 | |
andrewcrussell | 2:c449cfb7752c | 153 | l_roh = Ro*(pow(e,(-B/To))); |
andrewcrussell | 2:c449cfb7752c | 154 | t_left = (B/(log(r_left/l_roh)))-(273+cal65); |
andrewcrussell | 2:c449cfb7752c | 155 | |
andrewcrussell | 2:c449cfb7752c | 156 | r_roh = Ro*(pow(e,(-B/To))); |
andrewcrussell | 2:c449cfb7752c | 157 | t_right = (B/(log(r_right/r_roh)))-(273+cal65); |
andrewcrussell | 2:c449cfb7752c | 158 | |
andrewcrussell | 2:c449cfb7752c | 159 | //printf used for temp debugging. Clock must be slowed to ~ 1Hz |
andrewcrussell | 2:c449cfb7752c | 160 | //printf("t_left = %f t_right = %f \n\r", t_left, t_right); |
andrewcrussell | 2:c449cfb7752c | 161 | |
andrewcrussell | 2:c449cfb7752c | 162 | } |
andrewcrussell | 2:c449cfb7752c | 163 | |
andrewcrussell | 2:c449cfb7752c | 164 | |
andrewcrussell | 2:c449cfb7752c | 165 | /************************************* main() ***************************************/ |
andrewcrussell | 0:36494271e31a | 166 | int main(void) |
andrewcrussell | 0:36494271e31a | 167 | { |
andrewcrussell | 2:c449cfb7752c | 168 | __disable_irq(); // just to make sure we can set up correctly without problems |
andrewcrussell | 2:c449cfb7752c | 169 | INRUSH = LOW; // power bypass disabled |
andrewcrussell | 2:c449cfb7752c | 170 | SPKR = LOW; // speakers muted |
andrewcrussell | 2:c449cfb7752c | 171 | PWR_R = LOW; // power ON/OFF relay is OFF - only used if TRIGGER is used |
andrewcrussell | 2:c449cfb7752c | 172 | INDI = HIGH; // open drain indicator output is deactivated - active LOW |
andrewcrussell | 2:c449cfb7752c | 173 | clip = LOW; // clip LED driver output |
andrewcrussell | 2:c449cfb7752c | 174 | clip_in.mode(PullUp); // clip input |
andrewcrussell | 0:36494271e31a | 175 | clip_in.fall(&output_clip); |
andrewcrussell | 0:36494271e31a | 176 | clip_cnt = 0; |
andrewcrussell | 2:c449cfb7752c | 177 | ERROR.mode(PullUp); |
andrewcrussell | 2:c449cfb7752c | 178 | PWR_D.mode(PullDown); // used to detect power down |
andrewcrussell | 0:36494271e31a | 179 | ACDET1.mode(PullDown); |
andrewcrussell | 2:c449cfb7752c | 180 | ACDET1.rise(&acdetect); // trigger counter on rising edge |
andrewcrussell | 2:c449cfb7752c | 181 | SYNC = LOW; // SYNC flips once every mains cycle - 20ms period @ 50 Hz |
andrewcrussell | 2:c449cfb7752c | 182 | |
andrewcrussell | 2:c449cfb7752c | 183 | FLAG1 = FALSE; // force a fault condition until first pass through main loop |
andrewcrussell | 2:c449cfb7752c | 184 | F1 = TRUE; // assume we are over temperature until our first sensor reading |
andrewcrussell | 2:c449cfb7752c | 185 | INDI = HIGH; // power LED is ON steady |
andrewcrussell | 2:c449cfb7752c | 186 | __enable_irq(); |
andrewcrussell | 0:36494271e31a | 187 | |
andrewcrussell | 2:c449cfb7752c | 188 | Watchdog wdt; // enable the WDT just before we start the main loop |
andrewcrussell | 2:c449cfb7752c | 189 | wdt.kick(AC_LOSS); // kick the WDT at regular 25 ms intervals (set by AC_LOSS) |
andrewcrussell | 0:36494271e31a | 190 | |
andrewcrussell | 2:c449cfb7752c | 191 | LOOP: // this main loop is driven by the 20ms/16.6ms interrupts |
andrewcrussell | 2:c449cfb7752c | 192 | // generated by the ACDET1 input |
andrewcrussell | 2:c449cfb7752c | 193 | // if the INT's fail to arrive after 25ms, the AC power |
andrewcrussell | 2:c449cfb7752c | 194 | // is assumed lost and the WDT will reset the system = power down |
andrewcrussell | 2:c449cfb7752c | 195 | // de-engergizing all relays |
andrewcrussell | 2:c449cfb7752c | 196 | __WFI(); // waiting for ACDET1 INT |
andrewcrussell | 2:c449cfb7752c | 197 | wait_ms(1); // make sure we are away from the zero crossing 50 Hz or 60 Hz |
andrewcrussell | 2:c449cfb7752c | 198 | wdt.kick(); |
andrewcrussell | 2:c449cfb7752c | 199 | |
andrewcrussell | 2:c449cfb7752c | 200 | if ((mains_cnt >= amp_settle) && (ERROR == HIGH) && (FLAG1 == FALSE)) { |
andrewcrussell | 2:c449cfb7752c | 201 | INDI = HIGH; |
andrewcrussell | 2:c449cfb7752c | 202 | SPKR = HIGH; } // inrush and amp settle complete - enable SPKR if no ERROR |
andrewcrussell | 2:c449cfb7752c | 203 | |
andrewcrussell | 2:c449cfb7752c | 204 | if (mains_cnt > inrush_t) { |
andrewcrussell | 2:c449cfb7752c | 205 | INRUSH = HIGH;} |
andrewcrussell | 2:c449cfb7752c | 206 | |
andrewcrussell | 2:c449cfb7752c | 207 | // make sure the clip LED goes OFF if all is ok |
andrewcrussell | 2:c449cfb7752c | 208 | if((clip_cnt <= 0) && (SPKR == HIGH) && (ERROR == HIGH)) { |
andrewcrussell | 2:c449cfb7752c | 209 | INDI = HIGH; } |
andrewcrussell | 2:c449cfb7752c | 210 | |
andrewcrussell | 2:c449cfb7752c | 211 | if (clip_cnt <= 0) { // note: clip flashes on change of state |
andrewcrussell | 2:c449cfb7752c | 212 | clip = LOW;} // i.e on edges |
andrewcrussell | 2:c449cfb7752c | 213 | else if (clip_cnt > 0) { |
andrewcrussell | 2:c449cfb7752c | 214 | clip = HIGH; |
andrewcrussell | 2:c449cfb7752c | 215 | INDI = LOW; } // so LED is RED on clip and not ORANGE |
andrewcrussell | 2:c449cfb7752c | 216 | |
andrewcrussell | 2:c449cfb7752c | 217 | if ((ERROR == LOW) || (FLAG1 == FALSE)) { |
andrewcrussell | 2:c449cfb7752c | 218 | SPKR = LOW; |
andrewcrussell | 2:c449cfb7752c | 219 | indi_flash(); } |
andrewcrussell | 2:c449cfb7752c | 220 | |
andrewcrussell | 2:c449cfb7752c | 221 | temp_measure(); |
andrewcrussell | 2:c449cfb7752c | 222 | trip(); |
andrewcrussell | 2:c449cfb7752c | 223 | |
andrewcrussell | 2:c449cfb7752c | 224 | if (F1 == TRUE) { |
andrewcrussell | 0:36494271e31a | 225 | indi_flash(); } |
andrewcrussell | 0:36494271e31a | 226 | |
andrewcrussell | 2:c449cfb7752c | 227 | SYNC = !SYNC; // ACDET1 sync output for debugging |
andrewcrussell | 0:36494271e31a | 228 | indi_flip++; |
andrewcrussell | 0:36494271e31a | 229 | clip_cnt--; |
andrewcrussell | 0:36494271e31a | 230 | goto LOOP; |
andrewcrussell | 0:36494271e31a | 231 | |
andrewcrussell | 0:36494271e31a | 232 | } |
andrewcrussell | 0:36494271e31a | 233 | |
andrewcrussell | 0:36494271e31a | 234 | |
andrewcrussell | 0:36494271e31a | 235 |