STM32 U8g2 spi软件驱动,spi硬件驱动(优化u8g2的软件spi,速度翻好几倍)

首先准备一个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生成的代码已经初始化了。

  • 14
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: STM32是一种微控制器型号,它可以通过SPI总线和其他器件进行通信。U8g2是一种可以在STM32上使用的图形库,它可以让开发者很方便地绘制各种图形和文本。SPI则是一种通信协议,它可以在STM32和其他器件之间进行快速的数据传输。使用STM32U8g2SPI协议可以构建出各种有趣的应用,比如显示器、游戏控制器等。在进行开发过程中,需要注意硬件连接是否正确,以及软件编程是否符合规范。同时,还需要了解每个组件的功能和特点,以便更好地掌握整个系统的设计和实现。总之,STM32U8g2SPI协议是可以十分灵活地组合使用的,能够满足不同应用场景的需求,是一个十分有用的开发组合。 ### 回答2: STM32是一款高性能的微控制器芯片,广泛应用于嵌入式系统和物联网设备中。U8g2是一个用于显示屏驱动的开源库,支持多种显示屏类型,并且具有灵活的配置选项。SPI是一种串行通信协议,用于在芯片之间传输数据。 在STM32中使用U8g2驱动SPI接口的显示屏可以实现高效的显示效果,并且具有较低的系统开销。通过使用SPI接口,芯片之间的数据传输速度可以达到高达10 Mbps,这使得在数据量较大的情况下也可以实现快速传输。使用STM32硬件SPI接口进行数据传输可以减少系统负担,提高系统效率,对于需要快速响应和高并发的应用场景非常有利。 总之,STM32U8g2SPI的高度集成和协作可以使嵌入式系统和物联网设备得到强大的输入输出能力和高效的运行能力。在实际应用中,使用这些技术可以提高系统的稳定性、安全性和效率。 ### 回答3: STM32是意法半导体公司推出的一款基于ARM Cortex-M内核的微控制器,具有高性能和低功耗等特点,广泛应用于工业控制、电子制造、家电等领域。而U8g2是一种开源的单色OLED显示库,常用于嵌入式设备中,支持多种控制模式(SPI、I2C等)和不同型号的OLED屏幕。 SPI是一种串行外设接口,常用于连接MCU和各种外设(如闪存、OLED屏幕等)。STM32支持硬件SPI接口,具有高速传输、低功耗、数据可靠性高等特点,在访问U8g2库时,可以利用SPI接口进行数据传输,提高数据传输效率。 当STM32U8g2库配合使用时,可以实现OLED屏幕的显示,以及通过SPI接口快速传输数据实现控制效果。在具体的应用中,可以根据需求选择不同的OLED屏幕和STM32型号,以及不同的SPI接口配置,达到更好的性能和应用效果。 总之,STM32U8g2库的结合可以帮助嵌入式开发者实现高效、可靠的OLED显示控制,具有广泛的应用前景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值