2018-02-06 11:54:57 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** SSD1306 OLED library (code)
* @ file oled_ssd1306 . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2018
2018-03-21 23:20:05 +01:00
* @ note peripherals used : I2C @ ref oled_ssd1306_i2c
2018-02-06 11:54:57 +01:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdbool.h> // boolean type
# include <stdlib.h> // general utilities
2018-03-20 09:38:06 +01:00
/* STM32 (including CM3) libraries */
# include <libopencm3/stm32/i2c.h> // I2C library
2018-02-06 11:54:57 +01:00
/* own libraries */
# include "global.h" // global utilities
# include "oled_ssd1306.h" // OLED definitions
# include "i2c_master.h" // I2C header and definitions
/** SSD1306 OLED display I2C slave address */
# define OLED_SSD1306_SLAVE 0x3c
2018-03-20 09:38:06 +01:00
/** @defgroup oled_ssd1306_i2c I2C peripheral to communicate with the SSD1306 OLED
* @ {
*/
# define OLED_SSD1306_I2C I2C1 /**< I2C peripheral */
/** @} */
2018-02-06 11:54:57 +01:00
bool oled_ssd1306_setup ( void )
{
2018-04-02 20:38:59 +02:00
if ( ! i2c_master_check_signals ( OLED_SSD1306_I2C ) ) { // check if there are pull-ups to operator I2C
return false ;
}
2018-03-21 10:13:06 +01:00
i2c_master_setup ( OLED_SSD1306_I2C , 400 ) ; // setup I2C bus ( SSD1306 supports an I2C cleck up to 400 kHz)
2018-02-06 11:54:57 +01: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
2018-03-21 14:14:17 +01:00
return I2C_MASTER_RC_NONE = = i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_init , LENGTH ( oled_init ) ) ; // send command to initialize display
2018-02-06 11:54:57 +01: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
2018-03-21 10:13:06 +01:00
i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_display_on , LENGTH ( oled_display_on ) ) ; // sent command to switch on display
2018-02-06 11:54:57 +01: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
2018-03-21 10:13:06 +01:00
i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_display_off , LENGTH ( oled_display_off ) ) ; // sent command to switch onff display
2018-02-06 11:54:57 +01: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
2018-03-21 10:13:06 +01:00
i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_entire_display_on , LENGTH ( oled_entire_display_on ) ) ; // send command to switch entire display on
2018-02-06 11:54:57 +01: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
2018-03-21 10:13:06 +01:00
i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_entire_display_ram , LENGTH ( oled_entire_display_ram ) ) ; // send command to display RAM
2018-02-06 11:54:57 +01:00
}
void oled_ssd1306_display ( const uint8_t * display_data , uint16_t display_length )
{
// verify input
if ( 0 = = display_length | | NULL = = display_data ) {
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
2018-03-21 10:13:06 +01:00
i2c_master_slave_write ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , oled_start_page , LENGTH ( oled_start_page ) ) ; // send command to set addressing mode
2018-03-21 14:14:17 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_start ( OLED_SSD1306_I2C ) ) { // send start condition
2018-02-06 11:54:57 +01:00
return ;
}
2018-03-21 14:14:17 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_select_slave ( OLED_SSD1306_I2C , OLED_SSD1306_SLAVE , false , true ) ) { // select OLED display
2018-02-06 11:54:57 +01:00
return ;
}
const uint8_t oled_data [ ] = {
0x40 , // control byte: continuous (multiple byes), data
} ;
2018-03-21 14:14:17 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_write ( OLED_SSD1306_I2C , oled_data , LENGTH ( oled_data ) ) ) { // send data header
2018-02-06 11:54:57 +01:00
return ;
}
2018-03-21 14:14:17 +01:00
if ( I2C_MASTER_RC_NONE ! = i2c_master_write ( OLED_SSD1306_I2C , display_data , display_length ) ) { // send template picture to display
2018-02-06 11:54:57 +01:00
return ;
}
2018-03-20 09:38:06 +01:00
i2c_master_stop ( OLED_SSD1306_I2C ) ; // send stop condition
2018-02-06 11:54:57 +01:00
}