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