准备材料
- 一个正常运行的keil5工程模版
- 下载U8g2的Stm32实例模版(下载链接)
stm32实例模版获取
- 点击上文超链接进入github,点击Code下的Download ZIP,下载u8g2的源码,如下图
2. 点击“WiKi”,向下滑动选择“Porting to new MCR platform”,向下滑动选择“stm32”,之后点击Code下的Download ZIP,下载工程示例。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/
3. 解压下载的“u8g2-master”文件夹,选择csrc文件夹,将其移动到keil工程下。(该文件夹为u8g2的c语言文件)
4. 去掉无用的驱动文件,只保留u8x8_d_ssd1306_128x64_noname。剩下的_d文件如下图
- 解压“u8g2_template_stm32f103c8t6-master”取出文件中的main.c文件备用
源码移植
- 将上述精简之后的csrc文件加入工程中,千万不要忘记在魔术棒加入路径。
- 精简U8g2_d_setup.c,只保留u8g2_Setup_ssd1306_i2c_128x64_noname_f函数(370行)。
- 精简U8g2_d_memory.c,只保留u8g2_m_16_8_f(61行)
4. 编译修改报错至0 error,这一步根据自己的编译器环境不同会有不同的报错,主流的报错有变量定义位置报错,和最后一行非空行警告。
编写初始化函数
oled管脚初始化代码
/*oled.c*/
#include "oled.h"
//SDA = PB7 结合自身硬件确定SDA管脚号
//SCL = PB6 结合自身硬件确定SCL管脚号
/*引脚配置*/
#define IIC_OLED_SDA_Pin GPIO_Pin_7
#define IIC_OLED_SDA_GPIO GPIOB
#define IIC_OLED_SDA_GPIO_CLK RCC_APB2Periph_GPIOB
#define IIC_OLED_SCL_Pin GPIO_Pin_6
#define IIC_OLED_SCL_GPIO GPIOB
#define IIC_OLED_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(IIC_OLED_SDA_GPIO_CLK, ENABLE);
RCC_APB2PeriphClockCmd(IIC_OLED_SCL_GPIO_CLK, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = IIC_OLED_SDA_Pin;
GPIO_Init(IIC_OLED_SDA_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = IIC_OLED_SCL_Pin;
GPIO_Init(IIC_OLED_SCL_GPIO, &GPIO_InitStructure);
}
void IIC_OLED_SDA_HIGH(void)
{
GPIO_SetBits(IIC_OLED_SDA_GPIO,IIC_OLED_SDA_Pin);
}
void IIC_OLED_SDA_LOW(void)
{
GPIO_ResetBits(IIC_OLED_SDA_GPIO,IIC_OLED_SDA_Pin);
}
void IIC_OLED_SCL_HIGH(void)
{
GPIO_SetBits(IIC_OLED_SCL_GPIO,IIC_OLED_SCL_Pin);
}
void IIC_OLED_SCL_LOW(void)
{
GPIO_ResetBits(IIC_OLED_SCL_GPIO,IIC_OLED_SCL_Pin);
}
/*oled.h*/
#ifndef __OLED_H
#define __OLED_H
#include "stm32f10x.h"
void OLED_I2C_Init(void);
void IIC_OLED_SDA_HIGH(void);
void IIC_OLED_SDA_LOW(void);
void IIC_OLED_SCL_HIGH(void);
void IIC_OLED_SCL_LOW(void);
#endif
编写主函数
主函数由官方示例中给的HAL库代码等效移植到库函数下。
打开准备文件中所准备的main.c函数,示例中
在131行,定义了结构体u8g2;
在156行,调用了setup中的函数,我们找到U8g2_d_setup.c文件调用(在上述精简函数中剩下的那一个);
在54行,有u8g2_Setup_ssd1306_i2c_128x64_noname_f的回调函数,需要将其转化为标准库下对应代码。
//定义结构体 131行
u8g2_t u8g2;
//调用setup中的函数 156行
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_3wire_sw_spi, u8g2_gpio_and_delay_stm32);
//&u8g2:定义的结构体参数
//U8G2_R0:屏幕方向
//u8x8_byte_3wire_sw_spi:屏幕与MCU通讯方式这里我们所用的为软件i2c需要将参数改为u8x8_byte_sw_i2c
//u8g2_gpio_and_delay_stm32:回调函数,函数定义在第54行
//回调函数
uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
{
switch(msg){
//Initialize SPI peripheral
case U8X8_MSG_GPIO_AND_DELAY_INIT:
/* HAL initialization contains all what we need so we can skip this part. */
break;
//Function which implements a delay, arg_int contains the amount of ms
case U8X8_MSG_DELAY_MILLI:
HAL_Delay(arg_int);
break;
//Function which delays 10us
case U8X8_MSG_DELAY_10MICRO:
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
//Function which delays 100ns
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
//Function to define the logic level of the clockline
case U8X8_MSG_GPIO_SPI_CLOCK:
if (arg_int) HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, RESET);
else HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, SET);
break;
//Function to define the logic level of the data line to the display
case U8X8_MSG_GPIO_SPI_DATA:
if (arg_int) HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, SET);
else HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, RESET);
break;
// Function to define the logic level of the CS line
case U8X8_MSG_GPIO_CS:
if (arg_int) HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, RESET);
else HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, SET);
break;
//Function to define the logic level of the Data/ Command line
case U8X8_MSG_GPIO_DC:
// if (arg_int) HAL_GPIO_WritePin(CD_LCD_PORT, CD_LCD_PIN, SET);
// else HAL_GPIO_WritePin(CD_LCD_PORT, CD_LCD_PIN, RESET);
break;
//Function to define the logic level of the RESET line
case U8X8_MSG_GPIO_RESET:
if (arg_int) HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, SET);
else HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, RESET);
break;
default:
return 0; //A message was received which is not implemented, return 0 to indicate an error
}
return 1; // command processed successfully.
}
更改主函数后的代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "EXTI_Key.h"
#include "U8g2.h"
uint8_t u8g2_gpio_and_delay_stm32(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_DELAY_MILLI:
Delay_ms(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
Delay_us(10);
break;
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
if (arg_int) IIC_OLED_SCL_HIGH();
else IIC_OLED_SCL_LOW();
break;
case U8X8_MSG_GPIO_I2C_DATA:
if (arg_int) IIC_OLED_SDA_HIGH();
else IIC_OLED_SDA_LOW();
break;
default:
return 0;
}
return 1;
}
int main(void)
{
u8g2_t u8g2;
OLED_I2C_Init();
LED_Init();
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
u8g2_SetPowerSave(&u8g2, 0); // wake up display
Delay_ms(1000);
u8g2_DrawLine(&u8g2, 0,0, 127, 63);//划线,前两位为起点的横纵坐标,后两位为终点的横纵坐标
u8g2_DrawLine(&u8g2, 127,0 , 0,63);
u8g2_SendBuffer(&u8g2);
while (1)
{
}
}
}
结语
至此u8g2对stm32的库函数移植就完成了,u8g2的部分主要函数用法会在下篇文章中总结给出,下面还会给出用于测试移植效果的主函数代码。小白入门学习总结,有任何意见欢迎+Q:1825912434讨论。
测试代码
//main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "EXTI_Key.h"
#include "U8g2.h"
void draw(u8g2_t *u8g2);
uint8_t u8g2_gpio_and_delay_stm32(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_DELAY_MILLI:
Delay_ms(arg_int);
break;
case U8X8_MSG_DELAY_10MICRO:
Delay_us(10);
break;
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
if (arg_int) IIC_OLED_SCL_HIGH();
else IIC_OLED_SCL_LOW();
break;
case U8X8_MSG_GPIO_I2C_DATA:
if (arg_int) IIC_OLED_SDA_HIGH();
else IIC_OLED_SDA_LOW();
break;
default:
return 0;
}
return 1;
}
int main(void)
{
u8g2_t u8g2;
OLED_I2C_Init();
LED_Init();
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
u8g2_SetPowerSave(&u8g2, 0); // wake up display
u8g2_ClearBuffer(&u8g2);
Delay_ms(1000);
u8g2_DrawLine(&u8g2, 0,0, 127, 63);//划线,前两位为起点的横纵坐标,后两位为终点的横纵坐标
u8g2_DrawLine(&u8g2, 127,0 , 0,63);
u8g2_ClearBuffer(&u8g2);
draw(&u8g2);
while (1)
{
}
}
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);
Delay_ms(1000);
}