d3b660e95cca8ca5fe32aaa8be8706ed67066cbf
[digitaldcpower] / main.c
1 /*********************************************
2 * vim: set sw=8 ts=8 si et :
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 "uart.h"
23 #include "analog.h"
24 #include "hardware_settings.h"
25
26 // change this version string when you compile:
27 #define SWVERSION "ver: ddcp-0.6.4"
28 //#define DEBUGDISP 1
29
30 //debug LED:
31 // set output to VCC, red LED off
32 #define LEDOFF PORTD|=(1<<PORTD0)
33 // set output to GND, red LED on
34 #define LEDON PORTD&=~(1<<PORTD0)
35 // to test the state of the LED
36 #define LEDISOFF PORTD&(1<<PORTD0)
37 //
38 // the units are display units and work as follows: 100mA=10 5V=50
39 // The function int_to_dispstr is used to convert the intenal values
40 // into strings for the display
41 static int16_t measured_val[2]={0,0};
42 static int16_t set_val[2];
43 // the set values but converted to ADC steps
44 static int16_t set_val_adcUnits[2]; 
45 static uint8_t bpress=0;
46 // comment this out to use a debug LED on PD0 (RXD):
47 #define USE_UART 1
48 //
49 #ifdef USE_UART
50 #define UARTSTRLEN 10
51 static char uartstr[UARTSTRLEN+1];
52 static uint8_t uartstrpos=0;
53 static uint8_t uart_has_one_line=0;
54 #endif
55
56 // convert voltage values to adc values, disp=10 is 1.0V
57 // ADC for voltage is 11bit:
58 static int16_t disp_u_to_adc(int16_t disp){
59         return((int16_t)(((float)disp * 204.7) / (ADC_REF * U_DIVIDER )));
60 }
61 // calculate the needed adc offset for voltage drop on the
62 // current measurement shunt (the shunt has about 0.75 Ohm =1/1.33 Ohm)
63 // use 1/1.2 instead of 1/1.3 because cables and connectors have as well
64 // a loss.
65 static int16_t disp_i_to_u_adc_offset(int16_t disp){
66         return(disp_u_to_adc(disp/12));
67 }
68 // convert adc values to voltage values, disp=10 is 1.0V
69 // disp_i_val is needed to calculate the offset for the voltage drop over
70 // the current measurement shunt, voltage measurement is 11bit
71 static int16_t adc_u_to_disp(int16_t adcunits,int16_t disp_i_val){
72         int16_t adcdrop;
73         adcdrop=disp_i_to_u_adc_offset(disp_i_val);
74         if (adcunits < adcdrop){
75                 return(0);
76         }
77         adcunits=adcunits-adcdrop;
78         return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
79 }
80 // convert adc values to current values, disp=10 needed to be printed
81 // by the printing function as 0.10 A, current measurement is 10bit
82 static int16_t disp_i_to_adc(int16_t disp){
83         return((int16_t) (((disp * 10.23)* I_RESISTOR) / ADC_REF));
84 }
85 // convert adc values to current values, disp=10 needed to be printed
86 // by the printing function as 0.10 A, current measurement is 10bit
87 static int16_t adc_i_to_disp(int16_t adcunits){
88         return((int16_t) (((float)adcunits* ADC_REF)/(10.23 * I_RESISTOR)+0.5));
89 }
90
91 static void update_controlloop_targets(void){
92         // current
93         measured_val[0]=adc_i_to_disp(getanalogresult(0));
94         set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
95         set_target_adc_val(0,set_val_adcUnits[0]);
96         // voltage
97         measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
98         set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
99         set_target_adc_val(1,set_val_adcUnits[1]);
100 }
101
102 void delay_ms_uartcheck(uint8_t ms)
103 // delay for a minimum of <ms> 
104 {
105         uint8_t innerloop=1;
106         char c;
107         while(ms){
108 #ifdef USE_UART
109                 if(uart_has_one_line==0 && uart_getchar_isr_noblock(&c)){
110                         if (c=='\n') c='\r'; // Make unix scripting easier. A terminal, even under unix, does not send \n
111                         // ignore any white space and characters we do not use:
112                         if (!(c=='\b'||(c>='0'&&c<='z')||c==0x7f||c=='\r')){
113                                 goto NEXTCHAR;
114                         }
115                         if (c=='\r'){
116                                 uartstr[uartstrpos]='\0';
117                                 uart_sendchar('\r'); // the echo line end
118                                 uart_sendchar('\n'); // the echo line end
119                                 uart_has_one_line=1;
120                                 goto NEXTCHAR;
121                         }
122                         /*
123                                 // debug
124                                 itoa(c,buf,10);
125                                 uart_sendchar('\r'); // the echo line end
126                                 uart_sendchar('\n'); // the echo line end
127                                 uart_sendchar('|');uart_sendstr(buf);uart_sendchar('|');
128                                 uart_sendchar('\r'); // the echo line end
129                                 uart_sendchar('\n'); // the echo line end
130                                 */
131                         if (c=='\b'){ // backspace
132                                 if (uartstrpos>0){
133                                         uartstrpos--;
134                                         uart_sendchar(c); // echo back
135                                         uart_sendchar(' '); // clear char on screen
136                                         uart_sendchar('\b');
137                                 }
138                         }else if (c==0x7f){ // del
139                                 if (uartstrpos>0){
140                                         uartstrpos--;
141                                         uart_sendchar(c); // echo back
142                                 }
143                         }else{
144                                 uart_sendchar(c); // echo back
145                                 uartstr[uartstrpos]=c;
146                                 uartstrpos++;
147                         }
148                         if (uartstrpos>UARTSTRLEN){
149                                 uart_sendstr_P("\r\nERROR\r\n");
150                                 uartstrpos=0; // empty buffer
151                                 uartstr[0]='\0'; // just print prompt
152                                 uart_has_one_line=1; 
153                         }
154                 }
155 #endif
156 NEXTCHAR:
157                 innerloop--;
158                 if (innerloop==0){
159                         innerloop=45;
160                         ms--;
161                 }
162         }
163 }
164
165 // Convert an integer which is representing a float into a string.
166 // Our display is always 4 digits long (including one
167 // decimal point position). decimalpoint_pos defines
168 // after how many positions from the right we set the decimal point.
169 // The resulting string is fixed width and padded with leading space.
170 //
171 // decimalpoint_pos=2 sets the decimal point after 2 pos from the right: 
172 // e.g 74 becomes "0.74"
173 // The integer should not be larger than 999.
174 // The integer must be a positive number.
175 // decimalpoint_pos can be 0, 1 or 2
176 static void int_to_dispstr(uint16_t inum,char *outbuf,int8_t decimalpoint_pos){
177         int8_t i,j;
178         char chbuf[8];
179         itoa(inum,chbuf,10); // convert integer to string
180         i=strlen(chbuf);
181         if (i>3) i=3; //overflow protection
182         strcpy(outbuf,"   0"); //decimalpoint_pos==0
183         if (decimalpoint_pos==1) strcpy(outbuf," 0.0");
184         if (decimalpoint_pos==2) strcpy(outbuf,"0.00");
185         j=4;
186         while(i){
187                 outbuf[j-1]=chbuf[i-1];
188                 i--;
189                 j--;
190                 if (j==4-decimalpoint_pos){
191                         // jump over the pre-set dot
192                         j--;
193                 }
194         }
195 }
196
197 static void store_permanent(void){
198         int16_t tmp;
199         uint8_t changeflag=1;
200         lcd_clrscr();
201         if (eeprom_read_byte((uint8_t *)0x0) == 19){
202                 changeflag=0;
203                 // ok magic number matches accept values
204                 tmp=eeprom_read_word((uint16_t *)0x04);
205                 if (tmp != set_val[1]){
206                         changeflag=1;
207                 }
208                 tmp=eeprom_read_word((uint16_t *)0x02);
209                 if (tmp != set_val[0]){
210                         changeflag=1;
211                 }
212         }
213         delay_ms_uartcheck(1); // check for uart without delay
214         if (changeflag){
215                 lcd_puts_P("setting stored");
216                 eeprom_write_byte((uint8_t *)0x0,19); // magic number
217                 eeprom_write_word((uint16_t *)0x02,set_val[0]);
218                 eeprom_write_word((uint16_t *)0x04,set_val[1]);
219         }else{
220                 if (bpress> 2){
221                         // display software version after long press
222                         lcd_puts_P(SWVERSION);
223                         lcd_gotoxy(0,1);
224                         lcd_puts_P("tuxgraphics.org");
225                 }else{
226                         lcd_puts_P("already stored");
227                 }
228         }
229         delay_ms_uartcheck(200);
230         delay_ms_uartcheck(200);
231         delay_ms_uartcheck(200);
232 }
233
234 // check the keyboard
235 static uint8_t check_buttons(void){
236         uint8_t uartprint_ok=0;
237         uint8_t cmdok=0;
238 #ifdef USE_UART
239         char buf[21];
240 #endif
241         //
242 #ifdef USE_UART
243         if (uart_has_one_line){
244                         if (uartstr[0]=='i' && uartstr[1]=='=' && uartstr[2]!='\0'){
245                                 set_val[0]=atoi(&uartstr[2]);
246                                 if(set_val[0]>I_MAX){
247                                         set_val[0]=I_MAX;
248                                 }
249                                 if(set_val[0]<0){
250                                         set_val[0]=0;
251                                 }
252                                 uartprint_ok=1;
253                         }
254                         // version
255                         if (uartstr[0]=='v' && uartstr[1]=='e'){
256                                 uart_sendstr_p(P("  "));
257                                 uart_sendstr_p(P(SWVERSION));
258                                 uart_sendstr_p(P("\r\n"));
259                                 cmdok=1;
260                         }
261                         // store
262                         if (uartstr[0]=='s' && uartstr[1]=='t'){
263                                 store_permanent();
264                                 uartprint_ok=1;
265                         }
266                         if (uartstr[0]=='u' && uartstr[1]=='=' && uartstr[2]!='\0'){
267                                 set_val[1]=atoi(&uartstr[2]);
268                                 if(set_val[1]>U_MAX){
269                                         set_val[1]=U_MAX;
270                                 }
271                                 if(set_val[1]<0){
272                                         set_val[1]=0;
273                                 }
274                                 uartprint_ok=1;
275                         }
276                         // help
277                         if (uartstr[0]=='h' || uartstr[0]=='H'){
278                                 uart_sendstr_p(P("  Usage: u=V*10|i=mA/10|store|help|version\r\n"));
279                                 uart_sendstr_p(P("  Examples:\r\n"));
280                                 uart_sendstr_p(P("  set 6V: u=60\r\n"));
281                                 uart_sendstr_p(P("  max 200mA: i=20\r\n"));
282                                 cmdok=1;
283                         }
284                         if (uartprint_ok){
285                                 cmdok=1;
286                                 uart_sendstr_p(P("  ok\r\n"));
287                         }
288                         if (uartstr[0]!='\0' && cmdok==0){
289                                 uart_sendstr_p(P("  command unknown\r\n"));
290                         }
291                         uart_sendchar('#'); // marking char for script interface
292                         int_to_dispstr(measured_val[1],buf,1);
293                         uart_sendstr(buf);
294                         uart_sendchar('V');
295                         uart_sendchar(' ');
296                         uart_sendchar('[');
297                         int_to_dispstr(set_val[1],buf,1);
298                         uart_sendstr(buf);
299                         uart_sendchar(']');
300                         uart_sendchar(',');
301                         int_to_dispstr(measured_val[0],buf,2);
302                         uart_sendstr(buf);
303                         uart_sendchar('A');
304                         uart_sendchar(' ');
305                         uart_sendchar('[');
306                         int_to_dispstr(set_val[0],buf,2);
307                         uart_sendstr(buf);
308                         uart_sendchar(']');
309                         if (is_current_limit()){
310                                 uart_sendchar('I');
311                         }else{
312                                 uart_sendchar('U');
313                         }
314                         uart_sendchar('>');
315                 uartstrpos=0;
316                 uart_has_one_line=0;
317         }
318 #endif
319         if (check_u_button(&(set_val[1]))){
320                 if(set_val[1]>U_MAX){
321                         set_val[1]=U_MAX;
322                 }
323                 return(1);
324         }
325         if (check_i_button(&(set_val[0]))){
326                 if(set_val[0]>I_MAX){
327                         set_val[0]=I_MAX;
328                 }
329                 return(1);
330         }
331         if (check_store_button()){
332                 store_permanent();
333                 return(2);
334         };
335         return(0);
336 }
337
338 int main(void)
339 {
340         char out_buf[21];
341         uint8_t i=0;
342         uint8_t ilimit=0;
343
344 #ifndef USE_UART
345         // debug led, you can not have an LED if you use the uart
346         DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
347         LEDOFF;
348 #endif
349
350         init_dac();
351         lcd_init();
352         init_kbd();
353         set_val[0]=15;set_val[1]=50; // 150mA and 5V
354         if (eeprom_read_byte((uint8_t *)0x0) == 19){
355                 // ok magic number matches accept values
356                 set_val[1]=eeprom_read_word((uint16_t *)0x04);
357                 set_val[0]=eeprom_read_word((uint16_t *)0x02);
358                 // sanity check:
359                 if (set_val[0]<0) set_val[0]=0;
360                 if (set_val[1]<0) set_val[1]=0;
361         }
362 #ifdef USE_UART
363         uart_init();
364 #endif
365         sei();
366         init_analog();
367         while (1) {
368                 i++;
369                 // due to electrical interference we can get some
370                 // garbage onto the display especially if the power supply
371                 // source is not stable enough. We can remedy it a bit in
372                 // software with an ocasional reset:
373                 if (i==50){ // not every round to avoid flicker
374                         lcd_reset();
375                         i=0;
376                 }
377                 lcd_home();
378                 update_controlloop_targets();
379                 ilimit=is_current_limit();
380
381                 // voltage
382 #ifdef DEBUGDISP
383                 itoa(getanalogresult(1),out_buf,10);
384 #else
385                 int_to_dispstr(measured_val[1],out_buf,1);
386 #endif
387                 lcd_puts(out_buf);
388                 lcd_puts("V [");
389 #ifdef DEBUGDISP
390                 itoa(set_val_adcUnits[1],out_buf,10);
391 #else
392                 int_to_dispstr(set_val[1],out_buf,1);
393 #endif
394                 lcd_puts(out_buf);
395                 lcd_putc(']');
396                 delay_ms_uartcheck(1); // check for uart without delay
397                 if (!ilimit){
398                         // put a marker to show which value is currenlty limiting
399                         lcd_puts("<- ");
400                 }else{
401                         lcd_puts("   ");
402                 }
403
404                 // current
405                 lcd_gotoxy(0,1);
406 #ifdef DEBUGDISP
407                 itoa(getanalogresult(0),out_buf,10);
408 #else
409                 int_to_dispstr(measured_val[0],out_buf,2);
410 #endif
411                 lcd_puts(out_buf);
412                 lcd_puts("A [");
413 #ifdef DEBUGDISP
414                 itoa(set_val_adcUnits[0],out_buf,10);
415 #else
416                 int_to_dispstr(set_val[0],out_buf,2);
417 #endif
418                 lcd_puts(out_buf);
419                 lcd_putc(']');
420                 if (ilimit){
421                         // put a marker to show which value is currenlty limiting
422                         lcd_puts("<- ");
423                 }else{
424                         lcd_puts("   ");
425                 }
426
427                 update_controlloop_targets();
428
429                 // the buttons must be responsive but they must not 
430                 // scroll too fast if pressed permanently
431                 if (check_buttons()==0){
432                         // no buttons pressed
433                         delay_ms_uartcheck(80);
434                         bpress=0;
435                         if (check_buttons()==0){
436                                 // no buttons pressed
437                                 delay_ms_uartcheck(80);
438                         }else{
439                                 bpress++;
440                                 delay_ms_uartcheck(160);
441                                 delay_ms_uartcheck(160);
442                                 delay_ms_uartcheck(160);
443                                 delay_ms_uartcheck(160);
444                         }
445                 }else{
446                         // button press
447                         if (bpress > 10){
448                                 // somebody pressed permanetly the button=>scroll fast
449                                 delay_ms_uartcheck(120);
450                         }else{
451                                 bpress++;
452                                 delay_ms_uartcheck(180);
453                                 delay_ms_uartcheck(180);
454                                 delay_ms_uartcheck(180);
455                                 delay_ms_uartcheck(180);
456                         }
457                 }
458         }
459         return(0);
460 }
461