首先准备一个stm32的工程,和u8g2的源码

去gitee上拉取u8g2的代码

地址:https://gitcode.net/mirrors/olikraus/u8g2.git

 获取成功我们只需要关注里面csrc里面的文件

 将csrc里面的文件全部复制到自己的工程里面,

 然后打开keil,新建分组,添加源码,需要注意u8x8_d_开头的文件有很多,只需要留下来一个符合自己驱动的,我的是ssd1306OLED12864,所以我只需要添加u8x8_d_开头的u8x8_d_ssd1306_128x64_noname.c这一个文件,其它的不用添加。

注意这个文件上面的u8x8_capture.c跟下面的 u8x8_debounce.c 这之前的留一个自己屏幕的就可以了,然后添加头文件路径

 然后打开u8g2_d_setup.c这个文件,只留下u8g2_Setup_ssd1306_128x64_noname_f这个函数,其它的全都注销掉。

我是找到了这个文件复制了一份放在了前面。其它的全部宏定义注释掉了。 

接着追踪 u8g2_m_16_8_f这个函数,会跳转到u8g2_d_memory.c,跟上一步一样,提取出来u8g2_m_16_8_f函数,其他宏定义注销掉。

在main.c调用一下 #include "u8g2.h",变异看是否通过。

如果编译不通过,设置勾选c99标准,5个警告,追踪一下加一下回车就ok了 

 为了方便管理,我增加了oled.c 以及oled.h

main函数调用 U8g2_Init(); 剩下的全在oled.c里面处理,while(1) 500ms闪灯是为了确定程序在跑。

 先实现软件模拟spi的方式驱动OLED12864,,

由于要软件跟硬件都试一下,所以我选了spi1的引脚来接显示屏,这样软件硬件都可以驱动。

oled.h

#ifndef __OLED_H

#define __OLED_H

#include "stm32f10x.h"

#define OLED_SPIx SPI1

#define OLED_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_CLK RCC_APB2Periph_SPI1

//SCK引脚

#define OLED_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_SCK_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_SCK_PORT GPIOA

#define OLED_SPI_SCK_PIN GPIO_Pin_5

//MISO引脚

#define OLED_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_MISO_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_MISO_PORT GPIOA

#define OLED_SPI_MISO_PIN GPIO_Pin_6

//MOSI引脚

#define OLED_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_MOSI_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_MOSI_PORT GPIOA

#define OLED_SPI_MOSI_PIN GPIO_Pin_7

//CS(NSS)引脚 片选选普通GPIO即可//可以拉低,或者悬空

#define OLED_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_CS_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_CS_PORT GPIOA

#define OLED_SPI_CS_PIN GPIO_Pin_4

//RES引脚 片选选普通GPIO即可//可以接到系统复位引脚或者高电平

#define OLED_SPI_RES_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_RES_CLK RCC_APB2Periph_GPIOB

#define OLED_SPI_RES_PORT GPIOB

#define OLED_SPI_RES_PIN GPIO_Pin_0

//DC引脚 片选选普通GPIO即可

#define OLED_SPI_DC_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_DC_CLK RCC_APB2Periph_GPIOB

#define OLED_SPI_DC_PORT GPIOB

#define OLED_SPI_DC_PIN GPIO_Pin_1

#define OLED_CS_LOW() GPIO_ResetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN )

#define OLED_CS_HIGH() GPIO_SetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN )

#define OLED_RST_Clr() GPIO_ResetBits(OLED_SPI_RES_PORT, OLED_SPI_DC_PIN)//RES

#define OLED_RST_Set() GPIO_SetBits(OLED_SPI_RES_PORT, OLED_SPI_DC_PIN)

#define OLED_DC_Clr() GPIO_ResetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN)//DC

#define OLED_DC_Set() GPIO_SetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN)

void U8g2_Init(void);

#endif

下一步就是初始化IO

void sw_spi_gpio_init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

/* 使能SPI引脚相关的时钟 */

OLED_SPI_SCK_APBxClock_FUN (OLED_SPI_SCK_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_SCK_PIN; //引脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //频率(50M)

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_SCK_PORT, &GPIO_InitStructure);

OLED_SPI_MISO_APBxClock_FUN (OLED_SPI_MISO_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MISO_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_MISO_PORT, &GPIO_InitStructure);

OLED_SPI_MOSI_APBxClock_FUN (OLED_SPI_MOSI_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MOSI_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_MOSI_PORT, &GPIO_InitStructure);

OLED_SPI_CS_APBxClock_FUN (OLED_SPI_CS_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_CS_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_CS_PORT, &GPIO_InitStructure);

GPIO_ResetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN );

OLED_SPI_RES_APBxClock_FUN (OLED_SPI_RES_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_RES_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_RES_PORT, &GPIO_InitStructure);

GPIO_SetBits( OLED_SPI_RES_PORT, OLED_SPI_RES_PIN );

OLED_SPI_DC_APBxClock_FUN (OLED_SPI_DC_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_DC_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_DC_PORT, &GPIO_InitStructure);

}

oled初始化的时候需要调用之前留下来的一个函数

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)

我们需要实现

u8g2_gpio_and_delay_stm32

其它的官方已经实现了

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:

sw_spi_gpio_init();

break;

//Function which implements a delay, arg_int contains the amount of ms

case U8X8_MSG_DELAY_MILLI:

Delay_Ms(arg_int);

break;

//Function which delays 10us

case U8X8_MSG_DELAY_10MICRO:

Delay_Us(arg_int);

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) GPIO_SetBits(OLED_SPI_SCK_PORT, OLED_SPI_SCK_PIN);

else GPIO_ResetBits(OLED_SPI_SCK_PORT, OLED_SPI_SCK_PIN);

break;

//Function to define the logic level of the data line to the display

case U8X8_MSG_GPIO_SPI_DATA:

if(arg_int) GPIO_SetBits(OLED_SPI_MOSI_PORT, OLED_SPI_MOSI_PIN);

else GPIO_ResetBits(OLED_SPI_MOSI_PORT, OLED_SPI_MOSI_PIN);

break;

// Function to define the logic level of the CS line

case U8X8_MSG_GPIO_CS:

if(arg_int) GPIO_SetBits(OLED_SPI_CS_PORT ,OLED_SPI_CS_PIN);

else GPIO_ResetBits(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN);

break;

//Function to define the logic level of the Data/ Command line

case U8X8_MSG_GPIO_DC:

if(arg_int) GPIO_SetBits(OLED_SPI_DC_PORT,OLED_SPI_DC_PIN);

else GPIO_ResetBits(OLED_SPI_DC_PORT,OLED_SPI_DC_PIN);

break;

//Function to define the logic level of the RESET line

case U8X8_MSG_GPIO_RESET:

if(arg_int) GPIO_SetBits(OLED_SPI_RES_PORT,OLED_SPI_RES_PIN);

else GPIO_ResetBits(OLED_SPI_RES_PORT,OLED_SPI_RES_PIN);

break;

default:

return 0; //A message was received which is not implemented, return 0 to indicate an error

}

return 1; // command processed successfully.

}

void U8g2_Init(void)

{

u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8g2_gpio_and_delay_stm32);

u8g2_InitDisplay(&u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态

u8g2_SetPowerSave(&u8g2, 0); // 打开显示器

u8g2_DrawLine(&u8g2, 0, 0, 127, 63); //测试画线

u8g2_SendBuffer(&u8g2);

}

到此已经可以测试了,编译下载

 

当然仅仅软件spi怎么能够,还要试一下硬件spi

需要实现spi初始化,spi发送函数,实现u8x8_byte_3wire_hw_spi函数, 之前实现的u8g2_gpio_and_delay_stm32这个函数就直接返回1就可以了,

增加宏定义用来选择使用软件还是硬件

oled.c

#include "./oled/oled.h"

#include "./delay/delay.h"

#include "stm32f10x_spi.h"

#include "u8g2.h"

u8g2_t u8g2;

#if CONFIG_SW_SPI_U8G2

void sw_spi_gpio_init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

/* 使能SPI引脚相关的时钟 */

OLED_SPI_SCK_APBxClock_FUN (OLED_SPI_SCK_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_SCK_PIN; //引脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //频率(50M)

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_SCK_PORT, &GPIO_InitStructure);

OLED_SPI_MISO_APBxClock_FUN (OLED_SPI_MISO_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MISO_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_MISO_PORT, &GPIO_InitStructure);

OLED_SPI_MOSI_APBxClock_FUN (OLED_SPI_MOSI_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MOSI_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_MOSI_PORT, &GPIO_InitStructure);

OLED_SPI_CS_APBxClock_FUN (OLED_SPI_CS_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_CS_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_CS_PORT, &GPIO_InitStructure);

GPIO_ResetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN );

OLED_SPI_RES_APBxClock_FUN (OLED_SPI_RES_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_RES_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_RES_PORT, &GPIO_InitStructure);

GPIO_SetBits( OLED_SPI_RES_PORT, OLED_SPI_RES_PIN );

OLED_SPI_DC_APBxClock_FUN (OLED_SPI_DC_CLK, ENABLE );

GPIO_InitStructure.GPIO_Pin = OLED_SPI_DC_PIN; //输出类型(推挽式输出)

GPIO_Init(OLED_SPI_DC_PORT, &GPIO_InitStructure);

}

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:

sw_spi_gpio_init();

break;

//Function which implements a delay, arg_int contains the amount of ms

case U8X8_MSG_DELAY_MILLI:

Delay_Ms(arg_int);

break;

//Function which delays 10us

case U8X8_MSG_DELAY_10MICRO:

Delay_Us(arg_int);

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) GPIO_SetBits(OLED_SPI_SCK_PORT, OLED_SPI_SCK_PIN);

else GPIO_ResetBits(OLED_SPI_SCK_PORT, OLED_SPI_SCK_PIN);

break;

//Function to define the logic level of the data line to the display

case U8X8_MSG_GPIO_SPI_DATA:

if(arg_int) GPIO_SetBits(OLED_SPI_MOSI_PORT, OLED_SPI_MOSI_PIN);

else GPIO_ResetBits(OLED_SPI_MOSI_PORT, OLED_SPI_MOSI_PIN);

break;

// Function to define the logic level of the CS line

case U8X8_MSG_GPIO_CS:

if(arg_int) GPIO_SetBits(OLED_SPI_CS_PORT ,OLED_SPI_CS_PIN);

else GPIO_ResetBits(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN);

break;

//Function to define the logic level of the Data/ Command line

case U8X8_MSG_GPIO_DC:

if(arg_int) GPIO_SetBits(OLED_SPI_DC_PORT,OLED_SPI_DC_PIN);

else GPIO_ResetBits(OLED_SPI_DC_PORT,OLED_SPI_DC_PIN);

break;

//Function to define the logic level of the RESET line

case U8X8_MSG_GPIO_RESET:

if(arg_int) GPIO_SetBits(OLED_SPI_RES_PORT,OLED_SPI_RES_PIN);

else GPIO_ResetBits(OLED_SPI_RES_PORT,OLED_SPI_RES_PIN);

break;

default:

return 0; //A message was received which is not implemented, return 0 to indicate an error

}

return 1; // command processed successfully.

}

#endif

#if CONFIG_HW_SPI_U8G2

void SPI_OLED_Init(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

/* 使能SPI时钟 */

OLED_SPI_APBxClock_FUN ( OLED_SPI_CLK, ENABLE );

/* 使能SPI引脚相关的时钟 */

OLED_SPI_CS_APBxClock_FUN ( OLED_SPI_CS_CLK|OLED_SPI_SCK_CLK|OLED_SPI_MISO_PIN|OLED_SPI_MOSI_PIN, ENABLE );

/* 配置SPI的 CS引脚,普通IO即可 */

GPIO_InitStructure.GPIO_Pin = OLED_SPI_CS_PIN;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(OLED_SPI_CS_PORT, &GPIO_InitStructure);

GPIO_ResetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN );

/* 配置SPI的 SCK引脚*/

GPIO_InitStructure.GPIO_Pin = OLED_SPI_SCK_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(OLED_SPI_SCK_PORT, &GPIO_InitStructure);

/* 配置SPI的 MISO引脚*/

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MISO_PIN;

GPIO_Init(OLED_SPI_MISO_PORT, &GPIO_InitStructure);

/* 配置SPI的 MOSI引脚*/

GPIO_InitStructure.GPIO_Pin = OLED_SPI_MOSI_PIN;

GPIO_Init(OLED_SPI_MOSI_PORT, &GPIO_InitStructure);

OLED_SPI_DC_APBxClock_FUN(OLED_SPI_DC_CLK, ENABLE); //使能A端口时钟

GPIO_InitStructure.GPIO_Pin = OLED_SPI_RES_PIN|OLED_SPI_DC_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz

GPIO_Init(OLED_SPI_DC_PORT, &GPIO_InitStructure); //初始化GPIOD3,6

GPIO_SetBits(OLED_SPI_DC_PORT, OLED_SPI_RES_PIN|OLED_SPI_DC_PIN);

/* SPI 模式配置 */

// OLED芯片 支持SPI模式0及模式3,据此设置CPOL CPHA

SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 7;

SPI_Init(OLED_SPIx , &SPI_InitStructure);

/* 使能 SPI */

SPI_Cmd(OLED_SPIx , ENABLE);

}

/**

* @brief 使用SPI发送一个字节的数据

* @param byte:要发送的数据

*/

void SPI_OLED_SendByte(u8 byte)

{

/* 等待发送缓冲区为空,TXE事件 */

while (SPI_I2S_GetFlagStatus(OLED_SPIx , SPI_I2S_FLAG_TXE) == RESET);

/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */

SPI_I2S_SendData(OLED_SPIx , byte);

}

uint8_t u8x8_byte_3wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

{

uint8_t *p = (uint8_t*)arg_ptr;

switch (msg)

{

/*通过SPI发送arg_int个字节数据*/

case U8X8_MSG_BYTE_SEND:

for(int i = 0; i < arg_int; i++) SPI_OLED_SendByte((u8)*(p + i));

break;

/*设置DC引脚,DC引脚控制发送的是数据还是命令*/

case U8X8_MSG_BYTE_SET_DC:

if ( arg_int ) GPIO_SetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN);

else GPIO_ResetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN);

break;

/*初始化函数*/

case U8X8_MSG_BYTE_INIT:

SPI_OLED_Init();

break;

/* 下面功能无需定义 */

/*开始传输前会进行的操作,如果使用软件片选可以在这里进行控制*/

case U8X8_MSG_BYTE_START_TRANSFER:

break;

/*传输后进行的操作,如果使用软件片选可以在这里进行控制*/

case U8X8_MSG_BYTE_END_TRANSFER:

break;

default:

return 0;

}

return 1;

}

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)

{

return 1;

}

#endif

void U8g2_Init(void)

{

#if CONFIG_SW_SPI_U8G2

u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8g2_gpio_and_delay_stm32);

#endif

#if CONFIG_HW_SPI_U8G2

u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_3wire_hw_spi, u8g2_gpio_and_delay_stm32);

#endif

u8g2_InitDisplay(&u8g2); // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态

u8g2_SetPowerSave(&u8g2, 0); // 打开显示器

u8g2_DrawLine(&u8g2, 0, 0, 127, 63); //测试画线

u8g2_SendBuffer(&u8g2);

}

oled.h

#ifndef __OLED_H

#define __OLED_H

#include "stm32f10x.h"

/* 只能选择一个,注意端口号 */

#define CONFIG_SW_SPI_U8G2 0

#define CONFIG_HW_SPI_U8G2 1

//SPI选择

#define OLED_SPIx SPI1

#define OLED_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_CLK RCC_APB2Periph_SPI1

//SCK引脚

#define OLED_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_SCK_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_SCK_PORT GPIOA

#define OLED_SPI_SCK_PIN GPIO_Pin_5

//MISO引脚

#define OLED_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_MISO_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_MISO_PORT GPIOA

#define OLED_SPI_MISO_PIN GPIO_Pin_6

//MOSI引脚

#define OLED_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_MOSI_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_MOSI_PORT GPIOA

#define OLED_SPI_MOSI_PIN GPIO_Pin_7

//CS(NSS)引脚 片选选普通GPIO即可//可以拉低,或者悬空

#define OLED_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_CS_CLK RCC_APB2Periph_GPIOA

#define OLED_SPI_CS_PORT GPIOA

#define OLED_SPI_CS_PIN GPIO_Pin_4

//RES引脚 片选选普通GPIO即可//可以接到系统复位引脚或者高电平

#define OLED_SPI_RES_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_RES_CLK RCC_APB2Periph_GPIOB

#define OLED_SPI_RES_PORT GPIOB

#define OLED_SPI_RES_PIN GPIO_Pin_0

//DC引脚 片选选普通GPIO即可

#define OLED_SPI_DC_APBxClock_FUN RCC_APB2PeriphClockCmd

#define OLED_SPI_DC_CLK RCC_APB2Periph_GPIOB

#define OLED_SPI_DC_PORT GPIOB

#define OLED_SPI_DC_PIN GPIO_Pin_1

#define OLED_CS_LOW() GPIO_ResetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN )

#define OLED_CS_HIGH() GPIO_SetBits( OLED_SPI_CS_PORT, OLED_SPI_CS_PIN )

#define OLED_RST_Clr() GPIO_ResetBits(OLED_SPI_RES_PORT, OLED_SPI_DC_PIN)//RES

#define OLED_RST_Set() GPIO_SetBits(OLED_SPI_RES_PORT, OLED_SPI_DC_PIN)

#define OLED_DC_Clr() GPIO_ResetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN)//DC

#define OLED_DC_Set() GPIO_SetBits(OLED_SPI_DC_PORT, OLED_SPI_DC_PIN)

void U8g2_Init(void);

#endif

最后发现显示屏复位引脚接stm32复位引脚 或者拉高,而cs引脚拉低或者悬空也可以正常显示,哈哈,那样就只需要3跟线了。spi的两根(mosi clk),以及一根用来选择oled的命令或者数据的DC线就可以了。

最后我上传的代码屏蔽了cs 跟rest以及miso

注意注意注意   Example\Drivers\u8g2 里面有个压缩包,要解压到当前文件夹,不然少一个.c文件(那个文件太大了,上传不上去,就压缩了一下   不知道怎么上传     哈哈哈!)

https://github.com/hahahahahaxiaoming/stm32u8g2

2023-12-30 add

由于之前的c8t6板子已经找不到了,突然想玩一下oled了,所以追加下面内容

因为开发板的结构导致用硬件spi就需要用杜邦线,很难看还不方便,如果用软件spi,就可以直接把屏幕插在开发板的排针上,十分优雅。

如果用esp32开发板,硬件spi可以复用到任何一个io口上。 这里的这个软件spi是u8g2自带的,不推荐使用(速度超级慢),选择硬件spi,自己手动模拟spi的发送函数也要比这个快很多, 这里留着的原因是为了对比与硬件spi(自己模拟的)速度做对比。

这里是用的STM32CubeMX + Clion 的开发工具,所以就贴出来oled.c  oled.h文件,与上面之前搞的一样,可以移植上去。

oled.c

//

// Created by Administrator on 2023/12/30.

//

#include

#include

#include "oled.h"

#include "../Middlewares/u8g2/csrc/u8g2.h"

#include "debug.h"

#include "cmsis_os2.h"

#include "FreeRTOS.h"

static u8g2_t u8g2;

// 因为开发板的结构导致用硬件spi就需要用杜邦线,很难看还不方便,如果用软件spi,就可以直接把屏幕插在开发板的排针上,十分优雅

// 如果用esp32开发板,硬件spi可以复用到任何一个io口上,

// 这里的这个软件spi是u8g2自带的,不推荐使用(速度超级慢),选择硬件spi,自己手动模拟spi的发送函数也要比这个快很多,

// 这里留着的原因是为了对比与硬件spi(自己模拟的)速度做对比

#define CONFIG_SW_SPI_U8G2 (0)

#define CONFIG_HW_SPI_U8G2 (1)

#if CONFIG_SW_SPI_U8G2

static 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:

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:

break;

//Function which delays 100ns

case U8X8_MSG_DELAY_100NANO:

break;

//Function to define the logic level of the clockline

case U8X8_MSG_GPIO_SPI_CLOCK:

if (arg_int) OLED_CLK_Set();

else OLED_CLK_Clr();

break;

//Function to define the logic level of the data line to the display

case U8X8_MSG_GPIO_SPI_DATA:

if (arg_int) OLED_SDA_Set();

else OLED_SDA_Clr();

break;

// Function to define the logic level of the CS line

case U8X8_MSG_GPIO_CS:

if (arg_int) OLED_CS_Set();

else OLED_CS_Clr();

break;

//Function to define the logic level of the Data/ Command line

case U8X8_MSG_GPIO_DC:

if (arg_int) OLED_DC_Set();

else OLED_DC_Clr();

break;

//Function to define the logic level of the RESET line

case U8X8_MSG_GPIO_RESET:

if (arg_int) OLED_RST_Set();

else OLED_RST_Clr();

break;

default:

return 0; //A message was received which is not implemented, return 0 to indicate an error

}

return 1; // command processed successfully.

}

#endif

#if CONFIG_HW_SPI_U8G2

/*

* 函数名:uint8_t mySPI_ReadWriteByte(uint8_t TxData)

* 输入参数:TxData 待写入的数据

* 返回值:读取到的数据

* 函数作用:模拟SPI读写数据

*/

static uint8_t SpiReadWriteByte(uint8_t TxData)

{

uint8_t bit_ctr, RxData = 0x00;

for(bit_ctr=0;bit_ctr<8;bit_ctr++)

{

if(TxData & 0x80) OLED_SDA_Set();

else OLED_SDA_Clr();

TxData <<= 1;

OLED_CLK_Set();

// if(READ_MISO()) RxData |= (0x80 >> bit_ctr);//驱动屏幕没用到读取函数

OLED_CLK_Clr();

}

return(RxData);

}

static uint8_t u8x8_byte_3wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

{

uint8_t *p = (uint8_t*)arg_ptr;

switch (msg)

{

/*通过SPI发送arg_int个字节数据*/

case U8X8_MSG_BYTE_SEND:

OLED_CS_Clr();

for(int i = 0; i < arg_int; i++) SpiReadWriteByte((uint8_t)*(p + i));

OLED_CS_Set();

break;

/*设置DC引脚,DC引脚控制发送的是数据还是命令*/

case U8X8_MSG_BYTE_SET_DC:

if (arg_int) OLED_DC_Set();

else OLED_DC_Clr();

break;

/*初始化函数*/

case U8X8_MSG_BYTE_INIT:

break;

/* 下面功能无需定义 */

/*开始传输前会进行的操作,如果使用软件片选可以在这里进行控制*/

case U8X8_MSG_BYTE_START_TRANSFER:

break;

/*传输后进行的操作,如果使用软件片选可以在这里进行控制*/

case U8X8_MSG_BYTE_END_TRANSFER:

break;

default:

return 0;

}

return 1;

}

static 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)

{

return 1;

}

#endif

void U8G2_Init(void) {

#if CONFIG_SW_SPI_U8G2

u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8g2_gpio_and_delay_stm32);

#endif

#if CONFIG_HW_SPI_U8G2

u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_3wire_hw_spi, u8g2_gpio_and_delay_stm32);

#endif

u8g2_InitDisplay(&u8g2);

u8g2_SetPowerSave(&u8g2, 0);

u8g2_ClearDisplay(&u8g2);

u8g2_SetFont(&u8g2,u8g2_font_wqy12_t_chinese1);

// u8g2_DrawStr(&u8g2,0,16,"STM32 U8g2");

// u8g2_DrawUTF8(&u8g2,0,32,"中文测试");

// u8g2_SendBuffer(&u8g2);

}

//=================下面内容不重要===========================

typedef struct {

int firstshow; //第一行显示的位置

int nowmouse; //光标显示的位置

int nowindex; //索引

}MENU_DATA_STRUCT;

MENU_DATA_STRUCT menu1;

typedef struct

{

char* str;

}SETTING_LIST;

#define MAX_LINE (4)

#define LIST_LEN (9)

SETTING_LIST list[] =

{

{"11"},

{"222222"},

{"3333333333333333333"},

{"4444444"},

{"55555"},

{"6666666666"},

{"777777777"},

{"88888"},

{"99999"},

};

void ui_run(int *a, int *a_trg)

{

if(*a == *a_trg) return;

if(*a == *a_trg - 1) *a = *a_trg;

*a = (*a + *a_trg)>>1;

}

/* u8g2_cursor:光标移动函数

* x: 起始位置横坐标

* y:起始位置纵坐标

* s: 要显示的字符串

* return false 没有运行结束,还需要接着运行

* return true 运行结束

* */

bool u8g2_cursor(int x, int y, char * s)

{

static int y_m = -16;

static int w_m = 0;

int w = u8g2_GetStrWidth(&u8g2, s);

int h = u8g2_GetAscent(&u8g2)-u8g2_GetDescent(&u8g2);

ui_run(&y_m, &y);

ui_run(&w_m, &w);

u8g2_SetFontPosBaseline(&u8g2);

u8g2_DrawRFrame(&u8g2, x-2, y_m-u8g2_GetAscent(&u8g2)-2, w_m+4, h+3, 3);

return (w_m == w && y_m == y);

}

/*

* u8g2_content 主界面移动

* return false 没有运行结束,还需要接着运行

* return true 运行结束

* */

bool u8g2_content(int x, int index)

{

static int y_m = 15;

int y = (index+1)*15;

// DBG("%d --> %d\n", y_m, y);

ui_run(&y_m, &y);

for(int i = 0 ;i < LIST_LEN; i++)

{

u8g2_DrawStr(&u8g2, x, y_m+i*15, list[i].str);

}

return (y_m == y);

}

/*

* return false 没有运行结束,还需要接着运行

* return true 运行结束

* */

bool ui_show(uint16_t key)

{

bool ret = false;

bool ret1 = false;

switch(key)

{

case 0: break;

case KEY1_Pin:

if(menu1.nowindex < LIST_LEN-1)menu1.nowindex++;

if(menu1.nowmouse < MAX_LINE-1){

menu1.nowmouse++;

} else {

if (menu1.firstshow < LIST_LEN - MAX_LINE)menu1.firstshow++;

}

break;

case KEY2_Pin:

if(menu1.nowindex > 0)menu1.nowindex--;

if(menu1.nowmouse > 0){

menu1.nowmouse--;

} else {

if (menu1.firstshow > 0)menu1.firstshow--;

}

break;

default: break;

}

// DBG("Index = %d first = %d Mouse = %d\n", menu1.nowindex, menu1.firstshow, menu1.nowmouse);

u8g2_ClearBuffer(&u8g2); // 清除内部缓冲区

ret = u8g2_content(3, 0-menu1.firstshow);

ret1 = u8g2_cursor(3, 15*(menu1.nowmouse+1), list[menu1.nowindex].str);

u8g2_SendBuffer(&u8g2); // 发送内部缓冲区

return (ret && ret1);

}

//屏幕像素向右边移动1列

void u8g2_right_move(void)

{

uint8_t *p = u8g2_GetBufferPtr(&u8g2);

//这里只能倒序移动,先把126移动到127 再把125移动到126 >>>>

//移动到 1移动到2 的时候就要把0给清楚

// 移动1-127

for(int i = 1; i < u8g2.pixel_buf_width; i++)

{

//屏幕是128 * 64, 8个按一次 所以得移动8次

for(int j = 1; j <= u8g2.tile_buf_height; j++)

{

*(p + (u8g2.pixel_buf_width) * j - i) = *(p + (u8g2.pixel_buf_width) * j - i - 1);

}

}

//处理最后的0

for(int j = 0; j < u8g2.tile_buf_height; j++)

{

*(p + (u8g2.pixel_buf_width) * j) = 0x00;

}

u8g2_SendBuffer(&u8g2); // 发送内部缓冲区

}

//m控制周期,h控制幅度,绘制正弦波

static void grap(double m, int h)

{

static double i = 0;

static int y = 31, y1 = 31;

i+=m;

y = (int)(sin(i) * h + 31);

u8g2_DrawLine(&u8g2, 0, y, 0, y1);

u8g2_right_move();

y1 = y;

}

void ui_test(uint16_t key)

{

#define NUMBER 2 //1 采集按键, 2 正弦波

#if (NUMBER == 1)

/* 按KEY1 波形上下动*/

static int y = 63, y1 = 63;

if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)

{

y = 31-15;

}

else

{

y = 31+15;

}

u8g2_DrawLine(&u8g2, 0, y, 0, y1);

u8g2_right_move();

y1 = y;

#endif

#if (NUMBER == 2)

static double m = 0.1;//控制周期

static int h = 32;//控制幅度

switch(key)

{

case KEY1_Pin:

m += 0.02;

break;

case KEY2_Pin:

m -= 0.02;

break;

case KEY3_Pin:

if(h < 32) h += 4;

break;

case KEY4_Pin:

if(h > 0) h -= 4;

break;

default: break;

}

grap(m, h);

#endif

}

oled.h

//

// Created by Administrator on 2023/12/30.

//

#ifndef F103VET6_U8G2_OLED_H

#define F103VET6_U8G2_OLED_H

#include

#include "main.h"

#include "stm32f1xx_hal_gpio.h"

#if 0

#define OLED_CLK_Clr() HAL_GPIO_WritePin(OLED_SCL_GPIO_Port, OLED_SCL_Pin, GPIO_PIN_RESET) //SCL(D1)

#define OLED_CLK_Set() HAL_GPIO_WritePin(OLED_SCL_GPIO_Port, OLED_SCL_Pin, GPIO_PIN_SET)

#define OLED_SDA_Clr() HAL_GPIO_WritePin(OLED_SDA_GPIO_Port, OLED_SDA_Pin, GPIO_PIN_RESET)//DIN(D0)

#define OLED_SDA_Set() HAL_GPIO_WritePin(OLED_SDA_GPIO_Port, OLED_SDA_Pin, GPIO_PIN_SET)

#define OLED_RST_Clr() HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_RESET) //RES

#define OLED_RST_Set() HAL_GPIO_WritePin(OLED_RES_GPIO_Port, OLED_RES_Pin, GPIO_PIN_SET)

#define OLED_DC_Clr() HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, GPIO_PIN_RESET) //DC

#define OLED_DC_Set() HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, GPIO_PIN_SET)

#define OLED_CS_Clr() HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_RESET) //CS

#define OLED_CS_Set() HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_SET)

#else

#define OLED_CLK_Clr() OLED_SCL_GPIO_Port->BSRR = (uint32_t)OLED_SCL_Pin << 16u //SCL(D1)

#define OLED_CLK_Set() OLED_SCL_GPIO_Port->BSRR = OLED_SCL_Pin

#define OLED_SDA_Clr() OLED_SDA_GPIO_Port->BSRR = (uint32_t)OLED_SDA_Pin << 16u //DIN(D0)

#define OLED_SDA_Set() OLED_SDA_GPIO_Port->BSRR = OLED_SDA_Pin

#define OLED_RST_Clr() OLED_RES_GPIO_Port->BSRR = (uint32_t)OLED_RES_Pin << 16u //RES

#define OLED_RST_Set() OLED_RES_GPIO_Port->BSRR = OLED_RES_Pin

#define OLED_DC_Clr() OLED_DC_GPIO_Port->BSRR = (uint32_t)OLED_DC_Pin << 16u //DC

#define OLED_DC_Set() OLED_DC_GPIO_Port->BSRR = OLED_DC_Pin

#define OLED_CS_Clr() OLED_CS_GPIO_Port->BSRR = (uint32_t)OLED_CS_Pin << 16u //CS

#define OLED_CS_Set() OLED_CS_GPIO_Port->BSRR = OLED_CS_Pin

#endif

void U8G2_Init(void);

bool ui_show(uint16_t key);

void ui_test(uint16_t key);

#endif //F103VET6_U8G2_OLED_H

为什么没有引脚初始化,因为STM32CubeMX生成的代码已经初始化了。

相关链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。