+++ /dev/null
-/*********************************************
-* 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);
-}
-