U8g2 software i2c porting to stm32

Posted by kevdotbadger on Wed, 05 Jan 2022 03:40:18 +0100

U8g2 software i2c porting to stm32


Using stm32f407, the software i2c drives the oled screen
Screen is 128 * 64, 0.96 inch monochrome oled screen, driver chip ssd1306
Development environment: VSCODE+EIDE
Development language: C/C + + mixed programming

Step 1: prepare data

U8g2 source code https://github.com/olikraus/u8g2 download
Download the extracted directory

We mainly want to the contents of the CSR folder
1. Add the folder to your project directory and change its name. I'll change it to U8g2_drv
2. Don't forget to add the folder to the include directory

The picture is incomplete. There are many more. Please pay attention to the following points:
Files starting with 1.mui do not need to be added
2.u8x8_d_*****.c is the driver file. Select what we want, u8x8_d_ssd1306_128x64_noname.c. Others can be added or not
3. Add others except those not added above c Documents
Where my_u8g2.c is used to store our own code, mainly driving content

Step 2 write driver:

Objective: PE0-SCL PE1-SDA software IIC

my_u8g2.c Documents

#include "my_u8g2.h"

//Macro definition, easy to change value
#define SCL_Pin GPIO_PIN_0
#define SDA_Pin GPIO_PIN_1
#define I2C_GPIO GPIOE

void I2C_init(void)
{
    GPIO_RCC_ENABLE(I2C_GPIO);//Functions defined elsewhere are used to turn on the incoming clock and do not release it
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = SCL_Pin|SDA_Pin;//Enable corresponding pin
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(I2C_GPIO, &GPIO_InitStruct);
}

//Build u8g2 required callback function
uint8_t STM32_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
  case U8X8_MSG_DELAY_100NANO:		// delay arg_int * 100 nano seconds
  __NOP();
  break;
  case U8X8_MSG_DELAY_10MICRO:		// delay arg_int * 10 micro seconds
  HAL_Delay(10);
  break;
  case U8X8_MSG_DELAY_MILLI:			// delay arg_int * 1 milli second
  HAL_Delay(1);
  break;
  case U8X8_MSG_DELAY_I2C:		    // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
    HAL_Delay_us(5);						// arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
  case U8X8_MSG_GPIO_I2C_CLOCK:		// arg_int=0: Output low at I2C clock pin
  if(arg_int == 1) 
  {
  HAL_GPIO_WritePin(I2C_GPIO, SCL_Pin, GPIO_PIN_SET);
  }
  else if(arg_int == 0)
  {
  HAL_GPIO_WritePin(I2C_GPIO, SCL_Pin, GPIO_PIN_RESET);  
  }          
  break;							// arg_int=1: Input dir with pullup high for I2C clock pin
  case U8X8_MSG_GPIO_I2C_DATA:		// arg_int=0: Output low at I2C data pin
  if(arg_int == 1) 
  {
  HAL_GPIO_WritePin(I2C_GPIO, SDA_Pin, GPIO_PIN_SET);
  }
  else if(arg_int == 0)
  {
  HAL_GPIO_WritePin(I2C_GPIO, SDA_Pin, GPIO_PIN_RESET);  
  }         
  break;							// arg_int=1: Input dir with pullup high for I2C data pin
  default:
  u8x8_SetGPIOResult(u8x8, 1);		// default return value
  break;
  }
  return 1;
}

my_u8g2.h file

#ifndef _MY_U8G2_H__
#define _MY_U8G2_H__

#include "my_sys.h"
#include "u8g2.h"

void I2C_init(void);
uint8_t STM32_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

#endif

Step 3: modify u8g2 source file

1. Modification u8g2_d_setup.c

Annotate all functions,
Keep the following two functions, and the others do not need to be changed

/* ssd1306 */
/* ssd1306 1 *///GXT
void u8g2_Setup_ssd1306_i2c_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_16_8_1(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

/* ssd1306 f *///GXT
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_16_8_f(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

2. Modification u8g2_d_memory.c

Annotate all functions, then compile, find the error information, see what function is missing, find it and uncomment it accordingly
Of course, now that I have found this chip, I'll give it to you directly. If your screen is different, you have to use what you lack
Also two functions

uint8_t *u8g2_m_16_8_1(uint8_t *page_cnt)
{
  #ifdef U8G2_USE_DYNAMIC_ALLOC
  *page_cnt = 1;
  return 0;
  #else
  static uint8_t buf[128];
  *page_cnt = 1;
  return buf;
  #endif
}

uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt)
{
  #ifdef U8G2_USE_DYNAMIC_ALLOC
  *page_cnt = 8;
  return 0;
  #else
  static uint8_t buf[1024];
  *page_cnt = 8;
  return buf;
  #endif
}

So far, the modification work has been completed, more than half of it has been completed, and the rest is relatively simple

Step 4: start writing main cpp

Why main CPP, because my environment is C/C + + mixed programming, which is main for you C is OK. The difference will be made clear

1. The introduction of C files into C + + needs to be like this, otherwise an error will be reported, indicating that the function cannot be found / information is lost, etc

//The C + + version reads like this
extern "C" //c + + file calls c file
{
	#include "u8g2.h"
	#include "my_u8g2.h" / / this file is written by ourselves
}

//The C version says this
#include "u8g2.h"
#include "my_u8g2.h" / / this file is written by ourselves

2. Add main function code

int main(void)
{
	.....//System initialization
	I2C_init();//Initialize I2C pin, our own function
	u8g2_t u8g2;//Define structure
	u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, STM32_gpio_and_delay);  // init u8g2 structure
	//Pay attention to the previous sentence STM32_gpio_and_delay is the function we define ourselves
	u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
	u8g2_SetPowerSave(&u8g2, 0); // wake up display
	//********Initialization complete***********//
	//*******Test code start*******//
	u8g2_ClearBuffer(&u8g2);
	u8g2_SendBuffer(&u8g2);//Clear screen
	u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);
	u8g2_DrawStr(&u8g2,30,50,"Test_code");
	u8g2_SendBuffer(&u8g2);
	u8g2_DrawCircle(&u8g2,64,32,10,U8G2_DRAW_ALL);//Draw a circle in the middle of the screen
	u8g2_SendBuffer(&u8g2);
}

Then you can compile and download

Effect picture

!!! Possible error reports (must see)

1.C + + class compatibility error (ignored using C)

1. There may be errors such as header file reference
2. It may be – cpp11 --c99 and other errors
EIDE solution in VSCODE:

##########################################################################################
#                        Append Compiler Options For Source Files
#
# syntax:
#   <your matcher expr>: <your compiler command>
#
# examples:
#   'main.cpp':           --cpp11 -Og ...
#   'src/*.c':            -gnu -O2 ...
#   'src/lib/**/*.cpp':   --cpp11 -Os ...
#   '!Application/*.c':   -O0
#   '**/*.c':             -O2 -gnu ...
#
# For more syntax, please refer to: https://www.npmjs.com/package/micromatch
#
##########################################################################################

version: '1.0'

#
# for source files with filesystem paths
#
files:
#   './test/**/*.c': --c99
     './MY-DRIVERS/**/*.c': --cpp11
     './MY-DRIVERS/**/*.cpp': --cpp11
     './Core/**/*.c': --cpp11
     './Core/**/*.cpp': --cpp11
#
# for source files with virtual paths
#
virtualPathFiles:
#   'virtual_folder/**/*.c': --c99

2. No space in execution regions

Check the cause first, and first note the above main In C
u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);
u8g2_DrawStr(&u8g2,30,50,"Test_code");
u8g2_SendBuffer(&u8g2);
Wait three sentences, mainly u8g2_SetFont function

If an error is reported, the code optimization level needs to be changed

keil version

In keil, change the actual measurement to level 1
If the optimization level is raised, unexpected optimization results may occur, especially the use of the system (non bare metal)

EIDE version

1. Open the builder option
2. Select C/C + + compiler
Select to change the code optimization level. After actual measurement, it can be changed to level 1 or - Ospace(for code size)
I finally chose - Ospace(for code size)

Topics: stm32 ARM