之前的文章,介紹過ESP8266在Arduino IDE環境中使用U8g2庫,實現OLED上的各種圖形顯示。
本篇,介紹一下U8g2庫如何移植到STM32上,進行OLED的圖形顯示。
本次的實驗硬件為:
STM32:型號為最常見的STM32F103C8T6
OLED:0.96寸OLED,IIC接口(如果是SPI接口,文中也有對應的修改介紹)
1 U8g2簡介
U8g2 是一個用于嵌入式設備的單色圖形庫。U8g2支持單色OLED和LCD,并支持如SSD1306等多種類型的OLED驅動。
U8g2源碼的開源庫地址:https://github.com/olikraus/u8g2
2 移植步驟
首先下載U8g2的源碼,因為STM32主要是使用C語言編程,所以只需關注源碼中的C源碼部分,即csrc文件夾下的文件。
2.1 精簡c源碼
U8g2支持多種顯示驅動的屏幕,因為源碼中也包含了各個驅動對應的文件,為了減小整個工程的代碼體積,在移植U8g2時,可以刪除一些無用的文件。
2.1.1 去掉無用的驅動文件
這些驅動文件通常是u8x8_d_xxx.c,xxx包括驅動的型號和屏幕分辨率。ssd1306驅動芯片的OLED,使用u8x8_ssd1306_128x64_noname.c這個文件,其它的屏幕驅動和分辨率的文件可以刪掉。
2.1.2 精簡u8g2_d_setup.c
由于我的OLED是IIC接口,只留一個本次要用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f就好(如果是SPI接口,需要使用u8g2_Setup_ssd1306_128x64_noname_f這個函數),其它的可以刪掉或注釋掉。
#include "u8g2.h"
/* ssd1306 f */
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);
}
注意,與這個函數看起來十分相似的函數的有:
u8g2_Setup_ssd1306_128x64_noname_1
u8g2_Setup_ssd1306_128x64_noname_2
u8g2_Setup_ssd1306_128x64_noname_f
u8g2_Setup_ssd1306_i2c_128x64_noname_1
u8g2_Setup_ssd1306_i2c_128x64_noname_2
u8g2_Setup_ssd1306_i2c_128x64_noname_f
其中,前面3個,是給SPI接口的OLED用的,函數最后的數字或字母,代表顯示時的buf大小:
1:128字節
2:256字節
f:1024字節
2.1.3 精簡u8g2_d_memory.c
由于用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f函數中,只調用了u8g2_m_16_8_f這個函數,所以留下這個函數,其它的函數一定要刪掉或注釋掉,否則編譯時很可能會提示內存不足!!!
#include "u8g2.h"
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
}
2.2 編寫移植函數
精簡源碼之后,還需要編寫如下的配置函數。
2.2.1 GPIO初始化
對OLED用到的IIC接口進行GPIO的初始化配置:
#define SCL_Pin GPIO_Pin_6
#define SDA_Pin GPIO_Pin_7
#define IIC_GPIO_Port GPIOB
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = SCL_Pin|SDA_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_GPIO_Port, &GPIO_InitStructure);
}
如果是SPI接口,則初始化對應的SPI接口即可。
2.2.2 u8x8_gpio_and_delay
這個函數也需要自己寫,主要的修改包括:
賦予U8g2相應的延時函數,比如下面的delay_ms和delay_us
為U8g2提供IIC接口的高低電平調用:
U8X8_MSG_GPIO_I2C_CLOCK:IIC的SCL
U8X8_MSG_GPIO_I2C_DATA:IIC的SDA
uint8_t u8x8_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
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
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(5);
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 == 1)
{
GPIO_SetBits(IIC_GPIO_Port, SCL_Pin);
}
else if(arg_int == 0)
{
GPIO_ResetBits(IIC_GPIO_Port, SCL_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
if(arg_int == 1)
{
GPIO_SetBits(IIC_GPIO_Port, SDA_Pin);
}
else if(arg_int == 0)
{
GPIO_ResetBits(IIC_GPIO_Port, SDA_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;
}
如果是SPI接口,可以參考如下寫法:
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_SPI_DATA:
lcd_sdin((uint8_t)arg_int); //SPI - MOSI
break;
case U8X8_MSG_GPIO_SPI_CLOCK: //SPI - CLK
lcd_sclk(arg_int);
break;
case U8X8_MSG_GPIO_AND_DELAY_INIT:
oled_init(); //OLED初始化
Delay(1);
break;
case U8X8_MSG_DELAY_MILLI:
Delay(arg_int); //延時
break;
case U8X8_MSG_GPIO_CS: //SPI - CS
lcd_cs((uint8_t)arg_int);
case U8X8_MSG_GPIO_DC:
lcd_dc((uint8_t)arg_int); //SPI - MISO
break;
case U8X8_MSG_GPIO_RESET:
break;
}
return 1;
}
可以看出,對于IIC與SPI接口,只有分別進行對應的配置即可。
2.2.3 u8g2Init
U8g2的初始化,需要調用下面這個u8g2_Setup_ssd1306_128x64_noname_f函數,該函數的4個參數含義:
u8g2:傳入的U8g2結構體
U8G2_R0:默認使用U8G2_R0即可(用于配置屏幕是否要旋轉)
u8x8_byte_sw_i2c:使用軟件IIC驅動,該函數由U8g2源碼提供
u8x8_gpio_and_delay:就是上面我們寫的配置函數
void u8g2Init(u8g2_t *u8g2)
{
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_ClearBuffer(u8g2);
}
2.2.4 顯示測試函數
使用U8g2提供的測試函數,用于查看顯示效果
void draw(u8g2_t *u8g2)
{
u8g2_SetFontMode(u8g2, 1); /*字體模式選擇*/
u8g2_SetFontDirection(u8g2, 0); /*字體方向選擇*/
u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字庫選擇*/
u8g2_DrawStr(u8g2, 0, 20, "U");
u8g2_SetFontDirection(u8g2, 1);
u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
u8g2_DrawStr(u8g2, 21,8,"8");
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
u8g2_DrawStr(u8g2, 51,30,"g");
u8g2_DrawStr(u8g2, 67,30,"xb2");
u8g2_DrawHLine(u8g2, 2, 35, 47);
u8g2_DrawHLine(u8g2, 3, 36, 47);
u8g2_DrawVLine(u8g2, 45, 32, 12);
u8g2_DrawVLine(u8g2, 46, 33, 12);
u8g2_SetFont(u8g2, u8g2_font_4x6_tr);
u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");
}
2.3 源碼加入到MDK編譯
在一個STM32的基礎例程上進行修改。
2.3.1添加u8g2源碼到工程
左側工程目錄添加U8g2源碼,然后再添加U8g2的頭文件搜尋目錄,如下:
2.3.2 主函數
主函數中,首先是IIC的初始化和U8g2的初始化,然后就可以測試U8g2的圖形顯示功能了:
#include "delay.h"
#include "sys.h"
#include "u8g2.h"
int main(void)
{
delay_init();
IIC_Init();
u8g2_t u8g2;
u8g2Init(&u8g2);
while(1)
{
u8g2_FirstPage(&u8g2);
do
{
draw(&u8g2);
} while (u8g2_NextPage(&u8g2));
}
}
3 測試效果
4 總結
本篇介紹了如何將U8g2圖形庫移植到STM32中,其中主要的修改包括:
精簡源碼中的u8g2_d_setup.c和u8g2_d_memory.c
OLED所用IIC接口的GPIO初始化
編寫u8x8_gpio_and_delay和u8g2Init
其中,u8g2_d_memory.c文件一定要去掉無用的函數,否則編譯時會提示內存不足;對于SPI接口的OLED,參考IIC接口進行類似的修改即可。
-
單片機
+關注
關注
6042文章
44617瀏覽量
637475 -
嵌入式
+關注
關注
5090文章
19173瀏覽量
306841 -
OLED
+關注
關注
119文章
6218瀏覽量
224595 -
STM32
+關注
關注
2270文章
10923瀏覽量
357032 -
u8g2
+關注
關注
0文章
13瀏覽量
1882
發布評論請先 登錄
相關推薦
評論