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