首先准备一个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 <stdio.h>
#include <math.h>
#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 <stdbool.h>
#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生成的代码已经初始化了。