0.6.0 this is the first version for the new hardware
[digitaldcpower] / main.c
1 /*********************************************
2 * vim: set sw=8 ts=8 si :
3 * Author: Guido Socher, Copyright: GPL v3
4 * This is the main program for the digital dc power supply
5
6 * See http://www.tuxgraphics.org/electronics/
7
8 * Chip type           : ATMEGA8
9 * Clock frequency     : Internal clock 8 Mhz 
10 *********************************************/
11 #include <avr/io.h>
12 #include <inttypes.h>
13 #include <avr/interrupt.h>
14 #define F_CPU 8000000UL  // 8 MHz
15 #include <util/delay.h>
16 #include <stdlib.h> 
17 #include <string.h> 
18 #include <avr/eeprom.h> 
19 #include "lcd.h"
20 #include "dac.h"
21 #include "kbd.h"
22 #include "analog.h"
23 #include "hardware_settings.h"
24
25 #define SWVERSION "ver: ddcp-0.6.0"
26 //#define DEBUGDISP 1
27
28 //debug LED:
29 // set output to VCC, red LED off
30 #define LEDOFF PORTD|=(1<<PORTD0)
31 // set output to GND, red LED on
32 #define LEDON PORTD&=~(1<<PORTD0)
33 // to test the state of the LED
34 #define LEDISOFF PORTD&(1<<PORTD0)
35 //
36 // the units are display units and work as follows: 100mA=10 5V=50
37 // The function int_to_ascii is used to convert the intenal values
38 // into strings for the display
39 static int16_t measured_val[2]={0,0};
40 static int16_t set_val[2];
41 // the set values but converted to ADC steps
42 static int16_t set_val_adcUnits[2]; 
43 static uint8_t bpress=0;
44
45 void delay_ms(uint8_t ms)
46 // delay for a minimum of <ms> 
47 {
48         while(ms){
49                 _delay_ms(0.96);
50                 ms--;
51         }
52 }
53
54 // Convert an integer which is representing a float into a string.
55 // decimalpoint_pos sets the decimal point after 2 pos from the right: e.g 74 becomes "0.74"
56 // The integer may not be larger than 999.
57 // The integer must be a positive number.
58 // spacepadd can be used to add a leading speace if number is less than 10
59 //static void int_to_ascii(uint16_t inum,char *outbuf,int8_t decimalpoint_pos){
60 //      char chbuf[8];
61 //      itoa(inum,chbuf,10); // convert integer to string
62 static void int_to_ascii(int16_t inum,char *outbuf,int8_t decimalpoint_pos,uint8_t spacepadd){
63         int8_t i,j;
64         char chbuf[8];
65         j=0;
66         while(inum>9 && j<7){
67                 // zero is ascii 48:
68                 chbuf[j]=(char)48+ inum-((inum/10)*10);
69                 inum=inum/10;
70                 j++;
71                 if(decimalpoint_pos==j){
72                         chbuf[j]='.';
73                         j++;
74                 }
75         }
76         chbuf[j]=(char)48+inum; // most significant digit
77         decimalpoint_pos--;
78         while(j<decimalpoint_pos){
79                 j++;
80                 chbuf[j]='0';
81         }
82         if (spacepadd && j > (decimalpoint_pos+2)){
83                 // no leading space padding needed
84                 spacepadd=0;
85         }
86         if(decimalpoint_pos==j){
87                 j++;
88                 chbuf[j]='.';
89                 j++;
90                 chbuf[j]='0'; // leading zero
91         }
92         if (spacepadd){
93                 j++;
94                 chbuf[j]=' '; // leading space padding: "9.50" becomes " 9.50"
95         }
96         // now reverse the order 
97         i=0;
98         while(j>=0){
99                 outbuf[i]=chbuf[j];
100                 j--;
101                 i++;
102         }
103         outbuf[i]='\0';
104 }
105
106 // convert voltage values to adc values, disp=10 is 1.0V
107 // ADC for voltage is 11bit:
108 static int16_t disp_u_to_adc(int16_t disp){
109         return((int16_t)(((float)disp * 204.7) / (ADC_REF * U_DIVIDER )));
110 }
111 // calculate the needed adc offset for voltage drop on the
112 // current measurement shunt (the shunt has about 0.75 Ohm =1/1.33 Ohm)
113 // use 1/1.2 instead of 1/1.3 because cables and connectors have as well
114 // a loss.
115 static int16_t disp_i_to_u_adc_offset(int16_t disp){
116         return(disp_u_to_adc(disp/12));
117 }
118 // convert adc values to voltage values, disp=10 is 1.0V
119 // disp_i_val is needed to calculate the offset for the voltage drop over
120 // the current measurement shunt, voltage measurement is 11bit
121 static int16_t adc_u_to_disp(int16_t adcunits,int16_t disp_i_val){
122         int16_t adcdrop;
123         adcdrop=disp_i_to_u_adc_offset(disp_i_val);
124         if (adcunits < adcdrop){
125                 return(0);
126         }
127         adcunits=adcunits-adcdrop;
128         return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
129 }
130 // convert adc values to current values, disp=10 needed to be printed
131 // by the printing function as 0.10 A, current measurement is 10bit
132 static int16_t disp_i_to_adc(int16_t disp){
133         return((int16_t) (((disp * 10.23)* I_RESISTOR) / ADC_REF));
134 }
135 // convert adc values to current values, disp=10 needed to be printed
136 // by the printing function as 0.10 A, current measurement is 10bit
137 static int16_t adc_i_to_disp(int16_t adcunits){
138         return((int16_t) (((float)adcunits* ADC_REF)/(10.23 * I_RESISTOR)+0.5));
139 }
140
141 static void store_permanent(void){
142         int16_t tmp;
143         uint8_t changeflag=1;
144         lcd_clrscr();
145         if (eeprom_read_byte((uint8_t *)0x0) == 19){
146                 changeflag=0;
147                 // ok magic number matches accept values
148                 tmp=eeprom_read_word((uint16_t *)0x04);
149                 if (tmp != set_val[1]){
150                         changeflag=1;
151                 }
152                 tmp=eeprom_read_word((uint16_t *)0x02);
153                 if (tmp != set_val[0]){
154                         changeflag=1;
155                 }
156         }
157         if (changeflag){
158                 lcd_puts_P("setting stored");
159                 eeprom_write_byte((uint8_t *)0x0,19); // magic number
160                 eeprom_write_word((uint16_t *)0x02,set_val[0]);
161                 eeprom_write_word((uint16_t *)0x04,set_val[1]);
162         }else{
163                 if (bpress> 3){
164                         // display software version after long press
165                         lcd_puts_P(SWVERSION);
166                         lcd_gotoxy(0,1);
167                         lcd_puts_P("tuxgraphics.org");
168                 }else{
169                         lcd_puts_P("already stored");
170                 }
171         }
172         delay_ms(200);
173 }
174
175 // check the keyboard
176 static uint8_t check_buttons(void){
177         if (check_u_button(&(set_val[1]))){
178                 if(set_val[1]>U_MAX){
179                         set_val[1]=U_MAX;
180                 }
181                 return(1);
182         }
183         if (check_i_button(&(set_val[0]))){
184                 if(set_val[0]>I_MAX){
185                         set_val[0]=I_MAX;
186                 }
187                 return(1);
188         }
189         if (check_store_button()){
190                 store_permanent();
191                 return(2);
192         };
193         return(0);
194 }
195
196 int main(void)
197 {
198         char out_buf[21];
199         uint8_t i=0;
200         uint8_t ilimit=0;
201         // debug led
202         DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
203         LEDOFF;
204
205         init_dac();
206         lcd_init();
207         init_kbd();
208         set_val[0]=15;set_val[1]=50; // 150mA and 5V
209         if (eeprom_read_byte((uint8_t *)0x0) == 19){
210                 // ok magic number matches accept values
211                 set_val[1]=eeprom_read_word((uint16_t *)0x04);
212                 set_val[0]=eeprom_read_word((uint16_t *)0x02);
213         }
214         sei();
215         init_analog();
216         while (1) {
217                 i++;
218                 // due to electrical interference we can get some
219                 // garbage onto the display especially if the power supply
220                 // source is not stable enough. We can remedy it a bit in
221                 // software with an ocasional reset:
222                 if (i==50){ // not every round to avoid flicker
223                         lcd_reset();
224                         i=0;
225                 }
226                 lcd_home();
227                 // current
228                 measured_val[0]=adc_i_to_disp(getanalogresult(0));
229                 set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
230                 set_target_adc_val(0,set_val_adcUnits[0]);
231                 // voltage
232                 measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
233                 set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
234                 set_target_adc_val(1,set_val_adcUnits[1]);
235                 ilimit=is_current_limit();
236
237                         
238                 // voltage
239 #ifdef DEBUGDISP
240                 itoa(getanalogresult(1),out_buf,10);
241 #else
242                 int_to_ascii(measured_val[1],out_buf,1,1);
243 #endif
244                 lcd_puts(out_buf);
245                 lcd_puts("V [");
246 #ifdef DEBUGDISP
247                 itoa(set_val_adcUnits[1],out_buf,10);
248 #else
249                 int_to_ascii(set_val[1],out_buf,1,1);
250 #endif
251                 lcd_puts(out_buf);
252                 lcd_putc(']');
253                 if (!ilimit){
254                         // put a marker to show which value is currenlty limiting
255                         lcd_puts("<- ");
256                 }else{
257                         lcd_puts("   ");
258                 }
259
260                 // current
261                 lcd_gotoxy(0,1);
262 #ifdef DEBUGDISP
263                 itoa(getanalogresult(0),out_buf,10);
264 #else
265                 int_to_ascii(measured_val[0],out_buf,2,0);
266 #endif
267                 lcd_puts(out_buf);
268                 lcd_puts("A [");
269 #ifdef DEBUGDISP
270                 itoa(set_val_adcUnits[0],out_buf,10);
271 #else
272                 int_to_ascii(set_val[0],out_buf,2,0);
273 #endif
274                 lcd_puts(out_buf);
275                 lcd_putc(']');
276                 if (ilimit){
277                         // put a marker to show which value is currenlty limiting
278                         lcd_puts("<- ");
279                 }else{
280                         lcd_puts("   ");
281                 }
282
283                 // the buttons must be responsive but they must not 
284                 // scroll too fast if pressed permanently
285                 if (check_buttons()==0){
286                         // no buttons pressed
287                         delay_ms(20);
288                         bpress=0;
289                         if (check_buttons()==0){
290                                 // no buttons pressed
291                                 delay_ms(20);
292                         }else{
293                                 bpress++;
294                                 delay_ms(180);
295                         }
296                 }else{
297                         // button press
298                         if (bpress > 10){
299                                 // somebody pressed permanetly the button=>scroll fast
300                                 delay_ms(30);
301                         }else{
302                                 bpress++;
303                                 delay_ms(180);
304                         }
305                 }
306         }
307         return(0);
308 }
309