Editorial changes

master
wagiminator 2021-11-20 21:40:25 +01:00
parent 0e3dc83f54
commit db6a40723b
5 changed files with 617 additions and 609 deletions

View File

@ -11,7 +11,7 @@ Simple USB Power Tester based on ATtiny25/45/85 and INA219. The device measures
The device is equipped with a USB-A plug for the input and a USB-A socket for the output, so that it can be plugged between the power supply and the consumer. D+ and D- are passed through so that the consumer can negotiate the charging protocol. The device is equipped with a USB-A plug for the input and a USB-A socket for the output, so that it can be plugged between the power supply and the consumer. D+ and D- are passed through so that the consumer can negotiate the charging protocol.
## Voltage and Current Measurement ## Voltage and Current Measurement
An [INA219](https://www.ti.com/lit/ds/symlink/ina219.pdf) is used to measure voltage and current. The INA219 is a current shunt and power monitor with an I²C-compatible interface. The device monitors both shunt voltage drop and bus supply voltage, with programmable conversion times and filtering. A programmable calibration value, combined with an internal multiplier, enables direct readouts of current in amperes. The selected shunt resistance of 8 milliohms enables both a very small influence on the circuit and a measurement with a resolution of 1 milliampere. For an accurate measurement, a shunt resistor with a low tolerance (1% or better) should be selected. An [INA219](https://www.ti.com/lit/ds/symlink/ina219.pdf) is used to measure voltage and current. The INA219 is a current shunt and power monitor with an I²C-compatible interface. The device monitors both shunt voltage drop and bus supply voltage, with programmable conversion times and filtering. A programmable calibration value, combined with an internal multiplier, enables direct readouts of current in amperes. The selected shunt resistance of 8mΩ enables both a very small influence on the circuit and a measurement with a resolution of 1mA. For an accurate measurement, a shunt resistor with a low tolerance (1% or better) should be selected.
## User Interface ## User Interface
The user interface utilizes two buttons and a [128x64 pixels OLED display](http://aliexpress.com/wholesale?SearchText=128+64+0.96+oled+new+4pin). An [ATtiny24/45/85](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf) microcontroller handles the user interface as well as the calculation and display of the values. The user interface utilizes two buttons and a [128x64 pixels OLED display](http://aliexpress.com/wholesale?SearchText=128+64+0.96+oled+new+4pin). An [ATtiny24/45/85](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf) microcontroller handles the user interface as well as the calculation and display of the values.
@ -37,8 +37,7 @@ Since there is no ICSP header on the board, you have to program the ATtiny eithe
- Go to **Tools -> Board -> ATtinyCore** and select **ATtiny25/45/85 (No bootloader)**. - Go to **Tools -> Board -> ATtinyCore** and select **ATtiny25/45/85 (No bootloader)**.
- Go to **Tools** and choose the following board options: - Go to **Tools** and choose the following board options:
- **Chip:** ATtiny25 or 45 or 85 (depending on your chip) - **Chip:** ATtiny25 or 45 or 85 (depending on your chip)
- **Clock:** 8 MHz (internal) - **Clock:** 1 MHz (internal)
- **Millis/Micros:** disabled
- **B.O.D.Level:** B.O.D. enabled (2.7V) - **B.O.D.Level:** B.O.D. enabled (2.7V)
- Leave the rest at the default settings - Leave the rest at the default settings
- Connect your programmer to your PC and to the ATtiny. - Connect your programmer to your PC and to the ATtiny.
@ -53,7 +52,7 @@ Since there is no ICSP header on the board, you have to program the ATtiny eithe
- Navigate to the folder with the hex-file. - Navigate to the folder with the hex-file.
- Execute the following command (if necessary replace "usbasp" with the programmer you use): - Execute the following command (if necessary replace "usbasp" with the programmer you use):
``` ```
avrdude -c usbasp -p t85 -U lfuse:w:0xe2:m -U hfuse:w:0xd5:m -U efuse:w:0xff:m -U flash:w:usb_tester.hex avrdude -c usbasp -p t85 -U lfuse:w:0x62:m -U hfuse:w:0xd5:m -U efuse:w:0xff:m -U flash:w:usb_tester.hex
``` ```
### If using the makefile (Linux/Mac) ### If using the makefile (Linux/Mac)

494
software/USB_Tester.ino Normal file
View File

@ -0,0 +1,494 @@
// ===================================================================================
// Project: USB Power Tester based on ATtiny25/45/85
// Version: v1.4
// Year: 2020 - 2021
// Author: Stefan Wagner
// Github: https://github.com/wagiminator
// EasyEDA: https://easyeda.com/wagiminator
// License: http://creativecommons.org/licenses/by-sa/3.0/
// ===================================================================================
//
// Description:
// ------------
// Simple USB Power Tester based on ATtiny25/45/85 and INA219. The device
// measures voltage, current, power, energy, capacity and displays the values
// on an OLED screen. You can switch between different screens by pressing the
// SET button.
//
// References:
// -----------
// The I²C OLED implementation is based on TinyOLEDdemo
// https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
//
// Wiring:
// -------
// +-\/-+
// RESET --- RST ADC0 PB5 1|° |8 Vcc
// SET ------- ADC3 PB3 2| |7 PB2 ADC1 -------- OLED/INA SCK
// ------- ADC2 PB4 3| |6 PB1 AIN1 OC0B ---
// GND 4| |5 PB0 AIN0 OC0A --- OLED/INA SDA
// +----+
//
// Compilation Settings:
// ---------------------
// Core: ATtinyCore (https://github.com/SpenceKonde/ATTinyCore)
// Board: ATtiny25/45/85 (No bootloader)
// Chip: ATtiny25 or 45 or 85 (depending on your chip)
// Clock: 1 MHz (internal)
// B.O.D.: 2.7V
//
// Leave the rest on default settings. Don't forget to "Burn bootloader"!
// No Arduino core functions or libraries are used. Use the makefile if
// you want to compile without Arduino IDE.
//
// Note: The internal oscillator may need to be calibrated for precise
// energy and capacity calculation.
//
// Fuse settings: -U lfuse:w:0x62:m -U hfuse:w:0xd5:m -U efuse:w:0xff:m
//
// Operating Instructions:
// -----------------------
// Connect the device between a power supply and a consumer.
// Use the SET button to switch between the different screens.
// Use the RESET button to reset all values.
// ===================================================================================
// Libraries and Definitions
// ===================================================================================
// Oscillator calibration value (uncomment and set if necessary)
// #define OSCCAL_VAL 0x48
// Libraries
#include <avr/io.h> // for GPIO
#include <avr/pgmspace.h> // for data stored in program memory
#include <avr/interrupt.h> // for interrupts
#include <util/delay.h> // for delays
// Pin definitions
#define PIN_SDA PB0 // I2C serial data pin
#define PIN_SCL PB2 // I2C serial clock pin
#define PIN_SET PB3 // SET button
// ===================================================================================
// I2C Master Implementation
// ===================================================================================
// I2C macros
#define I2C_SDA_HIGH() DDRB &= ~(1<<PIN_SDA) // release SDA -> pulled HIGH by resistor
#define I2C_SDA_LOW() DDRB |= (1<<PIN_SDA) // SDA as output -> pulled LOW by MCU
#define I2C_SCL_HIGH() DDRB &= ~(1<<PIN_SCL) // release SCL -> pulled HIGH by resistor
#define I2C_SCL_LOW() DDRB |= (1<<PIN_SCL) // SCL as output -> pulled LOW by MCU
#define I2C_SDA_READ() (PINB & (1<<PIN_SDA)) // read SDA line
#define I2C_CLOCKOUT() I2C_SCL_HIGH();I2C_SCL_LOW() // clock out
// I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed
void I2C_write(uint8_t data) {
for(uint8_t i=8; i; i--, data<<=1) { // transmit 8 bits, MSB first
(data&0x80)?I2C_SDA_HIGH():I2C_SDA_LOW(); // SDA depending on bit
I2C_CLOCKOUT(); // clock out -> slave reads the bit
}
I2C_SDA_HIGH(); // release SDA for ACK bit of slave
I2C_CLOCKOUT(); // 9th clock pulse is for the ignored ACK bit
}
// I2C start transmission
void I2C_start(uint8_t addr) {
I2C_SDA_LOW(); // start condition: SDA goes LOW first
I2C_SCL_LOW(); // start condition: SCL goes LOW second
I2C_write(addr); // send slave address
}
// I2C restart transmission
void I2C_restart(uint8_t addr) {
I2C_SDA_HIGH(); // prepare SDA for HIGH to LOW transition
I2C_SCL_HIGH(); // restart condition: clock HIGH
I2C_start(addr); // start again
}
// I2C stop transmission
void I2C_stop(void) {
I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition
I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first
I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second
}
// I2C receive one data byte from the slave (ack=0 for last byte, ack>0 if more bytes to follow)
uint8_t I2C_read(uint8_t ack) {
uint8_t data = 0; // variable for the received byte
I2C_SDA_HIGH(); // release SDA -> will be toggled by slave
for(uint8_t i=8; i; i--) { // receive 8 bits
data <<= 1; // bits shifted in right (MSB first)
I2C_SCL_HIGH(); // clock HIGH
if(I2C_SDA_READ()) data |= 1; // read bit
I2C_SCL_LOW(); // clock LOW -> slave prepares next bit
}
if(ack) I2C_SDA_LOW(); // pull SDA LOW to acknowledge (ACK)
I2C_CLOCKOUT(); // clock out -> slave reads ACK bit
return data; // return the received byte
}
// ===================================================================================
// OLED Implementation
// ===================================================================================
// OLED definitions
#define OLED_ADDR 0x78 // OLED write address
#define OLED_CMD_MODE 0x00 // set command mode
#define OLED_DAT_MODE 0x40 // set data mode
#define OLED_INIT_LEN 11 // 9: no screen flip, 11: screen flip
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
0xA8, 0x1F, // set multiplex for 128x32
0x20, 0x01, // set vertical memory addressing mode
0xDA, 0x02, // set COM pins hardware configuration to sequential
0x8D, 0x14, // enable charge pump
0xAF, // switch on OLED
0xA1, 0xC8 // flip the screen
};
// OLED 6x16 font
const uint8_t OLED_FONT[] PROGMEM = {
0x7C, 0x1F, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x7C, 0x1F, // 0 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x1F, // 1 1
0x00, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x00, // 2 2
0x00, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 3 3
0x7C, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x7C, 0x1F, // 4 4
0x7C, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x00, 0x1F, // 5 5
0x7C, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x00, 0x1F, // 6 6
0x7C, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7C, 0x1F, // 7 7
0x7C, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 8 8
0x7C, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 9 9
0x00, 0x00, 0xF0, 0x3F, 0x8C, 0x00, 0x82, 0x00, 0x8C, 0x00, 0xF0, 0x3F, // 10 A
0x00, 0x00, 0xFE, 0x07, 0x00, 0x18, 0x00, 0x20, 0x00, 0x18, 0xFE, 0x07, // 11 V
0x00, 0x00, 0xFE, 0x1F, 0x00, 0x20, 0x00, 0x1F, 0x00, 0x20, 0xFE, 0x1F, // 12 W
0x00, 0x00, 0xFE, 0x3F, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x00, 0x3F, // 13 h
0x00, 0x00, 0x80, 0x3F, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00, 0x00, 0x3F, // 14 m
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // 15 .
0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, // 16 :
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, // 17 -
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 18 SPACE
};
// Character definitions
#define DECIMAL 15
#define COLON 16
#define SPACE 18
// OLED BCD conversion array
const uint16_t DIVIDER[] PROGMEM = {10000, 1000, 100, 10, 1};
// OLED init function
void OLED_init(void) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_CMD_MODE); // set command mode
for(uint8_t i=0; i<OLED_INIT_LEN; i++)
I2C_write(pgm_read_byte(&OLED_INIT_CMD[i]));// send the command bytes
I2C_stop(); // stop transmission
}
// OLED set the cursor
void OLED_setCursor(uint8_t xpos, uint8_t ypos) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_CMD_MODE); // set command mode
I2C_write(0x22); // command for min/max page
I2C_write(ypos); I2C_write(ypos+1); // min: ypos; max: ypos+1
I2C_write(xpos & 0x0F); // set low nibble of start column
I2C_write(0x10 | (xpos >> 4)); // set high nibble of start column
I2C_write(0xB0 | (ypos)); // set start page
I2C_stop(); // stop transmission
}
// OLED clear screen
void OLED_clearScreen(void) {
OLED_setCursor(0, 0); // set cursor at upper half
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t i = 0; // count variable
do {I2C_write(0x00);} while(--i); // clear upper half
I2C_stop(); // stop transmission
OLED_setCursor(0, 2); // set cursor at lower half
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
do {I2C_write(0x00);} while(--i); // clear upper half
I2C_stop(); // stop transmission
}
// OLED plot a character
void OLED_plotChar(uint8_t ch) {
ch = (ch << 3) + (ch << 2); // calculate position of character in font array
I2C_write(0x00); I2C_write(0x00); // print spacing between characters
for(uint8_t i=12; i; i--) I2C_write(pgm_read_byte(&OLED_FONT[ch++])); // print character
I2C_write(0x00); I2C_write(0x00); // print spacing between characters
}
// OLED print a character
void OLED_printChar(uint8_t ch) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
OLED_plotChar(ch); // plot the character
I2C_stop(); // stop transmission
}
// OLED print a string from program memory; terminator: 255
void OLED_printPrg(const uint8_t* p) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t ch = pgm_read_byte(p); // read first character from program memory
while(ch < 255) { // repeat until string terminator
OLED_plotChar(ch); // plot character on OLED
ch = pgm_read_byte(++p); // read next character
}
I2C_stop(); // stop transmission
}
// OLED print 16-bit value as 5-digit decimal (BCD conversion by substraction method)
void OLED_printDec16(uint16_t value) {
uint8_t leadflag = 0; // flag for leading spaces
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
for(uint8_t digit = 0; digit < 5; digit++) { // 5 digits
uint8_t digitval = 0; // start with digit value 0
uint16_t divider = pgm_read_word(&DIVIDER[digit]); // current divider
while(value >= divider) { // if current divider fits into the value
leadflag = 1; // end of leading spaces
digitval++; // increase digit value
value -= divider; // decrease value by divider
}
if(leadflag || (digit == 4)) OLED_plotChar(digitval); // print the digit
else OLED_plotChar(SPACE); // or print leading space
}
I2C_stop(); // stop transmission
}
// OLED print 16-bit value as 3-digit decimal (BCD conversion by substraction method)
void OLED_printDec12(uint16_t value) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
for(uint8_t digit = 2; digit < 5; digit++) { // 3 digits
uint8_t digitval = 0; // start with digit value 0
uint16_t divider = pgm_read_word(&DIVIDER[digit]); // current divider
while(value >= divider) { // if current divider fits into the value
digitval++; // increase digit value
value -= divider; // decrease value by divider
}
OLED_plotChar(digitval); // print the digit
}
I2C_stop(); // stop transmission
}
// OLED print 8-bit value as 2-digit decimal (BCD conversion by substraction method)
void OLED_printDec8(uint8_t value) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t digitval = 0; // start with digit value 0
while(value >= 10) { // if current divider fits into the value
digitval++; // increase digit value
value -= 10; // decrease value by divider
}
OLED_plotChar(digitval); // print first digit
OLED_plotChar(value); // print second digit
I2C_stop(); // stop transmission
}
// ===================================================================================
// INA219 Implementation
// ===================================================================================
// INA219 register values
#define INA_ADDR 0x80 // I2C write address of INA219
#define INA_CONFIG 0b0000011111111111 // INA config register according to datasheet
#define INA_CALIB 5120 // INA calibration register according to R_SHUNT
#define INA_REG_CONFIG 0x00 // INA configuration register address
#define INA_REG_CALIB 0x05 // INA calibration register address
#define INA_REG_SHUNT 0x01 // INA shunt voltage register address
#define INA_REG_VOLTAGE 0x02 // INA bus voltage register address
#define INA_REG_POWER 0x03 // INA power register address
#define INA_REG_CURRENT 0x04 // INA current register address
// INA219 write a register value
void INA_write(uint8_t reg, uint16_t value) {
I2C_start(INA_ADDR); // start transmission to INA219
I2C_write(reg); // write register address
I2C_write(value >> 8); // write register content high byte
I2C_write(value); // write register content low byte
I2C_stop(); // stop transmission
}
// INA219 read a register
uint16_t INA_read(uint8_t reg) {
uint16_t result; // result variable
I2C_start(INA_ADDR); // start transmission to INA219
I2C_write(reg); // write register address
I2C_restart(INA_ADDR | 0x01); // restart for reading
result = (uint16_t)(I2C_read(1) << 8) | I2C_read(0); // read register content
I2C_stop(); // stop transmission
return result; // return result
}
// INA219 write inital configuration and calibration values
void INA_init(void) {
INA_write(INA_REG_CONFIG, INA_CONFIG); // write INA219 configuration
INA_write(INA_REG_CALIB, INA_CALIB); // write INA219 calibration
}
// INA219 read voltage
uint16_t INA_readVoltage(void) {
return((INA_read(INA_REG_VOLTAGE) >> 1) & 0xFFFC);
}
// INA219 read sensor values
uint16_t INA_readCurrent(void) {
uint16_t result = INA_read(INA_REG_CURRENT); // read current from INA
if(result > 32767) result = 0; // ignore negative currents
return result; // return result
}
// ===================================================================================
// Millis Counter Implementation for Timer0
// ===================================================================================
volatile uint32_t MIL_counter = 0; // millis counter variable
// Init millis counter
void MIL_init(void) {
OCR0A = 124; // TOP: 124 = 1000kHz / (8 * 1kHz) - 1
TCCR0A = (1<<WGM01); // timer0 CTC mode
TCCR0B = (1<<CS01); // start timer0 with prescaler 8
TIMSK = (1<<OCIE0A); // enable output compare match interrupt
}
// Read millis counter
uint32_t MIL_read(void) {
cli(); // disable interrupt for atomic read
uint32_t result = MIL_counter; // read millis counter
sei(); // enable interrupts
return result; // return millis counter value
}
// Timer0 compare match A interrupt service routine (every millisecond)
ISR(TIM0_COMPA_vect) {
MIL_counter++; // increase millis counter
}
// ===================================================================================
// Main Function
// ===================================================================================
// Some "strings"
const uint8_t mA[] PROGMEM = { 14, 10, 18, 255 }; // "mA "
const uint8_t mV[] PROGMEM = { 14, 11, 18, 255 }; // "mV "
const uint8_t mW[] PROGMEM = { 14, 12, 18, 255 }; // "mW "
const uint8_t mAh[] PROGMEM = { 14, 10, 13, 255 }; // "mAh"
const uint8_t mWh[] PROGMEM = { 14, 12, 13, 255 }; // "mWh"
const uint8_t SEP[] PROGMEM = { 18, 17, 18, 255 }; // " - "
// Main function
int main(void) {
// Local variables
uint16_t voltage, current, power; // voltage in mV, current in mA, power in mW
uint16_t minvoltage = 65535, maxvoltage = 0; // min/max voltages in mV
uint16_t mincurrent = 65535, maxcurrent = 0; // min/max current in mA
uint16_t minpower = 65535, maxpower = 0; // min/max power in mV
uint32_t lastmillis, nowmillis, interval; // for timing calculation in millis
uint32_t duration = 0; // total duration in millis
uint16_t seconds; // total duration in seconds
uint32_t capacity = 0, energy = 0; // counter for capacity in uAh and energy in uWh
uint8_t primescreen = 0; // screen selection flag
uint8_t lastbutton = 1; // button flag (0: button pressed)
// Set oscillator calibration value
#ifdef OSCCAL_VAL
OSCCAL = OSCCAL_VAL; // set the value if defined above
#endif
// Setup
PORTB |= (1<<PIN_SET); // pullup for set button
MIL_init(); // init millis counter
INA_init(); // init INA219
OLED_init(); // init OLED
OLED_clearScreen(); // clear screen
lastmillis = MIL_read(); // read millis counter
// Loop
while(1) {
// Read sensor values
voltage = INA_readVoltage(); // read voltage in mV from INA219
current = INA_readCurrent(); // read current in mA from INA219
// Calculate timings
nowmillis = MIL_read(); // read millis counter
interval = nowmillis - lastmillis; // calculate recent time interval
lastmillis = nowmillis; // reset lastmillis
duration += interval; // calculate total duration in millis
seconds = duration / 1000; // calculate total duration in seconds
// Calculate power, capacity and energy
power = (uint32_t)voltage * current / 1000; // calculate power in mW
capacity += interval * current / 3600; // calculate capacity in uAh
energy += interval * power / 3600; // calculate energy in uWh
// Update min/max values
if(minvoltage > voltage) minvoltage = voltage;
if(maxvoltage < voltage) maxvoltage = voltage;
if(mincurrent > current) mincurrent = current;
if(maxcurrent < current) maxcurrent = current;
if(minpower > power ) minpower = power;
if(maxpower < power ) maxpower = power;
// Check SET button and set screen flag accordingly
if(PINB & (1<<PIN_SET)) lastbutton = 0;
else if(!lastbutton) {
if(++primescreen > 4) primescreen = 0;
OLED_clearScreen();
lastbutton = 1;
}
// Display values on the OLED
switch(primescreen) {
case 0: OLED_setCursor(0,0);
OLED_printDec16(voltage); OLED_printPrg(mV);
OLED_printDec16(power); OLED_printPrg(mW);
OLED_setCursor(0,2);
OLED_printDec16(current); OLED_printPrg(mA);
OLED_printDec16(capacity / 1000); OLED_printPrg(mAh);
break;
case 1: OLED_setCursor(0,0);
OLED_printDec16(minvoltage); OLED_printPrg(SEP);
OLED_printDec16(maxvoltage); OLED_printPrg(mV);
OLED_setCursor(0,2);
OLED_printDec16(mincurrent); OLED_printPrg(SEP);
OLED_printDec16(maxcurrent); OLED_printPrg(mA);
break;
case 2: OLED_setCursor(0,1);
OLED_printDec16(minpower); OLED_printPrg(SEP);
OLED_printDec16(maxpower); OLED_printPrg(mW);
break;
case 3: // ATtiny25 without decimal places to make it fit into the flash
#if defined(__AVR_ATtiny25__)
OLED_setCursor(32,0);
OLED_printDec16(capacity / 1000); OLED_printPrg(mAh);
OLED_setCursor(32,2);
OLED_printDec16(energy / 1000); OLED_printPrg(mWh);
#else
OLED_setCursor(16,0);
OLED_printDec16(capacity / 1000); OLED_printChar(DECIMAL);
OLED_printDec12(capacity % 1000); OLED_printPrg(mAh);
OLED_setCursor(16,2);
OLED_printDec16(energy / 1000); OLED_printChar(DECIMAL);
OLED_printDec12(energy % 1000); OLED_printPrg(mWh);
#endif
break;
case 4: OLED_setCursor(32,1);
OLED_printDec8(seconds / 3600); OLED_printChar(COLON);
seconds %= 3600;
OLED_printDec8(seconds / 60 ); OLED_printChar(COLON);
OLED_printDec8(seconds % 60 );
break;
default: break;
}
_delay_ms(50);
}
}

View File

@ -1,480 +0,0 @@
// USB Power Tester - Basic
//
// This code implements the basic functionality for the USB Power Tester.
// It reads voltage, current and power from the INA219, calculates
// capacity and shows the values on the OLED. The SET button is used to
// switch between different screens.
//
// +-\/-+
// RESET --- A0 (D5) PB5 1|° |8 Vcc
// SET ----- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- OLED/INA (SCK)
// A2 (D4) PB4 3| |6 PB1 (D1)
// GND 4| |5 PB0 (D0) ------ OLED/INA (SDA)
// +----+
//
// Core: ATtinyCore (https://github.com/SpenceKonde/ATTinyCore)
// Board: ATtiny25/45/85 (No bootloader)
// Chip: ATtiny25 or 45 or 85 (depending on your chip)
// Clock: 8 MHz (internal)
// Millis: disabled
// B.O.D.: 2.7V
// Leave the rest on default settings. Don't forget to "Burn bootloader"!
// No Arduino core functions or libraries are used. Use the makefile if
// you want to compile without Arduino IDE.
//
// Note: The internal oscillator may need to be calibrated for precise
// energy and capacity calculation.
//
// The I²C OLED implementation is based on TinyOLEDdemo
// https://github.com/wagiminator/ATtiny13-TinyOLEDdemo
//
// 2020 by Stefan Wagner
// Project Files (EasyEDA): https://easyeda.com/wagiminator
// Project Files (Github): https://github.com/wagiminator
// License: http://creativecommons.org/licenses/by-sa/3.0/
// Oscillator calibration value (uncomment and set if necessary)
// #define OSCCAL_VAL 0x48
// Libraries
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// Pin definitions
#define I2C_SDA PB0 // I2C serial data pin
#define I2C_SCL PB2 // I2C serial clock pin
#define SETBUTTON PB3 // SET button
// -----------------------------------------------------------------------------
// I2C Implementation
// -----------------------------------------------------------------------------
// I2C macros
#define I2C_SDA_HIGH() DDRB &= ~(1<<I2C_SDA) // release SDA -> pulled HIGH by resistor
#define I2C_SDA_LOW() DDRB |= (1<<I2C_SDA) // SDA as output -> pulled LOW by MCU
#define I2C_SCL_HIGH() DDRB &= ~(1<<I2C_SCL) // release SCL -> pulled HIGH by resistor
#define I2C_SCL_LOW() DDRB |= (1<<I2C_SCL) // SCL as output -> pulled LOW by MCU
#define I2C_SDA_READ() (PINB & (1<<I2C_SDA)) // read SDA line
#define I2C_DELAY() asm("lpm") // delay 3 clock cycles
#define I2C_CLOCKOUT() I2C_SCL_HIGH();I2C_DELAY();I2C_SCL_LOW() // clock out
// I2C init function
void I2C_init(void) {
DDRB &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); // pins as input (HIGH-Z) -> lines released
PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL)); // should be LOW when as ouput
}
// I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed
void I2C_write(uint8_t data) {
for(uint8_t i=8; i; i--, data<<=1) { // transmit 8 bits, MSB first
(data & 0x80) ? (I2C_SDA_HIGH()) : (I2C_SDA_LOW()); // SDA HIGH if bit is 1
I2C_CLOCKOUT(); // clock out -> slave reads the bit
}
I2C_DELAY(); // delay 3 clock cycles
I2C_SDA_HIGH(); // release SDA for ACK bit of slave
I2C_CLOCKOUT(); // 9th clock pulse is for the ignored ACK bit
}
// I2C start transmission
void I2C_start(uint8_t addr) {
I2C_SDA_LOW(); // start condition: SDA goes LOW first
I2C_SCL_LOW(); // start condition: SCL goes LOW second
I2C_write(addr); // send slave address
}
// I2C restart transmission
void I2C_restart(uint8_t addr) {
I2C_SDA_HIGH(); // prepare SDA for HIGH to LOW transition
I2C_SCL_HIGH(); // restart condition: clock HIGH
I2C_start(addr); // start again
}
// I2C stop transmission
void I2C_stop(void) {
I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition
I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first
I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second
}
// I2C receive one data byte from the slave (ack=0 for last byte, ack>0 if more bytes to follow)
uint8_t I2C_read(uint8_t ack) {
uint8_t data = 0; // variable for the received byte
I2C_SDA_HIGH(); // release SDA -> will be toggled by slave
for(uint8_t i=8; i; i--) { // receive 8 bits
data<<=1; // bits shifted in right (MSB first)
I2C_DELAY(); // delay 3 clock cycles
I2C_SCL_HIGH(); // clock HIGH
if(I2C_SDA_READ()) data |=1; // read bit
I2C_SCL_LOW(); // clock LOW -> slave prepares next bit
}
if (ack) I2C_SDA_LOW(); // pull SDA LOW to acknowledge (ACK)
I2C_DELAY(); // delay 3 clock cycles
I2C_CLOCKOUT(); // clock out -> slave reads ACK bit
return(data); // return the received byte
}
// -----------------------------------------------------------------------------
// OLED Implementation
// -----------------------------------------------------------------------------
// OLED definitions
#define OLED_ADDR 0x78 // OLED write address
#define OLED_CMD_MODE 0x00 // set command mode
#define OLED_DAT_MODE 0x40 // set data mode
#define OLED_INIT_LEN 11 // 9: no screen flip, 11: screen flip
// OLED init settings
const uint8_t OLED_INIT_CMD[] PROGMEM = {
0xA8, 0x1F, // set multiplex for 128x32
0x20, 0x01, // set vertical memory addressing mode
0xDA, 0x02, // set COM pins hardware configuration to sequential
0x8D, 0x14, // enable charge pump
0xAF, // switch on OLED
0xA1, 0xC8 // flip the screen
};
// OLED 6x16 font
const uint8_t OLED_FONT[] PROGMEM = {
0x7C, 0x1F, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x7C, 0x1F, // 0 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x1F, // 1 1
0x00, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x00, // 2 2
0x00, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 3 3
0x7C, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x7C, 0x1F, // 4 4
0x7C, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x00, 0x1F, // 5 5
0x7C, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x00, 0x1F, // 6 6
0x7C, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7C, 0x1F, // 7 7
0x7C, 0x1F, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 8 8
0x7C, 0x00, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x82, 0x20, 0x7C, 0x1F, // 9 9
0x00, 0x00, 0xF0, 0x3F, 0x8C, 0x00, 0x82, 0x00, 0x8C, 0x00, 0xF0, 0x3F, // 10 A
0x00, 0x00, 0xFE, 0x07, 0x00, 0x18, 0x00, 0x20, 0x00, 0x18, 0xFE, 0x07, // 11 V
0x00, 0x00, 0xFE, 0x1F, 0x00, 0x20, 0x00, 0x1F, 0x00, 0x20, 0xFE, 0x1F, // 12 W
0x00, 0x00, 0xFE, 0x3F, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x00, 0x3F, // 13 h
0x00, 0x00, 0x80, 0x3F, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00, 0x00, 0x3F, // 14 m
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, // 15 .
0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, // 16 :
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, // 17 -
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 18 SPACE
};
// Character definitions
#define DECIMAL 15
#define COLON 16
#define SPACE 18
// For BCD conversion
const uint16_t divider[5] = {10000, 1000, 100, 10, 1};
// OLED init function
void OLED_init(void) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_CMD_MODE); // set command mode
for (uint8_t i=0; i<OLED_INIT_LEN; i++)
I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); // send the command bytes
I2C_stop(); // stop transmission
}
// OLED set the cursor
void OLED_setCursor(uint8_t xpos, uint8_t ypos) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_CMD_MODE); // set command mode
I2C_write(0x22); // command for min/max page
I2C_write(ypos); I2C_write(ypos+1); // min: ypos; max: ypos+1
I2C_write(xpos & 0x0F); // set low nibble of start column
I2C_write(0x10 | (xpos >> 4)); // set high nibble of start column
I2C_write(0xB0 | (ypos)); // set start page
I2C_stop(); // stop transmission
}
// OLED clear screen
void OLED_clearScreen(void) {
OLED_setCursor(0, 0); // set cursor at upper half
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t i = 0; // count variable
do {I2C_write(0x00);} while (--i); // clear upper half
I2C_stop(); // stop transmission
OLED_setCursor(0, 2); // set cursor at lower half
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
do {I2C_write(0x00);} while (--i); // clear upper half
I2C_stop(); // stop transmission
}
// OLED plot a character
void OLED_plotChar(uint8_t ch) {
ch = (ch << 3) + (ch << 2); // calculate position of character in font array
I2C_write(0x00); I2C_write(0x00); // print spacing between characters
for(uint8_t i=12; i; i--) I2C_write(pgm_read_byte(&OLED_FONT[ch++])); // print character
I2C_write(0x00); I2C_write(0x00); // print spacing between characters
}
// OLED print a character
void OLED_printChar(uint8_t ch) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
OLED_plotChar(ch); // plot the character
I2C_stop(); // stop transmission
}
// OLED print a string from program memory; terminator: 255
void OLED_printPrg(const uint8_t* p) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t ch = pgm_read_byte(p); // read first character from program memory
while (ch < 255) { // repeat until string terminator
OLED_plotChar(ch); // plot character on OLED
ch = pgm_read_byte(++p); // read next character
}
I2C_stop(); // stop transmission
}
// OLED print 16-bit value as 5-digit decimal (BCD conversion by substraction method)
void OLED_printDec16(uint16_t value) {
uint8_t leadflag = 0; // flag for leading spaces
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
for(uint8_t digit = 0; digit < 5; digit++) { // 5 digits
uint8_t digitval = 0; // start with digit value 0
while (value >= divider[digit]) { // if current divider fits into the value
leadflag = 1; // end of leading spaces
digitval++; // increase digit value
value -= divider[digit]; // decrease value by divider
}
if (leadflag || (digit == 4)) OLED_plotChar(digitval); // print the digit
else OLED_plotChar(SPACE); // or print leading space
}
I2C_stop(); // stop transmission
}
// OLED print 16-bit value as 3-digit decimal (BCD conversion by substraction method)
void OLED_printDec12(uint16_t value) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
for(uint8_t digit = 2; digit < 5; digit++) { // 3 digits
uint8_t digitval = 0; // start with digit value 0
while (value >= divider[digit]) { // if current divider fits into the value
digitval++; // increase digit value
value -= divider[digit]; // decrease value by divider
}
OLED_plotChar(digitval); // print the digit
}
I2C_stop(); // stop transmission
}
// OLED print 8-bit value as 2-digit decimal (BCD conversion by substraction method)
void OLED_printDec8(uint8_t value) {
I2C_start(OLED_ADDR); // start transmission to OLED
I2C_write(OLED_DAT_MODE); // set data mode
uint8_t digitval = 0; // start with digit value 0
while (value >= 10) { // if current divider fits into the value
digitval++; // increase digit value
value -= 10; // decrease value by divider
}
OLED_plotChar(digitval); // print first digit
OLED_plotChar(value); // print second digit
I2C_stop(); // stop transmission
}
// -----------------------------------------------------------------------------
// INA219 Implementation
// -----------------------------------------------------------------------------
// INA219 register values
#define INA_ADDR 0x80 // I2C write address of INA219
#define INA_CONFIG 0b0000011111111111 // INA config register according to datasheet
#define INA_CALIB 5120 // INA calibration register according to R_SHUNT
#define INA_REG_CONFIG 0x00 // INA configuration register address
#define INA_REG_CALIB 0x05 // INA calibration register address
#define INA_REG_SHUNT 0x01 // INA shunt voltage register address
#define INA_REG_VOLTAGE 0x02 // INA bus voltage register address
#define INA_REG_POWER 0x03 // INA power register address
#define INA_REG_CURRENT 0x04 // INA current register address
// INA219 write a register value
void INA_write(uint8_t reg, uint16_t value) {
I2C_start(INA_ADDR); // start transmission to INA219
I2C_write(reg); // write register address
I2C_write(value >> 8); // write register content high byte
I2C_write(value); // write register content low byte
I2C_stop(); // stop transmission
}
// INA219 read a register
uint16_t INA_read(uint8_t reg) {
uint16_t result; // result variable
I2C_start(INA_ADDR); // start transmission to INA219
I2C_write(reg); // write register address
I2C_restart(INA_ADDR | 0x01); // restart for reading
result = (uint16_t)(I2C_read(1) << 8) | I2C_read(0); // read register content
I2C_stop(); // stop transmission
return(result); // return result
}
// INA219 write inital configuration and calibration values
void INA_init(void) {
INA_write(INA_REG_CONFIG, INA_CONFIG); // write INA219 configuration
INA_write(INA_REG_CALIB, INA_CALIB); // write INA219 calibration
}
// INA219 read voltage
uint16_t INA_readVoltage(void) {
return((INA_read(INA_REG_VOLTAGE) >> 1) & 0xFFFC);
}
// INA219 read sensor values
uint16_t INA_readCurrent(void) {
uint16_t result = INA_read(INA_REG_CURRENT);
if (result > 32767) result = 0;
return(result);
}
// -----------------------------------------------------------------------------
// Millis Counter Implementation for Timer0
// -----------------------------------------------------------------------------
volatile uint32_t MIL_counter = 0; // millis counter variable
// Init millis counter
void MIL_init(void) {
OCR0A = 124; // TOP: 124 = 8000kHz / (64 * 1kHz) - 1
TCCR0A = (1<<WGM01); // timer0 CTC mode
TCCR0B = (1<<CS01)|(1<<CS00); // start timer0 with prescaler 64
TIMSK = (1<<OCIE0A); // enable output compare match interrupt
sei(); // enable global interrupts
}
// Read millis counter
uint32_t MIL_read(void) {
cli(); // disable interrupt for atomic read
uint32_t result = MIL_counter; // read millis counter
sei(); // enable interrupts
return(result); // return millis counter value
}
// Timer0 compare match A interrupt service routine (every millisecond)
ISR(TIM0_COMPA_vect) {
MIL_counter++; // increase millis counter
}
// -----------------------------------------------------------------------------
// Main Function
// -----------------------------------------------------------------------------
// Some "strings"
const uint8_t mA[] PROGMEM = { 14, 10, 18, 255 }; // "mA "
const uint8_t mV[] PROGMEM = { 14, 11, 18, 255 }; // "mV "
const uint8_t mW[] PROGMEM = { 14, 12, 18, 255 }; // "mW "
const uint8_t mAh[] PROGMEM = { 14, 10, 13, 255 }; // "mAh"
const uint8_t mWh[] PROGMEM = { 14, 12, 13, 255 }; // "mWh"
const uint8_t SEP[] PROGMEM = { 18, 17, 18, 255 }; // " - "
int main(void) {
// Local variables
uint16_t voltage, current, power; // voltage in mV, current in mA, power in mW
uint16_t minvoltage = 65535, maxvoltage = 0; // min/max voltages in mV
uint16_t mincurrent = 65535, maxcurrent = 0; // min/max current in mA
uint16_t minpower = 65535, maxpower = 0; // min/max power in mV
uint32_t lastmillis, nowmillis, interval; // for timing calculation in millis
uint32_t duration = 0; // total duration in millis
uint16_t seconds; // total duration in seconds
uint32_t capacity = 0, energy = 0; // counter for capacity in uAh and energy in uWh
uint8_t primescreen = 0; // screen selection flag
uint8_t lastbutton = 1; // button flag (0: button pressed)
// Set oscillator calibration value
#ifdef OSCCAL_VAL
OSCCAL = OSCCAL_VAL; // set the value if defined above
#endif
// Setup
PORTB = (1<<SETBUTTON); // pullup for set button
MIL_init(); // init millis counter
I2C_init(); // init I2C
INA_init(); // init INA219
OLED_init(); // init OLED
OLED_clearScreen(); // clear screen
lastmillis = MIL_read(); // read millis counter
// Loop
while(1) {
// Read sensor values
voltage = INA_readVoltage(); // read voltage in mV from INA219
current = INA_readCurrent(); // read current in mA from INA219
// Calculate timings
nowmillis = MIL_read(); // read millis counter
interval = nowmillis - lastmillis; // calculate recent time interval
lastmillis = nowmillis; // reset lastmillis
duration += interval; // calculate total duration in millis
seconds = duration / 1000; // calculate total duration in seconds
// Calculate power, capacity and energy
power = (uint32_t)voltage * current / 1000; // calculate power in mW
capacity += interval * current / 3600; // calculate capacity in uAh
energy += interval * power / 3600; // calculate energy in uWh
// Update min/max values
if (minvoltage > voltage) minvoltage = voltage;
if (maxvoltage < voltage) maxvoltage = voltage;
if (mincurrent > current) mincurrent = current;
if (maxcurrent < current) maxcurrent = current;
if (minpower > power ) minpower = power;
if (maxpower < power ) maxpower = power;
// Check SET button and set screen flag accordingly
if (PINB & (1<<SETBUTTON)) lastbutton = 0;
else if (!lastbutton) {
if (++primescreen > 4) primescreen = 0;
OLED_clearScreen();
lastbutton = 1;
}
// Display values on the OLED
switch (primescreen) {
case 0: OLED_setCursor(0,0);
OLED_printDec16(voltage); OLED_printPrg(mV);
OLED_printDec16(power); OLED_printPrg(mW);
OLED_setCursor(0,2);
OLED_printDec16(current); OLED_printPrg(mA);
OLED_printDec16(capacity / 1000); OLED_printPrg(mAh);
break;
case 1: OLED_setCursor(0,0);
OLED_printDec16(minvoltage); OLED_printPrg(SEP);
OLED_printDec16(maxvoltage); OLED_printPrg(mV);
OLED_setCursor(0,2);
OLED_printDec16(mincurrent); OLED_printPrg(SEP);
OLED_printDec16(maxcurrent); OLED_printPrg(mA);
break;
case 2: OLED_setCursor(0,1);
OLED_printDec16(minpower); OLED_printPrg(SEP);
OLED_printDec16(maxpower); OLED_printPrg(mW);
break;
case 3: // ATtiny25 without decimal places to make it fit into the flash
#if defined(__AVR_ATtiny25__)
OLED_setCursor(32,0);
OLED_printDec16(capacity / 1000); OLED_printPrg(mAh);
OLED_setCursor(32,2);
OLED_printDec16(energy / 1000); OLED_printPrg(mWh);
#else
OLED_setCursor(16,0);
OLED_printDec16(capacity / 1000); OLED_printChar(DECIMAL);
OLED_printDec12(capacity % 1000); OLED_printPrg(mAh);
OLED_setCursor(16,2);
OLED_printDec16(energy / 1000); OLED_printChar(DECIMAL);
OLED_printDec12(energy % 1000); OLED_printPrg(mWh);
#endif
break;
case 4: OLED_setCursor(32,1);
OLED_printDec8(seconds / 3600); OLED_printChar(COLON);
seconds %= 3600;
OLED_printDec8(seconds / 60 ); OLED_printChar(COLON);
OLED_printDec8(seconds % 60 );
break;
default: break;
}
_delay_ms(100);
}
}

View File

@ -1,20 +1,19 @@
# Project: USB-Tester # Project: USB-Tester
# Author: Stefan Wagner # Author: Stefan Wagner
# Year: 2020 # Year: 2020
# URL: https://easyeda.com/wagiminator # URL: https://github.com/wagiminator
# https://github.com/wagiminator
# #
# Type "make help" in the command line. # Type "make help" in the command line.
# Input and Output File Names # Input and Output File Names
SKETCH = USB_Tester_v1.3.ino SKETCH = USB_Tester.ino
TARGET = usb_tester TARGET = usb_tester
# Microcontroller Options # Microcontroller Options
DEVICE = attiny85 DEVICE = attiny85
CLOCK = 8000000 CLOCK = 1000000
PROGRMR = usbasp PROGRMR = usbasp
LFUSE = 0xE2 LFUSE = 0x62
HFUSE = 0xD5 HFUSE = 0xD5
EFUSE = 0xFF EFUSE = 0xFF

View File

@ -1,5 +1,5 @@
:1000000092C0ACC0ABC0AAC0A9C0A8C0A7C0A6C0BF :1000000097C0A6C0A5C0A4C0A3C0A2C0A1C0A0C0E4
:10001000A5C0A4C0BDC1A2C0A1C0A0C09FC07C1F7C :100010009FC09EC0B3C19CC09BC09AC099C07C1FAA
:1000200002200220022002207C1F000000000000AD :1000200002200220022002207C1F000000000000AD
:10003000000000007C1F001F82208220822082207E :10003000000000007C1F001F82208220822082207E
:100040007C00000082208220822082207C1F7C0095 :100040007C00000082208220822082207C1F7C0095
@ -14,122 +14,118 @@
:1000D000003F000000000030003000000000000081 :1000D000003F000000000030003000000000000081
:1000E0000000300630060000000080008000800024 :1000E0000000300630060000000080008000800024
:1000F0008000800080000000000000000000000080 :1000F0008000800080000000000000000000000080
:100100000000A81F2001DA028D14AFA1C80E0C0D4B :1001000000001027E80364000A000100A81F200176
:10011000FF121112FF0E0A0DFF0E0A12FF0E0C1233 :10011000DA028D14AFA1C80E0C0DFF121112FF0EE2
:10012000FF0E0B12FF0011241FBECFE5D2E0DEBF91 :100120000A0DFF0E0A12FF0E0C12FF0E0B12FF003B
:10013000CDBF10E0A0E6B0E0E6E4F8E002C0059034 :1001300011241FBECFE5D2E0DEBFCDBF20E0A0E698
:100140000D92AA36B107D9F720E0AAE6B0E001C0C7 :10014000B0E001C01D92A436B207E1F73CD163C311
:100150001D92AE36B207E1F740D173C351CF282FBD :1001500057CF282FB89898E080E0880FBA98B099C8
:10016000B89898E080E0880FC895BA98B0998160F7 :100160008160BA9A9150C9F72111B89ABA98BA9A8F
:10017000BA9A9150C1F72111B89AC895BA98C89502 :100170000895B89ABA98B898089598E087FF02C091
:10018000BA9A0895B89ABA98B898089598E087FFEF :10018000B89801C0B89ABA98BA9A880F9150B1F746
:1001900002C0B89801C0B89ABA98C895BA9A880FA0 :10019000B898BA98BA9A08951F93CF93DF93E82F2F
:1001A0009150A9F7C895B898BA98C895BA9A089581 :1001A000EE0FEE0FEE0F880F880FCE2FC80F80E0F6
:1001B0001F93CF93DF93E82FEE0FEE0FEE0F880F14 :1001B000E4DF80E0E2DFDCE0DC0F11E01C0FEC2F7D
:1001C000880FCE2FC80F80E0E1DF80E0DFDFDCE0CA :1001C000F0E0E25EFF4F8491D8DFC12FD113F5CF6D
:1001D000DC0F11E01C0FEC2FF0E0E25EFF4F84918A :1001D00080E0D3DF80E0DF91CF911F91CECFB89A3E
:1001E000D5DFC12FD113F5CF80E0D0DF80E0DF91E4 :1001E000BA9ACBCFCF93DF93C82F80E8F8DF8C2F5C
:1001F000CF911F91CBCFB89ABA9AC8CFCF93DF9344 :1001F000C4DFB898BA9881E8F2DF81E0AADFC82F9F
:10020000C82F80E8F8DF8C2FC1DFB898BA9881E852 :1002000080E0A7DFD0E0DC2FCC27C82BB2DFCE0107
:10021000F2DF81E0A4DFC82F80E0A1DFD0E0DC2F97 :10021000DF91CF910895CF93C82F88E7E0DF80E486
:10022000CC27C82BAFDFCE01DF91CF910895CF93BC :10022000ACDF80E0CA3018F08F5FCA50FBCFB4DF7C
:10023000C82F88E7E0DF80E4A9DF80E0CA3018F04B :100230008C2FB2DFCF919DCF0F931F93CF93DF937E
:100240008F5FCA50FBCFB4DF8C2FB2DFCF919ACF34 :100240008C0188E7CCDF80E498DFC0E0D0E0FE01DD
:100250000F931F93CF93DF938C0188E7CCDF80E46B :10025000EA5FFE4F2591349180E00217130720F0EA
:1002600095DFC4E6D0E02991399180E002171307A9 :100260008F5F021B130BF9CF97DF2296C630D105A3
:1002700020F08F5F021B130BF9CF9ADF80E0CA36A4 :1002700071F7DF91CF911F910F917BCFEF92FF929A
:10028000D80789F7DF91CF911F910F917BCFEF9224 :100280000F931F93CF93DF937C0188E7A8DF80E46F
:10029000FF920F931F93CF93DF938C0188E7ABDF1F :1002900074DF02E011E0C0E0D0E0F8012591349174
:1002A00080E474DF80E6E82E80E0F82EC0E0D0E045 :1002A00080E0E216F30628F08F5FE21AF30AD1E04D
:1002B000F701219131917F0180E00217130728F0A7 :1002B000F8CFD11103C0C43009F082E16DDFCF5F08
:1002C0008F5F021B130BD1E0F8CFD11103C0C430F4 :1002C0000E5F1F4FC53049F7DF91CF911F910F91FE
:1002D00009F082E16DDFCF5FC53051F7DF91CF913B :1002D000FF90EF904ECFCF93DF93EC0188E77FDF65
:1002E0001F910F91FF90EF904DCFCF93DF93EC01D3 :1002E00080E44BDFFE0184918F3F19F055DF2196AA
:1002F00088E781DF80E44ADFFE0184918F3F19F0B7 :1002F000F9CFDF91CF913DCFCF93C82F88E76FDF44
:1003000057DF2196F9CFDF91CF913CCFCF93C82F04 :1003000080E43BDF8C2F48DFCF9133CFCF93DF9357
:1003100088E771DF80E43ADF8C2F4ADFCF9132CF5C :10031000C82FD62F88E763DF80E02FDF82E22DDF52
:10032000CF93DF93C82FD62F88E765DF80E02EDFDD :100320008D2F2BDF81E08D0F28DF8C2F8F7025DF45
:1003300082E22CDF8D2F2ADF81E08D0F27DF8C2FCB :100330008C2F82958F70806120DF8D2F806B1DDF69
:100340008F7024DF8C2F82958F7080611FDF8D2F3F :10034000DF91CF9116CFCF9360E080E0DFDF88E7C9
:10035000806B1CDFDF91CF9115CFCF9360E080E001 :1003500046DF80E412DFC0E080E00FDFC150E1F74C
:10036000DFDF88E748DF80E411DFC0E080E00EDFF8 :1003600008DF62E080E0D2DF88E739DF80E405DF84
:10037000C150E1F707DF62E080E0D2DF88E73BDFD2 :1003700080E003DFC150E1F7CF91FBCE1F920F92D7
:1003800080E404DF80E002DFC150E1F7CF91FACED4 :100380000FB60F9211248F939F93AF93BF938091D9
:100390001F920F920FB60F9211248F939F93AF93DA :10039000600090916100A0916200B09163000196AD
:1003A000BF9380916A0090916B00A0916C00B09116 :1003A000A11DB11D8093600090936100A093620035
:1003B0006D000196A11DB11D80936A0090936B00A2 :1003B000B0936300BF91AF919F918F910F900FBE4B
:1003C000A0936C00B0936D00BF91AF919F918F91FE :1003C0000F901F901895CF93DF93CDB7DEB7A4970A
:1003D0000F900FBE0F901F901895CF93DF93CDB75E :1003D0000FB6F894DEBF0FBECDBFC39A8CE789BDC0
:1003E000DEB7A4970FB6F894DEBF0FBECDBF88E08E :1003E00082E08ABD83BF80E189BF80E8F8DE80E0DB
:1003F00088BB8CE789BD82E08ABD83E083BF80E152 :1003F000C4DE87E0C2DE8FEFC0DEBBDE80E8EFDE6A
:1004000089BF789487B38A7F87BB88B38A7F88BB8C :1004000085E0BBDE84E1B9DE80E0B7DEB2DE88E7FE
:1004100080E8F1DE80E0BADE86E0B8DE87E6B6DEB0 :10041000E6DE80E0B2DE2CE031E03C832B8307E1B6
:10042000B1DE80E8E8DE85E0B1DE84E1AFDE80E0C9 :1004200011E0EB81FC818491A8DE2B813C812F5F60
:10043000ADDEA8DE88E7DFDE80E0A8DE22E031E086 :100430003F4F3C832B830217130799F79ADE83DF24
:100440003A8329830DE011E0E981FA8184919EDEEF :10044000F894C0906000D0906100E0906200F0905D
:1004500029813A812F5F3F4F3A832983021713077F :100450006300789431E03A831982198A1A8A1B8AD8
:1004600099F790DE7ADFF89440906A0050906B0024 :100460001C8A412C512C32011D861E861F86188A3B
:1004700060906C0070906D00789431E03CA31BA2FA :100470001C861B864FEF5FEF5A87498718861F82BD
:100480001D8A1E8A1F8A188E19821A821B821C825C :100480008FEF9FEF9E838D83212C312CAFEFBFEF39
:10049000198A1A8A1B8A1C8A188A1F868FEF9FEF77 :10049000BC83AB8338EE832E33E0932EA12CB12C9A
:1004A0009E878D871C861B86AFEFBFEFBA87A98713 :1004A00082E0A0DE969587958C010C7F84E09ADE31
:1004B00018861F82EFEFFFEFFE83ED8338EE832E69 :1004B0009CA38BA397FF02C01CA21BA2F8942091BF
:1004C00033E0932EA12CB12C82E098DE969587958F :1004C00060003091610040916200509163002F8F75
:1004D0001C01FCEF2F2284E091DE8C0197FF02C00B :1004D00038A349A35AA378942C193D094E095F0902
:1004E00000E010E0A80160E070E0C101A0E0B0E031 :1004E0002D8B3E8B4F8B588F8D859E85AF85B88920
:1004F000498F5A8F6B8F7C8F9A01AB01BC01CD0164 :1004F000820F931FA41FB51F8D879E87AF87B88B70
:1005000051D1A50194017BD169017A013E8F2D8FD4 :10050000ABA1BCA1AD0160E070E0C801A0E0B0E02B
:10051000F89480916A0090916B00A0916C00B0916A :10051000498F5A8F6B8F7C8F9A01AB01BC01CD0143
:100520006D008F8F98A3A9A3BAA378948419950915 :100520002BD1A501940155D169017A013E8F2D8F00
:10053000A609B7092C013D0189899A89AB89BC8933 :10053000298D3A8D4B8D5C8D6D897E898F89988D43
:10054000840D951DA61DB71D898B9A8BAB8BBC8B1B :100540001BD120E13EE040E050E043D1420E531E7B
:10055000A3019201698D7A8D8B8D9C8D23D120E131 :10055000641E751EC701B601882799272D893E891B
:100560003EE040E050E04BD189819A81AB81BC8173 :100560004F89588D09D120E13EE040E050E031D183
:10057000820F931FA41FB51F89839A83AB83BC830B :1005700089899A89AB89BC89820F931FA41FB51FF3
:10058000C701B60188279927A30192010BD120E169 :10058000898B9A8BAB8BBC8BAB81BC810A171B0709
:100590003EE040E050E033D18D899E89AF89B88D2F :1005900010F41C830B832016310608F41801ED813A
:1005A000820F931FA41FB51F8D8B9E8BAF8BB88FAF :1005A000FE812BA13CA12E173F0710F43E832D8323
:1005B000AD81BE812A163B0610F43E822D82EF816A :1005B0004F8158858BA19CA14817590710F4988743
:1005C000F885E215F30510F438862F8229853A85DF :1005C0008F83A985BA85ED8DFE8DEA17FB0710F4A0
:1005D0000217130710F41A8709878B859C858017EB :1005D000DA86C9862B853C854D8D5E8D24173507BF
:1005E000910710F41C870B87AD85BE85ED8DFE8DC0 :1005E00010F4DC86CB86B3990DC05A8151110CC032
:1005F000EA17FB0710F4DE86CD862F8538898D8DAE :1005F00089818F5F8983853008F01982A4DE91E0BC
:100600009E8D2817390710F4D88ACF86B3990DC06C :100600009A8304C01A8202C0A1E0AA83B981B230E1
:100610009CA191110CC0ABA1AF5FABA3A53008F0BA :1006100009F46FC018F5BB2309F44BC0B13009F0E1
:100620001BA29BDEB1E0BCA304C01CA202C0E1E09F :10062000A0C060E080E072DE8B819C8127DE8BE1E0
:10063000ECA3FBA1F23009F46DC020F5FF2309F40F :1006300091E051DEC10122DE8BE291E04CDE62E00E
:1006400048C0F13009F0A0C060E080E069DE8D8133 :1006400080E064DE8D819E8119DE8BE191E043DEE6
:100650009E811DDE81E191E048DE8F81988517DE65 :100650008F81988513DE83E291E059C0E981E33010
:1006600081E291E042DE62E080E05ADE89859A858F :1006600009F457C0E43009F07CC06D857E858F8524
:100670000EDE81E191E039DE8B859C8508DE89E123 :100670009889A5019401ADD03E8B2D8B61E080E27D
:1006800091E056C02BA1233009F454C0243009F066 :1006800045DE8D899E8960E17EE08FD09E8B8D8BCB
:100690007BC069897A898B899C89A5019401AFD037 :10069000862FC1DD80E130DE8D899E896CE370E0BC
:1006A000890161E080E23CDEC80160E17EE093D038 :1006A00084D08D8B862FB7DD80E126DE8D89B3DD8A
:1006B0008C01862FBCDD80E129DEC8016CE370E08F :1006B00058C060E080E02ADEC801E0DD8BE291E016
:1006C0008AD0182F862FB3DD80E120DE812FAFDDA9 :1006C0000ADEC601DBDD87E291E005DE62E080E064
:1006D0005BC060E080E024DEC101D9DD81E291E011 :1006D0001DDE8BA19CA1D2DD83E291E0FCDDC30194
:1006E00004DEC601D4DD8DE191E0FFDD62E080E053 :1006E000B201A501940175D0C901C8DD8FE191E087
:1006F00017DEC801CCDD89E191E0F7DD69817A81FF :1006F0000EC061E080E00ADE89859A85BFDD8BE16E
:100700008B819C81A50194017AD0C901C0DD85E16E :1007000091E0E9DD8B859C85B9DD87E291E0E3DD51
:1007100091E00EC061E080E003DE8D859E85B7DD4F :1007100028C060E080E1FADDC301B201A5019401C7
:1007200081E191E0E2DD8F859889B1DD8DE191E095 :1007200058D06B017C01C901A9DD8FE0E5DDC60170
:10073000DCDD2AC060E080E1F3DD69817A818B81B4 :1007300083DD8FE191E0CFDD62E080E1E7DD698973
:100740009C81A50194015BD06B017C01C9019FDDF7 :100740007A898B899C89A501940143D06B017C0136
:100750008FE0DCDDC6017CDD85E191E0C6DD62E095 :10075000C90194DD8FE0D0DDC6016EDD87E191E057
:1007600080E1DEDD6D897E898F89988DA5019401F8 :10076000D6CF83ED90E30197F1F700C00000CF8C66
:1007700046D06B017C01C9018ADD8FE0C7DDC6016F :10077000D8A0E9A0FAA094CEEE27FF27AA27BB278E
:1007800067DD8DE091E0D4CF3FEF80E792E031501C :1007800008C0A20FB31FE41FF51F220F331F441F21
:1007900080409040E1F700C000004F8C58A069A055 :10079000551F969587957795679598F37040A9F7BB
:1007A0007AA092CEEE27FF27AA27BB2708C0A20F68 :1007A000009799F7BD01CF010895AA1BBB1B51E12A
:1007B000B31FE41FF51F220F331F441F551F9695CB :1007B00007C0AA1FBB1FA617B70710F0A61BB70BD1
:1007C00087957795679598F37040A9F7009799F703 :1007C000881F991F5A95A9F780959095BC01CD0176
:1007D000BD01CF010895AA1BBB1B51E107C0AA1F91 :1007D0000895A1E21A2EAA1BBB1BFD010DC0AA1F82
:1007E000BB1FA617B70710F0A61BB70B881F991FD2 :1007E000BB1FEE1FFF1FA217B307E407F50720F09A
:1007F0005A95A9F780959095BC01CD010895A1E285 :1007F000A21BB30BE40BF50B661F771F881F991F15
:100800001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F8A :100800001A9469F760957095809590959B01AC015D
:10081000FF1FA217B307E407F50720F0A21BB30BD5 :0A081000BD01CF010895F894FFCF59
:10082000E40BF50B661F771F881F991F1A9469F751
:1008300060957095809590959B01AC01BD01CF01AD
:060840000895F894FFCFBB
:0A0846001027E80364000A00010017
:00000001FF :00000001FF