Web Banner

Introduction

This is the second part of my ATTiny development. The goal of this project is to make a custom 7-segment display controlled through the I2C interface. The development will be broken down into several parts to show how I evolved the design. The display module will be controlled by an ATTiny85 and this will then drive the 7-segment displays using the 74HC595 8-bit shift register.

If you would like to see the video, please feel free to click the link below, otherwise just read on.

Before getting started, If you want more information on how to use the 74HC595 or how to program the ATTiny85 then I have sections dedicated to these tasks.

  • ATTiny Programmer Part 1
  • 74HC595 Overview
  • Loading the I2C Libraries

    The first step in using the ATTiny as an I2C device is to download the libraries. The default Arduino libraries will not work with the ATTiny product line. There are a few libraries, but I chose to use the TinyWireS library. If you click on the link below, it will take you to the git hub repository where you can download the latest files or you can download files I am using from the bottom of this page.

    https://github.com/nadavmatalon/TinyWireS

    If you are like me and don't know how to download the files from Github, On the git hub page, you should see a green button with "< > Code".If you click on this, a pull down menu will be displayed and at the bottom if an option to Download.zip file. Click this and the files will be downloaded.

    After you have downloaded the files, unzip them and copy them into the Arduino library folder. You should find this under your 'Documents/Arduino/Library/ on your computer.

    You are now ready to create a sketch for the ATTiny with I2C support.

    Creating the Sketch

    If you look at the .ino file below you can see that its a very simple process, the TinyWireS library does all of the hard work. If you look in the setup section you will see that we just need to initialize it and set a ISR method which gets called when data is recieved on the I2C address. I am using the I2C address 0x04, but will be making this settable in the next step.

    In the recieve section, all I do is take the bytes recieved and convert the data into serial data which I then clock on to the Shift-registers. Once all of the data has been processed I then clock the 'RCLK' line to latch the data onto the output.

    ATTiny85 .INO File

    1 : // Please see credits and usage for usiTwiSlave and TinyWireS in the .h files of 2 : // those libraries. 3 : 4 : #include <avr/sleep.h> 5 : #include <avr/wdt.h> 6 : #include "TinyWireS.h" 7 : 8 : uint8_t i2c_address = 0x04; 9 : 10 : // Pin 1 - Reserved for Reset 11 : uint8_t SER = 3; // Pin 2 - SER to 74HC595 12 : uint8_t SRCLK = 4; // Pin 3 - SRCLK to 74HC595 13 : // Pin 4 - Gnd 14 : // Pin 5 - SDA 15 : uint8_t RCLK = 1; // Pin 6 - RCLK to 74HC595 16 : // Pin 7 - SCL 17 : // Pin 8 - Vcc 18 : 19 : void setup() 20 : { 21 : pinMode(SER,OUTPUT); //Configure PB3 as output 22 : pinMode(RCLK,OUTPUT); //Configure PB1 as output 23 : pinMode(SRCLK,OUTPUT); //Configure PB4 as output 24 : 25 : 26 : //Clear any junk data from the Shift Registers 27 : digitalWrite(SER, 0); //Set SER line LOW 28 : digitalWrite(RCLK, 0); //Set RCLK line LOW 29 : digitalWrite(SRCLK, 0); //Set SRCLK line LOW 30 : 31 : for(int x = 0; x<(8*6);x++){ // For each settable bit in the Shift-registers 8-bits * 6 ICs 32 : digitalWrite(SRCLK, 1); // Clock the Serial Clock line High 33 : digitalWrite(SRCLK, 0); // Clock the Serial Clock line Low 34 : } 35 : digitalWrite(RCLK, 1); // Pulse the RLCK to clear any junk data on the output pins. 36 : digitalWrite(RCLK, 0); 37 : 38 : TinyWireS.begin(i2c_address); // Initiazlize the I2C Slave mode 39 : TinyWireS.onReceive(receiveEvent); // Register the onReceive() callback function 40 : } 41 : 42 : void loop() 43 : { 44 : } 45 : 46 : // Gets called when the ATtiny receives an i2c write slave request 47 : // This routine runs from the usiTwiSlave interrupt service routine (ISR) 48 : // so interrupts are disabled while it runs. 49 : void receiveEvent(uint8_t num_bytes) 50 : { 51 : uint8_t display_byte[16]; 52 : uint8_t master_bytes = num_bytes; 53 : 54 : for (uint8_t i = 0; i < master_bytes; i++){ // Process each byte of data from the master 55 : display_byte[i] = TinyWireS.receive(); 56 : for (uint8_t x = 0; x < 8; x++){ //Loop for each bit within data byte 57 : digitalWrite(SER, bitRead(display_byte[i], x)); //Set the SER pin based on the bit of data 58 : digitalWrite(SRCLK, 1); //Set SRCLK High 59 : digitalWrite(SRCLK, 0); //Set SRCLK Low 60 : } 61 : } 62 : digitalWrite(RCLK, 1); // Pulse the RLCK to data on the output pins. 63 : digitalWrite(RCLK, 0); 64 : } 65 : 66 : 67 :

    Upload this sketch using the Arduino ISP programmer and then place the ATTiny85 into the development board

    Detecting the ATTiny85 on the I2C bus

    When using anything on the I2C bus, the very first test should be using the included I2C scanner sketch in the Arduino Examples (Under Wire). If this does not detect the device, you have a probelm and need to resolve it before moving on. If you watch my video, you will see me forget to connect the Gnd wire to the ATTiny85 and later on I missed a Vcc connection to the 74HC595 ICs.

    Once you can detect the ATTiny85 as a device on the I2C bus, then you can try to communicate with the module.

    Driving the I2C Display Module

    Below is a simple sketch that I used to test the display module. I started by sending 6 bytes with 0xff, this made sure that I was trying to turing on all of the outputs. If I had missed any connections then they would not light up and I could trouble-shoot. I then played with the bit sequence to confirm which bit set which segment on the display.

    1 : #include <Wire.h> 2 : #include <Arduino.h> 3 : 4 : uint8_t i2c_address = 0x04; 5 : 6 : void setup() { 7 : Wire.begin(); 8 : display("121212"); 9 : } 10 : 11 : void loop() { 12 : } 13 : 14 : 15 : void display (String data){ 16 : 17 : Wire.beginTransmission(i2c_address); //Start the I2C Session 18 : 19 : for (int x=data.length() ; x>=0 ; x--){ //Process the char array one byte at a time 20 : char aByte=data.charAt(x); 21 : char value = 0; 22 : char dp = 0; 23 : 24 : if ((x+1)//Check if we are setting the decimal place 25 : if (data.charAt(x+1) == '.'){ 26 : dp = 0x01; 27 : } 28 : } 29 : 30 : switch (aByte){ //Send the correct bit sequence for each char 31 : case '-': // '-' sign 32 : value = 0x04|dp; // Add the deciamal place if set 33 : Wire.write(value); //Send the encoded value to the display 34 : break 35 : 36 : case '0': // '0' 37 : value = 0xfa|dp; // Add the deciamal place if set 38 : Wire.write(value); //Send the encoded value to the display 39 : break 40 : 41 : case '1': // '1' 42 : value = 0x60|dp; // Add the deciamal place if set 43 : Wire.write(value); //Send the encoded value to the display 44 : break 45 : 46 : case '2': // '2' 47 : value = 0xdc|dp; // Add the deciamal place if set 48 : Wire.write(value); //Send the encoded value to the display 49 : break 50 : 51 : case '3': // '3' 52 : value = 0xf4|dp; // Add the deciamal place if set 53 : Wire.write(value); //Send the encoded value to the display 54 : break 55 : 56 : case '4': // '4' 57 : value = 0x66|dp; // Add the deciamal place if set 58 : Wire.write(value); //Send the encoded value to the display 59 : break 60 : 61 : case '5': // '5' 62 : value = 0xb6|dp; // Add the deciamal place if set 63 : Wire.write(value); //Send the encoded value to the display 64 : break 65 : 66 : case '6': // '6' 67 : value = 0xbe|dp; // Add the deciamal place if set 68 : Wire.write(value); //Send the encoded value to the display 69 : break 70 : 71 : case '7': // '7' 72 : value = 0xe0|dp; // Add the deciamal place if set 73 : Wire.write(value); //Send the encoded value to the display 74 : break 75 : 76 : case '8': // '8' 77 : value = 0xff|dp; // Add the deciamal place if set 78 : Wire.write(value); //Send the encoded value to the display 79 : break 80 : 81 : case '9': // '9' 82 : value = 0xe6|dp; // Add the deciamal place if set 83 : Wire.write(value); //Send the encoded value to the display 84 : break 85 : 86 : case 'C': // 'C' 87 : value = 0x9a|dp; // Add the deciamal place if set 88 : Wire.write(value); //Send the encoded value to the display 89 : break 90 : 91 : case 'F': // 'F' 92 : value = 0x8e|dp; // Add the deciamal place if set 93 : Wire.write(value); //Send the encoded value to the display 94 : break 95 : 96 : case 'o': // 'o' 97 : value = 0x3c|dp; // Add the deciamal place if set 98 : Wire.write(value); //Send the encoded value to the display 99 : break 100 : } 101 : } 102 : Wire.endTransmission(); //End the transmission 103 : } 104 :

    After a few minutes messing around I had gotten it working. In the next part I add a command interface to allow me to set the I2C address of the dispay module.

    Download

    74HC595

    The 74HC595 8-bit Shift Register datasheet.

    Zip file containing source code

    TinyWireS Library

    The Library file I used for this project.

    Zip file containing source code

    ATTiny85 Project (2)

    The files shown on this page.

    Zip file containing source code