文章目录[隐藏]
STM32移植U8g2库
MCU为STM32F1030C8T6最小系统,使用STM32CubeMX生成HAL库工程模板,采用硬件四线SPI连接0.96寸单色OLED(驱动芯片为SSD1306,128*64分辨率)
移植过程中参考了以下章,对博主的分享表示感谢。
参考文章
1.建立工程模板
建立工程模板主要对时钟,调试端口,IO端口,调试串口和SPI进行初始化,如下图1所示:
图1 F103C8T6初始化示意图
时钟,串口,调试接口和IO口初始化很简单,不再介绍。SPI初始化需要根据器件特性进行配置,根据查询到的OLED屏的资料,将SPI初始化为高位在前MSB,32分频后时钟频率为2.25MBit/s,由于只有一个SPI器件 ,所以CPOL选择Low,即为片选信号常有效,CPHA为2Edge,即第二个时钟上升沿进行采样。SSD1306 4线SPI通信时序如下图2所示:
图2 SSD1306 4线SPI通信时序
2.获取U8g2源码
从源码地址下载源码到本地,解压后将csrc文件夹下复制到MDK工程下面,在MDK工程中新建U8g2文件夹并将csrc文件夹下面的所有文件添加到文件夹下面。
3.修改源码
U8g2的源文件中,u8╳8_d_xxxxx.c类型的文件都是兼容的显示器驱动文件,我们只需要使用自己使用的显示器驱动文件即可,不用全部添加,所以需要将没有用到的显示器支持文件从MDK工程U8g2文件夹下Remove掉,只保留我们需要的u8╳8_d_ssd1306_128x64nomane.c文件即可。
由于我们移除了很多显示器兼容文件,所以在编译时会报错,在u8g2_d_setup.c中注释掉我们 不需要使用的构造函数即可,同时注释掉u8g2_d_memory.c中不使用的全局变量,以减小程序占用空间。
在u8g2_d_setup.c中我们仅保留以下函数:
- void u8g2_Setup_ssd1306_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
- void u8g2_Setup_ssd1306_128x64_noname_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
- void u8g2_Setup_ssd1306_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
- void u8g2_Setup_sh1106_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
- void u8g2_Setup_sh1106_128x64_noname_2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
- void u8g2_Setup_sh1106_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
前三个构造函数为0.96寸OLED的初始化构造函数,后三个为1.3寸OLED的初始化构造函数。可以只保留一种OLED的初始化函数,把另外三个也注释掉,我为了以后使用1.3寸OLED方便,全部保留。
u8g2_d_memory.c文件下只保留以下代码,其余代码全部注释掉,保留代码如下:
/* u8g2_d_memory.c */
/* generated code, codebuild, u8g2 project */
#include "u8g2.h"
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_2(uint8_t *page_cnt)
{
#ifdef U8G2_USE_DYNAMIC_ALLOC
*page_cnt = 2;
return 0;
#else
static uint8_t buf[256];
*page_cnt = 2;
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
}
4.添加想要显示代码
我的工程中我建立了OLED显示文件md_oled_driver.c用以在主函数中调用,在md_oled_driver.c的接口函数中添加stm32对底层的操作。代码如下:
/*******************************************************************************
* 文件名称:md_oled_driver.h
*
* 摘 要:STM32F1的OLED中间驱动层头文件
*
* 当前版本:V1.0
* 作 者:yang_xi
* 日 期:2022/01/20
* 编译环境:keil_5
* 历史信息:
*******************************************************************************/
#ifndef __MD_OLED_DRIVER_H
#define __MD_OLED_DRIVER_H
#include "stdlib.h"
#include "main.h"
#include "gpio.h"
#include "u8g2.h"
#include "u8x8.h"
//OLED模式设置
//0:4线串行模式
//1:并行8080模式
#define OLED_MODE 0
#define SIZE 16
#define XLevelL 0x00
#define XLevelH 0x00
#define Max_Column 128
#define Max_Row 64
#define Brightness 0xFF //亮度
#define X_WIDTH 128
#define Y_WIDTH 64
//-----------------OLED端口定义----------------
#define MD_OLED_RST_Clr() HAL_GPIO_WritePin(OLED_RES_GPIO_Port,OLED_RES_Pin,GPIO_PIN_RESET) //oled 复位端口操作
#define MD_OLED_RST_Set() HAL_GPIO_WritePin(OLED_RES_GPIO_Port,OLED_RES_Pin,GPIO_PIN_SET)
//OLED控制用函数
uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr);
uint8_t u8x8_stm32_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8,U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,U8X8_UNUSED void *arg_ptr) ;
void u8g2Init(u8g2_t *u8g2);
void draw(u8g2_t *u8g2);
#endif
/********************************* end_of_file **********************************/
/*******************************************************************************
* 文件名称:md_oled_driver.c
*
* 摘 要:STM32F1的OLED中间驱动层c文件
*
* 当前版本:V1.0
* 作 者:yang_xi
* 日 期:2022/01/20
* 编译环境:keil_5
* 历史信息:
*******************************************************************************/
//----------------------------------------------------------------
//GND 电源地
//VCC 接5V或3.3v电源
//D0 接PA5(SCL)
//D1 接PA7(SDA)
//RES 接PB0
//DC 接PB1
//CS 接PA4
//----------------------------------------------------------------
//******************************************************************************/
#include "md_oled_driver.h"
#include "stdlib.h"
#include "md_oled_font.h"
#include "spi.h"
#include "u8g2.h"
#include "u8x8.h"
uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_BYTE_SEND: /*通过SPI发送arg_int个字节数据*/
HAL_SPI_Transmit(&hspi1,(uint8_t *)arg_ptr,arg_int,200);
break;
case U8X8_MSG_BYTE_INIT: /*初始化函数*/
break;
case U8X8_MSG_BYTE_SET_DC: /*设置DC引脚,表明发送的是数据还是命令*/
HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,arg_int);
break;
case U8X8_MSG_BYTE_START_TRANSFER:
break;
case U8X8_MSG_BYTE_END_TRANSFER:
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_stm32_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8,
U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,
U8X8_UNUSED void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_GPIO_AND_DELAY_INIT: /*delay和GPIO的初始化,在main中已经初始化完成了*/
break;
case U8X8_MSG_DELAY_MILLI: /*延时函数*/
HAL_Delay(arg_int); //调用谁stm32系统延时函数
break;
case U8X8_MSG_GPIO_CS: /*片选信号*/ //由于只有一个SPI设备,所以片选信号在初始化时已经设置为为常有效
break;
case U8X8_MSG_GPIO_DC: /*设置DC引脚,表明发送的是数据还是命令*/
HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,arg_int);
break;
case U8X8_MSG_GPIO_RESET:
break;
}
return 1;
}
void u8g2Init(u8g2_t *u8g2)
{
/********************************************
U8G2_R0 //不旋转,不镜像
U8G2_R1 //旋转90度
U8G2_R2 //旋转180度
U8G2_R3 //旋转270度
U8G2_MIRROR //没有旋转,横向显示左右镜像
U8G2_MIRROR_VERTICAL //没有旋转,竖向显示镜像
********************************************/
// u8g2_Setup_sh1106_128x64_noname_2(u8g2, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay); // 初始化1.3寸OLED u8g2 结构体
u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay); // 初始化0.96寸OLED u8g2 结构体
u8g2_InitDisplay(u8g2); //初始化显示
u8g2_SetPowerSave(u8g2, 0); //开启显示
}
void draw(u8g2_t *u8g2)
{
u8g2_DrawLine(u8g2, 0, 0, 127, 0); //画直线
u8g2_DrawLine(u8g2, 0, 0, 0, 63);
u8g2_DrawLine(u8g2, 0, 63, 127, 63);
u8g2_DrawLine(u8g2, 127, 0, 127, 63);
u8g2_DrawLine(u8g2, 0, 0, 127, 63); //画直线
u8g2_DrawLine(u8g2, 0, 63, 127, 0);
u8g2_DrawPixel(u8g2, 115, 32); //画点
u8g2_DrawBox(u8g2,56,44,14,10); //画实心矩形
u8g2_DrawCircle(u8g2,62,13,10,U8G2_DRAW_ALL); //画空心矩形
u8g2_DrawFrame(u8g2,5,24,18,16); //画空心圆形
u8g2_SetFont(u8g2, u8g2_font_6x12_tf); //设定字体
u8g2_DrawStr(u8g2, 80,36,"YYDS"); //显示字符
}
/********************************* end_of_file **********************************/
在主函数中定义初始化结构体
u8g2_t u8g2; // 显示器初始化结构体
并调用显示器初始化函数完成初始化
MD_OLED_RST_Set(); //复位拉高
u8g2Init(&u8g2); //显示器调用初始化函数
在while(1)中进行显示
u8g2_FirstPage(&u8g2);
do
{
draw(&u8g2);
} while (u8g2_NextPage(&u8g2));
显示效果如下:
整个工程文件可以从以下链接下载
工程文件
版权声明:本文为CSDN博主「yang__xi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangxishigehaoren/article/details/122701811
暂无评论