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
6 * See http://www.tuxgraphics.org/electronics/
9 * Clock frequency : Internal clock 8 Mhz
10 *********************************************/
12 #include <avr/pgmspace.h>
14 #include <avr/interrupt.h>
15 #define F_CPU 8000000UL // 8 MHz
16 #include <util/delay.h>
19 #include <avr/eeprom.h>
25 #include "hardware_settings.h"
27 // change this version string when you compile:
28 #define SWVERSION "ver: ddcp-0.6.6"
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)
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):
52 static char uartstr[UARTSTRLEN+1];
53 static uint8_t uartstrpos=0;
54 static uint8_t uart_has_one_line=0;
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 )));
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
66 static int16_t disp_i_to_u_adc_offset(int16_t disp){
67 return(disp_u_to_adc(disp/12));
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){
74 adcdrop=disp_i_to_u_adc_offset(disp_i_val);
75 if (adcunits < adcdrop){
78 adcunits=adcunits-adcdrop;
79 return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
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));
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));
92 static void update_controlloop_targets(void){
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]);
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]);
103 void delay_ms_uartcheck(uint8_t ms)
104 // delay for a minimum of <ms>
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')){
117 uartstr[uartstrpos]='\0';
118 uart_sendchar('\r'); // the echo line end
119 uart_sendchar('\n'); // the echo line end
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
132 if (c=='\b'){ // backspace
135 uart_sendchar(c); // echo back
136 uart_sendchar(' '); // clear char on screen
139 }else if (c==0x7f){ // del
142 uart_sendchar(c); // echo back
145 uart_sendchar(c); // echo back
146 uartstr[uartstrpos]=c;
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
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.
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){
180 itoa(inum,chbuf,10); // convert integer to string
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");
188 outbuf[j-1]=chbuf[i-1];
191 if (j==4-decimalpoint_pos){
192 // jump over the pre-set dot
198 static void store_permanent(void){
200 uint8_t changeflag=1;
202 if (eeprom_read_byte((uint8_t *)0x0) == 19){
204 // ok magic number matches accept values
205 tmp=eeprom_read_word((uint16_t *)0x04);
206 if (tmp != set_val[1]){
209 tmp=eeprom_read_word((uint16_t *)0x02);
210 if (tmp != set_val[0]){
214 delay_ms_uartcheck(1); // check for uart without delay
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]);
222 // display software version after long press
223 lcd_puts_p(PSTR(SWVERSION));
225 lcd_puts_p(PSTR("tuxgraphics.org"));
227 lcd_puts_p(PSTR("already stored"));
230 delay_ms_uartcheck(200);
231 delay_ms_uartcheck(200);
232 delay_ms_uartcheck(200);
235 // check the keyboard
236 static uint8_t check_buttons(void){
237 uint8_t uartprint_ok=0;
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){
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"));
263 if (uartstr[0]=='s' && uartstr[1]=='t'){
267 if (uartstr[0]=='u' && uartstr[1]=='=' && uartstr[2]!='\0'){
268 set_val[1]=atoi(&uartstr[2]);
269 if(set_val[1]>U_MAX){
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"));
287 uart_sendstr_p(PSTR(" ok\r\n"));
289 if (uartstr[0]!='\0' && cmdok==0){
290 uart_sendstr_p(PSTR(" command unknown\r\n"));
292 uart_sendchar('#'); // marking char for script interface
293 int_to_dispstr(measured_val[1],buf,1);
298 int_to_dispstr(set_val[1],buf,1);
302 int_to_dispstr(measured_val[0],buf,2);
307 int_to_dispstr(set_val[0],buf,2);
310 if (is_current_limit()){
320 if (check_u_button(&(set_val[1]))){
321 if(set_val[1]>U_MAX){
326 if (check_i_button(&(set_val[0]))){
327 if(set_val[0]>I_MAX){
332 if (check_store_button()){
346 // debug led, you can not have an LED if you use the uart
347 DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
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);
360 if (set_val[0]<0) set_val[0]=0;
361 if (set_val[1]<0) set_val[1]=0;
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
379 update_controlloop_targets();
380 ilimit=is_current_limit();
384 itoa(getanalogresult(1),out_buf,10);
386 int_to_dispstr(measured_val[1],out_buf,1);
391 itoa(set_val_adcUnits[1],out_buf,10);
393 int_to_dispstr(set_val[1],out_buf,1);
397 delay_ms_uartcheck(1); // check for uart without delay
399 // put a marker to show which value is currenlty limiting
408 itoa(getanalogresult(0),out_buf,10);
410 int_to_dispstr(measured_val[0],out_buf,2);
415 itoa(set_val_adcUnits[0],out_buf,10);
417 int_to_dispstr(set_val[0],out_buf,2);
422 // put a marker to show which value is currenlty limiting
428 update_controlloop_targets();
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);
436 if (check_buttons()==0){
437 // no buttons pressed
438 delay_ms_uartcheck(80);
441 delay_ms_uartcheck(160);
442 delay_ms_uartcheck(160);
443 delay_ms_uartcheck(160);
444 delay_ms_uartcheck(160);
449 // somebody pressed permanetly the button=>scroll fast
450 delay_ms_uartcheck(120);
453 delay_ms_uartcheck(180);
454 delay_ms_uartcheck(180);
455 delay_ms_uartcheck(180);
456 delay_ms_uartcheck(180);