/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.core.targets;
import java.util.ArrayList;
import java.util.List;
/**
* Generator for ISP / JTAG clock frequencies.
* <p>
* This class is used by the user interface to get a list of all possible clock frequencies a
* programmer can accept.
* </p>
* <p>
* This class has only one static method, {@link #getValues(ClockValuesType)}.
* </p>
*
* @author Thomas Holland
* @since 2.4
*
*/
public final class ClockValuesGenerator {
/**
* Enumeration of all available protocol types that {@link ClockValuesGenerator} can generate
* clock values for.
*/
public enum ClockValuesType {
/** AVR ICE MkI. */
JTAG1,
/** AVR ICE MkII and AVR Dragon in JTAG or DW mode. */
JTAG2,
/** STK500, STK500v2 and clones. */
STK500,
/** AVR ISP MkII and AVR Dragon in ISP, PP or HVSP modes. */
AVRISPMK2,
/** AVR STK600. */
STK600,
/** USBTiny programmer. */
USBTINY;
}
/**
* The clock frequency of the STK500 / STK 500v2 programmers. Used to calculate the STK500 list.
*/
private final static int STK500_XTAL = 7372800;
/**
* The values for the JTAG1 protocol. AVR ICE MkI uses a fixed list with just 4 values from 125
* KHz to 1 MHz.
*/
private final static int[] fJTAG1Values = new int[] { 0, 125000, 250000, 500000,
1000000 };
/** JTAG2 values cache. */
private static int[] fJTAG2Values = null;
/** STK500 values cache. */
private static int[] fSTK500Values = null;
/** AVRISPMK2 values cache. */
private static int[] fAVRISPMK2Values = null;
/** STK600 values cache. */
private static int[] fSTK600Values = null;
/** USBTiny values cache. */
private static int[] fUSBTinyValues = null;
/**
* Static list of all possible AVRISPMk2 values.
* <p>
* This list is used for the AVR ISP MkII / AVR Dragon programmers in ISP, PP or HVSP modes.
* </p>
* <p>
* The list of frequencies is taken from AVR069. All numbers with fractions have been rounded up
* to the next integer.
*/
private final static double[] AVRICEMK2CLOCKS = new double[] { 8000000, 4000000, 2000000,
1000000, 500000, 250000, 125000, 96386, 89888, 84211, 79208, 74767, 70797, 67227,
64000, 61069, 58395, 55945, 51613, 49690, 47905, 46243, 43244, 41885, 39409, 38278,
36200, 34335, 32654, 31129, 29740, 28470, 27304, 25724, 24768, 23461, 22285, 21221,
20254, 19371, 18562, 17583, 16914, 16097, 15356, 14520, 13914, 13224, 12599, 12031,
11511, 10944, 10431, 9963, 9468, 9081, 8612, 8239, 7851, 7498, 7137, 6809, 6478, 6178,
5879, 5607, 5359, 5093, 4870, 4633, 4418, 4209, 4019, 3823, 3645, 3474, 3310, 3161,
3011, 2869, 2734, 2611, 2484, 2369, 2257, 2152, 2052, 1956, 1866, 1779, 1695, 1615,
1539, 1468, 1398, 1333, 1271, 1212, 1155, 1101, 1049, 1000, 953, 909, 866, 826, 787,
750, 715, 682, 650, 619, 590, 563, 536, 511, 487, 465, 443, 422, 402, 384, 366, 349,
332, 317, 302, 288, 274, 261, 249, 238, 226, 216, 206, 196, 187, 178, 170, 162, 154,
147, 140, 134, 128, 122, 116, 111, 105, 100, 95.4, 90.9, 86.6, 82.6, 78.7, 75.0, 71.5,
68.2, 65.0, 61.9, 59.0, 56.3, 53.6, 51.1 };
// prohibit instantiation
private ClockValuesGenerator() {
}
/**
* Get the list of clock frequencies for the given programmer type.
* <p>
* To reduce the sizes of the lists and to make them more user friendly the values in the lists
* are rounded.
* <ul>
* <li>Values up to 1,000 are rounded to the next highest 10.</li>
* <li>Values up to 10,000 are rounded to the next highest 100.</li>
* <li>Values up to 100,000 are rounded to the next highest 1000.</li>
* </ul>
* </p>
* Due to the rounding the actual clock frequency used by the programmer can be slightly
* different. It is assumed that these aliasing errors will be small enough to not matter. </p>
*
* @param type
* The type of programmer.
* @return Array with some or all possible clock values. The first element in the array is
* always 0, representing the default.
*/
public static int[] getValues(ClockValuesType type) {
switch (type) {
case JTAG1:
return getJTAG1Values();
case JTAG2:
return getJTAG2Values();
case STK500:
return getSTK500Values();
case AVRISPMK2:
return getAVRISPMK2Values();
case STK600:
return getSTK600Values();
case USBTINY:
return getUSBTinyValues();
default:
// TODO
return null;
}
}
/**
* Calculate the JTAG1 protocol clocks.
* <p>
* The JTAG1 protocol can use only 4 fixed clock frequencies: 125KHz, 250KHz, 500KHz and 1MHz.
* </p>
*
* @return
*/
private static int[] getJTAG1Values() {
return fJTAG1Values;
}
/**
* Calculate the JTAG2 protocol clocks.
* <p>
* The JTAG2 protocol uses an 8 bit value representing frequencies from 3.6 KHz to 6.4 MHz. The
* actual formula is
*
* <pre>
* freq = 5.35 MHz / value ; value = [2;255]
* </pre>
*
* with the two special cases <code>value = 0</code> → <code>freq = 6.4 MHz</code> and
* <code>value = 1</code> → </code>freq = 2.8 MHz</code>.
* </p>
*
* @return
*/
private static int[] getJTAG2Values() {
if (fJTAG2Values == null) {
List<Integer> values = new ArrayList<Integer>(256);
int lastvalue = -1;
// Step over all 256 possible duration values
for (int i = 0; i < 256; i++) {
// Special case for 0 and 1
if (i == 0) {
values.add(6400000);
} else if (i == 1) {
values.add(2800000);
} else {
double value = 5.35e6 / i;
int newvalue = ceilRound(value);
if (newvalue != lastvalue) {
values.add(newvalue);
lastvalue = newvalue;
}
}
}
values.add(0); // representing the default
// Now reverse the list and convert it to integers
int numvalues = values.size();
int[] tmpvalues = new int[numvalues];
for (int i = 0; i < numvalues; i++) {
tmpvalues[i] = values.get(numvalues - i - 1);
}
fJTAG2Values = tmpvalues;
}
return fJTAG2Values;
}
/**
* Calculate the STK500 protocol clocks.
* <p>
* The STK500 protocol uses an 8 bit value representing frequencies from 4.0 KHz to 1.0 MHz. The
* actual formula is
*
* <pre>
* freq = 9.216 KHz / value ; value = [1;256]
* </pre>
*
* where <code>9.216 KHz = STK500_XTAL / 8.0</code> (1/8th of the STK500 master clock)
* </p>
* <p>
* This uses the reverse of the formula used by avrdude, which differs from the formula given in
* AVR061, even though the avrdude source comments states that its algorithm is based on AVR061.
* </p>
* <p>
* Excerpt from the avrdude source code: <br/>
* <code>
* This code assumes that each count of the SCK duration parameter represents 8/f, where f is
* the clock frequency of the STK500 master processors (not the target). It appears that the
* STK500 bit bangs SCK. For small duration values, the actual SCK width is larger than
* expected. As the duration value increases, the SCK width error diminishes.
* </code>
* </p>
*
* @return
*/
private static int[] getSTK500Values() {
if (fSTK500Values == null) {
List<Integer> values = new ArrayList<Integer>(256);
int lastvalue = -1;
double stepsize = STK500_XTAL / 8.0;
// Step over all 256 possible duration values
for (int i = 1; i <= 256; i++) {
double value = stepsize / i;
int newvalue = ceilRound(value);
if (newvalue != lastvalue) {
values.add(newvalue);
lastvalue = newvalue;
}
}
values.add(0); // representing the default
// Now reverse the list and convert it to integers
int numvalues = values.size();
int[] tmpvalues = new int[numvalues];
for (int i = 0; i < numvalues; i++) {
tmpvalues[i] = values.get(numvalues - i - 1);
}
fSTK500Values = tmpvalues;
}
return fSTK500Values;
}
/**
* Calculate the AVR ISP MkII protocol clocks.
* <p>
* The clock frequencies are based on values from a table defined in AVR069. The values are just
* rounded but otherwise left as is.
* </p>
*
* @return
*/
private static int[] getAVRISPMK2Values() {
if (fAVRISPMK2Values == null) {
List<Integer> values = new ArrayList<Integer>(256);
int lastvalue = -1;
// Step over all possible clock values
for (double value : AVRICEMK2CLOCKS) {
int newvalue = ceilRound(value);
if (newvalue != lastvalue) {
values.add(newvalue);
lastvalue = newvalue;
}
}
values.add(0); // representing the default
// Now reverse the list
int numvalues = values.size();
int[] tmpvalues = new int[numvalues];
for (int i = 0; i < numvalues; i++) {
tmpvalues[i] = values.get(numvalues - i - 1);
}
fAVRISPMK2Values = tmpvalues;
}
return fAVRISPMK2Values;
}
/**
* Calculate the STK600 protocol clocks.
* <p>
* The STK600 protocol uses an 12 bit value representing frequencies from 1953 Hz to 8.0 MHz.
* The actual formula is
*
* <pre>
* freq = 16 MHz / (2*(value+1)) ; value = [0;4095]
* </pre>
*
* </p>
*
* @return
*/
private static int[] getSTK600Values() {
if (fSTK600Values == null) {
List<Integer> values = new ArrayList<Integer>(4096);
int lastvalue = -1;
// Step over all 4096 possible duration values
for (int i = 0; i < 4096; i++) {
double value = 16e6 / (2 * (i + 1));
int newvalue = ceilRound(value);
if (newvalue != lastvalue) {
values.add(newvalue);
lastvalue = newvalue;
}
}
values.add(0); // representing the default
// Now reverse the list
int numvalues = values.size();
int[] tmpvalues = new int[numvalues];
for (int i = 0; i < numvalues; i++) {
tmpvalues[i] = values.get(numvalues - i - 1);
}
fSTK600Values = tmpvalues;
}
return fSTK600Values;
}
/**
* Calculate the TinyUSB protocol clocks.
* <p>
* The USBTiny protocol uses an 8 bit value representing frequencies from 4.0 KHz to 1.0 MHz.
* The actual formula is
*
* <pre>
* freq = 1.0 MHz / value ; value = [1;250]
* </pre>
*
* </p>
*
* @return
*/
private static int[] getUSBTinyValues() {
if (fUSBTinyValues == null) {
List<Integer> values = new ArrayList<Integer>(250);
int lastvalue = -1;
// Step over all 250 possible duration values
for (int i = 1; i <= 250; i++) {
double value = 1e6 / i;
int newvalue = ceilRound(value);
if (newvalue != lastvalue) {
values.add(newvalue);
lastvalue = newvalue;
}
}
values.add(0); // representing the default
// Now reverse the list and convert it to integers
int numvalues = values.size();
int[] tmpvalues = new int[numvalues];
for (int i = 0; i < numvalues; i++) {
tmpvalues[i] = values.get(numvalues - i - 1);
}
fUSBTinyValues = tmpvalues;
}
return fUSBTinyValues;
}
/**
* Round the argument depending on the magnitude to the next higher value.
* <p>
* <ul>
* <li>Values up to 1,000 are rounded to the next highest 10.</li>
* <li>Values up to 10,000 are rounded to the next highest 100.</li>
* <li>Values up to 100,000 are rounded to the next highest 1000.</li>
* </ul>
*
* @param value
* The double to be rounded
* @return The rounded integer
*/
private static int ceilRound(double value) {
if (value < 1000) {
return (int) Math.ceil(value / 10) * 10;
} else if (value < 10000) {
return (int) Math.ceil(value / 100) * 100;
} else {
return (int) Math.ceil(value / 1000) * 1000;
}
}
}