0.6.3 Changes since last version
authorGuido Socher <guidosocher@fastmail.fm>
Fri, 2 Jul 2010 22:00:00 +0000 (00:00 +0200)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Sun, 23 Feb 2014 21:35:04 +0000 (22:35 +0100)
-Accelerate UART command polling
-Line editing with backspace if terminal supports it
-Set BOD (brown out detector enable) fuse
-Script interface (ddcp-script)

Makefile
README.htm
analog.c
ddcp-script-example.sh [new file with mode: 0755]
ddcp-script-getval.c [new file with mode: 0644]
ddcp-script-setval.c [new file with mode: 0644]
ddcp-script-ttyinit.c [new file with mode: 0644]
main.c
main.c.1 [new file with mode: 0644]
uart.c
uart.h

index 0c37a99..a07cb92 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -25,10 +25,12 @@ OBJCOPY=avr-objcopy
 # optimize for size:
 CFLAGS=-g -mmcu=$(MCU) -Wall -W -Os -mcall-prologues
 # #-------------------
-.PHONY: test_lcd test_dac all main 
+.PHONY: test_lcd test_dac all main ddcp-script
 #
 all: main.hex test_lcd.hex test_dac.hex
 #
+ddcp-script: ddcp-script-setval ddcp-script-getval ddcp-script-ttyinit
+#
 main: main.hex 
 #
 test_lcd: test_lcd.hex
@@ -36,6 +38,13 @@ test_lcd: test_lcd.hex
 #
 test_dac: test_dac.hex
        echo "OK"
+#
+ddcp-script-setval: ddcp-script-setval.c
+       gcc -Wall -o ddcp-script-setval ddcp-script-setval.c
+ddcp-script-getval: ddcp-script-getval.c
+       gcc -Wall -o ddcp-script-getval ddcp-script-getval.c
+ddcp-script-ttyinit: ddcp-script-ttyinit.c
+       gcc -Wall -o ddcp-script-ttyinit ddcp-script-ttyinit.c
 #-------------------
 size:
        avr-size *.elf
@@ -47,10 +56,12 @@ help:
        @echo "       program using the avrdude programmer"
        @echo " "
        @echo "Usage: make clean"
-       @echo "       delete all generated files except the pre-compiled ones"
+       @echo "       delete all generated files"
        @echo "Test programs:"
        @echo "Usage: make test_lcd|load_test_lcd|test_dac|load_test_dac"
        @echo "       compile and load test programs"
+       @echo "Usage: make ddcp-script-ttyinit"
+       @echo "       compile unix program to set serial line speed such that one can use scripts to change settings"
 #-------------------
 main.hex: main.elf 
        $(OBJCOPY) -R .eeprom -O ihex main.elf main.hex 
@@ -104,7 +115,8 @@ load_test_dac: test_dac.hex
 #-------------------
 # fuse byte settings:
 #  Atmel AVR ATmega8 
-#  Fuse Low Byte      = 0xe1 (1MHz internal), 0xe3 (4MHz internal), 0xe4 (8MHz internal)
+#  Fuse Low Byte      = 0xe1 (1MHz internal), 0xe4 (8MHz internal)
+#  Fuse Low Byte with BOD  = 0xa1 (1MHz internal), 0xa4 (8MHz internal)
 #  Fuse High Byte     = 0xd9 
 #  Factory default is 0xe1 for low byte and 0xd9 for high byte
 # Check this with make rdfuses
@@ -112,12 +124,12 @@ rdfuses:
        $(LOADCMD) -p $(DUDECPUTYPE) -c stk500v2 -v -q
 # use internal RC oscillator 8 Mhz (lf=0xe4 hf=0xd9)
 fuses:
-       $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U lfuse:w:0xe4:m
+       $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U lfuse:w:0xa4:m
        $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U hfuse:w:0xd9:m
 fuse:
-       $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U lfuse:w:0xe4:m
+       $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U lfuse:w:0xa4:m
        $(LOADCMD) -p  $(DUDECPUTYPE) -c stk500v2 -u -v -U hfuse:w:0xd9:m
 #-------------------
 clean:
-       rm -f *.o *.map *.elf test*.hex main.hex 
+       rm -f *.o *.map *.elf test*.hex main.hex ddcp-script-ttyinit ddcp-script-getval ddcp-script-setval
 #-------------------
index 9019f19..00e9973 100644 (file)
@@ -10,15 +10,25 @@ Compile the software and program the microcontroller:
 make 
 make load
 
-3) set the fuse bytes to 8MHz internal:
+3) set the fuse bytes to 8MHz internal with BOD:
 make fuse
 
 This should result in the following settings:
 
 avrdude: Device signature = 0x1e9307
-avrdude: safemode: lfuse reads as E4
+avrdude: safemode: lfuse reads as A4
 avrdude: safemode: hfuse reads as D9
 
+Fuse Low Byte details
+
+BODLEVEL  Brown out detector trigger level = 1
+BODEN     Brown out detector, 0=enable     = 0
+SUT1      Select start-up time             = 1
+SUT0      Select start-up time             = 0
+CKSEL3    Select Clock source              = 0
+CKSEL2    Select Clock source              = 1
+CKSEL1    Select Clock source              = 0
+CKSEL0    Select Clock source              = 0
 
 The make fuse needs to be done only once
 unless you change the Atmega8 chip. make/make load
@@ -95,24 +105,56 @@ Terminal settings for remote control via your computer
 ======================================================
 You can e.g use putty under windows
 http://www.chiark.greenend.org.uk/~sgtatham/putty/ 
-it supports as well serial connections
+it supports as well serial connections. The other common
+option under windows is HyperTerminal but it is more complicated
+to use than putty.
 For Linux I can recommend picocom
 http://code.google.com/p/picocom/
 (use command picocom -l -b 9600 /dev/ttyUSB0)
+Another good serial terminal for linux is gtkterm
+http://www.jls-info.com/julien/linux/
+
+Serial port settings for the terminal:
 
 port       : Virtual com port (e.g /dev/ttyUSB1 or /dev/ttyUSB0
-             or COM5 under windows or ... to whatever port the
-            virtual com port maps)
-flowcontrol: none
+             or COM5 under windows or ... whatever port the
+            virtual com-port maps to)
+            Under linux or Mac you can use the command dmesg
+            after you have plugged in the USB cable to see to 
+            which com-port/device the new virtual com port maps to.
+            
 baudrate   : 9600
 parity     : none
+flowcontrol: none
+stopbits   : 1
 databits   : 8
 
 </pre>
 <br>
 <img src=screenshot-cmd-interface.gif>
 <br>
+<br>
+At shop.tuxgraphics.org you can get an optically insulated
+USB to serial interface add-on kit such that your computer
+is galvanically separated from the power supply but still able
+to give commands. This enables you as well to use two power supplies
+to provide positive and negative supply voltages for e.g. an operational amplifier.
+
+<br>
+<br>
 <pre>
+You can control the digital power supply by commands. A number 
+of commands are provided for this purpose. They are at the moment
+available for Linux and Mac:
+
+
+ddcp-script-ttyinit   - initialize the COM port
+ddcp-script-getval    - get current values (same as you see on the LCD)
+ddcp-script-setval    - send a command to the power supply
+
+ddcp-script-example.sh  - an example shell script showing how to use 
+                          the above commands
+
 -------------------------------------------------------------------
 Copyright: GPL V2
 Author: Guido Socher
@@ -128,6 +170,11 @@ digitaldcpower-0.6.1 -- 2010-06-06 Number conversion to display string
                      -- Basic uart interface prompt, no remote control yet
 
 digitaldcpower-0.6.2 -- 2010-06-26 Full UART command interface
+digitaldcpower-0.6.3 -- 2010-07-03 Accelerate UART command polling to
+                        not loose characters when copied/pasted at high speed.
+                       Line editing with backspace if terminal supports it.
+                       Set BOD (brown out detector enable) fuse.
+                       Script interface (ddcp-script)
                         
 -------------------------------------------------------------------
 </pre>
index eedeb29..6c4b3d8 100644 (file)
--- a/analog.c
+++ b/analog.c
@@ -10,6 +10,7 @@
 #include <inttypes.h>
 #include <stdlib.h>
 #include "dac.h"
+#include "uart.h"
 #include "hardware_settings.h"
 
 
@@ -225,6 +226,8 @@ ISR(ADC_vect) {
        if (channel==1){
                // only after full measurement cycle
                control_loop();
+       }else{
+               uart_poll_getchar_isr();
        }
        // end of interrupt handler
 }
diff --git a/ddcp-script-example.sh b/ddcp-script-example.sh
new file mode 100755 (executable)
index 0000000..44a9ea4
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+# Example showing how to control the tuxgraphics digital power supply
+# by scripts.
+if [ -z "$1" ]; then
+       echo "USAGE: ddcp-script-example.sh /dev/ttyUSB0"
+       echo "or     ddcp-script-example.sh /dev/ttyUSB1"
+       exit 0;
+fi
+dev="$1"
+# included the current directory to have access to the command
+# ddcp-script-getval to make testing easier. Normally you would
+# install ddcp-script-getval  in /usr/bin and then delete the
+# line that sets the PATH
+PATH="${PATH}:."
+ddcp-script-ttyinit "$dev"
+echo "current settings are:"
+ddcp-script-getval "$dev"
+echo "setting voltage to 3.3 V"
+ddcp-script-setval "u=33" "$dev"
+echo "new settings are:"
+ddcp-script-getval "$dev"
+echo "wait one sec, it takes a moment for the display values to adjust as they are polled in the avr software"
+sleep 1
+echo "print settings again:"
+ddcp-script-getval "$dev"
diff --git a/ddcp-script-getval.c b/ddcp-script-getval.c
new file mode 100644 (file)
index 0000000..3484254
--- /dev/null
@@ -0,0 +1,55 @@
+/* vim: set sw=8 ts=8 si et: */
+/* 
+ * Linux software to communicate with the DDCP
+ * Written by Guido Socher 
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+int main(int argc, char *argv[])
+{
+        char *device;
+        char c;
+        int c_cnt,state;
+        int fd;
+
+        if (argc != 2){
+                printf("USAGE: ddcp-script-getval /dev/ttyUSB0\n");
+                printf("or     ddcp-script-getval /dev/ttyUSB1\n");
+                exit(0);
+        }
+        device=argv[1];
+
+        /* Set up io port correctly, and open it... */
+        fd = open(device, O_RDWR );
+        if (fd == -1) {
+                fprintf(stderr, "ERROR: open for %s failed.\n",device);
+                exit(1);
+        }
+        write(fd,"\r",1); // send empty line
+        usleep(100000); // commands are polled in the avr and it can take 100ms
+        write(fd,"\r",1); // send empty line
+        usleep(100000); // commands are polled in the avr and it can take 100ms
+        state=0;
+        while ((c_cnt=read(fd,&c,1))){
+                //printf(":0x%x:\n",c); // debug
+                if (c=='#') { // find begining of prompt
+                        state=1;
+                }
+                if (state<1) continue;
+                if (c=='>') {
+                        putc(c,stdout);
+                        state=2;
+                }
+                if (state>1) break;
+                putc(c,stdout);
+        }
+        printf("\n");
+        close(fd);
+        usleep(100000); // commands are polled in the avr and it can take 100ms
+        return(0);
+}
diff --git a/ddcp-script-setval.c b/ddcp-script-setval.c
new file mode 100644 (file)
index 0000000..506975a
--- /dev/null
@@ -0,0 +1,41 @@
+/* vim: set sw=8 ts=8 si et: */
+/* 
+ * Linux software to communicate with the DDCP
+ * Written by Guido Socher 
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <termios.h>
+
+int main(int argc, char *argv[])
+{
+        char *device;
+        char *str;
+        int fd;
+
+        if (argc != 3){
+                printf("USAGE: ddcp-script-setval \"u=33\" /dev/ttyUSB0\n");
+                printf("or     ddcp-script-setval \"u=33\" /dev/ttyUSB1\n");
+                exit(0);
+        }
+        str=argv[1];
+        device=argv[2];
+
+        /* Set up io port correctly, and open it... */
+        fd = open(device, O_WRONLY );
+        if (fd == -1) {
+                fprintf(stderr, "ERROR: open for %s failed.\n",device);
+                exit(1);
+        }
+        write(fd,"\r",1); 
+        usleep(100000); // commands are polled in the avr and it can take 100ms
+        write(fd,str,strlen(str)); 
+        write(fd,"\r",1); 
+        close(fd);
+        usleep(150000);
+        return(0);
+}
diff --git a/ddcp-script-ttyinit.c b/ddcp-script-ttyinit.c
new file mode 100644 (file)
index 0000000..238c002
--- /dev/null
@@ -0,0 +1,42 @@
+/* vim: set sw=8 ts=8 si et: */
+/* Linux software to set the speed on the serial line 
+* Written by Guido Socher 
+* run this program like this:
+* ttydevinit /dev/ttyUSB0 (for usb com1) and then use
+* cat > /dev/ttyUSB0 to write or cat /dev/ttyUSB0 to read the answers
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+int main(int argc, char *argv[])
+{
+        struct termios portset;
+        char *device;
+        int fd;
+
+        if (argc != 2){
+                printf("USAGE: ddcp-script-ttyinit /dev/ttyUSB0\n");
+                printf("or     ddcp-script-ttyinit /dev/ttyUSB1\n");
+                exit(0);
+        }
+        device=argv[1];
+
+        /* Set up io port correctly, and open it... */
+        fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
+        if (fd == -1) {
+                fprintf(stderr, "ERROR: open for %s failed.\n",device);
+                exit(1);
+        }
+        tcgetattr(fd, &portset);
+        cfmakeraw(&portset);
+        cfsetospeed(&portset, B9600); /* speed */
+        //cfsetospeed(&portset, B115200); /* speed */
+        //cfsetospeed(&portset, B19200); /* speed */
+        tcsetattr(fd, TCSANOW, &portset);
+        close(fd);
+        return(0);
+}
diff --git a/main.c b/main.c
index 39f3f5c..514edf5 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,5 +1,5 @@
 /*********************************************
-* vim: set sw=8 ts=8 si :
+* vim: set sw=8 ts=8 si et :
 * Author: Guido Socher, Copyright: GPL v3
 * This is the main program for the digital dc power supply
 * 
@@ -23,8 +23,8 @@
 #include "analog.h"
 #include "hardware_settings.h"
 
-// change this when you compile:
-#define SWVERSION "ver: ddcp-0.6.2"
+// change this version string when you compile:
+#define SWVERSION "ver: ddcp-0.6.3"
 //#define DEBUGDISP 1
 
 //debug LED:
@@ -47,7 +47,7 @@ static uint8_t bpress=0;
 #define USE_UART 1
 //
 #ifdef USE_UART
-#define UARTSTRLEN 8
+#define UARTSTRLEN 10
 static char uartstr[UARTSTRLEN+1];
 static uint8_t uartstrpos=0;
 static uint8_t uart_has_one_line=0;
@@ -56,36 +56,63 @@ static uint8_t uart_has_one_line=0;
 void delay_ms_uartcheck(uint8_t ms)
 // delay for a minimum of <ms> 
 {
-       int ist_start_of_line=1;
+        uint8_t innerloop=1;
+        char c;
         while(ms){
-                _delay_ms(0.85);
 #ifdef USE_UART
-               if(uart_has_one_line==0 && uart_getchar_noblock(&uartstr[uartstrpos])){
-                       uart_sendchar(uartstr[uartstrpos]); // echo back
-                       if (uartstr[uartstrpos]=='\n'||uartstr[uartstrpos]=='\r'){
-                               uartstr[uartstrpos]='\0';
-                               uart_has_one_line=1;
-                               uart_sendchar('\n'); // the echo back puts a \r
-                       }
-                       // ignore leading white space on the line:
-                       if (!(uartstr[uartstrpos]==' ' || uartstr[uartstrpos]=='\t')){
-                               ist_start_of_line=0;
-                               uartstrpos++;
-                       }else{
-                               // white space
-                               if (ist_start_of_line==0){
-                                       uartstrpos++;
-                               }
-                       }
-                       if (uartstrpos>UARTSTRLEN){
-                               uart_sendstr_P("\r\nERROR\r\n");
-                               uartstrpos=0; // empty buffer
-                               uartstr[uartstrpos]='\0'; // just print prompt
-                               uart_has_one_line=1; 
-                       }
-               }
+                if(uart_has_one_line==0 && uart_getchar_isr_noblock(&c)){
+                        if (c=='\n') c='\r'; // Make unix scripting easier. A terminal, even under unix, does not send \n
+                        // ignore any white space and characters we do not use:
+                        if (!(c=='\b'||(c>='0'&&c<='z')||c==0x7f||c=='\r')){
+                                goto NEXTCHAR;
+                        }
+                        if (c=='\r'){
+                                uartstr[uartstrpos]='\0';
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                                uart_has_one_line=1;
+                                goto NEXTCHAR;
+                        }
+                        /*
+                                // debug
+                                itoa(c,buf,10);
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                                uart_sendchar('|');uart_sendstr(buf);uart_sendchar('|');
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                                */
+                        if (c=='\b'){ // backspace
+                                if (uartstrpos>0){
+                                        uartstrpos--;
+                                        uart_sendchar(c); // echo back
+                                        uart_sendchar(' '); // clear char on screen
+                                        uart_sendchar('\b');
+                                }
+                        }else if (c==0x7f){ // del
+                                if (uartstrpos>0){
+                                        uartstrpos--;
+                                        uart_sendchar(c); // echo back
+                                }
+                        }else{
+                                uart_sendchar(c); // echo back
+                                uartstr[uartstrpos]=c;
+                                uartstrpos++;
+                        }
+                        if (uartstrpos>UARTSTRLEN){
+                                uart_sendstr_P("\r\nERROR\r\n");
+                                uartstrpos=0; // empty buffer
+                                uartstr[0]='\0'; // just print prompt
+                                uart_has_one_line=1; 
+                        }
+                }
 #endif
-                ms--;
+NEXTCHAR:
+                innerloop--;
+                if (innerloop==0){
+                        innerloop=45;
+                        ms--;
+                }
         }
 }
 
@@ -101,105 +128,108 @@ void delay_ms_uartcheck(uint8_t ms)
 // The integer must be a positive number.
 // decimalpoint_pos can be 0, 1 or 2
 static void int_to_dispstr(uint16_t inum,char *outbuf,int8_t decimalpoint_pos){
-       int8_t i,j;
-       char chbuf[8];
-       itoa(inum,chbuf,10); // convert integer to string
-       i=strlen(chbuf);
-       if (i>3) i=3; //overflow protection
-       strcpy(outbuf,"   0"); //decimalpoint_pos==0
-       if (decimalpoint_pos==1) strcpy(outbuf," 0.0");
-       if (decimalpoint_pos==2) strcpy(outbuf,"0.00");
-       j=4;
-       while(i){
-               outbuf[j-1]=chbuf[i-1];
-               i--;
-               j--;
-               if (j==4-decimalpoint_pos){
-                       // jump over the pre-set dot
-                       j--;
-               }
-       }
+        int8_t i,j;
+        char chbuf[8];
+        itoa(inum,chbuf,10); // convert integer to string
+        i=strlen(chbuf);
+        if (i>3) i=3; //overflow protection
+        strcpy(outbuf,"   0"); //decimalpoint_pos==0
+        if (decimalpoint_pos==1) strcpy(outbuf," 0.0");
+        if (decimalpoint_pos==2) strcpy(outbuf,"0.00");
+        j=4;
+        while(i){
+                outbuf[j-1]=chbuf[i-1];
+                i--;
+                j--;
+                if (j==4-decimalpoint_pos){
+                        // jump over the pre-set dot
+                        j--;
+                }
+        }
 }
 
 // convert voltage values to adc values, disp=10 is 1.0V
 // ADC for voltage is 11bit:
 static int16_t disp_u_to_adc(int16_t disp){
-       return((int16_t)(((float)disp * 204.7) / (ADC_REF * U_DIVIDER )));
+        return((int16_t)(((float)disp * 204.7) / (ADC_REF * U_DIVIDER )));
 }
 // calculate the needed adc offset for voltage drop on the
 // current measurement shunt (the shunt has about 0.75 Ohm =1/1.33 Ohm)
 // use 1/1.2 instead of 1/1.3 because cables and connectors have as well
 // a loss.
 static int16_t disp_i_to_u_adc_offset(int16_t disp){
-       return(disp_u_to_adc(disp/12));
+        return(disp_u_to_adc(disp/12));
 }
 // convert adc values to voltage values, disp=10 is 1.0V
 // disp_i_val is needed to calculate the offset for the voltage drop over
 // the current measurement shunt, voltage measurement is 11bit
 static int16_t adc_u_to_disp(int16_t adcunits,int16_t disp_i_val){
-       int16_t adcdrop;
-       adcdrop=disp_i_to_u_adc_offset(disp_i_val);
-       if (adcunits < adcdrop){
-               return(0);
-       }
-       adcunits=adcunits-adcdrop;
-       return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
+        int16_t adcdrop;
+        adcdrop=disp_i_to_u_adc_offset(disp_i_val);
+        if (adcunits < adcdrop){
+                return(0);
+        }
+        adcunits=adcunits-adcdrop;
+        return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
 }
 // convert adc values to current values, disp=10 needed to be printed
 // by the printing function as 0.10 A, current measurement is 10bit
 static int16_t disp_i_to_adc(int16_t disp){
-       return((int16_t) (((disp * 10.23)* I_RESISTOR) / ADC_REF));
+        return((int16_t) (((disp * 10.23)* I_RESISTOR) / ADC_REF));
 }
 // convert adc values to current values, disp=10 needed to be printed
 // by the printing function as 0.10 A, current measurement is 10bit
 static int16_t adc_i_to_disp(int16_t adcunits){
-       return((int16_t) (((float)adcunits* ADC_REF)/(10.23 * I_RESISTOR)+0.5));
+        return((int16_t) (((float)adcunits* ADC_REF)/(10.23 * I_RESISTOR)+0.5));
 }
 
 static void store_permanent(void){
-       int16_t tmp;
-       uint8_t changeflag=1;
-       lcd_clrscr();
-       if (eeprom_read_byte((uint8_t *)0x0) == 19){
-               changeflag=0;
-               // ok magic number matches accept values
-               tmp=eeprom_read_word((uint16_t *)0x04);
-               if (tmp != set_val[1]){
-                       changeflag=1;
-               }
-               tmp=eeprom_read_word((uint16_t *)0x02);
-               if (tmp != set_val[0]){
-                       changeflag=1;
-               }
-       }
-       if (changeflag){
-               lcd_puts_P("setting stored");
-               eeprom_write_byte((uint8_t *)0x0,19); // magic number
-               eeprom_write_word((uint16_t *)0x02,set_val[0]);
-               eeprom_write_word((uint16_t *)0x04,set_val[1]);
-       }else{
-               if (bpress> 2){
-                       // display software version after long press
-                       lcd_puts_P(SWVERSION);
-                       lcd_gotoxy(0,1);
-                       lcd_puts_P("tuxgraphics.org");
-               }else{
-                       lcd_puts_P("already stored");
-               }
-       }
-       delay_ms_uartcheck(200);
+        int16_t tmp;
+        uint8_t changeflag=1;
+        lcd_clrscr();
+        if (eeprom_read_byte((uint8_t *)0x0) == 19){
+                changeflag=0;
+                // ok magic number matches accept values
+                tmp=eeprom_read_word((uint16_t *)0x04);
+                if (tmp != set_val[1]){
+                        changeflag=1;
+                }
+                tmp=eeprom_read_word((uint16_t *)0x02);
+                if (tmp != set_val[0]){
+                        changeflag=1;
+                }
+        }
+        delay_ms_uartcheck(1); // check for uart without delay
+        if (changeflag){
+                lcd_puts_P("setting stored");
+                eeprom_write_byte((uint8_t *)0x0,19); // magic number
+                eeprom_write_word((uint16_t *)0x02,set_val[0]);
+                eeprom_write_word((uint16_t *)0x04,set_val[1]);
+        }else{
+                if (bpress> 2){
+                        // display software version after long press
+                        lcd_puts_P(SWVERSION);
+                        lcd_gotoxy(0,1);
+                        lcd_puts_P("tuxgraphics.org");
+                }else{
+                        lcd_puts_P("already stored");
+                }
+        }
+        delay_ms_uartcheck(200);
+        delay_ms_uartcheck(200);
+        delay_ms_uartcheck(200);
 }
 
 // check the keyboard
 static uint8_t check_buttons(void){
-       uint8_t uartprint_ok=0;
-       uint8_t cmdok=0;
+        uint8_t uartprint_ok=0;
+        uint8_t cmdok=0;
 #ifdef USE_UART
-       char buf[21];
+        char buf[21];
 #endif
-       //
+        //
 #ifdef USE_UART
-       if (uart_has_one_line){
+        if (uart_has_one_line){
                         if (uartstr[0]=='i' && uartstr[1]=='=' && uartstr[2]!='\0'){
                                 set_val[0]=atoi(&uartstr[2]);
                                 if(set_val[0]>I_MAX){
@@ -210,18 +240,18 @@ static uint8_t check_buttons(void){
                                 }
                                 uartprint_ok=1;
                         }
-                       // version
-                       if (uartstr[0]=='v' && uartstr[1]=='e'){
-                               uart_sendstr_p(P("  "));
-                               uart_sendstr_p(P(SWVERSION));
-                               uart_sendstr_p(P("\r\n"));
-                               cmdok=1;
-                       }
-                       // store
-                       if (uartstr[0]=='s' && uartstr[1]=='t'){
-                               store_permanent();
+                        // version
+                        if (uartstr[0]=='v' && uartstr[1]=='e'){
+                                uart_sendstr_p(P("  "));
+                                uart_sendstr_p(P(SWVERSION));
+                                uart_sendstr_p(P("\r\n"));
+                                cmdok=1;
+                        }
+                        // store
+                        if (uartstr[0]=='s' && uartstr[1]=='t'){
+                                store_permanent();
                                 uartprint_ok=1;
-                       }
+                        }
                         if (uartstr[0]=='u' && uartstr[1]=='=' && uartstr[2]!='\0'){
                                 set_val[1]=atoi(&uartstr[2]);
                                 if(set_val[1]>U_MAX){
@@ -232,185 +262,195 @@ static uint8_t check_buttons(void){
                                 }
                                 uartprint_ok=1;
                         }
-                       // help
-                       if (uartstr[0]=='h' || uartstr[0]=='H'){
-                               uart_sendstr_p(P("  Usage: u=V*10|i=mA/10|store|help|version\r\n"));
-                               uart_sendstr_p(P("  Examples:\r\n"));
-                               uart_sendstr_p(P("  set 6V: u=60\r\n"));
-                               uart_sendstr_p(P("  max 200mA: i=20\r\n"));
-                               cmdok=1;
-                       }
-                       if (uartprint_ok){
-                               cmdok=1;
-                               uart_sendstr_p(P("  ok\r\n"));
-                       }
-                       if (uartstr[0]!='\0' && cmdok==0){
-                               uart_sendstr_p(P("  command unknown\r\n"));
-                       }
-
-                       int_to_dispstr(measured_val[1],buf,1);
-                       uart_sendstr(buf);
-                       uart_sendchar('V');
-                       uart_sendchar(' ');
-                       uart_sendchar('[');
-                       int_to_dispstr(set_val[1],buf,1);
-                       uart_sendstr(buf);
-                       uart_sendchar(']');
-                       uart_sendchar(',');
-                       int_to_dispstr(measured_val[0],buf,2);
-                       uart_sendstr(buf);
-                       uart_sendchar('A');
-                       uart_sendchar(' ');
-                       uart_sendchar('[');
-                       int_to_dispstr(set_val[0],buf,2);
-                       uart_sendstr(buf);
-                       uart_sendchar(']');
-                       if (is_current_limit()){
-                               uart_sendchar('I');
-                       }else{
-                               uart_sendchar('U');
-                       }
-                       uart_sendchar('>');
-               uart_has_one_line=0;
-               uartstrpos=0;
-       }
+                        // help
+                        if (uartstr[0]=='h' || uartstr[0]=='H'){
+                                uart_sendstr_p(P("  Usage: u=V*10|i=mA/10|store|help|version\r\n"));
+                                uart_sendstr_p(P("  Examples:\r\n"));
+                                uart_sendstr_p(P("  set 6V: u=60\r\n"));
+                                uart_sendstr_p(P("  max 200mA: i=20\r\n"));
+                                cmdok=1;
+                        }
+                        if (uartprint_ok){
+                                cmdok=1;
+                                uart_sendstr_p(P("  ok\r\n"));
+                        }
+                        if (uartstr[0]!='\0' && cmdok==0){
+                                uart_sendstr_p(P("  command unknown\r\n"));
+                        }
+                        uart_sendchar('#'); // marking char for script interface
+                        int_to_dispstr(measured_val[1],buf,1);
+                        uart_sendstr(buf);
+                        uart_sendchar('V');
+                        uart_sendchar(' ');
+                        uart_sendchar('[');
+                        int_to_dispstr(set_val[1],buf,1);
+                        uart_sendstr(buf);
+                        uart_sendchar(']');
+                        uart_sendchar(',');
+                        int_to_dispstr(measured_val[0],buf,2);
+                        uart_sendstr(buf);
+                        uart_sendchar('A');
+                        uart_sendchar(' ');
+                        uart_sendchar('[');
+                        int_to_dispstr(set_val[0],buf,2);
+                        uart_sendstr(buf);
+                        uart_sendchar(']');
+                        if (is_current_limit()){
+                                uart_sendchar('I');
+                        }else{
+                                uart_sendchar('U');
+                        }
+                        uart_sendchar('>');
+                uartstrpos=0;
+                uart_has_one_line=0;
+        }
 #endif
-       if (check_u_button(&(set_val[1]))){
-               if(set_val[1]>U_MAX){
-                       set_val[1]=U_MAX;
-               }
-               return(1);
-       }
-       if (check_i_button(&(set_val[0]))){
-               if(set_val[0]>I_MAX){
-                       set_val[0]=I_MAX;
-               }
-               return(1);
-       }
-       if (check_store_button()){
-               store_permanent();
-               return(2);
-       };
-       return(0);
+        if (check_u_button(&(set_val[1]))){
+                if(set_val[1]>U_MAX){
+                        set_val[1]=U_MAX;
+                }
+                return(1);
+        }
+        if (check_i_button(&(set_val[0]))){
+                if(set_val[0]>I_MAX){
+                        set_val[0]=I_MAX;
+                }
+                return(1);
+        }
+        if (check_store_button()){
+                store_permanent();
+                return(2);
+        };
+        return(0);
 }
 
 int main(void)
 {
-       char out_buf[21];
-       uint8_t i=0;
-       uint8_t ilimit=0;
+        char out_buf[21];
+        uint8_t i=0;
+        uint8_t ilimit=0;
 
 #ifndef USE_UART
-       // debug led, you can not have an LED if you use the uart
-       DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
-       LEDOFF;
+        // debug led, you can not have an LED if you use the uart
+        DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
+        LEDOFF;
 #endif
 
-       init_dac();
-       lcd_init();
-       init_kbd();
-       set_val[0]=15;set_val[1]=50; // 150mA and 5V
-       if (eeprom_read_byte((uint8_t *)0x0) == 19){
-               // ok magic number matches accept values
-               set_val[1]=eeprom_read_word((uint16_t *)0x04);
-               set_val[0]=eeprom_read_word((uint16_t *)0x02);
-       }
+        init_dac();
+        lcd_init();
+        init_kbd();
+        set_val[0]=15;set_val[1]=50; // 150mA and 5V
+        if (eeprom_read_byte((uint8_t *)0x0) == 19){
+                // ok magic number matches accept values
+                set_val[1]=eeprom_read_word((uint16_t *)0x04);
+                set_val[0]=eeprom_read_word((uint16_t *)0x02);
+                // sanity check:
+                if (set_val[0]<0) set_val[0]=0;
+                if (set_val[1]<0) set_val[1]=0;
+        }
 #ifdef USE_UART
-       uart_init();
+        uart_init();
 #endif
-       sei();
-       init_analog();
-       while (1) {
-               i++;
-               // due to electrical interference we can get some
-               // garbage onto the display especially if the power supply
-               // source is not stable enough. We can remedy it a bit in
-               // software with an ocasional reset:
-               if (i==50){ // not every round to avoid flicker
-                       lcd_reset();
-                       i=0;
-               }
-               lcd_home();
-               // current
-               measured_val[0]=adc_i_to_disp(getanalogresult(0));
-               set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
-               set_target_adc_val(0,set_val_adcUnits[0]);
-               // voltage
-               measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
-               set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
-               set_target_adc_val(1,set_val_adcUnits[1]);
-               ilimit=is_current_limit();
+        sei();
+        init_analog();
+        while (1) {
+                i++;
+                // due to electrical interference we can get some
+                // garbage onto the display especially if the power supply
+                // source is not stable enough. We can remedy it a bit in
+                // software with an ocasional reset:
+                if (i==50){ // not every round to avoid flicker
+                        lcd_reset();
+                        i=0;
+                }
+                lcd_home();
+                // current
+                measured_val[0]=adc_i_to_disp(getanalogresult(0));
+                set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
+                set_target_adc_val(0,set_val_adcUnits[0]);
+                // voltage
+                measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
+                set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
+                set_target_adc_val(1,set_val_adcUnits[1]);
+                ilimit=is_current_limit();
 
-                       
-               // voltage
+                        
+                // voltage
 #ifdef DEBUGDISP
-               itoa(getanalogresult(1),out_buf,10);
+                itoa(getanalogresult(1),out_buf,10);
 #else
-               int_to_dispstr(measured_val[1],out_buf,1);
+                int_to_dispstr(measured_val[1],out_buf,1);
 #endif
-               lcd_puts(out_buf);
-               lcd_puts("V [");
+                lcd_puts(out_buf);
+                lcd_puts("V [");
 #ifdef DEBUGDISP
-               itoa(set_val_adcUnits[1],out_buf,10);
+                itoa(set_val_adcUnits[1],out_buf,10);
 #else
-               int_to_dispstr(set_val[1],out_buf,1);
+                int_to_dispstr(set_val[1],out_buf,1);
 #endif
-               lcd_puts(out_buf);
-               lcd_putc(']');
-               if (!ilimit){
-                       // put a marker to show which value is currenlty limiting
-                       lcd_puts("<- ");
-               }else{
-                       lcd_puts("   ");
-               }
+                lcd_puts(out_buf);
+                lcd_putc(']');
+                delay_ms_uartcheck(1); // check for uart without delay
+                if (!ilimit){
+                        // put a marker to show which value is currenlty limiting
+                        lcd_puts("<- ");
+                }else{
+                        lcd_puts("   ");
+                }
 
-               // current
-               lcd_gotoxy(0,1);
+                // current
+                lcd_gotoxy(0,1);
 #ifdef DEBUGDISP
-               itoa(getanalogresult(0),out_buf,10);
+                itoa(getanalogresult(0),out_buf,10);
 #else
-               int_to_dispstr(measured_val[0],out_buf,2);
+                int_to_dispstr(measured_val[0],out_buf,2);
 #endif
-               lcd_puts(out_buf);
-               lcd_puts("A [");
+                lcd_puts(out_buf);
+                lcd_puts("A [");
 #ifdef DEBUGDISP
-               itoa(set_val_adcUnits[0],out_buf,10);
+                itoa(set_val_adcUnits[0],out_buf,10);
 #else
-               int_to_dispstr(set_val[0],out_buf,2);
+                int_to_dispstr(set_val[0],out_buf,2);
 #endif
-               lcd_puts(out_buf);
-               lcd_putc(']');
-               if (ilimit){
-                       // put a marker to show which value is currenlty limiting
-                       lcd_puts("<- ");
-               }else{
-                       lcd_puts("   ");
-               }
+                lcd_puts(out_buf);
+                lcd_putc(']');
+                if (ilimit){
+                        // put a marker to show which value is currenlty limiting
+                        lcd_puts("<- ");
+                }else{
+                        lcd_puts("   ");
+                }
 
-               // the buttons must be responsive but they must not 
-               // scroll too fast if pressed permanently
-               if (check_buttons()==0){
-                       // no buttons pressed
-                       delay_ms_uartcheck(20);
-                       bpress=0;
-                       if (check_buttons()==0){
-                               // no buttons pressed
-                               delay_ms_uartcheck(20);
-                       }else{
-                               bpress++;
-                               delay_ms_uartcheck(180);
-                       }
-               }else{
-                       // button press
-                       if (bpress > 10){
-                               // somebody pressed permanetly the button=>scroll fast
-                               delay_ms_uartcheck(30);
-                       }else{
-                               bpress++;
-                               delay_ms_uartcheck(180);
-                       }
-               }
-       }
-       return(0);
+                // the buttons must be responsive but they must not 
+                // scroll too fast if pressed permanently
+                if (check_buttons()==0){
+                        // no buttons pressed
+                        delay_ms_uartcheck(80);
+                        bpress=0;
+                        if (check_buttons()==0){
+                                // no buttons pressed
+                                delay_ms_uartcheck(80);
+                        }else{
+                                bpress++;
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                        }
+                }else{
+                        // button press
+                        if (bpress > 10){
+                                // somebody pressed permanetly the button=>scroll fast
+                                delay_ms_uartcheck(120);
+                        }else{
+                                bpress++;
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                        }
+                }
+        }
+        return(0);
 }
 
diff --git a/main.c.1 b/main.c.1
new file mode 100644 (file)
index 0000000..cacf266
--- /dev/null
+++ b/main.c.1
@@ -0,0 +1,449 @@
+/*********************************************
+* vim: set sw=8 ts=8 si et :
+* Author: Guido Socher, Copyright: GPL v3
+* This is the main program for the digital dc power supply
+* 
+* See http://www.tuxgraphics.org/electronics/
+* 
+* Chip type           : ATMEGA8
+* Clock frequency     : Internal clock 8 Mhz 
+*********************************************/
+#include <avr/io.h>
+#include <inttypes.h>
+#include <avr/interrupt.h>
+#define F_CPU 8000000UL  // 8 MHz
+#include <util/delay.h>
+#include <stdlib.h> 
+#include <string.h> 
+#include <avr/eeprom.h> 
+#include "lcd.h"
+#include "dac.h"
+#include "kbd.h"
+#include "uart.h"
+#include "analog.h"
+#include "hardware_settings.h"
+
+// change this version string when you compile:
+#define SWVERSION "ver: ddcp-0.6.3"
+//#define DEBUGDISP 1
+
+//debug LED:
+// set output to VCC, red LED off
+#define LEDOFF PORTD|=(1<<PORTD0)
+// set output to GND, red LED on
+#define LEDON PORTD&=~(1<<PORTD0)
+// to test the state of the LED
+#define LEDISOFF PORTD&(1<<PORTD0)
+//
+// the units are display units and work as follows: 100mA=10 5V=50
+// The function int_to_dispstr is used to convert the intenal values
+// into strings for the display
+static int16_t measured_val[2]={0,0};
+static int16_t set_val[2];
+// the set values but converted to ADC steps
+static int16_t set_val_adcUnits[2]; 
+static uint8_t bpress=0;
+// comment this out to use a debug LED on PD0 (RXD):
+#define USE_UART 1
+//
+#ifdef USE_UART
+#define UARTSTRLEN 10
+static char uartstr[UARTSTRLEN+1];
+static uint8_t uartstrpos=0;
+static uint8_t uart_has_one_line=0;
+#endif
+
+void delay_ms_uartcheck(uint8_t ms)
+// delay for a minimum of <ms> 
+{
+        uint8_t innerloop=1;
+        while(ms){
+#ifdef USE_UART
+                if(uart_has_one_line==0 && uart_getchar_isr_noblock(&uartstr[uartstrpos])){
+                        // ignore any white space:
+                        if (uartstr[uartstrpos]==' ' || uartstr[uartstrpos]=='\t'){
+                                goto NEXTCHAR;
+                        }
+                        if (uartstr[uartstrpos]=='\r'){
+                                uartstr[uartstrpos]='\0';
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                                //uart_sendchar('|'); uart_sendstr(&uartstr[0]);uart_sendchar('|'); //debug
+                                uart_has_one_line=1;
+                                goto NEXTCHAR;
+                        }
+                        uart_sendchar(uartstr[uartstrpos]);// echo back
+                        /*
+                                // debug
+                                itoa(uartstr[uartstrpos],buf,10);
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                                uart_sendstr(buf);
+                                uart_sendchar('\r'); // the echo line end
+                                uart_sendchar('\n'); // the echo line end
+                        */
+                        if (uartstr[uartstrpos]=='\b'){
+                                if (uartstrpos>0){
+                                        uartstrpos--;
+                                        uart_sendchar(' '); // clear char on screen
+                                        uart_sendchar('\b');
+                                }
+                        }else if (uartstr[uartstrpos]==0x7f){ // del
+                                if (uartstrpos>0){
+                                        uartstrpos--;
+                                }
+                        }else{
+                                uartstrpos++;
+                        }
+                        if (uartstrpos>UARTSTRLEN){
+                                uart_sendstr_P("\r\nERROR\r\n");
+                                uartstrpos=0; // empty buffer
+                                uartstr[0]='\0'; // just print prompt
+                                uart_has_one_line=1; 
+                        }
+                }
+#endif
+NEXTCHAR:
+                innerloop--;
+                if (innerloop==0){
+                        innerloop=45;
+                        ms--;
+                }
+        }
+}
+
+// Convert an integer which is representing a float into a string.
+// Our display is always 4 digits long (including one
+// decimal point position). decimalpoint_pos defines
+// after how many positions from the right we set the decimal point.
+// The resulting string is fixed width and padded with leading space.
+//
+// decimalpoint_pos=2 sets the decimal point after 2 pos from the right: 
+// e.g 74 becomes "0.74"
+// The integer should not be larger than 999.
+// The integer must be a positive number.
+// decimalpoint_pos can be 0, 1 or 2
+static void int_to_dispstr(uint16_t inum,char *outbuf,int8_t decimalpoint_pos){
+        int8_t i,j;
+        char chbuf[8];
+        itoa(inum,chbuf,10); // convert integer to string
+        i=strlen(chbuf);
+        if (i>3) i=3; //overflow protection
+        strcpy(outbuf,"   0"); //decimalpoint_pos==0
+        if (decimalpoint_pos==1) strcpy(outbuf," 0.0");
+        if (decimalpoint_pos==2) strcpy(outbuf,"0.00");
+        j=4;
+        while(i){
+                outbuf[j-1]=chbuf[i-1];
+                i--;
+                j--;
+                if (j==4-decimalpoint_pos){
+                        // jump over the pre-set dot
+                        j--;
+                }
+        }
+}
+
+// convert voltage values to adc values, disp=10 is 1.0V
+// ADC for voltage is 11bit:
+static int16_t disp_u_to_adc(int16_t disp){
+        return((int16_t)(((float)disp * 204.7) / (ADC_REF * U_DIVIDER )));
+}
+// calculate the needed adc offset for voltage drop on the
+// current measurement shunt (the shunt has about 0.75 Ohm =1/1.33 Ohm)
+// use 1/1.2 instead of 1/1.3 because cables and connectors have as well
+// a loss.
+static int16_t disp_i_to_u_adc_offset(int16_t disp){
+        return(disp_u_to_adc(disp/12));
+}
+// convert adc values to voltage values, disp=10 is 1.0V
+// disp_i_val is needed to calculate the offset for the voltage drop over
+// the current measurement shunt, voltage measurement is 11bit
+static int16_t adc_u_to_disp(int16_t adcunits,int16_t disp_i_val){
+        int16_t adcdrop;
+        adcdrop=disp_i_to_u_adc_offset(disp_i_val);
+        if (adcunits < adcdrop){
+                return(0);
+        }
+        adcunits=adcunits-adcdrop;
+        return((int16_t)((((float)adcunits /204.7)* ADC_REF * U_DIVIDER)+0.5));
+}
+// convert adc values to current values, disp=10 needed to be printed
+// by the printing function as 0.10 A, current measurement is 10bit
+static int16_t disp_i_to_adc(int16_t disp){
+        return((int16_t) (((disp * 10.23)* I_RESISTOR) / ADC_REF));
+}
+// convert adc values to current values, disp=10 needed to be printed
+// by the printing function as 0.10 A, current measurement is 10bit
+static int16_t adc_i_to_disp(int16_t adcunits){
+        return((int16_t) (((float)adcunits* ADC_REF)/(10.23 * I_RESISTOR)+0.5));
+}
+
+static void store_permanent(void){
+        int16_t tmp;
+        uint8_t changeflag=1;
+        lcd_clrscr();
+        if (eeprom_read_byte((uint8_t *)0x0) == 19){
+                changeflag=0;
+                // ok magic number matches accept values
+                tmp=eeprom_read_word((uint16_t *)0x04);
+                if (tmp != set_val[1]){
+                        changeflag=1;
+                }
+                tmp=eeprom_read_word((uint16_t *)0x02);
+                if (tmp != set_val[0]){
+                        changeflag=1;
+                }
+        }
+        delay_ms_uartcheck(1); // check for uart without delay
+        if (changeflag){
+                lcd_puts_P("setting stored");
+                eeprom_write_byte((uint8_t *)0x0,19); // magic number
+                eeprom_write_word((uint16_t *)0x02,set_val[0]);
+                eeprom_write_word((uint16_t *)0x04,set_val[1]);
+        }else{
+                if (bpress> 2){
+                        // display software version after long press
+                        lcd_puts_P(SWVERSION);
+                        lcd_gotoxy(0,1);
+                        lcd_puts_P("tuxgraphics.org");
+                }else{
+                        lcd_puts_P("already stored");
+                }
+        }
+        delay_ms_uartcheck(200);
+        delay_ms_uartcheck(200);
+        delay_ms_uartcheck(200);
+}
+
+// check the keyboard
+static uint8_t check_buttons(void){
+        uint8_t uartprint_ok=0;
+        uint8_t cmdok=0;
+#ifdef USE_UART
+        char buf[21];
+#endif
+        //
+#ifdef USE_UART
+        if (uart_has_one_line){
+                        if (uartstr[0]=='i' && uartstr[1]=='=' && uartstr[2]!='\0'){
+                                set_val[0]=atoi(&uartstr[2]);
+                                if(set_val[0]>I_MAX){
+                                        set_val[0]=I_MAX;
+                                }
+                                if(set_val[0]<0){
+                                        set_val[0]=0;
+                                }
+                                uartprint_ok=1;
+                        }
+                        // version
+                        if (uartstr[0]=='v' && uartstr[1]=='e'){
+                                uart_sendstr_p(P("  "));
+                                uart_sendstr_p(P(SWVERSION));
+                                uart_sendstr_p(P("\r\n"));
+                                cmdok=1;
+                        }
+                        // store
+                        if (uartstr[0]=='s' && uartstr[1]=='t'){
+                                store_permanent();
+                                uartprint_ok=1;
+                        }
+                        if (uartstr[0]=='u' && uartstr[1]=='=' && uartstr[2]!='\0'){
+                                set_val[1]=atoi(&uartstr[2]);
+                                if(set_val[1]>U_MAX){
+                                        set_val[1]=U_MAX;
+                                }
+                                if(set_val[1]<0){
+                                        set_val[1]=0;
+                                }
+                                uartprint_ok=1;
+                        }
+                        // help
+                        if (uartstr[0]=='h' || uartstr[0]=='H'){
+                                uart_sendstr_p(P("  Usage: u=V*10|i=mA/10|store|help|version\r\n"));
+                                uart_sendstr_p(P("  Examples:\r\n"));
+                                uart_sendstr_p(P("  set 6V: u=60\r\n"));
+                                uart_sendstr_p(P("  max 200mA: i=20\r\n"));
+                                cmdok=1;
+                        }
+                        if (uartprint_ok){
+                                cmdok=1;
+                                uart_sendstr_p(P("  ok\r\n"));
+                        }
+                        if (uartstr[0]!='\0' && cmdok==0){
+                                uart_sendstr_p(P("  command unknown\r\n"));
+                        }
+
+                        int_to_dispstr(measured_val[1],buf,1);
+                        uart_sendstr(buf);
+                        uart_sendchar('V');
+                        uart_sendchar(' ');
+                        uart_sendchar('[');
+                        int_to_dispstr(set_val[1],buf,1);
+                        uart_sendstr(buf);
+                        uart_sendchar(']');
+                        uart_sendchar(',');
+                        int_to_dispstr(measured_val[0],buf,2);
+                        uart_sendstr(buf);
+                        uart_sendchar('A');
+                        uart_sendchar(' ');
+                        uart_sendchar('[');
+                        int_to_dispstr(set_val[0],buf,2);
+                        uart_sendstr(buf);
+                        uart_sendchar(']');
+                        if (is_current_limit()){
+                                uart_sendchar('I');
+                        }else{
+                                uart_sendchar('U');
+                        }
+                        uart_sendchar('>');
+                uart_has_one_line=0;
+                uartstrpos=0;
+        }
+#endif
+        if (check_u_button(&(set_val[1]))){
+                if(set_val[1]>U_MAX){
+                        set_val[1]=U_MAX;
+                }
+                return(1);
+        }
+        if (check_i_button(&(set_val[0]))){
+                if(set_val[0]>I_MAX){
+                        set_val[0]=I_MAX;
+                }
+                return(1);
+        }
+        if (check_store_button()){
+                store_permanent();
+                return(2);
+        };
+        return(0);
+}
+
+int main(void)
+{
+        char out_buf[21];
+        uint8_t i=0;
+        uint8_t ilimit=0;
+
+#ifndef USE_UART
+        // debug led, you can not have an LED if you use the uart
+        DDRD|= (1<<DDD0); // LED, enable PD0, LED as output
+        LEDOFF;
+#endif
+
+        init_dac();
+        lcd_init();
+        init_kbd();
+        set_val[0]=15;set_val[1]=50; // 150mA and 5V
+        if (eeprom_read_byte((uint8_t *)0x0) == 19){
+                // ok magic number matches accept values
+                set_val[1]=eeprom_read_word((uint16_t *)0x04);
+                set_val[0]=eeprom_read_word((uint16_t *)0x02);
+        }
+#ifdef USE_UART
+        uart_init();
+#endif
+        sei();
+        init_analog();
+        while (1) {
+                i++;
+                // due to electrical interference we can get some
+                // garbage onto the display especially if the power supply
+                // source is not stable enough. We can remedy it a bit in
+                // software with an ocasional reset:
+                if (i==50){ // not every round to avoid flicker
+                        lcd_reset();
+                        i=0;
+                }
+                lcd_home();
+                // current
+                measured_val[0]=adc_i_to_disp(getanalogresult(0));
+                set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
+                set_target_adc_val(0,set_val_adcUnits[0]);
+                // voltage
+                measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
+                set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
+                set_target_adc_val(1,set_val_adcUnits[1]);
+                ilimit=is_current_limit();
+
+                        
+                // voltage
+#ifdef DEBUGDISP
+                itoa(getanalogresult(1),out_buf,10);
+#else
+                int_to_dispstr(measured_val[1],out_buf,1);
+#endif
+                lcd_puts(out_buf);
+                lcd_puts("V [");
+#ifdef DEBUGDISP
+                itoa(set_val_adcUnits[1],out_buf,10);
+#else
+                int_to_dispstr(set_val[1],out_buf,1);
+#endif
+                lcd_puts(out_buf);
+                lcd_putc(']');
+                delay_ms_uartcheck(1); // check for uart without delay
+                if (!ilimit){
+                        // put a marker to show which value is currenlty limiting
+                        lcd_puts("<- ");
+                }else{
+                        lcd_puts("   ");
+                }
+
+                // current
+                lcd_gotoxy(0,1);
+#ifdef DEBUGDISP
+                itoa(getanalogresult(0),out_buf,10);
+#else
+                int_to_dispstr(measured_val[0],out_buf,2);
+#endif
+                lcd_puts(out_buf);
+                lcd_puts("A [");
+#ifdef DEBUGDISP
+                itoa(set_val_adcUnits[0],out_buf,10);
+#else
+                int_to_dispstr(set_val[0],out_buf,2);
+#endif
+                lcd_puts(out_buf);
+                lcd_putc(']');
+                if (ilimit){
+                        // put a marker to show which value is currenlty limiting
+                        lcd_puts("<- ");
+                }else{
+                        lcd_puts("   ");
+                }
+
+                // the buttons must be responsive but they must not 
+                // scroll too fast if pressed permanently
+                if (check_buttons()==0){
+                        // no buttons pressed
+                        delay_ms_uartcheck(80);
+                        bpress=0;
+                        if (check_buttons()==0){
+                                // no buttons pressed
+                                delay_ms_uartcheck(80);
+                        }else{
+                                bpress++;
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                        }
+                }else{
+                        // button press
+                        if (bpress > 10){
+                                // somebody pressed permanetly the button=>scroll fast
+                                delay_ms_uartcheck(120);
+                        }else{
+                                bpress++;
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                                delay_ms_uartcheck(180);
+                        }
+                }
+        }
+        return(0);
+}
+
diff --git a/uart.c b/uart.c
index 72cc33f..eb7c4e1 100644 (file)
--- a/uart.c
+++ b/uart.c
 #include "uart.h"
 #define F_CPU 8000000UL  // 8 MHz
 
+// a receiver stack:
+#define SENDSTACKSIZE 12
+static volatile char ustack[SENDSTACKSIZE];
+static volatile uint8_t stackpointer_end=0;
+static uint8_t stackpointer_start=0;
+
 void uart_init(void) 
 {
         unsigned int baud=51;   // 9600 baud at 8MHz
@@ -62,6 +68,35 @@ void uart_sendstr_p(const prog_char *progmem_s)
 
 }
 
+// call this function from interrupt to fill the
+// ustack reveiver buffer. We need this big buffer
+// to handle fast copy/paste of strings comming
+// into the UART
+void uart_poll_getchar_isr(void)
+{
+#ifdef VAR_88CHIP
+        if(!(UCSR0A & (1<<RXC0))) return;
+        ustack[stackpointer_end]=UDR0;
+#else
+        if(!(UCSRA & (1<<RXC))) return;
+        ustack[stackpointer_end]=UDR;
+#endif
+        stackpointer_end=(stackpointer_end+1) % SENDSTACKSIZE;
+}
+
+// get the characters out of the buffer which is filled by
+// the above interrupt function
+unsigned char uart_getchar_isr_noblock(char *returnval)  
+{
+        if (stackpointer_start!=stackpointer_end){
+                *returnval=ustack[stackpointer_start];
+                stackpointer_start=(stackpointer_start+1) % SENDSTACKSIZE;
+                return(1);
+        }
+        return(0);
+}
+
+/*
 // get a byte from rs232
 // this function does a blocking read 
 char uart_getchar(void)  
@@ -109,3 +144,4 @@ void uart_flushRXbuf(void)
 #endif
 }
 
+*/
diff --git a/uart.h b/uart.h
index bd69475..b64b2d3 100644 (file)
--- a/uart.h
+++ b/uart.h
@@ -2,18 +2,25 @@
 /*************************************************************************
  Title:   C include file for uart
  Target:    atmega8
+ Copyright: GPL, Guido Socher
 ***************************************************************************/
 #ifndef UART_H
 #define UART_H
 #include <avr/pgmspace.h>
 
 extern void uart_init(void);
-extern void uart_sendchar(char c);
-extern void uart_sendstr(char *s);
-extern void uart_sendstr_p(const prog_char *progmem_s);
+extern void uart_poll_getchar_isr(void); // call this periodically from interrupt, has a buffer bigger than one char
+extern unsigned char uart_getchar_isr_noblock(char *returnval); // get a char from buffer if available
+extern void uart_sendchar(char c); // blocking, no buffer
+extern void uart_sendstr(char *s); // blocking, no buffer
+extern void uart_sendstr_p(const prog_char *progmem_s); // blocking, no buffer
+/*
+// you can either use the above _isr functions or one of the
+// following two but you can not mix them.
 extern char uart_getchar(void);
 extern unsigned char uart_getchar_noblock(char *returnval);
 extern void uart_flushRXbuf(void);
+*/
 
 /*
 ** macros for automatically storing string constant in program memory