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)