Editorial changes
parent
0e3dc83f54
commit
db6a40723b
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue