前言:这个文章内容就是将及其牛的U8g2图形库移植到STM单片机上,这里主要用型号为STMF103C8T6的单片机演示,用于显示及其精美的UI,目前,GitHub上有着非常优秀的开源库,但是都不适合主流的0.96寸OLED,相信大部分的单片机入门都是使用着0.96寸OLED,所以在这里介绍一下U8g2和演示移植一下目前最为主流的0.96寸OLED的GUI图形库。后续还会发布利用U8g2图形库制作一个多级菜单教程。
这个是通过看很多篇CSDN大佬的文章和网上视频教学精减出来的教程,个人觉得写得特别好的就是这个大佬的文章,大家可以去看看:玩转U8g2,一篇就够
U8g2库开源网址:U8g2图形库
如果打不开网址的大佬们可以下载下面的链接!
图形库资源百度网盘链接:通过百度网盘分享的文件:00_u8g2-master.zip
链接:https://pan.baidu.com/s/1FhaTQFKZuthkbNYac0PWnA?pwd=arm6
提取码:arm6
--来自百度网盘超级会员V3的分享
有需要我个人整理的文件可以评论区留言:
所使硬件:STM32F103C8T6 0.96寸OLED
这里演示使用的时i2c2,对应的是
SCKL:PB10
SDA: PB11
对应接线可参考:
一、U8g2简介
1.1、U8g2是什么?
U8g2是一个功能强大的C++图形库,专为嵌入式系统而设计,是一个开源的图形库, 主要用来驱动小屏幕, 内部继承了I2C/SPI, 支持常用的小屏幕支持很多型号!
1.2、主要特点以及优势
主要特点:
1、支持多种屏幕:U8g2支持近200种单色屏,包括LCD、OLED等,这使得开发者能够轻松地在不同的显示屏上实现图形界面。
2、跨平台支持:除了Arduino平台外,U8g2还可以在其他嵌入式平台上使用,增强了其灵活性和可移植性。
3、多种字体支持:U8g2支持多种字体,包括中文、韩文、日文、梵文等,满足了不同项目的需求。
4、高效的内存管理:采用页缓冲策略,减少了对内存的需求,这在资源有限的嵌入式平台上尤为重要。
5、丰富的图形功能:提供了绘制文本、线条、形状等基本图形操作,以及自动滚动、页面缓冲区管理等高级特性。
优势:
1、U8g2库平台支持性好,基本上支持绝大部分Arduino与STM32开发板,也包含物联网比较常用的esp8266;
2、U8g2库显示控制器支持性好,基本上市面上的OLED都完美支持;
3、U8g2库 API函数众多,特别支持了中文,支持了不同字体,这是一个对于开发者俩说不小的福利;
4、U8g2 库移植简单,容易使用;
二、移植U8g2库准备
移植的前提,我们需要有一个正常编译无错误的MDK,hal库的话可以使用CubeMX生成,使用标准库的可以自己准备一个(江科大的或者野火的,等等。。。),这里我使用的是hal库。下面我就来使用CubeMX生成一个MDK工程!相信各位大佬在学习STM32的时候都有写过OLED的驱动代码或者有着自己的驱动代码,这里U8g2图形库有着自己的驱动,所以在生成一个MDK工程的时候就不用把自己的OLED驱动添加进去了!
2.1、CubeMX配置
1、RCC配置外部高速晶振——HSE;
2、SYS配置:Debug设置成Serial Wire;
3、I2C2配置;
在移植U8g2过程中我们要使用到us级别的延时,所以需要配置定时器!(详细后面会说)
4、TIM1配置:U8g2图形库需要us级延迟
5、时钟树配置:
6、生成工程:
提示:工程名字里面不要有文字
现在就已经得到了一个没有错误的MDK工程了;
2.2、资料下载
移植U8g2图像库需要准备好,U8g2的源码是在GitHub上开源的。
下载地址:https://github.com/olikraus/u8g2
如果打不开网址的大佬们可以下载下面的链接!
百度网盘:通过百度网盘分享的文件:00_u8g2-master.zip
链接:https://pan.baidu.com/s/1FhaTQFKZuthkbNYac0PWnA?pwd=arm6
提取码:arm6
--来自百度网盘超级会员V3的分享
需要我学习过程中整理好的资料,可以评论区留言,我邮箱发送过去个你。
三、移植U8g2
现在我们已经万事俱备只欠东风了。下载好了我们就得到了一个压缩包!
里面有很多东西,我们只关心里面的csrc文件,将其解压出来!里面有很多.C和头文件
3.1、精简csrc文件
去掉无用的驱动文件,这里我使用的是i2c驱动,所以只留下u8x8_d_ssd1306_128x64_noname
以u8x8_d开头的时驱动文件,只留下u8x8_d_ssd1306_128x64_noname即可!!!
3.2、将csrc文件加入工程中
根据自己的习惯将csrc文件加入到工程文件中,可以参考我的!
我将其加入到Drivers文件中,并命名为U8g2
我重建了一个组,专门存放U8g2库的文件,方便管理!
这个时候编译的话会有一堆错误,不用管,继续后面的操作即可!(不熟练的小白可以完全跟着我做)
3.3、在keil中精简u8g2_d_setup.c
C8T6内存不是很大,所以我们需要取舍一下,由于用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f函数中,只调用了u8g2_m_16_8_f这个函数,所以留下这个函数,其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足!!
打开u8g2_d_setup.c
只留下这个函数 注意:有很多非常相似的函数,所以一定要看准一点!
眼神不好的建议CTRL+F搜索,避免删错
/* 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);
}
/* end of generated code */
3.4、keil中精简u8g2_d_memory.c
打开精简u8g2_d_memory.c,删除全部函数,只留下u8g2_m_16_8_f函数。
函数原型:
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
}
/* end of generated code */
还是那句话,眼神不好的建议CTRL+F搜索,避免删错!
到这里,移植工作基本已经做完了!
这个时候编译就是0错误0警告!!!看到就心情大好了!
后面我们用到里面的函数只需要包含头文件"u8g2.h"即可使用!所以我们需要添加这个文件的路径到keil工程中!
3.5、添加路径
我们前面将文件丢入的位置是C:\Users\86157\Desktop\U8g2\05_hal_U8g2显示U8G2_logo\Drivers\U8g2 每一个人的位置不一样,自己看自己的!
复制路径添加到keil中
复制进去添加即可:
到这里我们就大功告成了!!!
四、简单使用U8g2图形库显示Logo
前面我们不是配置了定时器吗?为啥移植一个图形库要配置定时器呢?
因为里面用到了us级别的延时,hal库函数没有这种精度的延时函数,所以我们需要自己写!
4.1、编写us级别延时函数
#define DLY_TIM_Handle (&htim1) //定义
/* USER CODE BEGIN 1 */
void Tims_delay_us(uint16_t nus)
{
__HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0);
__HAL_TIM_ENABLE(DLY_TIM_Handle);
while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus)
{
}
__HAL_TIM_DISABLE(DLY_TIM_Handle);
}
/* USER CODE END 1 */
这里我把它放在了tim.c文件里面,你们可以根据自己的爱好放置!
4.2、驱动初始化,缓存区初始化
#define u8 unsigned char // ?unsigned char ????
#define MAX_LEN 128 //
#define OLED_ADDRESS 0x78 // oled
#define OLED_CMD 0x00 //
#define OLED_DATA 0x40 //
/* 填空初始化u8x8_byte_hw_i2c函数 */
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
static uint8_t buffer[128];
static uint8_t buf_idx;
uint8_t *data;
switch (msg)
{
case U8X8_MSG_BYTE_INIT:
{
/* add your custom code to init i2c subsystem */
MX_I2C2_Init(); //I2C初始化
}
break;
case U8X8_MSG_BYTE_START_TRANSFER:
{
buf_idx = 0;
}
break;
case U8X8_MSG_BYTE_SEND:
{
data = (uint8_t *)arg_ptr;
while (arg_int > 0)
{
buffer[buf_idx++] = *data;
data++;
arg_int--;
}
}
break;
case U8X8_MSG_BYTE_END_TRANSFER:
{
if (HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDRESS, buffer, buf_idx, 1000) != HAL_OK)
return 0;
}
break;
case U8X8_MSG_BYTE_SET_DC:
break;
default:
return 0;
}
return 1;
}
/* 填空初始化u8x8_gpio_and_delay函数 */
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
HAL_Delay(1);
break;
case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
Tims_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
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 u8g2Init(u8g2_t *u8g2)
{
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); // 初始化u8g2 结构体
u8g2_InitDisplay(u8g2); //对缓存进行初始化 // 初始化u8x8_gpio_and_delay函数
u8g2_SetPowerSave(u8g2, 0); //wake up 屏幕 // 初始化u8x8_byte_hw_i2c函数
u8g2_ClearBuffer(u8g2); //清除缓存区 //对驱动进行初始化
}
到这里使用U8g2图形库的编程前提工作就初始化好了!
4.3、使用U8g2图形库显示两个线
前面我们编写好了U8g2的驱动初始化函数,只需要在main函数里面使用即可:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_TIM1_Init();
/* USER CODE BEGIN */
//初始化U8G2
u8g2_t u8g2;
u8g2Init(&u8g2); //初始化
// draw(&u8g2); //显示U8G2logo
u8g2_DrawLine(&u8g2, 0,0, 127, 63);
u8g2_DrawLine(&u8g2, 127,0, 0, 63);//显示两个对角线
u8g2_SendBuffer(&u8g2);
u8g2_ClearBuffer(&u8g2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
显示效果:
4.4、使用U8g2图形库实例显示U8g2Logo
实例代码:
void draw(u8g2_t *u8g2)
{
u8g2_ClearBuffer(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");
u8g2_SendBuffer(u8g2);
HAL_Delay(1000);
}
main函数:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_TIM1_Init();
/* USER CODE BEGIN */
//初始化U8G2
u8g2_t u8g2;
u8g2Init(&u8g2); //初始化
draw(&u8g2); //显示U8G2logo
// u8g2_DrawLine(&u8g2, 0,0, 127, 63);
// u8g2_DrawLine(&u8g2, 127,0, 0, 63);//显示两个对角线
u8g2_SendBuffer(&u8g2);
u8g2_ClearBuffer(&u8g2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
显示效果:
五、总结
U8g2图形库可以说目前小尺寸OLED首选的GUI,其可以呈现出的图形远不止上述测试中的图形,更多的功能还需要读者朋友们自己去好好发掘。优秀GUI的移植可以达到大大缩短开发周期,优化UI界面等目的。感兴趣的读者朋友可以点波关注,感谢!!!
后面我会更新更多的实战使用教程,如果这个文章对各位大佬有帮助的话,可以点点赞!
六、关于spi协议U8g2移植
这个文章主要是移植i2c协议的U8g2库,因为OLED屏幕不仅可以支持i2c协议,还支持spi协议,其移植过程大同小异,只不过是删除的文件的不同,驱动初始化函数参数使用不同!后续有空会继续更新的!关于spi的U8g2移植可以参考官方示例!、
spi移植U8g2库官方示例:00_u8g2_template_stm32f103c8t6-mast...
链接:https://pan.baidu.com/s/1voi1xffxcQt8lcQt4Ipqfg?pwd=arm6
提取码:arm6
--来自百度网盘超级会员V3的分享