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
6 * See http://www.tuxgraphics.org/electronics/
9 * Clock frequency : Internal clock 8 Mhz
10 *********************************************/
13 #include <avr/interrupt.h>
14 #define F_CPU 8000000UL // 8 MHz
15 #include <util/delay.h>
18 #include <avr/eeprom.h>
24 #include "hardware_settings.h"
26 // change this when you compile:
27 #define SWVERSION "ver: ddcp-0.6.2"
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)
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):
51 static char uartstr[UARTSTRLEN+1];
52 static uint8_t uartstrpos=0;
53 static uint8_t uart_has_one_line=0;
56 void delay_ms_uartcheck(uint8_t ms)
57 // delay for a minimum of <ms>
59 int ist_start_of_line=1;
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';
68 uart_sendchar('\n'); // the echo back puts a \r
70 // ignore leading white space on the line:
71 if (!(uartstr[uartstrpos]==' ' || uartstr[uartstrpos]=='\t')){
76 if (ist_start_of_line==0){
80 if (uartstrpos>UARTSTRLEN){
81 uart_sendstr_P("\r\nERROR\r\n");
82 uartstrpos=0; // empty buffer
83 uartstr[uartstrpos]='\0'; // just print prompt
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.
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){
106 itoa(inum,chbuf,10); // convert integer to string
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");
114 outbuf[j-1]=chbuf[i-1];
117 if (j==4-decimalpoint_pos){
118 // jump over the pre-set dot
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 )));
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
133 static int16_t disp_i_to_u_adc_offset(int16_t disp){
134 return(disp_u_to_adc(disp/12));
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){
141 adcdrop=disp_i_to_u_adc_offset(disp_i_val);
142 if (adcunits < adcdrop){
145 adcunits=adcunits-adcdrop;
146 return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
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));
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));
159 static void store_permanent(void){
161 uint8_t changeflag=1;
163 if (eeprom_read_byte((uint8_t *)0x0) == 19){
165 // ok magic number matches accept values
166 tmp=eeprom_read_word((uint16_t *)0x04);
167 if (tmp != set_val[1]){
170 tmp=eeprom_read_word((uint16_t *)0x02);
171 if (tmp != set_val[0]){
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]);
182 // display software version after long press
183 lcd_puts_P(SWVERSION);
185 lcd_puts_P("tuxgraphics.org");
187 lcd_puts_P("already stored");
190 delay_ms_uartcheck(200);
193 // check the keyboard
194 static uint8_t check_buttons(void){
195 uint8_t uartprint_ok=0;
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){
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"));
221 if (uartstr[0]=='s' && uartstr[1]=='t'){
225 if (uartstr[0]=='u' && uartstr[1]=='=' && uartstr[2]!='\0'){
226 set_val[1]=atoi(&uartstr[2]);
227 if(set_val[1]>U_MAX){
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"));
245 uart_sendstr_p(P(" ok\r\n"));
247 if (uartstr[0]!='\0' && cmdok==0){
248 uart_sendstr_p(P(" command unknown\r\n"));
251 int_to_dispstr(measured_val[1],buf,1);
256 int_to_dispstr(set_val[1],buf,1);
260 int_to_dispstr(measured_val[0],buf,2);
265 int_to_dispstr(set_val[0],buf,2);
268 if (is_current_limit()){
278 if (check_u_button(&(set_val[1]))){
279 if(set_val[1]>U_MAX){
284 if (check_i_button(&(set_val[0]))){
285 if(set_val[0]>I_MAX){
290 if (check_store_button()){
304 // debug led, you can not have an LED if you use the uart
305 DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
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);
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
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]);
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();
347 itoa(getanalogresult(1),out_buf,10);
349 int_to_dispstr(measured_val[1],out_buf,1);
354 itoa(set_val_adcUnits[1],out_buf,10);
356 int_to_dispstr(set_val[1],out_buf,1);
361 // put a marker to show which value is currenlty limiting
370 itoa(getanalogresult(0),out_buf,10);
372 int_to_dispstr(measured_val[0],out_buf,2);
377 itoa(set_val_adcUnits[0],out_buf,10);
379 int_to_dispstr(set_val[0],out_buf,2);
384 // put a marker to show which value is currenlty limiting
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);
396 if (check_buttons()==0){
397 // no buttons pressed
398 delay_ms_uartcheck(20);
401 delay_ms_uartcheck(180);
406 // somebody pressed permanetly the button=>scroll fast
407 delay_ms_uartcheck(30);
410 delay_ms_uartcheck(180);