6c4b3d87fdf551089b2482f018e4513119a4d719
[digitaldcpower] / analog.c
1 /* vim: set sw=8 ts=8 si : */
2 /*********************************************
3 * Author: Guido Socher, Copyright: GPL 
4 *
5 * Digital analog conversion of channel ADC0 and ADC1 in
6 * free running mode. 
7 **********************************************/
8 #include <avr/interrupt.h>
9 #include <avr/io.h>
10 #include <inttypes.h>
11 #include <stdlib.h>
12 #include "dac.h"
13 #include "uart.h"
14 #include "hardware_settings.h"
15
16
17 //debug LED:
18 // set output to VCC, red LED off
19 #define LEDOFF PORTD|=(1<<PORTD0)
20 // set output to GND, red LED on
21 #define LEDON PORTD&=~(1<<PORTD0)
22 // to test the state of the LED
23 #define LEDISOFF PORTD&(1<<PORTD0)
24 static volatile uint8_t currentcontrol=1; // 0=voltage control, otherwise current control
25 // adc measurement results (11bit ADC):
26 static volatile int16_t analog_result[2];  
27
28
29 // target_val is the value that is requested (control loop calibrates to this).
30 // We use the same units a the ADC produces.
31 static volatile int16_t target_val[2];  // datatype int is 16 bit
32
33 static volatile int16_t dac_val=800; // the current dac setting
34
35 // You must enable interrupt with sei() in the main program 
36 // before you call init_analog 
37 void init_analog(void) 
38 {
39         // initialize the adc result to very high values
40         // to keep the control-loop down until proper measurements
41         // are done:
42         analog_result[0]=0; // I
43         analog_result[1]=20;  // U
44         target_val[0]=0; // initialize to zero, I
45         target_val[1]=0; // initialize to zero, U
46         /* enable analog to digital conversion in free run mode
47         *  without noise canceler function. See datasheet of atmega8a page 210
48         * We set ADPS2=1,ADPS1=1,ADPS0=0 to have a clock division factor of 64.
49         * This is needed to stay in the recommended range of 50-200kHz 
50         * ADEN: Analog Digital Converter Enable
51         * ADIE: ADC Interrupt Enable
52         * ADIF: ADC Interrupt Flag
53         * ADFR: ADC Free Running Mode
54         * ADCSR: ADC Control and Status Register
55         * ADPS2..ADPS0: ADC Prescaler Select Bits
56         * REFS: Reference Selection Bits (page 203)
57         */
58
59         // int-ref with external capacitor at AREF pin: 
60         // 2.56V int ref=REFS1=1,REFS0=1
61         // write only the lower 3 bit for channel selection
62         
63         // 2.56V ref, start with channel 0
64         ADMUX=(1<<REFS1)|(1<<REFS0);
65
66         ADCSR=(1<<ADEN)|(1<<ADIE)|(1<<ADFR)|(1<<ADIF)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0);
67
68         //  start conversion 
69         ADCSR|=(1<<ADSC);
70 }
71
72 int16_t get_dacval(void) 
73 {
74         return(dac_val);
75 }
76
77 uint8_t is_current_limit(void) 
78 {
79         // return 1 if current control loop active
80         if (currentcontrol){
81                 return(1);
82         }
83         return(0);
84 }
85
86 /* set the target adc value for the control loop
87  * values for item: 1 = u, 0 = i, units must be of the same as the values 
88  * from the dac.
89  */
90 void set_target_adc_val(uint8_t item,int16_t val) 
91 {
92         // here we can directly write to target_val 
93         target_val[item]=val;
94 }
95
96 int16_t getanalogresult(uint8_t channel) 
97 {
98         return(analog_result[channel]);
99 }
100
101 // the control loop changes the dac:
102 static void control_loop(void){
103         int16_t tmp;
104         int8_t ptmp=0;
105         tmp=target_val[0] - analog_result[0]; // current diff
106         if (tmp <0){
107                 // stay in currnet control if we are
108                 // close to the target. We never regulate
109                 // the difference down to zero otherweise
110                 // we would suddenly hop to voltage control
111                 // and then back to current control. Permanent
112                 // hopping would lead to oscillation and current
113                 // spikes.
114                 if (tmp>-4) tmp=0;
115                 currentcontrol=40; // I control
116                 if (analog_result[1]>target_val[1]){
117                         tmp=-20;
118                         currentcontrol=0; // U control
119                 }
120         }else{
121                 // if we are in current control then we can only go
122                 // down (tmp is negative). To increase the current
123                 // we come here to voltage control. We must slowly
124                 // count up.
125                 tmp=1 + target_val[1]  - analog_result[1]; // voltage diff
126                 //
127                 if (currentcontrol){
128                         currentcontrol--;
129                         if (currentcontrol%8==0){
130                                 // slowly up, 20 will become 1 further down
131                                 if (tmp>0) tmp=20;
132                         }else{
133                                 tmp=0;
134                         }
135                 }
136         }
137         if (tmp==0){
138                 return; // nothing to change
139         }
140         if (tmp> -5 && tmp<5){ // avoid LSB bouncing if we are close
141                 if (tmp>0){
142                         ptmp++;
143                         tmp=0;
144                         if (ptmp>1){
145                                 tmp=1;
146                                 ptmp=0;
147                         }
148                 }
149                 if (tmp<0){
150                         ptmp--;
151                         tmp=0;
152                         if (ptmp<-1){
153                                 tmp=-1;
154                                 ptmp=0;
155                         }
156                 }
157         }
158         // put a cap on increase
159         if (tmp>1){
160                 tmp=1;
161         }
162         // put a cap on decrease
163         if (tmp<-1){
164                 tmp=-1;
165         }
166         dac_val+=tmp;
167         if (dac_val>0xFFF){
168                 dac_val=0xFFF; //max, 12bit
169         }
170         if (dac_val<400){  // the output is zero below 400 due to transistor threshold
171                 dac_val=400;
172         }
173         dac(dac_val);
174 }
175 /* the following function will be called when analog conversion is done.
176  * It will be called every 13th cycle of the converson clock. At 8Mhz
177  * and a ADPS factor of 64 this is: ((8000 kHz/ 64) / 13)= 9.6KHz intervalls
178  *
179  * We do 4 fold oversampling as explained in atmel doc AVR121.
180  */
181 //SIGNAL(SIG_ADC) {
182 ISR(ADC_vect) {
183         uint8_t i=0;
184         uint8_t adlow;
185         int16_t currentadc;
186         static uint8_t channel=0; 
187         static uint8_t chpos=0;
188         // raw 10bit values:
189         static int16_t raw_analog_u_result[8];  
190         int16_t new_analog_u_result=0;
191         adlow=ADCL; // read low first !! 
192         currentadc=(ADCH<<8)|adlow;
193         // toggel the channel between 0 an 1. This will however
194         // not effect the next conversion as that one is already
195         // ongoing.
196         channel^=1;
197         // 2.56V ref
198         ADMUX=(1<<REFS1)|(1<<REFS0)|channel;
199         // channel=1 = U, channel=0 = I
200         if (channel==1) {
201                 raw_analog_u_result[chpos]=currentadc;
202                 //
203                 // we do 4 bit oversampling to get 11bit ADC resolution
204                 chpos=(chpos+1)%4; // rotate over 4 
205                 //analog_result[1]=0;
206                 while(i<4){
207                         new_analog_u_result+=raw_analog_u_result[i];
208                         i++;
209                 }
210                 new_analog_u_result=new_analog_u_result>>1; // 11bit
211                 // mean value:
212                 analog_result[1]=(new_analog_u_result+analog_result[1])/2; 
213         }else{
214                 analog_result[0]=currentadc; // 10bit
215         }
216         // short circuit protection does not use the over sampling results
217         // for speed reasons.
218         // short circuit protection, current is 10bit ADC
219         if (channel==0 && currentadc > SH_CIR_PROT){
220                 dac_val=400;
221                 dac(dac_val);
222                 currentcontrol=20;
223                 return;
224         }
225         //
226         if (channel==1){
227                 // only after full measurement cycle
228                 control_loop();
229         }else{
230                 uart_poll_getchar_isr();
231         }
232         // end of interrupt handler
233 }
234