2401_83890637 2024-05-06 22:54 采纳率: 0%
浏览 64
已结题

OLED(SSD1306)驱动通信协议如何由I2C改为SPI

#嵌入式课程项目打算制作一个温湿度计,使用的是STM32L432KC开发板,DHT11温湿度传感器和SSD1306(七针脚)OLED显示屏(各部件如下图),但只找到四针脚I2C通信协议的驱动程序,无法正常点亮

img

img

img

主要驱动程序(I2C通信协议):

#include "i2c_oled.h"
#include "font.h"
#include "mbed.h"
#include "genshin.h"
#include <cstdint>

DigitalOut OLED_SCL(D5);
DigitalOut OLED_SDA(D4);

uint8_t buffer[144][8];

/**
 * @brief  OLED次方函数
 * @retval 返回值等于X的Y次方
 */
uint32_t OLED_Pow(uint32_t X, uint32_t Y) {
  uint32_t Result = 1;
  while (Y--) {
    Result *= X;
  }
  return Result;
}

void OLED_W_SCL(uint8_t x) { OLED_SCL = x; }

void OLED_W_SDA(uint8_t x) { OLED_SDA = x; }

void OLED_I2C_Init() {
  OLED_W_SCL(1);
  OLED_W_SDA(1);
}

void OLED_I2C_Start(void) {
  OLED_W_SDA(1);
  OLED_W_SCL(1);
  OLED_W_SDA(0);
  OLED_W_SCL(0);
}

/**
 * @brief  I2C停止
 * @param  无
 * @retval 无
 */
void OLED_I2C_Stop(void) {
  OLED_W_SDA(0);
  OLED_W_SCL(1);
  OLED_W_SDA(1);
}

/**
 * @brief  I2C发送一个字节
 * @param  Byte 要发送的一个字节
 * @retval 无
 */
void OLED_I2C_SendByte(uint8_t Byte) {
  uint8_t i;
  for (i = 0; i < 8; i++) {
    OLED_W_SDA(Byte & (0x80 >> i));
    OLED_W_SCL(1);
    OLED_W_SCL(0);
  }
  OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
  OLED_W_SCL(0);
}

/**
 * @brief  OLED写命令
 * @param  Command 要写入的命令
 * @retval 无
 */
void OLED_WriteCommand(uint8_t Command) {
  OLED_I2C_Start();
  OLED_I2C_SendByte(0x78); //从机地址
  OLED_I2C_SendByte(0x00); //写命令
  OLED_I2C_SendByte(Command);
  OLED_I2C_Stop();
}

/**
 * @brief  OLED写数据
 * @param  Data 要写入的数据
 * @retval 无
 */
void OLED_WriteData(uint8_t Data) {
  OLED_I2C_Start();
  OLED_I2C_SendByte(0x78); //从机地址
  OLED_I2C_SendByte(0x40); //写数据
  OLED_I2C_SendByte(Data);
  OLED_I2C_Stop();
}

void OLED_Genshin() {
  OLED_ClearBuffer();
  OLED_ShowPictureBuffered(0, 0, 128, 64, genshinImage, 0);
  OLED_SendBuffer();
}

//清除显存
void OLED_ClearBuffer(void) {
  uint8_t i, n;
  for (i = 0; i < 8; i++) {
    for (n = 0; n < 128; n++) {
      buffer[n][i] = 0; //清除所有数据
    }
  }
}

//更新显存到OLED
void OLED_SendBuffer(void) {
  uint8_t i, n;
  for (i = 0; i < 8; i++) {
    OLED_WriteCommand(0xb0 + i); //设置行起始地址
    OLED_WriteCommand(0x00);     //设置低列起始地址
    OLED_WriteCommand(0x10);     //设置高列起始地址
    for (n = 0; n < 128; n++)
      OLED_WriteData(buffer[n][i]);
  }
}

//画点
// x:0~127
// y:0~63
// t:1 填充 0,清空
void OLED_DrawPointBuffered(uint8_t x, uint8_t y, uint8_t t) {
  uint8_t i, m, n;
  i = y / 8;
  m = y % 8;
  n = 1 << m;
  if (t) {
    buffer[x][i] |= n;
  } else {
    buffer[x][i] = ~buffer[x][i];
    buffer[x][i] |= n;
    buffer[x][i] = ~buffer[x][i];
  }
}

//在指定位置显示一个字符,包括部分字符
// x:0~127
// y:0~63
// size1:选择字体 6x8/6x12/8x16/12x24
// mode:0,反色显示;1,正常显示
void OLED_ShowCharBuffered(uint8_t x, uint8_t y, uint8_t chr, uint8_t size1,
                           uint8_t mode) {
  uint8_t i, m, temp, size2, chr1;
  uint8_t x0 = x, y0 = y;
  if (size1 == 8)
    size2 = 6;
  else
    size2 = (size1 / 8 + ((size1 % 8) ? 1 : 0)) *
            (size1 / 2); //得到字体一个字符对应点阵集所占的字节数
  chr1 = chr - ' ';      //计算偏移后的值
  for (i = 0; i < size2; i++) {
    if (size1 == 8) { //调用0806字体
      temp = asc2_0806[chr1][i];
    } else if (size1 == 12) { //调用1206字体
      temp = asc2_1206[chr1][i];
    } else if (size1 == 16) { //调用1608字体
      temp = asc2_1608[chr1][i];
    } else if (size1 == 24) { //调用2412字体
      temp = asc2_2412[chr1][i];
    } else
      return;
    for (m = 0; m < 8; m++) {
      if (temp & 0x01)
        OLED_DrawPointBuffered(x, y, mode);
      else
        OLED_DrawPointBuffered(x, y, !mode);
      temp >>= 1;
      y++;
    }
    x++;
    if ((size1 != 8) && ((x - x0) == size1 / 2)) {
      x = x0;
      y0 = y0 + 8;
    }
    y = y0;
  }
}

//显示字符串
// x,y:起点坐标
// size1:字体大小
//*chr:字符串起始地址
// mode:0,反色显示;1,正常显示
void OLED_ShowStringBuffered(uint8_t x, uint8_t y, char string[], uint8_t size1,
                             uint8_t mode) {
  uint8_t i=0;
  while ((string[i] >= ' ') && (string[i] <= '~')) //判断是不是非法字符!
  {
    OLED_ShowCharBuffered(x, y, string[i], size1, mode);
    if (size1 == 8)
      x += 6;
    else
      x += size1 / 2;
    i++;
  }
}
//显示数字
// x,y :起点坐标
// num :要显示的数字
// len :数字的位数
// size:字体大小
// mode:0,反色显示;1,正常显示
void OLED_ShowNumBuffered(uint8_t x, uint8_t y, uint32_t num, uint8_t len,
                          uint8_t size1, uint8_t mode) {
  uint8_t t, temp, m = 0;
  if (size1 == 8)
    m = 2;
  for (t = 0; t < len; t++) {
    temp = (num / OLED_Pow(10, len - t - 1)) % 10;
    if (temp == 0) {
      OLED_ShowCharBuffered(x + (size1 / 2 + m) * t, y, '0', size1, mode);
    } else {
      OLED_ShowCharBuffered(x + (size1 / 2 + m) * t, y, temp + '0', size1,
                            mode);
    }
  }
}

//画线
// x1,y1:起点坐标
// x2,y2:结束坐标
void OLED_DrawLineBuffered(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,
                           uint8_t mode) {
  uint16_t t;
  int xerr = 0, yerr = 0, delta_x, delta_y, distance;
  int incx, incy, uRow, uCol;
  delta_x = x2 - x1; //计算坐标增量
  delta_y = y2 - y1;
  uRow = x1; //画线起点坐标
  uCol = y1;
  if (delta_x > 0)
    incx = 1; //设置单步方向
  else if (delta_x == 0)
    incx = 0; //垂直线
  else {
    incx = -1;
    delta_x = -delta_x;
  }
  if (delta_y > 0)
    incy = 1;
  else if (delta_y == 0)
    incy = 0; //水平线
  else {
    incy = -1;
    delta_y = -delta_x;
  }
  if (delta_x > delta_y)
    distance = delta_x; //选取基本增量坐标轴
  else
    distance = delta_y;
  for (t = 0; t < distance + 1; t++) {
    OLED_DrawPointBuffered(uRow, uCol, mode); //画点
    xerr += delta_x;
    yerr += delta_y;
    if (xerr > distance) {
      xerr -= distance;
      uRow += incx;
    }
    if (yerr > distance) {
      yerr -= distance;
      uCol += incy;
    }
  }
}

// x,y:圆心坐标
// r:圆的半径
void OLED_DrawCircleBuffered(uint8_t x, uint8_t y, uint8_t r) {
  int a, b, num;
  a = 0;
  b = r;
  while (2 * b * b >= r * r) {
    OLED_DrawPointBuffered(x + a, y - b, 1);
    OLED_DrawPointBuffered(x - a, y - b, 1);
    OLED_DrawPointBuffered(x - a, y + b, 1);
    OLED_DrawPointBuffered(x + a, y + b, 1);

    OLED_DrawPointBuffered(x + b, y + a, 1);
    OLED_DrawPointBuffered(x + b, y - a, 1);
    OLED_DrawPointBuffered(x - b, y - a, 1);
    OLED_DrawPointBuffered(x - b, y + a, 1);

    a++;
    num = (a * a + b * b) - r * r; //计算画的点离圆心的距离
    if (num > 0) {
      b--;
      a--;
    }
  }
}

// x,y:起点坐标
// sizex,sizey,图片长宽
// BMP[]:要写入的图片数组
// mode:0,反色显示;1,正常显示
void OLED_ShowPictureBuffered(uint8_t x, uint8_t y, uint8_t sizex,
                              uint8_t sizey, uint8_t BMP[], uint8_t mode) {
  uint16_t j = 0;
  uint8_t i, n, temp, m;
  uint8_t x0 = x, y0 = y;
  sizey = sizey / 8 + ((sizey % 8) ? 1 : 0);
  for (n = 0; n < sizey; n++) {
    for (i = 0; i < sizex; i++) {
      temp = BMP[j];
      j++;
      for (m = 0; m < 8; m++) {
        if (temp & 0x01)
          OLED_DrawPointBuffered(x, y, mode);
        else
          OLED_DrawPointBuffered(x, y, !mode);
        temp >>= 1;
        y++;
      }
      x++;
      if ((x - x0) == sizex) {
        x = x0;
        y0 = y0 + 8;
      }
      y = y0;
    }
  }
}

/**
 * @brief  OLED初始化
 * @param  无
 * @retval 无
 */
void OLED_Init(void) {
  uint32_t i, j;

  for (i = 0; i < 1000; i++) //上电延时
  {
    for (j = 0; j < 1000; j++)
      ;
  }

  OLED_I2C_Init(); //端口初始化

  OLED_WriteCommand(0xAE); //关闭显示

  OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
  OLED_WriteCommand(0x80);

  OLED_WriteCommand(0xA8); //设置多路复用率
  OLED_WriteCommand(0x3F);

  OLED_WriteCommand(0xD3); //设置显示偏移
  OLED_WriteCommand(0x00);

  OLED_WriteCommand(0x40); //设置显示开始行

  OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置

  OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置

  OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
  OLED_WriteCommand(0x12);

  OLED_WriteCommand(0x81); //设置对比度控制
  OLED_WriteCommand(0xCF);

  OLED_WriteCommand(0xD9); //设置预充电周期
  OLED_WriteCommand(0xF1);

  OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
  OLED_WriteCommand(0x30);

  OLED_WriteCommand(0xA4); //设置整个显示打开/关闭

  OLED_WriteCommand(0xA6); //设置正常/倒转显示

  OLED_WriteCommand(0x8D); //设置充电泵
  OLED_WriteCommand(0x14);

  OLED_WriteCommand(0xAF); //开启显示

  OLED_ClearBuffer(); // 清除显存
  OLED_SendBuffer();
}

麻烦各位佬,帮忙解决一下吧😭

  • 写回答

3条回答 默认 最新

  • 码农阿豪@新空间 新星创作者: 前端开发技术领域 2024-05-06 22:55
    关注
    让阿豪来帮你解答,本回答参考chatgpt3.5编写提供,如果还有疑问可以评论或留言
    问题描述:如何使得只有四针脚I2C通信协议的驱动程序正常点亮七针脚的SSD1306 OLED显示屏? 解决方法:
    1. 确认SSD1306 OLED型号是否正确。首先需要确认所购买的七针脚的SSD1306 OLED显示屏型号是否正确,是否支持I2C通信协议。
    2. 在开发板上找到合适的引脚。如果无法找到适用的OLED库,需要手动编写I2C通信协议的驱动程序,然后在开发板上选取合适的GPIO引脚来进行I2C通信。 例如,STM32L432KC开发板可以使用D14引脚作为数据线SDA,D15引脚作为时钟线SCL。
    3. 修改驱动程序中的引脚定义。修改驱动程序中的GPIO引脚定义,将其对应到选取的引脚上。 例如,在下面的驱动程序中,将OLED_SCL和OLED_SDA定义为D15和D14即可使用:
    DigitalOut OLED_SCL(D15);
    DigitalOut OLED_SDA(D14);
    
    1. 调整显示屏的初始化配置。如果驱动程序使用的显示屏型号与实际使用的不同,需要调整驱动程序中的初始化配置,确保它们适用于这种显示屏。 例如,在下面的驱动程序中,需要根据SSD1306 OLED的数据手册,对应修改初始化命令,确保其适用于SSD1306 OLED显示屏。
    void OLED_I2C_Init() {
      OLED_W_SCL(1);
      OLED_W_SDA(1);
      ...
      OLED_WriteCommand(0xAE);  // 关闭显示
      OLED_WriteCommand(0x20);  // 设置内存地址模式
      OLED_WriteCommand(0x10);  // 垂直模式
      OLED_WriteCommand(0xb0);  // 设置起始行
      OLED_WriteCommand(0xc8);  // 设置映射
      OLED_WriteCommand(0x00);  // 设置低列地址
      OLED_WriteCommand(0x10);  // 设置高列地址
      OLED_WriteCommand(0x40);  // 设置开始行
      OLED_WriteCommand(0x81);  // 对比度设置
      OLED_WriteCommand(0xff);
      ...
    }
    
    1. 确认温湿度传感器和OLED显示屏之间的电路连接。最后,需要检查两者之间的电路连接是否正确,确保温湿度传感器和OLED显示屏之间的通信正常。如果连接有误,需要重新连接电路并重新测试。 参考代码:
    #include "mbed.h"
    #include "i2c_oled.h"
    #include "font.h"
    #include <cstdint>
    DigitalOut OLED_SCL(D15);
    DigitalOut OLED_SDA(D14);
    uint8_t buffer[144][8];
    // OLED次方函数
    uint32_t OLED_Pow(uint32_t X, uint32_t Y) {
      uint32_t Result = 1;
      while (Y--) {
        Result *= X;
      }
      return Result;
    }
    void OLED_W_SCL(uint8_t x) { OLED_SCL = x; }
    void OLED_W_SDA(uint8_t x) { OLED_SDA = x; }
    void OLED_I2C_Init() {
      OLED_W_SCL(1);
      OLED_W_SDA(1);
      OLED_WriteCommand(0xAE);  // 关闭显示
      OLED_WriteCommand(0x20);  // 设置内存地址模式
      OLED_WriteCommand(0x10);  // 垂直模式
      OLED_WriteCommand(0xb0);  // 设置起始行
      OLED_WriteCommand(0xc8);  // 设置映射
      OLED_WriteCommand(0x00);  // 设置低列地址
      OLED_WriteCommand(0x10);  // 设置高列地址
      OLED_WriteCommand(0x40);  // 设置开始行
      OLED_WriteCommand(0x81);  // 对比度设置
      OLED_WriteCommand(0xff);
      ...
    }
    void OLED_I2C_Start(void) {
      OLED_W_SDA(1);
      OLED_W_SCL(1);
      OLED_W_SDA(0);
      OLED_W_SCL(0);
    }
    void OLED_I2C_Stop(void) {
      OLED_W_SDA(0);
      OLED_W_SCL(1);
      OLED_W_SDA(1);
    }
    void OLED_I2C_SendByte(uint8_t Byte) {
      uint8_t i;
      for (i = 0; i < 8; i++) {
        OLED_W_SDA(Byte & (0x80 >> i));
        OLED_W_SCL(1);
        OLED_W_SCL(0);
      }
      OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
      OLED_W_SCL(0);
    }
    void OLED_WriteCommand(uint8_t Command) {
      OLED_I2C_Start();
      OLED_I2C_SendByte(0x78); //从机地址
      OLED_I2C_SendByte(0x00); //写命令
      OLED_I2C_SendByte(Command);
      OLED_I2C_Stop();
    }
    void OLED_WriteData(uint8_t Data) {
      OLED_I2C_Start();
      OLED_I2C_SendByte(0x78); //从机地址
      OLED_I2C_SendByte(0x40); //写数据
      OLED_I2C_SendByte(Data);
      OLED_I2C_Stop();
    }
    
    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 5月16日
  • 创建了问题 5月6日