// coreboot test interface board
// by Ayush Sagar ([email protected]) for coreboot GSoC 2013
// Last revised on 8 Sep 2013
// Pin assignments
#define pinICP 4
#define pinPowerSwitch 5
#define pinResetSwitch 6
#define pinPowerLED 7
#define pinVCCP A0
#define pinVCCF A1
#define pinVCCM A2
// Note: HSync and VSync pins are assigned to pins 2 and 3 using attachInterrupt() in beginFreqCounter()
// ADC related
/* INTERNAL ADC VOLTAGE REFERENCE (important)
Set 1.1V for Atmega168/328 and 2.56 for Atmega32U4
e.g. for Uno, Duemilanove use 1.1V and for Leonardo use 2.56V. See the chip name on your board and refer to its datasheet*/
#define internalAnalogReference 2.56
// sets voltage divider resistances in ohms. Use tight tolerance resistors to ensure accuracy of ADC readings.
#define upperResistance 9940 // connected across measured voltage and ADC input.
#define lowerResistance 457 // connected across ADC input and ground.
// Sync frequency counter
// stores count of HS/VS pulses in given time window
volatile unsigned short HSPulseCount = 0;
volatile unsigned short VSPulseCount = 0;
// stores HSync and VSync frequency in Hz
unsigned short HSync = 0;
unsigned short VSync = 0;
// sets timer limit in msec for HSync and VSync frequency counter
#define fcTimerLimit 500
// timer for freq. calculations
unsigned long fcTimer;
// flag to convey state of counter
boolean flagCounterActive = false;
// flag that conveys whether to print sync frequency or sync presence
boolean flagPrintFreq;
// Valid sync limits
#define VSyncLB 30
#define VSyncUB 125
#define HSyncLB 25000
#define HSyncUB 60000
// Other declarations
// init ICP mode flag (in circuit programming)
boolean flagICP = false;
// sets the unit switch press time in milliseconds
#define switchPressUnit 500;
// init power and reset switch flags
boolean flagPWR_SW;
boolean flagRST_SW;
// time holder for switch release/deactivation
unsigned long powerSwitchReleaseTime;
unsigned long resetSwitchReleaseTime;
void setup()
{
// start serial at 9600 bps, 8 bits, no parity, 1 stop bit
Serial.begin(9600);
// set pin modes for ICP and power LED pins.
pinMode(pinICP, OUTPUT);
pinMode(pinPowerLED, INPUT_PULLUP); //internal pull-up is required to translate open-drain output of PWR_LED signal source.
// set ADC reference to internal
analogReference(INTERNAL); //set ADC reference to internal
// set ICP mode depending on flag initialization
if (flagICP) digitalWrite(pinICP, HIGH);
else digitalWrite(pinICP, LOW);
// init power and reset button flags and outputs
flagPWR_SW = false;
openDrainOutput(pinPowerSwitch, false);
flagRST_SW = false;
openDrainOutput(pinResetSwitch, false);
}
void loop()
{
// check if data bytes are available on serial port. If true, pop a byte from
// serial FIFO buffer and send it to command interpreter.
if (Serial.available() > 0) commandInterpreter(Serial.read());
// check if sync frequency counter is active and timer has finished. If true, end counter.
if ( flagCounterActive and (millis() - fcTimer >= fcTimerLimit) ) endFreqCounter();
// check if power switch is activated and whether activation time has passed. If true, deactivate it.
if (flagPWR_SW and (millis() > powerSwitchReleaseTime))
{
flagPWR_SW = false;
openDrainOutput(pinPowerSwitch, false);
}
// check if reset switch is activated and whether activation time has passed. If true, deactivate it.
if (flagRST_SW and (millis() > resetSwitchReleaseTime))
{
flagRST_SW = false;
openDrainOutput(pinResetSwitch, false);
}
}
// command interpreter that checks for vaild command byte and executes the given command
void commandInterpreter(char command)
{
switch(command)
{
case 'P':
//print VCCP voltage, rounded to 1 decimal digit
printFloat(getVoltage(pinVCCP), 1);
break;
case 'F':
//print VCCF voltage, rounded to 1 decimal digit
printFloat(getVoltage(pinVCCF), 1);
break;
case 'M':
//print VCCM voltage, rounded to 1 decimal digit
printFloat(getVoltage(pinVCCM), 1);
break;
case '3':
//print A3 voltage, rounded to 1 decimal digit
printFloat(getVoltage(A3), 1);
break;
case '4':
//print A4 voltage, rounded to 1 decimal digit
printFloat(getVoltage(A4), 1);
break;
case '5':
//print A5 voltage, rounded to 1 decimal digit
printFloat(getVoltage(A5), 1);
break;
case 'f': //print HSync,VSync freq in Hz
flagPrintFreq = true;
beginFreqCounter();
break;
case 'v': //print whether VGA is present
flagPrintFreq = false;
beginFreqCounter();
break;
case 'i': //get ICP mode
Serial.println(flagICP ? '1':'0');
break;
case 'I': //toggle ICP mode
if (flagICP)
{
digitalWrite(pinICP, LOW);
flagICP = false;
}
else
{
digitalWrite(pinICP, HIGH);
flagICP = true;
}
break;
case 'p': //activate soft power switch for 500ms
if (flagPWR_SW)
{
powerSwitchReleaseTime += switchPressUnit;
}
else
{
powerSwitchReleaseTime = millis() + switchPressUnit;
openDrainOutput(pinPowerSwitch, true);
flagPWR_SW = true;
}
break;
case 'r': //activate reset switch for 500ms
if (flagRST_SW)
{
resetSwitchReleaseTime += switchPressUnit;
}
else
{
resetSwitchReleaseTime = millis() + switchPressUnit;
openDrainOutput(pinResetSwitch, true);
flagRST_SW = true;
}
break;
case 'l': //get power LED state
if (digitalRead(pinPowerLED) == HIGH)
{
Serial.println('0');
}
else
{
Serial.println('1');
}
break;
}
}
// HSync pulse counting interrupt service routine
void incrementHSPulseCount()
{
HSPulseCount++;
}
// VSync pulse counting interrupt service routine
void incrementVSPulseCount()
{
VSPulseCount++;
}
// starts sync frequency counter.
void beginFreqCounter()
{
// reset timer and counters
fcTimer = millis();
HSPulseCount = 0;
VSPulseCount = 0;
// enable frequency counter interrupts
attachInterrupt(0, incrementHSPulseCount, FALLING); // falling edge on pin 2 calls increment function for HSync counter
attachInterrupt(1, incrementVSPulseCount, FALLING); // falling edge on pin 3 calls increment function for VSync counter
// convey to loop() that counter is active
flagCounterActive = true;
}
// ends sync frequency counter and prints out the result
void endFreqCounter()
{
// Count pulses and calculate frequency
HSync = HSPulseCount * (1000 / float(fcTimerLimit));
VSync = VSPulseCount * (1000 / float(fcTimerLimit));
//remove interrupts
detachInterrupt(0);
detachInterrupt(1);
// check for result type
if (flagPrintFreq)
{
//print frequencies
Serial.print('(');
Serial.print(HSync);
Serial.print(',');
Serial.print(VSync);
Serial.println(')');
}
else
{
// determine if sync frequencies are in correct range and print VGA presence
if ( VSyncLB < VSync and VSync < VSyncUB and HSyncLB < HSync and HSync < HSyncUB) Serial.println('1');
else Serial.println('0');
}
// set counter state to inactive
flagCounterActive = false;
}
// sorts given array. This is used in mode filtering
void isort(short *a, short n){
// *a is an array pointer function
for (short i = 1; i < n; ++i)
{
short j = a[i];
short k;
for (k = i - 1; (k >= 0) and (j < a[k]); k--)
{
a[k + 1] = a[k];
}
a[k + 1] = j;
}
}
// returns the mode of given array or return median if there's no mode.
// (thanks to Arduino Playground)
short mode(short *x,short n){
short i = 0;
short count = 0;
short maxCount = 0;
short mode = 0;
short bimodal;
short prevCount = 0;
while(i<(n-1))
{
prevCount=count;
count=0;
while(x[i]==x[i+1])
{
count++;
i++;
}
if(count>prevCount&count>maxCount)
{
mode=x[i];
maxCount=count;
bimodal=0;
}
if(count==0){
i++;
}
if(count==maxCount) // If the sample array has 2 or more modes.
{
bimodal=1;
}
if(mode==0||bimodal==1) // Return the median if there is no unique mode.
{
mode=x[(n/2)];
}
return mode;
}
}
// takes samples from ADC on the given pin and applies
// mode function to get a concordant value
short filteredAnalogRead(short pin)
{
#define sampleArraySize 6 // set sample count for mode filtering
short sampleArray[sampleArraySize];
for(short i=0; i < sampleArraySize; i++)
{
sampleArray[i] = analogRead(pin);
delay(1);
}
// sort array
isort(sampleArray, sampleArraySize);
// return mode
return mode(sampleArray, sampleArraySize);
}
// calculates voltage on given analog pin
float getVoltage(short pin)
{
return float(filteredAnalogRead(pin))/1023 * (upperResistance + lowerResistance) / lowerResistance * internalAnalogReference;
}
// emulates open drain output on power and reset switch outputs
void openDrainOutput(int pin, boolean activate)
{
if (activate) //if activate is true, set pin to Lo-Z
{
digitalWrite(pin, LOW);
pinMode(pin, OUTPUT);
}
else //else set pin to Hi-Z
{
digitalWrite(pin, LOW);
pinMode(pin, INPUT);
}
}
// prints out the float 'value' rounded to 'places' places after
// the decimal point. (Thanks to Arduino Playground)
void printFloat(float value, int places) {
// this is used to cast digits
int digit;
float tens = 0.1;
int tenscount = 0;
int i;
float tempfloat = value;
// calculate rounding term d: 0.5/pow(10,places)
float d = 0.5;
if (value < 0)
d *= -1.0;
// divide by ten for each decimal place
for (i = 0; i < places; i++)
d/= 10.0;
// this small addition, combined with truncation will round our values properly
tempfloat += d;
// first get value tens to be the large power of ten less than value
// tenscount isn't necessary but it would be useful if you wanted to know after this how many chars the number will take
if (value < 0)
tempfloat *= -1.0;
while ((tens * 10.0) <= tempfloat) {
tens *= 10.0;
tenscount += 1;
}
// write out the negative if needed
if (value < 0)
Serial.print('-');
if (tenscount == 0)
Serial.print(0, DEC);
for (i=0; i< tenscount; i++) {
digit = (int) (tempfloat/tens);
Serial.print(digit, DEC);
tempfloat = tempfloat - ((float)digit * tens);
tens /= 10.0;
}
// if no places after decimal, stop now and return
if (places <= 0)
return;
// otherwise, write the point and continue on
Serial.print('.');
// now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value
for (i = 0; i < places; i++) {
tempfloat *= 10.0;
digit = (int) tempfloat;
Serial.print(digit,DEC);
// once written, subtract off that digit
tempfloat = tempfloat - (float) digit;
}
Serial.print("\r\n"); //print carriage return and line feed as println() does
}
Comments