2018-04-03 17:00:04 +02:00
/** SSD1306 OLED library (code)
2019-12-04 23:15:25 +01:00
* @ file
2018-04-03 17:00:04 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-12-12 14:09:35 +01:00
* @ date 2018 - 2020
* @ note peripherals used : I2C @ ref i2c_master_i2c
2018-04-03 17:00:04 +02:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdbool.h> // boolean type
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
# include <libopencm3/stm32/i2c.h> // I2C library
/* own libraries */
# include "global.h" // global utilities
# include "oled_ssd1306.h" // OLED definitions
2019-12-05 13:28:07 +01:00
# include "i2c_master.h" // I²C header and definitions
2018-04-03 17:00:04 +02:00
2019-12-05 13:28:07 +01:00
/** SSD1306 OLED display I²C slave address */
2019-12-04 23:14:27 +01:00
static uint8_t oled_ssd1306_slave_addr = 0x3c ;
2018-04-03 17:00:04 +02:00
2019-12-04 23:14:27 +01:00
bool oled_ssd1306_setup ( uint8_t slave_addr )
2018-04-03 17:00:04 +02:00
{
2020-12-12 14:09:35 +01:00
if ( ! i2c_master_check_signals ( ) ) { // check if there are pull-ups to operator I²C
2018-04-03 17:00:04 +02:00
return false ;
}
2019-12-05 13:28:07 +01:00
oled_ssd1306_slave_addr = slave_addr ; // save I²C slave address of SSD1306
2020-12-12 14:09:35 +01:00
i2c_master_setup ( 400 ) ; // setup I²C bus (SSD1306 supports an I²C clock up to 400 kHz)
2018-04-03 17:00:04 +02:00
const uint8_t oled_init [ ] = {
0x00 , // control byte: continuous (multiple byes), command
0xae , // Set Display ON/OFF: OFF
// hardware configuration
0xa8 , 0x3f , // Set Multiplex Ratio: 64
0xd3 , 0x00 , // Set Display Offset: 0
0xa1 , // Set Segment Re-map: column address 0 is mapped to SEG127
0xc8 , // Set COM Output Scan Direction: normal mode (RESET) Scan from COM[N-1] to COM[0]
0xda , 0x12 , // Set COM Pins Hardware Configuration: Alternative COM pin configuration, Disable COM Left/Right remap
0x40 , // Set Display Start Line: start line register from 0
// fundamental commands
0x81 , 0xff , // Set Contrast Control: 256
0xa6 , // Set Normal/Inverse Display: Normal display (RESET)
// Timing & Driving Scheme Setting
0xd5 , 0xf0 , // Set Display Clock Divide Ratio/Oscillator Frequency: Divide ratio=129, F_OSC=1
0xd9 , 0x22 , // Set Pre-charge Period: Phase 1=2 DCLK, Phase 2=2DCLK
0xdb , 0x20 , // Set V_COMH Deselect Level: ~0.77xV_CC
// Charge Pump
0x8d , 0x14 , // Charge Pump Setting: Enable Charge Pump
// Addressing Setting
0x20 , 0x00 // Set Memory Addressing Mode: Horizontal Addressing Mode
} ; // command to initialize the display
2020-12-12 14:09:35 +01:00
return I2C_MASTER_RC_NONE = = i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_init , LENGTH ( oled_init ) ) ; // send command to initialize display
2018-04-03 17:00:04 +02:00
}
void oled_ssd1306_on ( void )
{
const uint8_t oled_display_on [ ] = {
0x80 , // control byte: no continuation, command
0xaf , // Set Display ON/OFF: ON
} ; // command to switch on display
2020-12-12 14:09:35 +01:00
i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_display_on , LENGTH ( oled_display_on ) ) ; // sent command to switch on display
2018-04-03 17:00:04 +02:00
}
void oled_ssd1306_off ( void )
{
const uint8_t oled_display_off [ ] = {
0x80 , // control byte: no continuation, command
0xae , // Set Display ON/OFF: OFF
} ; // command to switch off display
2020-12-12 14:09:35 +01:00
i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_display_off , LENGTH ( oled_display_off ) ) ; // sent command to switch font display
2018-04-03 17:00:04 +02:00
}
void oled_ssd1306_test ( void )
{
const uint8_t oled_entire_display_on [ ] = {
0x80 , // control byte: no continuation, command
0xa5 // Entire Display ON: Entire display ON Output ignores RAM content
} ; // command to set entire display on
2020-12-12 14:09:35 +01:00
i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_entire_display_on , LENGTH ( oled_entire_display_on ) ) ; // send command to switch entire display on
2018-04-03 17:00:04 +02:00
oled_ssd1306_on ( ) ; // set display on
sleep_ms ( 200 ) ; // let is on for a bit (enough for the user to see it is completely on
oled_ssd1306_off ( ) ; // set display off
const uint8_t oled_entire_display_ram [ ] = {
0x80 , // control byte: no continuation, command
0xa4 // Entire Display ON: Resume to RAM content display
} ; // command to display RAM
2020-12-12 14:09:35 +01:00
i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_entire_display_ram , LENGTH ( oled_entire_display_ram ) ) ; // send command to display RAM
2018-04-03 17:00:04 +02:00
}
void oled_ssd1306_display ( const uint8_t * display_data , uint16_t display_length )
{
// verify input
2019-12-03 23:00:58 +01:00
if ( 0 = = display_length | | NULL = = display_data ) {
2018-04-03 17:00:04 +02:00
return ;
}
const uint8_t oled_start_page [ ] = {
0x00 , // control byte: continuous (multiple byes), command
0xb0 , // Set Page Start Address for Page Addressing Mode: PAGE0
0x00 , // Set Lower Column Start Address for Page Addressing Mode: 0
0x10 // Set Higher Column Start Address for Page Addressing Mode: 0
} ; // command to set addressing mode
2020-12-12 14:09:35 +01:00
i2c_master_slave_write ( oled_ssd1306_slave_addr , false , oled_start_page , LENGTH ( oled_start_page ) ) ; // send command to set addressing mode
if ( I2C_MASTER_RC_NONE ! = i2c_master_start ( ) ) { // send start condition
2018-04-03 17:00:04 +02:00
return ;
}
2020-12-12 14:09:35 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_select_slave ( oled_ssd1306_slave_addr , false , true ) ) { // select OLED display
2018-04-03 17:00:04 +02:00
return ;
}
2020-02-27 19:43:14 +01:00
uint8_t oled_data [ display_length + 1 ] ; // we have to copy the data because we need to send a single block since i2c_master_write sends a stop
oled_data [ 0 ] = 0x40 ; // control byte: continuous (multiple byes), data
for ( uint16_t i = 0 ; i < display_length ; i + + ) {
oled_data [ i + 1 ] = display_data [ i ] ;
2018-04-03 17:00:04 +02:00
}
2020-12-12 14:09:35 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_write ( oled_data , display_length + 1 ) ) { // send data header
2018-04-03 17:00:04 +02:00
return ;
}
2020-12-12 14:09:35 +01:00
i2c_master_stop ( ) ; // send stop condition
2018-04-03 17:00:04 +02:00
}