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.
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.
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.
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.
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
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.
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.