Advertisement
Tempist

Self_CONTROL-USB_VOLTMETER

Jun 19th, 2019
264
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 0.48 KB | None | 0 0
  1. // Edit one: Updated value place
  2. // Edit two: added new libs for UTFT control
  3. // Edit three (final): fixed the delay issue on the resgister
  4.  
  5.  
  6. // Snippet one
  7. #define RESAMPLES   255   // values taken from ADC before averaging
  8.  
  9. #include <avr/io.h>
  10. #include <util/delay.h>
  11. #include <avr/eeprom.h>
  12. #include "TTM1637.h"    // the library to drive a TM1637 display driver
  13.  
  14. uint16_t V;             // Voltage, obviously
  15. uint16_t C;             // Current, obviously
  16. uint8_t Rv,ShowMode;    // Voltage dividers, and a display mode variable
  17.  
  18.  
  19. /* this function is used to read and average ADC values*/
  20. uint16_t readADC(uint8_t chn)
  21. {
  22.     uint16_t samples;
  23.     uint32_t adcVal = 0;
  24.     ADMUX = 0x40 | chn;
  25.     ADCSRA  |= (1 << ADSC);
  26.     while (!ADIF) ;
  27.     for (uint8_t i = 0; i < RESAMPLES; i++)
  28.     {
  29.         ADCSRA  |= (1 << ADSC);
  30.         while (!ADIF) ;
  31.         samples = ADCL | (ADCH << 8);
  32.         adcVal += samples;
  33.         _delay_us(500);
  34.     }
  35.     return (adcVal / RESAMPLES);
  36. }
  37.  
  38. /* this one just shows a number. the sprintf is so cumbersome*/
  39. void ShowValue(uint16_t Val)
  40. {
  41.     TM_WritePos(0, Val / 10000, false);
  42.     TM_WritePos(1, (Val % 10000) / 1000, true);
  43.     TM_WritePos(2, (Val % 1000) / 100, false);
  44.     TM_WritePos(3, (Val % 100) / 10, false);
  45. }
  46.  
  47. /* hell goes here*/
  48. int main()
  49. {
  50.   /* read the divider value from previous calibrations*/
  51.     Rv = eeprom_read_byte(0);
  52.    
  53.     TM_Init(); // init the display driver IC
  54.    
  55.     PORTB   |= (1 << PORTB4);
  56.     ADMUX   = (1<<REFS0);
  57.     ADCSRA  = (1 << ADEN) | 6;
  58.     ADCSRA  |= (1 << ADSC);
  59.  
  60.  
  61.   /* 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!)*/
  62.     if (!(PINB & (1 << PINB4)))
  63.     {
  64.         TM_SendData(0, 0x39);
  65.         _delay_ms(2000);
  66.         Rv = readADC(1) / 5;
  67.         eeprom_write_byte(0, Rv);
  68.     }
  69.    
  70.    
  71.     while (1)
  72.     {
  73.       /* a big integer is still better than a floating point. so calculate milliVolts, not volts*/
  74.         V = (readADC(1) * 1000L) / Rv;
  75.        
  76.         /* these values for current are experimental. based on 0.033 sense resistor I used*/
  77.         C = (readADC(3) * 4827L) / (247L);
  78.        
  79.         /* check if the key is pressed and change display mode accordingly*/
  80.         if (!(PINB & (1 << PINB4)))
  81.         {
  82.             TM_Clear();
  83.             if(ShowMode < 2)    ShowMode++;
  84.             else                ShowMode = 0;
  85.         }
  86.         if (ShowMode == 1)
  87.         {
  88.             ShowValue(V);   // just show the voltage
  89.             //TM_SendData(3, 0x1C);
  90.         }
  91.         else if (ShowMode == 2)
  92.         {
  93.             ShowValue(C);   // just show the current
  94.             //TM_SendData(3, 0x77);
  95.         }
  96.         else
  97.         {
  98.             // show both current and voltage
  99.             TM_WritePos(0, V / 1000, true);
  100.             TM_WritePos(1, (V % 1000) / 100, false);
  101.             TM_WritePos(2, C / 1000, true);
  102.             TM_WritePos(3, (C % 1000) / 100, false);
  103.         }
  104.     }
  105. }
  106.  
  107.  
  108. // Snippet two
  109. // Instructions
  110. #define TM_DATA_CMD             0x40
  111. #define TM_DISP_CTRL            0x80
  112. #define TM_ADDR_CMD             0xC0
  113.  
  114. // Data command set
  115. #define TM_WRITE_DISP           0x00
  116. #define TM_READ_KEYS            0x02
  117. #define TM_FIXED_ADDR           0x04
  118.  
  119. // Display control command
  120. #define TM_DISP_PWM_MASK        0x07 // First 3 bits are brightness (PWM controlled)
  121. #define TM_DISP_ENABLE          0x08
  122.  
  123.  
  124. #include "TTM1637.h"
  125.  
  126.  
  127. /* UNCOMMENT UNUSED CHARACTERS IF YOU WANT TO USE IT ON ANOTHER PROJECT*/
  128.  const uint8_t FLASH_DIR TM_DIGITS[] =
  129. {
  130.     0x3F, // 0
  131.     0x06, // 1
  132.     0x5B, // 2
  133.     0x4F, // 3
  134.     0x66, // 4
  135.     0x6D, // 5
  136.     0x7D, // 6
  137.     0x07, // 7
  138.     0x7F, // 8
  139.     0x6F, // 9
  140.  
  141. //    0x77, // A
  142. //    0x7C, // b
  143. //    0x39, // C
  144. //    0x5E, // d
  145. //    0x79, // E
  146. //    0x71, // F
  147. //
  148. //
  149. //    0x3D, // G
  150. //    0x76, // H
  151. //    0x06, // I
  152. //    0x1F, // J
  153. //    0x76, // K
  154. //    0x38, // L
  155. //    0x15, // M
  156. //    0x54, // n
  157. //    0x3F, // O
  158. //    0x73, // P
  159. //    0x67, // Q
  160. //    0x50, // r
  161. //    0x6D, // S
  162. //    0x78, // t
  163. //    0x3E, // U
  164. //    0x1C, // V
  165. //    0x2A, // W
  166. //    0x76, // X
  167. //    0x6E, // Y
  168. //    0x5B  // Z
  169. };
  170.  
  171.  
  172.  
  173.  
  174. void TM_PortInit()
  175. {
  176.   DIO_SETOUT;
  177.   CLK_SETOUT;
  178.   DIO_HI;
  179.   CLK_HI;
  180. }
  181.  
  182. void TM_Start()
  183. {
  184.   TM_PortInit();
  185.   DEL_US;
  186.   DIO_LO;
  187.   DEL_US;
  188. }
  189.  
  190. void TM_Stop()
  191. {
  192.   CLK_LO;
  193.   DEL_US;
  194.   CLK_HI;
  195.   DIO_LO;
  196.   DEL_US;
  197.   DIO_HI;
  198. }
  199.  
  200. void TM_Send(uint8_t b)
  201. {
  202.     uint8_t i;
  203.   // Clock data bits
  204.   for ( i = 8; i; --i, b >>= 1)
  205.   {
  206.     CLK_LO;
  207.     if (b & 1)
  208.       DIO_HI;
  209.     else
  210.       DIO_LO;
  211.     DEL_US;
  212.  
  213.     CLK_HI;
  214.     DEL_US;
  215.   }
  216.  
  217.   // Clock out ACK bit
  218.   CLK_LO;
  219.   DIO_LO;
  220.   DEL_US;
  221.  
  222.   CLK_HI;
  223.   DEL_US;
  224. }
  225.  
  226. void TM_SendCMD(uint8_t cmd)
  227. {
  228.   TM_Start();
  229.   TM_Send(cmd);
  230.   TM_Stop();
  231. }
  232.  
  233. void TM_SendData(const uint8_t addr, const uint8_t dat)
  234. {
  235.   TM_SendCMD(TM_DATA_CMD | TM_FIXED_ADDR);
  236.  
  237.   TM_Start();
  238.   TM_Send(TM_ADDR_CMD | addr);
  239.   TM_Send(dat);
  240.   TM_Stop();
  241.  
  242.   DEL_US;
  243. }
  244.  
  245. void TM_SetBrightness(const uint8_t brightness)
  246. {
  247.   TM_SendCMD(TM_DISP_CTRL | TM_DISP_ENABLE | (brightness & TM_DISP_PWM_MASK));
  248. }
  249.  
  250. void TM_Clear()
  251. {
  252.     uint8_t a;
  253.   for (a = 0; a != TM1637_DIGITS; ++a)
  254.     TM_SendData(a, 0x00);
  255. }
  256.  
  257. void TM_Init()
  258. {
  259.   TM_PortInit();
  260.   TM_SendCMD(TM_DATA_CMD | TM_WRITE_DISP);
  261.   TM_SendCMD(TM_DISP_CTRL | TM_DISP_ENABLE | TM_DISP_PWM_MASK);
  262.  
  263.   TM_Clear();
  264. }
  265.  
  266.  
  267.  
  268. void TM_WritePos(const uint8_t addr, const uint8_t dat, bool dot)
  269. {
  270.     if(dat<36)
  271.         TM_SendData(addr,TM_DIGITS[dat] | ((uint8_t)dot<<7));
  272.     else if (dat>64 && dat<91)
  273.         TM_SendData(addr,TM_DIGITS[dat-55] | ((uint8_t)dot<<7));
  274.     else if (dat>96 && dat<123)
  275.         TM_SendData(addr,TM_DIGITS[dat-87] | ((uint8_t)dot<<7));
  276. }
  277.  
  278. void TM_Write(uint16_t dat)
  279. {
  280.     TM_SendCMD(TM_DATA_CMD);
  281.  
  282.   TM_Start();
  283.   TM_Send(TM_ADDR_CMD);
  284.   TM_Send(TM_DIGITS[dat/1000]);
  285.     TM_Send(TM_DIGITS[(dat%1000)/100]);
  286.     TM_Send(TM_DIGITS[(dat%100)/10]);
  287.     TM_Send(TM_DIGITS[dat%10]);
  288.   TM_Stop();
  289.  
  290.   DEL_US;
  291. }
  292.  
  293. void TM_WriteSTR(char const* str)
  294. {
  295.     uint8_t i=0;
  296.    
  297.     while(str[i] != '\0')
  298.     {
  299.         TM_WritePos(i,str[i],false);
  300.         i++;
  301.     }
  302. }
  303.  
  304. // Snippet three
  305. #ifndef __TTM1637_H__
  306. #define __TTM1637_H__
  307.  
  308.  
  309. // Enter MCU specific header files here: make sure they are "include guard"ed
  310. #include <avr/io.h>
  311. #include <util/delay.h>
  312. //=================================//
  313. // END OF MCU specific header inclusion
  314. //=================================//
  315.  
  316. // Define the instruction which makes the pin output (e.g. DDRA |= (1<<5))
  317. // DON'T ADD ";" in the end.
  318. // CLK Corresponds to TM1637 clock and DIO corresponds to TM1637 DATA pin
  319. #define CLK_SETOUT      DDRB |= (1<<PORTB0)
  320. #define DIO_SETOUT      DDRB |= (1<<PORTB1)
  321.  
  322. // Define the instruction which makes the pins High or Low (e.g. PORTA |= (1<<5))
  323. #define CLK_HI              PORTB |= (1<<PORTB0)
  324. #define CLK_LO              PORTB &= ~(1<<PORTB0)
  325. #define DIO_HI              PORTB |= (1<<PORTB1)
  326. #define DIO_LO              PORTB &= ~(1<<PORTB1)
  327. // Define a few microseconds delay. (e.g. _delay_ms(1))
  328. #define DEL_US              //_delay_us(1)
  329. // Define an Special directive to store data on flash
  330. #define FLASH_DIR  
  331. // Define the connected 7segments displays
  332. #define TM1637_DIGITS   4
  333. //=================================//
  334. // END OF USER DEFINITIONS
  335. //=================================//
  336.  
  337.  
  338.  
  339. // initialize pins and other stuff
  340. void TM_Init();
  341. // set brightness from 0 to 7
  342. void TM_SetBrightness(const uint8_t brightness);
  343. // writes at the position in 7seg. from 0 to TM1637_DIGITS-1
  344. // also, you can write any character like 'A' or 'a' but not symbols like '+'
  345. void TM_WritePos(const uint8_t addr, const uint8_t dat, bool dot);
  346. void TM_SendData(const uint8_t addr, const uint8_t dat);
  347. // writes a number on screen. TM_Write(1942);
  348. void TM_Write(uint16_t dat);
  349. // writes an string on screen.TM_WriteSTR("Err");
  350. void TM_WriteSTR(char const* str);
  351. // obviously clears the display
  352. void TM_Clear();
  353.  
  354.  
  355. #endif
  356.  
  357. /*
  358. 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?
  359. 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.
  360. 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.
  361. 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.
  362. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement