12. Output devices¶
- Add an output device to a microcontroller board you’ve designed, and program it to do something
- Measure the power consumption of an output device
I decided to choose the SSD1306 OLED display as it would be useful for displaying information for my final project. In addition, the display uses I2C communications, so I connected the SCL and SDA pins of the display to the corresponding pins on the ATTiny412 MCU. These pins are reserved for the faster hardware I2C support.
I utilized the same 1/64 Bantam Tools DRC file and autorouter to generate the traces.
However, Mr. Rudolph recommended some changes to the board design. He advised that I should have pull-up resistors on the SCL and SDA lines and the capacitor next to the VCC and ground of the MCU.
Here is the milled board:
I first soldered the board with the OLED display soldered directly to the SMD pads. For the pullup resistors, I searched online and found an answer that said that 4.99K resistors generally work, so I sued 4.99K resistors.
However, I realized that the SCL and SDA pads were flipped, so I was forced to solder on right angle pins to use jumper wire. In addition, I looked at Neil’s board design and saw that he used 10K pull-up resistors, so I switched out the 4.99K resistors for 10K resistors.
Programming was the most difficult part as I had no experience with interfacing through I2C. First, I tried Neil’s code, but Neil’s code was written for the ATTiny45s and it was incompatable with the new tinyAVR 1-series.
Next, I tried external libraries. The first library that I tried was the Adafruit SSD1306 library. However, the Adafruit library was too large to fit onto the memory of the ATTiny412.
The next library that I tried was the u8g2 library. The u8g2 library encountered the same size issues as the Adafruit library.
With little options left, I decided to bite the bullet and attempt to port Neil’s code over to the ATTiny412 MCU. I first tried to make changes in the register defines as the compiler complained about invalid register names. The changes did not cause the OLED display to display anything.
#define SCL_pin (1 << 2) #define SCL_pins PORTA.IN #define SCL_port PORTA.OUT #define SCL_direction PORTA.DIR #define SDA_pin (1 << 1) #define SDA_pins PORTA.IN #define SDA_port PORTA.OUT #define SDA_direction PORTA.DIR
Next, I replaced the macros used to manipulate the registers. The result did not change.
//ATTiny 412 Macros #define outputT412(bit_mask) PORTA.DIRSET |= bit_mask #define inputT412(bit_mask) PORTA.DIRCLR |= bit_mask #define setT412(bit_mask) VPORTA.OUT |= bit_mask #define clearT412(bit_mask) VPORTA.OUT &= (~bit_mask) #define pin_testT412(bit_mask) VPORTA.IN & bit_mask
Meanwhile, I realized that I had not changed the defined I2C address of the display. It had been set to
0x3C, but the display’s address was
0x78. However, this did not fix the issue.
//#define I2C_slave_address 0x3C //#define I2C_slave_address 0x3D #define I2C_slave_address 0x78 //#define I2C_slave_address 0x7A #define I2C_delay() _delay_us(5)
I began to surmise that Neil’s code was the software implementation of I2C since he directly manipulated the signals of the ports. Since I connected SCL and SDA to the hardware I2C pins, it made sense that I would have to utilise a special API for hardware I2C communications.
On the internet, I found an AVR Freaks thread where a forum member had shared several functions that utilised the TWI API.
Looking on the ATTiny412 datasheet, I realized that the TWI API was in fact the method to communicate over I2C.
I also realized that the
I2C_master_write() function was the main function to write bytes over the SDA and SCL ports. All other functions depended on the
I2C_master_write() function for sending display data, so I surmised that this function was the main culprit.
I decided to delete any old code relating to data writing, including the
I2C_master_write() function, and wrote the
TWI_master_write() function with the helper functions as the direct replacement. I replaced the logic and functions used within the
I2C_master_write() function with the equivalent functions from the forum member. In the
OLED128x64init() function, I changed the initialization function from
TWI_init(). Neil’s code to start I2C and send the slave address was replaced by a call to the
TWI_start() start that serves both purposes. Neil’s function to write individuals bits over the SDA and SCL lines was replaced with the equivalent
TWI_write() function. Neil’s code to stop the I2C transaction was replaced by the
After applying the changes, the screen finally displayed the Hello World code. Here is a video of the OLED in action:
This week will aid in the development of my final project because it allowed me to explore and sucessfully implement the I2C protocol on my boards. I am now more famaliar with master and slave communication which will enable me to create robust communications for other devices in my final projects. In addition, my experience with displaying text on the screen will help me to display information for my final project.