赞
踩
MCU | STM32F103C8T6 |
---|---|
Module | 0.96 inch OLED with SSD1306 |
Library | Standard peripheral library |
Porting to new MCU platform · olikraus/u8g2 Wiki (github.com)
为了减小编译后的大小,我们需要删除或精简以下的文件
U8g2 的源码为了支持多种控制器(controller),包含了许多兼容性的代码。首先,类似 u8x8_d_xxx.c
命名的文件中包含 U8x8 的驱动兼容,文件名包括控制器的型号和屏幕分辨率,因此需要删除无用的文件,只保留当前控制器的文件。例如,本次使用的是 128x64 OLED with SSD1306 controller,那么只需要保留 u8x8_d_ssd1306_128x64_noname.c
文件,删除其它类似的文件即可。
u8g2_d_setup.c
删去其他函数,只保留 u8g2_Setup_ssd1306_i2c_128x64_noname_f()
根据 u8g2/doc/faq.txt at master · olikraus/u8g2 (github.com),我们可以知道 F 意为 full buffer mode,需要 RAM 大小 1024 bytes
u8g2_d_memory.c
u8g2_d_memory.c
文件也是同理,它需要根据 u8g2_d_setup.c
中的调用情况决定用到哪些函数。由于 u8g2_Setup_ssd1306_i2c_128x64_noname_f()
函数只用到 u8g2_m_16_8_f()
这一个函数,因此只需要保留它,其余函数全部删除即可。
还有一处必要的精简是字体文件 u8x8_fonts.c
和 u8g2_fonts.c
,尤其是 u8g2_fonts.c
,该文件提供了包括汉字在内的几万个文字的多种字体,仅源文件就有 30MB ,编译后占据的内存非常大。
字体类型的变量非常多,建议先复制一个备份后将所有变量删除,之后视情况再添加字体。字体变量的命名大致遵循以下规则:
<prefix> '_' <name> '_' <purpose> <charset>
其中:
<prefix>
前缀基本上以 u8g2 开头;<name>
字体名,其中可能包含字符大小<purpose>
含义如下表所示:名称 | 描述 |
---|---|
t | 透明字体形式 |
h | 所有字符等高 |
m | monospace 字体(等宽字体) |
8 | 每一个字符都是 8x8 大小的 |
<charset>
是字体支持的字符集,如下表所示:名称 | 描述 |
---|---|
f | 只包含单字节字符 |
r | 只包含 ASCII 范围为 32~127 的字符 |
u | 只包含 ASCII 范围为 32~95 的字符,即不包括小写英文 |
n | 只包含数字及一些特殊用途字符 |
… | 还包括许多自定义的字符集,例如有一些结尾带 gb2312 或 Chinese 的字体名就包括中文 |
一般建议只保留需要的字体即可。
官方提供了 GPIO and Delay callback 函数模板
uint8_t u8x8_gpio_and_delay_template(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch(msg) { case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 break; // can be used to setup pins case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second break; case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds break; case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds break; case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second break; case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us case U8X8_MSG_GPIO_D0: // D0 or SPI clock pin: Output level in arg_int //case U8X8_MSG_GPIO_SPI_CLOCK: break; case U8X8_MSG_GPIO_D1: // D1 or SPI data pin: Output level in arg_int //case U8X8_MSG_GPIO_SPI_DATA: break; case U8X8_MSG_GPIO_D2: // D2 pin: Output level in arg_int break; case U8X8_MSG_GPIO_D3: // D3 pin: Output level in arg_int break; case U8X8_MSG_GPIO_D4: // D4 pin: Output level in arg_int break; case U8X8_MSG_GPIO_D5: // D5 pin: Output level in arg_int break; case U8X8_MSG_GPIO_D6: // D6 pin: Output level in arg_int break; case U8X8_MSG_GPIO_D7: // D7 pin: Output level in arg_int break; case U8X8_MSG_GPIO_E: // E/WR pin: Output level in arg_int break; case U8X8_MSG_GPIO_CS: // CS (chip select) pin: Output level in arg_int break; case U8X8_MSG_GPIO_DC: // DC (data/cmd, A0, register select) pin: Output level in arg_int break; case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int break; case U8X8_MSG_GPIO_CS1: // CS1 (chip select) pin: Output level in arg_int break; case U8X8_MSG_GPIO_CS2: // CS2 (chip select) pin: Output level in arg_int break; case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin 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 break; // arg_int=1: Input dir with pullup high for I2C data pin case U8X8_MSG_GPIO_MENU_SELECT: u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0); break; case U8X8_MSG_GPIO_MENU_NEXT: u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0); break; case U8X8_MSG_GPIO_MENU_PREV: u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0); break; case U8X8_MSG_GPIO_MENU_HOME: u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0); break; default: u8x8_SetGPIOResult(u8x8, 1); // default return value break; } return 1; }
一个示例:
void HW_I2C_Init(void) { RCC_APB2PeriphClockCmd(I2C_RCC_APBx_GPIOx, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = SCL_Pin | SDA_Pin; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(I2C_GPIOx, &GPIO_InitStructure); RCC_APB1PeriphClockCmd(I2C_RCC_APBx_I2Cx, ENABLE); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed = 400000; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack = I2C_Ack_Disable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_Init(I2Cx, &I2C_InitStructure); I2C_Cmd(I2Cx, ENABLE); } uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch (msg) { case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 SW_I2C_Init(); break; // can be used to setup pins case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second Delay_ms(1); break; case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz Delay_us(1); // 1us = 500kHz, just for SW I2C break; // 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 == 0) { SW_MCU_SCL(Bit_RESET); } else { SW_MCU_SCL(Bit_SET); } 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 == 0) { SW_MCU_SDA(Bit_RESET); } else { SW_MCU_SDA(Bit_SET); } break; // arg_int=1: Input dir with pullup high for I2C data pin default: u8x8_SetGPIOResult(u8x8, 1); // default return value break; } return 1; }
使用
u8g2_t u8g2; // a structure which will contain all the data for one display
...
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器
这里需要调用之前保留的 u8g2_Setup_ssd1306_128x64_noname_f()
函数,该函数的4个参数,其含义为:
u8g2
:需要配置的 U8g2 结构体rotation
:配置屏幕是否要旋转,默认使用 U8G2_R0
即可byte_cb
:传输字节的方式,这里使用软件 I2C 驱动,因此使用 U8g2 提供的 u8x8_byte_sw_i2c()
函数。如果是硬件 I2C 的话,可以参照编写自己的函数gpio_and_delay_cb
:提供给软件模拟 I2C 的 GPIO 输出和延时,使用之前编写的配置函数 u8x8_gpio_and_delay()
使用硬件 I2C,则需要实现 u8x8_byte_hw_i2c()
,官方文档给的模板实现后无法正常工作。
在此我自己实现,并解决了问题。
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { uint8_t *data = (uint8_t *)arg_ptr; uint8_t data_length = arg_int; uint8_t retry = 0; switch (msg) { case U8X8_MSG_BYTE_INIT: /* add your custom code to init i2c subsystem */ HW_I2C_Init(); break; case U8X8_MSG_BYTE_START_TRANSFER: while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) && retry < 200) { retry++; } retry = 0; I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && retry < 200) { retry++; } retry = 0; I2C_Send7bitAddress(I2C1, u8x8_GetI2CAddress(u8x8), I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && retry < 200) { retry++; } break; case U8X8_MSG_BYTE_SEND: for (int i = 0; i < data_length; i++) { I2C_SendData(I2C1, data[i]); while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET) { } } break; case U8X8_MSG_BYTE_END_TRANSFER: I2C_GenerateSTOP(I2C1, ENABLE); break; default: return 0; } return 1; }
使用
u8g2_t u8g2; // a structure which will contain all the data for one display
...
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器
Reference:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。