Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Edit one: Updated value place
- // Edit two: added new libs for UTFT control
- // Edit three (final): fixed the delay issue on the resgister
- // Snippet one
- #define RESAMPLES 255 // values taken from ADC before averaging
- #include <avr/io.h>
- #include <util/delay.h>
- #include <avr/eeprom.h>
- #include "TTM1637.h" // the library to drive a TM1637 display driver
- uint16_t V; // Voltage, obviously
- uint16_t C; // Current, obviously
- uint8_t Rv,ShowMode; // Voltage dividers, and a display mode variable
- /* this function is used to read and average ADC values*/
- uint16_t readADC(uint8_t chn)
- {
- uint16_t samples;
- uint32_t adcVal = 0;
- ADMUX = 0x40 | chn;
- ADCSRA |= (1 << ADSC);
- while (!ADIF) ;
- for (uint8_t i = 0; i < RESAMPLES; i++)
- {
- ADCSRA |= (1 << ADSC);
- while (!ADIF) ;
- samples = ADCL | (ADCH << 8);
- adcVal += samples;
- _delay_us(500);
- }
- return (adcVal / RESAMPLES);
- }
- /* this one just shows a number. the sprintf is so cumbersome*/
- void ShowValue(uint16_t Val)
- {
- TM_WritePos(0, Val / 10000, false);
- TM_WritePos(1, (Val % 10000) / 1000, true);
- TM_WritePos(2, (Val % 1000) / 100, false);
- TM_WritePos(3, (Val % 100) / 10, false);
- }
- /* hell goes here*/
- int main()
- {
- /* read the divider value from previous calibrations*/
- Rv = eeprom_read_byte(0);
- TM_Init(); // init the display driver IC
- PORTB |= (1 << PORTB4);
- ADMUX = (1<<REFS0);
- ADCSRA = (1 << ADEN) | 6;
- ADCSRA |= (1 << ADSC);
- /* check if the button is pressed in startup. if it is, calculated the divider value and save it to eeprom(it should be a byte!)*/
- if (!(PINB & (1 << PINB4)))
- {
- TM_SendData(0, 0x39);
- _delay_ms(2000);
- Rv = readADC(1) / 5;
- eeprom_write_byte(0, Rv);
- }
- while (1)
- {
- /* a big integer is still better than a floating point. so calculate milliVolts, not volts*/
- V = (readADC(1) * 1000L) / Rv;
- /* these values for current are experimental. based on 0.033 sense resistor I used*/
- C = (readADC(3) * 4827L) / (247L);
- /* check if the key is pressed and change display mode accordingly*/
- if (!(PINB & (1 << PINB4)))
- {
- TM_Clear();
- if(ShowMode < 2) ShowMode++;
- else ShowMode = 0;
- }
- if (ShowMode == 1)
- {
- ShowValue(V); // just show the voltage
- //TM_SendData(3, 0x1C);
- }
- else if (ShowMode == 2)
- {
- ShowValue(C); // just show the current
- //TM_SendData(3, 0x77);
- }
- else
- {
- // show both current and voltage
- TM_WritePos(0, V / 1000, true);
- TM_WritePos(1, (V % 1000) / 100, false);
- TM_WritePos(2, C / 1000, true);
- TM_WritePos(3, (C % 1000) / 100, false);
- }
- }
- }
- // Snippet two
- // Instructions
- #define TM_DATA_CMD 0x40
- #define TM_DISP_CTRL 0x80
- #define TM_ADDR_CMD 0xC0
- // Data command set
- #define TM_WRITE_DISP 0x00
- #define TM_READ_KEYS 0x02
- #define TM_FIXED_ADDR 0x04
- // Display control command
- #define TM_DISP_PWM_MASK 0x07 // First 3 bits are brightness (PWM controlled)
- #define TM_DISP_ENABLE 0x08
- #include "TTM1637.h"
- /* UNCOMMENT UNUSED CHARACTERS IF YOU WANT TO USE IT ON ANOTHER PROJECT*/
- const uint8_t FLASH_DIR TM_DIGITS[] =
- {
- 0x3F, // 0
- 0x06, // 1
- 0x5B, // 2
- 0x4F, // 3
- 0x66, // 4
- 0x6D, // 5
- 0x7D, // 6
- 0x07, // 7
- 0x7F, // 8
- 0x6F, // 9
- // 0x77, // A
- // 0x7C, // b
- // 0x39, // C
- // 0x5E, // d
- // 0x79, // E
- // 0x71, // F
- //
- //
- // 0x3D, // G
- // 0x76, // H
- // 0x06, // I
- // 0x1F, // J
- // 0x76, // K
- // 0x38, // L
- // 0x15, // M
- // 0x54, // n
- // 0x3F, // O
- // 0x73, // P
- // 0x67, // Q
- // 0x50, // r
- // 0x6D, // S
- // 0x78, // t
- // 0x3E, // U
- // 0x1C, // V
- // 0x2A, // W
- // 0x76, // X
- // 0x6E, // Y
- // 0x5B // Z
- };
- void TM_PortInit()
- {
- DIO_SETOUT;
- CLK_SETOUT;
- DIO_HI;
- CLK_HI;
- }
- void TM_Start()
- {
- TM_PortInit();
- DEL_US;
- DIO_LO;
- DEL_US;
- }
- void TM_Stop()
- {
- CLK_LO;
- DEL_US;
- CLK_HI;
- DIO_LO;
- DEL_US;
- DIO_HI;
- }
- void TM_Send(uint8_t b)
- {
- uint8_t i;
- // Clock data bits
- for ( i = 8; i; --i, b >>= 1)
- {
- CLK_LO;
- if (b & 1)
- DIO_HI;
- else
- DIO_LO;
- DEL_US;
- CLK_HI;
- DEL_US;
- }
- // Clock out ACK bit
- CLK_LO;
- DIO_LO;
- DEL_US;
- CLK_HI;
- DEL_US;
- }
- void TM_SendCMD(uint8_t cmd)
- {
- TM_Start();
- TM_Send(cmd);
- TM_Stop();
- }
- void TM_SendData(const uint8_t addr, const uint8_t dat)
- {
- TM_SendCMD(TM_DATA_CMD | TM_FIXED_ADDR);
- TM_Start();
- TM_Send(TM_ADDR_CMD | addr);
- TM_Send(dat);
- TM_Stop();
- DEL_US;
- }
- void TM_SetBrightness(const uint8_t brightness)
- {
- TM_SendCMD(TM_DISP_CTRL | TM_DISP_ENABLE | (brightness & TM_DISP_PWM_MASK));
- }
- void TM_Clear()
- {
- uint8_t a;
- for (a = 0; a != TM1637_DIGITS; ++a)
- TM_SendData(a, 0x00);
- }
- void TM_Init()
- {
- TM_PortInit();
- TM_SendCMD(TM_DATA_CMD | TM_WRITE_DISP);
- TM_SendCMD(TM_DISP_CTRL | TM_DISP_ENABLE | TM_DISP_PWM_MASK);
- TM_Clear();
- }
- void TM_WritePos(const uint8_t addr, const uint8_t dat, bool dot)
- {
- if(dat<36)
- TM_SendData(addr,TM_DIGITS[dat] | ((uint8_t)dot<<7));
- else if (dat>64 && dat<91)
- TM_SendData(addr,TM_DIGITS[dat-55] | ((uint8_t)dot<<7));
- else if (dat>96 && dat<123)
- TM_SendData(addr,TM_DIGITS[dat-87] | ((uint8_t)dot<<7));
- }
- void TM_Write(uint16_t dat)
- {
- TM_SendCMD(TM_DATA_CMD);
- TM_Start();
- TM_Send(TM_ADDR_CMD);
- TM_Send(TM_DIGITS[dat/1000]);
- TM_Send(TM_DIGITS[(dat%1000)/100]);
- TM_Send(TM_DIGITS[(dat%100)/10]);
- TM_Send(TM_DIGITS[dat%10]);
- TM_Stop();
- DEL_US;
- }
- void TM_WriteSTR(char const* str)
- {
- uint8_t i=0;
- while(str[i] != '\0')
- {
- TM_WritePos(i,str[i],false);
- i++;
- }
- }
- // Snippet three
- #ifndef __TTM1637_H__
- #define __TTM1637_H__
- // Enter MCU specific header files here: make sure they are "include guard"ed
- #include <avr/io.h>
- #include <util/delay.h>
- //=================================//
- // END OF MCU specific header inclusion
- //=================================//
- // Define the instruction which makes the pin output (e.g. DDRA |= (1<<5))
- // DON'T ADD ";" in the end.
- // CLK Corresponds to TM1637 clock and DIO corresponds to TM1637 DATA pin
- #define CLK_SETOUT DDRB |= (1<<PORTB0)
- #define DIO_SETOUT DDRB |= (1<<PORTB1)
- // Define the instruction which makes the pins High or Low (e.g. PORTA |= (1<<5))
- #define CLK_HI PORTB |= (1<<PORTB0)
- #define CLK_LO PORTB &= ~(1<<PORTB0)
- #define DIO_HI PORTB |= (1<<PORTB1)
- #define DIO_LO PORTB &= ~(1<<PORTB1)
- // Define a few microseconds delay. (e.g. _delay_ms(1))
- #define DEL_US //_delay_us(1)
- // Define an Special directive to store data on flash
- #define FLASH_DIR
- // Define the connected 7segments displays
- #define TM1637_DIGITS 4
- //=================================//
- // END OF USER DEFINITIONS
- //=================================//
- // initialize pins and other stuff
- void TM_Init();
- // set brightness from 0 to 7
- void TM_SetBrightness(const uint8_t brightness);
- // writes at the position in 7seg. from 0 to TM1637_DIGITS-1
- // also, you can write any character like 'A' or 'a' but not symbols like '+'
- void TM_WritePos(const uint8_t addr, const uint8_t dat, bool dot);
- void TM_SendData(const uint8_t addr, const uint8_t dat);
- // writes a number on screen. TM_Write(1942);
- void TM_Write(uint16_t dat);
- // writes an string on screen.TM_WriteSTR("Err");
- void TM_WriteSTR(char const* str);
- // obviously clears the display
- void TM_Clear();
- #endif
- /*
- There are a lot of so-called "USB doctor" modules which are used for monitoring currents flowing through a USB connection. They are used for cable or charger testing, quick charge detection and load current consumption measurements. They're cheap of course and someone may ask why should we build one? If you can do it better, then do it. So what's better about my design?
- Self-calibration: You don't need to know the exact resistor values used for voltage dividers. Just use a proper divider to map your desired voltage range into 0~1.1v range (1.1v is the internal V-ref of ATtiny13. The feature we needed it for). after that all you need to do is to connect the module to a precise 5v supply while holding the onboard button. the device starts up and shows "C" character on the display, calculates the divider values itself and saves them in the internal e2prom. So you just need to do it once.
- Various display options: Using the button, you can switch between current display, voltage display and both. It is possible to save the preferred display on e2prom for startup.
- A 1KB code challenge: Despite there are better options than using 2 chips (1 micro controller + 1 display driver), I like the discrete designs. I had to implement a display library too. The display driver is universal.
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement