作为一个单片机开发者,平时最喜欢的外设就是各种各样的屏幕。可是在使用单色屏时,网络上常见的驱动程序大多只是显示几个标准字符,相比之下,各种商业产品使用同样的屏幕,同样可以显示出多样的图形效果,让人艳羡不已。
u8g2作为一个适用于嵌入式设备的轻量级单色图形库,极大地解决了这个问题,它支持近 200 种单色屏,该显示库支持多种字体,具有完整的图形功能(直线、圆形、斜线、字符旋转、镜像反白、bitmap一应俱全),并具备很强的可移植性。u8g2开源项目在github上拥有1400+ Stars、1900+ Commits和众多的维护者。
进入正题,本节我们将在ufun开发板上使用u8g2驱动SSD1306 OLED显示屏,驱动方式为I2C。
在上一节工程的基础上,我们只需添加I2C驱动与u8g2相关文件即可。
第一步、github下载u8g2工程。
Github地址如下:
第二步、打开STM32CubeMX,配置工程
在上一节LED工程的基础上,我们添加I2C的驱动即可,其他配置保持不变。
I2C配置如下,为了保证高刷新率,我们将I2C速度置为Fast Mode:
接下来生成工程即可。
第三步、修改MDK工程
打开MDK工程目录,将u8g2源代码的csrc目录复制到MDK工程目录下,将其重命名为libu8g2。
接下来,在MDK中添加u8g2源文件。
并添加头文件路径。
第四步、增加移植代码
移植u8g2库,我们需要自己添加两个函数,一个是I2C底层驱动接口,另一个是延时函数接口。我们创建一个新的文件“u8x8_stm32_HAL.c”和“u8x8_stm32_HAL.h”来存放这两个接口函数。
实现代码如下:
u8x8_stm32_HAL.c
/* ****************************************************************************** * File Name : u8x8_stm32_HAL.c * Description : Porting Codes ****************************************************************************** * spezifische Anweisungen zw. u8g2/u8x8 und stm32 * Hal-Driver im Einsatz ****************************************************************************** */ #include "u8g2.h" #include "stm32f1xx_hal.h" #include "u8x8_stm32_HAL.h" uint8_t u8x8_stm32_gpio_and_delay_cb(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr) { switch(msg) // there is no need for any delay { case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 -> Make with HAL_init 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 break; case U8X8_MSG_GPIO_D1: // D1 or SPI data pin: Output level in arg_int 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 } return 1; } uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { uint8_t *ptr; static uint8_t buffer_count; static uint8_t buffer[DATA_BUFFER_SIZE+1]; //the size of buffer depends on how many pages are transfered at once //e.g. one page are 128byte and one byte more for command switch(msg) { case U8X8_MSG_BYTE_SEND: //collect { ptr = arg_ptr; for (int i=1; i <= arg_int; i++) { buffer[buffer_count] = *(ptr++); buffer_count++; } } break; case U8X8_MSG_BYTE_INIT: break; case U8X8_MSG_BYTE_SET_DC: break; case U8X8_MSG_BYTE_START_TRANSFER: buffer_count = 0; // start break; case U8X8_MSG_BYTE_END_TRANSFER: // send all at once HAL_I2C_Master_Transmit(&I2C_HANDLER, DEVICE_ADDRESS, (uint8_t *)buffer, buffer_count, I2C_TIMEOUT); break; default: return 0; } return 1; }
复制代码u8x8_stm32_HAL.h
#ifndef _U8X8_STM32_HAL_H#define _U8X8_STM32_HAL_H #include "u8g2.h" #include "stm32f1xx_hal.h" #define DATA_BUFFER_SIZE 128 //the size of buffer depends on how many pages are transfered at once e.g. one page are 128byte #define I2C_TIMEOUT 1000 #define DEVICE_ADDRESS 0x78 //device address is added #define I2C_HANDLER hi2c1 extern I2C_HandleTypeDef hi2c1; // use your i2c handler uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); uint8_t u8x8_stm32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); #endif
复制代码最后我们在main函数里添加初始化及示例代码。
最后编译烧写程序,OLED屏幕即可顺利驱动。
PS:完整工程将在最后一节开源。
【新UFUN试用体验】制作ufun开源小游戏机(一):LED指示灯
【新UFUN试用体验】制作ufun开源小游戏机(二):移植u8g2图形库驱动OLED屏
【新UFUN试用体验】制作ufun开源小游戏机(三):移植游戏,制作完成