For kicks, this is "Dave C" for my last uSupply USB prototype, the entire program:

`/* ****************************************`

uSuppyUSB Firmware

Version 1.0

Last Edit: 8/9/12

Written in: AVR GCC C under AVR Studio 6

Written by: David L. Jones

Copyright(C)2012 David L. Jones

[url=http://www.eevblog.com]www.eevblog.com[/url]

NOTES:

- Written for an ATmega88A target on Rev 1 uSupplyUSB Hardware

Revision notes

1.0 - First prototype version

******************************************* */

#define F_CPU 8000000 //clock frequency 8MHz

#include <avr/io.h>

#include <util/delay.h>

#include <avr/interrupt.h>

#include <avr/eeprom.h>

#include <string.h>

#include <stdio.h>

//definitions

#define version 1

#define TRUE 1

#define FALSE 0

#define CURRbutton (PINB & 1<<PB7) //CURRENT button

#define SETbutton (PINB & 1<<PB6) //SET button

#define bit_get(p,m) ((p) & (m))

#define bit_set(p,m) ((p) |= (m))

#define bit_clear(p,m) ((p) &= ~(m))

//#define POWPIN_HIGH PORTC.PINC4=1

//#define POWPIN_HIGH bit_set(PORTC, PINC4);

#define POWPIN_HIGH PORTC|=0b00010000

#define POWPIN_LOW bit_clear(PORTC, PINC4);

#define DIG1ON PORTB=0xE //Switch ON the first digit, current display

#define DIG2ON PORTB=0xD //Switch ON the second digit, current display

#define DIG3ON PORTB=0xB //Switch ON the third digit, current display

#define DIGITSOFF PORTB=0xF //Switch OFF all digits, current display

#define DIG1VON PORTB=0xFB //Switch ON the first digit, voltage display

#define DIG2VON PORTB=0xF7 //Switch ON the second digit, voltage display

#define DIG3VON PORTB=0xEF //Switch ON the third digit, voltage display

#define DIGITSVOFF PORTB=0xFF //Switch OFF all digits, current display

#define SEGPORT PORTD // the LED display digits are on PORTD

// define the two rotary encoder input pins

#define PHASE_A (PINC & 1<<PC5)

#define PHASE_B (PINC & 1<<PC6)

#define ADCcomplete (ADCSRA & (1<<ADSC)) //TRUE if ADC is complete

//#define ADCcomplete (ADCSRA & 1<<ADIE) //TRUE if ADC is complete

#define setADCIE (ADCSRA|=1<<ADIE)

//global variables

unsigned char DIGIT_NUM;

int SET_VOLTAGE; //the set output voltage in mV

int SET_CURRENT; //the set output current limit mA

int READ_VOLTAGE; //the measured output voltage in mV

int READ_CURRENT; //the measured current output in mA

int READ_VOLTAGE_ARRAY[40]; //array for averages

int READ_CURRENT_ARRAY[40]; //array for averages

int READ_VOLTAGE_AVERAGE; //the measured output voltage in mV

int READ_CURRENT_AVERAGE; //the measured current output in mA

unsigned char digit; //the number of the currently displayed LED digit

unsigned char CCmode; //TRUE if in Constant Curent mode

int VoltageSETcount; //counter for temp display of the set voltage as the knob is turned.

int SETbuttonCount; //counter for the time the SET button has been pressed

int DPblinkCount; //counter for the constant current mode decimal point blink mode

char blink;

const unsigned char digitdata[11]={0x3F,0x6,0x5B,0x4F,0x66,0x6D,0x7D,0x7,0x7F,0x6F,0}; // 7 segment digit display data

volatile int8_t enc_delta; // -128 ... 127

static int8_t last;

static int8_t knobcount;

//*****************************************************************

//saves the current voltage and current figures to EEPROM

void SaveEEPROM(void)

{

cli(); //disable interrupts so it doesn't corrupt the write process

// eeprom_write_word(0,SET_VOLTAGE);

// eeprom_write_word(2,SET_CURRENT);

sei(); //enable interrupts again

return;

}

//*****************************************************************

//loads the stored voltage and current data from the EEPROM

void LoadEEPROM(void)

{

cli();

// SET_VOLTAGE=eeprom_read_word(0);

// SET_CURRENT=eeprom_read_word(2);

sei();

return;

}

//*****************************************************************

//displays a number on the selected 7seg display

// If DP is TRUE the associated Decimal Point turns ON

void Disp7SEG(unsigned char digit, unsigned char num, unsigned char DP)

{

unsigned char temp;

DIGITSOFF; //disable the entire LED display

DIGITSVOFF;

// if ((CCmode!=0) && (digit<4)) DP=1; //force decimal point on for current display in CCmode

if (num>9) num=10; // if unknown digit then display a blank digit

temp=digitdata[num];

if (DP!=0) temp=temp|128; // turn on decimal point if requested

SEGPORT=temp; //output the display segments

switch(digit)

{

case 1: DIG1ON; break;

case 2: DIG2ON; break;

case 3: DIG3ON; break;

case 4: DIG1VON; break;

case 5: DIG2VON; break;

case 6: DIG3VON; break;

}

//leave the digit displayed until the next call, so we can continue processing and we get persistence of vision.

return;

}

//*****************************************************************

// Updates the voltage and current PWM outputs

void UpdateOutput(void)

{

unsigned long PWMvalue;

PWMvalue=((unsigned long)SET_VOLTAGE*1000)/47695; //(12210mV full scale / 256 bits)

if (PWMvalue>255) PWMvalue=255; //overflow trap, we don't want any undesirable wrap

OCR1A=PWMvalue; //set the output VOLTAGE

PWMvalue=((unsigned long)SET_CURRENT*1000)/1078; //0-255 = 0-275mV/

if (PWMvalue>255) PWMvalue=255; //overflow trap, we don't want any undesirable wrap

OCR1B=PWMvalue; //set the output CURRENT

return;

}

//*****************************************************************

// reads the voltage and current values from the ADC and updates the global variables

void ReadValues(void)

{

unsigned long value;

unsigned long total;

unsigned char c,temp;

if (CURRbutton)

{

//measure VOLTAGE

ADMUX=0b00000001; //select ADC1 input and 1.1V reference, right adjusted

value=ADCL; //get the upper 8 bit result

value=value+(ADCH*256); //get the lower 8 bit result and add it on.

value=value*1074; //convert to microvolts for 1.1V reference

value=value/1000; //convert back to millivolts

READ_VOLTAGE=value*12; //multiply for the resistor divider

for(c=0;c<39;c++) //update the moving average array

READ_VOLTAGE_ARRAY[c]=READ_VOLTAGE_ARRAY[c+1];

READ_VOLTAGE_ARRAY[39]=READ_VOLTAGE;

total=0;

for(c=0;c<40;c++)

total=total+READ_VOLTAGE_ARRAY[c];

READ_VOLTAGE_AVERAGE=total/40;

}

else

{

//measure CURRENT

total=0;

ADMUX=0; //select ADC0 input and 1.1V reference, right adjusted

value=ADCL; //get the upper 2 bit result

value=value+(ADCH*256); //get the lower 8 bit result and add it on.

value=value*10740; //convert to microvolts for 1.1V reference

value=value/10000; //convert back to mV (/1000)

//the final value is now in mA*10

READ_CURRENT=value;

for(c=0;c<39;c++) //update the moving average value array

READ_CURRENT_ARRAY[c]=READ_CURRENT_ARRAY[c+1];

READ_CURRENT_ARRAY[39]=READ_CURRENT;

total=0;

for(c=0;c<40;c++)

total=total+READ_CURRENT_ARRAY[c];

READ_CURRENT_AVERAGE=total/40;

}

return;

}

//*****************************************************************

// Checks to see if the user has turned the rotary encoder knob

void CheckKnob(void)

{

if (enc_delta==1)

{

knobcount++;

VoltageSETcount=0;

}

if (enc_delta==-1)

{

knobcount--;

VoltageSETcount=0;

}

enc_delta=0;

if (knobcount>3)

{

if (CURRbutton) //adjust the VOLTAGE

{

SET_VOLTAGE=SET_VOLTAGE-50;

if (SET_VOLTAGE<0) SET_VOLTAGE=0;

}

else //adjust the CURRENT

{

SET_CURRENT=SET_CURRENT-1;

if (SET_CURRENT<0) SET_CURRENT=0;

}

UpdateOutput(); //update the output voltage/current on the terminals

knobcount=0;

}

if (knobcount<-3)

{

if (CURRbutton) //adjust the VOLTAGE

{

SET_VOLTAGE=SET_VOLTAGE+50;

if (SET_VOLTAGE>12200) SET_VOLTAGE=12200;

}

else //adjust the CURRENT

{

SET_CURRENT=SET_CURRENT+1;

if (SET_CURRENT>300) SET_CURRENT=300;

}

UpdateOutput(); //update the output voltage/current on the terminals

knobcount=0;

}

}

//*****************************************************************

// Interrupt Service Routine

// This ISR has several functions. It is based on TIMER0

// 1) Updates the 7 segment display at regular intervals

// (This display only one digit every interrupt, digits get cycled through each ISR pass)

// 2) Reads and updates the status of the rotary encoder knob

// 3) Ticks over a counter to hold the display in a set state for a certain period

ISR(TIMER0_OVF_vect)

{

unsigned int temp;

unsigned char dig1, dig2, dig3, dig4, dig5, dig6, dp1, dp2, dp3;

int8_t state, diff, CURRset;

cli(); //disable interrupts

VoltageSETcount++;

if (VoltageSETcount>=500) VoltageSETcount=500;

if (!SETbutton) SETbuttonCount++; else SETbuttonCount=0;

if (CCmode)

{

if (blink) DPblinkCount++; else DPblinkCount--;

if (DPblinkCount>100) blink=FALSE;

if (DPblinkCount<=0) blink=TRUE;

}

dp1=0; //turn all decimal points OFF

dp2=0;

dp3=0;

CURRset=0; //assume we are displaying the READ current and not the SET current

if (digit==4||digit==5||digit==6) //display the VOLATGE display digits

{

if (SETbutton) temp=READ_VOLTAGE_AVERAGE; else temp=SET_VOLTAGE;

if (VoltageSETcount<500) //force display of the SET voltage or current if the knob has been turned within the time limit

{

if (CURRbutton) temp=SET_VOLTAGE;

else

{

temp=SET_CURRENT;

CURRset=TRUE;

}

}

if (temp>9999) //check for voltages 10V or greater, we have to switch the decimal point and calcs

{

dig4=temp/10000;

temp=temp-(dig4*10000);

dig5=temp/1000;

temp=temp-(dig5*1000);

dig6=temp/100;

dp2=TRUE; //decimal point for 99.9

}

else

{

dig4=temp/1000;

temp=temp-(dig4*1000);

dig5=temp/100;

temp=temp-(dig5*100);

dig6=temp/10;

dp1=TRUE;

} //decimal point for 9.99

}

else // ELSE display the current display digits

{

if (SETbutton) temp=READ_CURRENT_AVERAGE; else temp=SET_CURRENT;

if (!CURRset && SETbutton) dp2=TRUE; //turn on decimal point for 99.9mA display if we are displaying the READ current

//otherwise display the SET current with 1mA resolution as 999mA

if (temp>=999) //if current is >99.9mA

{

dig1=temp/1000;

temp=temp-(dig1*1000);

dig2=temp/100;

temp=temp-(dig2*100);

dig3=temp/10;

}

else

{

dig1=temp/100;

temp=temp-(dig1*100);

dig2=temp/10;

temp=temp-(dig2*10);

dig3=temp;

}

if (CCmode) //if in constant current mode then blink the decimal point

{

if (blink)

{

dp1=FALSE;

dp2=FALSE;

dp3=FALSE;

}

}

}

//display the currently selected digit

//this digit will stay on until the next ISR loop, giving persistence of vision

if (digit==1) Disp7SEG(1,dig1,dp1);

if (digit==2) Disp7SEG(2,dig2,dp2);

if (digit==3) Disp7SEG(3,dig3,dp3);

if (digit==4) Disp7SEG(4,dig4,dp1);

if (digit==5) Disp7SEG(5,dig5,dp2);

if (digit==6) Disp7SEG(6,dig6,dp3);

digit++; //display the next digit next time through the ISR loop

if (digit>6) digit=1;

//read the rotary encoder knob

state = 0;

if( PHASE_A ) state = 3;

if( PHASE_B ) state ^= 1; // convert gray to binary

diff = last - state; // difference value

if( diff & 1){ // bit 0 = value (1)

last = state; // store new as next last

enc_delta += (diff & 2) - 1; // result will be +1 for positive direction and -1 for negative direction

}

ReadValues(); //read the ADC values

sei(); //enable interrupts again

}

//*****************************************************************

int main(void){

unsigned char c,d,average;

unsigned char keyspeed, keycount;

DDRB =0b00000111; //PB0, PB1, PB2, PB3, PB4 are OUTPUTS, all others INPUTS

PORTB=0b11111000; //enable pull-ups on all PORTB pins except PB1 and PB2 which are outputs

DDRC =0b00111000; //PORTC are all INPUTS, except for 3 LED digit outputs

PORTC=0b11000111; //enable pull-ups on only upper 3 pins PORTC pins

DDRD =0b11111110; //PORTD are all OUTPUTS

PORTD=0;

MCUCR=0; //Enable pin pull-ups and Disable Sleep BOD stuff.

POWPIN_HIGH; //Latch the power ON

//PORTC=0xFF;

// this must be done BEFORE the PWM is turned on to ensure we DON'T OVERSHOOT at power on!

LoadEEPROM(); //restore the saved voltage and current settings

if (SET_VOLTAGE>12200) SET_VOLTAGE=12200; //do some out of bounds checking

if (SET_VOLTAGE<50) SET_VOLTAGE=50;

if (SET_CURRENT>100) SET_CURRENT=100;

if (SET_CURRENT<1) SET_CURRENT=1;

//set up TIMER0 used for the display updating (165Hz rate per digit)

TCCR0A=0x3; //use the /64 prescaler for clock source

TIFR0=0;

TCNT0=0x0;

TIMSK0=1; //enable the TIMER0 interrupt

//set up PWM in TIMER1

TCCR1A=0b10100001; //clear on compare match, SET at TOP for both PWM output pins. 8bit FAST PWM mode.

TCCR1B=0b00001001; //matchig 8bit fast PWM mode setting

TCNT1=0x3FE;

UpdateOutput(); //set the output voltage and current

//set up ADC

PRR=0; //make sure all periphers are turned ON

ADCSRB=0; //free running mode, manual trigger

DIDR0=3; //diable digital input on ADC0 and ADC1 pins

ADCSRA |= (1 << ADATE);

// ADMUX |= (1 << ADLAR); //left align ADC result

ADCSRA |= (1 << ADEN); //enable the ADC

ADCSRA |= (1 << ADSC); //start conversion

ADCSRA |= (1 << ADPS2); //prescaler = 128

ADCSRA |= (1 << ADPS1); //prescaler = 128

// ADCSRA |= (1 << ADPS0); //prescaler = 128

//set up the user control knob interrupt

// PCMSK1=32; //enable knob pin change interrupt

// PCICR=2; //ebale the PCIE1 pin change interrupt

Disp7SEG(3,version,0); //display version number at power on

_delay_ms(2000);

digit=1;

average=0;

keyspeed=0;

keycount=0;

VoltageSETcount=0;

SETbuttonCount=0;

DPblinkCount=0;

ReadValues();

sei(); // enable interrupts (turns on display and functions)

while(TRUE)

{

if ((SET_CURRENT*10) < (READ_CURRENT_AVERAGE+1)) CCmode=TRUE; else CCmode=FALSE; //check to see if we are in Constant Current Mode

CheckKnob(); //check if the user has turned the knob

if (SETbuttonCount>=1500) //check to see if the SET button has been pressed for 3 seconds

{

SETbuttonCount=0;

SaveEEPROM();

cli();

Disp7SEG(1,5,0);

_delay_ms(1000);

sei();

}

}

}