#define P12F675 #include "allpic.h" #pragma config = 0b000110010100 // grundkonfiguration des PICs // PIOs #define POTI 0 // analog Poti Sollvorgabe #define SPANNUNG 1 // analog Ausgangsspannung #define STROM 2 // analog Batteriestrom #define NC_3 3 // wird nicht benutzt #define PWM_0 4 // PWM Ausgang #define PWM_1 5 // PWM Ausgang #define _GIE 7 #define _PEIE 6 #define I_MAX 1000 // [mA] #define L 5 // [mH] #define RV 82 // [kOhm] spannungsteiler spannungsmessung #define RQ 10 // [kOhm] spannungsteiler spannungsmessung #define PERIODE 400 // [us] #define ADC_SEL 20 // [us] Wartezeit nach ADC-Select #define U_MIN 6 // [V] darunter sollte er nicht anschwingen #define U_MAX 30 // [V] und da ist schluss #define timeloop_init() \ { \ T1CON = 0x05; /* Timer1 parametrieren */\ } #define usec_set(t) { \ TMR1ON = FALSE; \ TMR1L = ~(t).low8; \ TMR1H = ~(t).high8; \ TMR1IF = FALSE; \ TMR1ON = TRUE; \ } #define usec_wait() { \ while(!TMR1IF); \ } #define adc_select(a,b,c) { \ CHS1 = (a); \ CHS0 = (b); \ TMR1ON = FALSE; \ TMR1L = ~(c); \ TMR1H = ~0; \ TMR1IF = FALSE; \ TMR1ON = TRUE; \ } #define wait_ADC(a) { \ while(GO); \ RP0 = 1; (a).low8 = ADRESL; RP0 = 0;\ (a).high8 = ADRESH; /* wandlung abholen */\ } #define make_save_regs unsigned char s1, s2 #define save_W { s1 = W;} #define restore_W { s1 = swap(s1); W = swap(s1);} #define save_regs { save_W; s2 = swap(STATUS); } #define restore_regs_return { STATUS = swap(s2); restore_W; return; } // volatile uns8 _takt; // Arbeitstakt 0, 1, ..., 6, 7 uns16 _t_off; // [us] nur in der ISR verwendet uns16 _strom; // [mA] ADC-Ergebnis der ISR uns16 _spannung; // [V] ADC-Ergebnis der ISR uns16 _p_soll; // [mW] ADC-Ergebnis der ISR uns16 t_on; // [us] vom vordergrund kommend uns16 u_b; // [V] im Vordergrund gemessen #define ISR_RETURN { \ PCLATH = svrPCLATH; \ restore_regs_return; \ } #pragma origin 4 interrupt server(void) // ISR: nur von TMR1 gestartet { make_save_regs; // sicherungsregister deklarieren save_regs; // W, STATUS retten uns8 svrPCLATH = PCLATH; // sichern für const-zugriff skip(_takt); // blitzschnelles "computed goto" goto takt_0; // auch wegen gleicher Laufzeiten goto takt_1; goto takt_2; goto takt_3; goto takt_4; goto takt_5; goto takt_6; goto takt_7; takt_0: GPIO.PWM_0 = TRUE; // spulenladung beginnen adc_select(FALSE,FALSE,ADC_SEL - 7); usec_wait(); GO = TRUE; // Hold! Neue wandelung starten usec_set(t_on); // timer starten _t_off = PERIODE - t_on; // wartezeit nutzen _takt++; // wartezeit nutzen wait_ADC(_p_soll); // poti einlesen ISR_RETURN; takt_1: GPIO.PWM_0 = FALSE; // spulenladung beenden usec_set(_t_off); // timer starten _takt++; ISR_RETURN; takt_2: GPIO.PWM_1 = TRUE; // spulenladung beginnen adc_select(FALSE,FALSE,ADC_SEL - 7); usec_wait(); GO = TRUE; // Hold! Neue wandelung starten usec_set(t_on); // timer starten _takt++; // wartezeit nutzen wait_ADC(_p_soll); // poti einlesen ISR_RETURN; takt_3: GPIO.PWM_1 = FALSE; // spulenladung beenden usec_set(_t_off); // timer starten _takt++; ISR_RETURN; takt_4: GPIO.PWM_0 = TRUE; // spulenladung beginnen adc_select(TRUE,FALSE,ADC_SEL - 7); usec_wait(); GO = TRUE; // Hold! Neue wandelung starten usec_set(t_on); // timer starten _takt++; // wartezeit nutzen wait_ADC(_strom); // spulenstrom einlesen ISR_RETURN; takt_5: GPIO.PWM_0 = FALSE; // spulenladung beenden usec_set(_t_off); // timer starten _takt++; ISR_RETURN; takt_6: GPIO.PWM_1 = TRUE; // spulenladung beginnen adc_select(FALSE,TRUE,ADC_SEL - 7); usec_wait(); GO = TRUE; // Hold! Neue wandelung starten usec_set(t_on); // timer starten _takt++; // wartezeit nutzen wait_ADC(_spannung); // ausgangsspannung einlesen ISR_RETURN; takt_7: GPIO.PWM_1 = FALSE; // spulenladung beenden usec_set(_t_off); // timer starten _takt = 0; ISR_RETURN; } #include "math16.h" // beiliegende mathe-lib //#include "mymath32.h" // meine eigenen mathe-routinen void main(void) { RP0 = 1; // erstmal alle spezialregister... #asm DW /*CALL*/ 0x2000 + /*ADRESSE*/ 0x03FF // oscal abholen #endasm OSCCAL = W; // und osc kalibration speichern OPTION = 0b00001000; // no global weak-pullup WPU = 0; // eingänge definieren TRISIO = (1 << POTI) | (1 << SPANNUNG) | (1 << STROM) | (1 << NC_3); ANSEL = 0b00010111; // AD: 8 TOSC & ANS0 & ANS1 & ANS2 TMR1IE = TRUE; RP0 = 0; // nun die normalen register und ram ADCON0 = 0b10000001; // AD-Wandler-config CMCON = 0x07; // komparator aus timeloop_init(); restart: adc_select(FALSE,TRUE,ADC_SEL); // betriebsspannung messen usec_wait(); do { GO = TRUE; // Hold! Neue wandelung starten wait_ADC(u_b); u_b *= 5; // Batteriespannung berechnen u_b /= RQ; // überläufe vermeiden u_b *= (RV + RQ); // und compiler schonen u_b /= 1024; // [V] = ADC * 5 * (RV + RQ) / (1024 * RQ) } while(u_b.low8 < U_MIN); // warten, bis die Spannung stimmt // nun den hintergrund-task anwerfen t_on = _spannung = _strom = _p_soll = 0; _takt = TMR1L = TMR1H = 0; TMR1IF = FALSE; INTCON = (1 << _GIE) | (1 << _PEIE); // interrupts erlauben TMR1ON = TRUE; // ints anwerfen FOREVER { // t_on regeln uns16 val = _spannung * 5; // ausgangsspannung berechnen val /= RQ; // überläufe vermeiden val *= (RV + RQ); // und compiler schonen val /= 1024; // [V] = ADC * 5 * (RV + RQ) / (1024 * RQ) if(val.low8 > U_MAX) { // überspannungsabbruch INTCON = 0; // interrupts aus t_on = 1000; // warte eine millisekunde val = 10000; // also 10 sekunden do { usec_set(t_on); usec_wait(); } while(--val); goto restart; // restart } val = _strom * 5; // Strom auf [mA] umrechnen uns16 t_max = ((I_MAX - val) * L) / u_b.low8;// maximalladezeit berechnen uns16 p_ist = t_on * u_b.low8;// eingangsleistung in [mW] p_ist /= (2 * L); // mittleren rampenstrom ausrechnen p_ist += val; // startstrom dazuaddieren p_ist *= u_b.low8; // leistung berechnen uns16 p_soll = _p_soll * 10; // Sollleistung berechnen 0 - 10.240 [mW] if(p_ist > p_soll) { if(t_on) t_on--; } else if(t_on < t_max) t_on++; } } /* ENDE */