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