zhouminjie 发表于 2022-8-8 00:37

【CW32F030CxTx StartKit测评】4.软件模拟SPI驱动OLED

SPI是一种高速、全双工、同步串行总线,SPI与I2C对比:    SPI是全双工,I2C是半双工    SPI只能有一个主机,I2C支持多主机多从机    I2C占用更少的GPIO,更节省资源
    SPI的数据位宽更灵活,可以根据需要选择多位数数据宽度
    SPI协议没有响应机制,主机无法得知从机是否接收到所发的数据,需采用一些方法来防止数据丢帧
    SPI协议可以做到非常高的速率,每一个SCLK都可以进行数据的传输,通过引入CRC等校验方法,可以即高速传输数据,又能保持数据的准确度
    I2C通过器件地址来选择从机,从机数量的增加不会导致GPIO的增加,而SPI通过CS片选信号选择从机,每增加一个从机就要多占用一个GPIO;也可以通过加入译码器来实现多从机控制
    SPI、I2C都应用于板内器件短距离通讯SPI总线详解可参考:此篇主要介绍GPIO软件模拟实现SPI来驱动0.96寸OLED显示屏硬件连接GND ——GNDVCC ——3.3VDO——PA5
DI——PA7
RES ——PB1
DC——PB0
CS——PA4软件代码SPI代码://SPI初始化
void SPI_Initial(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    //根据GPIO组初始化GPIO时钟
    RCC_AHBPeriphClk_Enable( RCC_SPI_CS | RCC_SPI_SCK | RCC_SPI_MOSI | RCC_SPI_MISO, ENABLE);

    //GPIO_CS初始化设置
    GPIO_InitStructure.Pins = PIN_SPI_CS;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(PORT_SPI_CS, &GPIO_InitStructure);
    //GPIO_SCK初始化设置
    GPIO_InitStructure.Pins = PIN_SPI_SCK;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(PORT_SPI_SCK, &GPIO_InitStructure);
    //GPIO_MISO初始化设置
    GPIO_InitStructure.Pins = PIN_SPI_MISO;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(PORT_SPI_MISO, &GPIO_InitStructure);
    //GPIO_MOSI初始化设置
    GPIO_InitStructure.Pins = PIN_SPI_MOSI;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(PORT_SPI_MOSI, &GPIO_InitStructure);

    SPI_CS_DISABLE;
    SPI_SCK_HIGH;
    SPI_MOSI_HIGH;

}


//SPI写一字节数据,TxData --- 发送的字节数据
void SPI_WriteByte(unsigned char TxData)
{
    unsigned char cnt;

    for(cnt = 0; cnt < 8; cnt++)
    {
      SPI_SCK_LOW;                                 //时钟 - 低
      Delay_Us(1);

      if(TxData & 0x80)                            //发送数据
            SPI_MOSI_HIGH;
      else
            SPI_MOSI_LOW;

      TxData <<= 1;

      Delay_Us(1);
      SPI_SCK_HIGH;                              //时钟 - 高
      Delay_Us(1);
    }
}


//SPI读一字节数据,读回来的字节数据
unsigned char SPI_ReadByte(void)
{
    unsigned char cnt;
    unsigned char RxData = 0;

    for(cnt = 0; cnt < 8; cnt++)
    {
      SPI_SCK_LOW;                                 //时钟 - 低
      Delay_Us(1);

      RxData <<= 1;

      if(SPI_MISO_READ)                            //读取数据
      {
            RxData |= 0x01;
      }

      SPI_SCK_HIGH;                              //时钟 - 高
      Delay_Us(1);
    }

    return RxData;
}OLED代码://向设备写控制命令
static void OLED_Write_CMD(unsigned char cmd)
{
    #ifdef HW_I2C
    unsigned char tx_buf = {0x00, cmd};
    I2C_MasterSendDataToSlave(CW_I2C2, tx_buf, BUF_SIZE);
    #endif
    #ifdef SW_I2C
    I2C_Start();
    I2C_Send_Byte(0x78);
    I2C_Wait_Ack();
    I2C_Send_Byte(0x00);
    I2C_Wait_Ack();
    I2C_Send_Byte(cmd);
    I2C_Wait_Ack();
    I2C_Stop();
    #endif
    #ifdef HW_SPI
    OLED_CS_LOW;
    OLED_DC_LOW;

    while (SPI_GetFlagStatus(SPI_MASTER, SPI_FLAG_TXE) == RESET);

    SPI_SendData(SPI_MASTER, cmd);

    while (SPI_GetFlagStatus(SPI_MASTER, SPI_FLAG_RXNE) == RESET);

    SPI_ReceiveData(SPI_MASTER);
    OLED_CS_HIGH;
    #endif
    #ifdef SW_SPI
    OLED_CS_LOW;
    OLED_DC_LOW;
    SPI_WriteByte(cmd);
    OLED_CS_HIGH;
    #endif
}

//向设备写数据
static void OLED_Write_Date(unsigned char date)
{
    #ifdef HW_I2C
    unsigned char tx_buf = {0x40, date};
    I2C_MasterSendDataToSlave(CW_I2C2, tx_buf, BUF_SIZE);
    #endif
    #ifdef SW_I2C
    I2C_Start();
    I2C_Send_Byte(0x78);
    I2C_Wait_Ack();
    I2C_Send_Byte(0x40);
    I2C_Wait_Ack();
    I2C_Send_Byte(date);
    I2C_Wait_Ack();
    I2C_Stop();
    #endif
    #ifdef HW_SPI
    OLED_CS_LOW;
    OLED_DC_HIGH;

    while (SPI_GetFlagStatus(SPI_MASTER, SPI_FLAG_TXE) == RESET);

    SPI_SendData(SPI_MASTER, date);

    while (SPI_GetFlagStatus(SPI_MASTER, SPI_FLAG_RXNE) == RESET);

    SPI_ReceiveData(SPI_MASTER);
    OLED_CS_HIGH;
    #endif
    #ifdef SW_SPI
    OLED_CS_LOW;
    OLED_DC_HIGH;
    SPI_WriteByte(date);
    OLED_CS_HIGH;
    #endif
}

//坐标设置
static void OLED_Set_Pos(unsigned char x, unsigned char y)
{
    OLED_Write_CMD(0xB0 + y);
    OLED_Write_CMD(((x & 0xF0) >> 4) | 0x10);
    OLED_Write_CMD(x & 0x0F);
}

//开启OLED显示
static void OLED_Display_On(void)
{
    OLED_Write_CMD(0x8D); //SET DCDC命令
    OLED_Write_CMD(0x14); //DCDC ON
    OLED_Write_CMD(0xAF); //DISPLAY ON
}

//关闭OLED显示
static void OLED_Display_Off(void)
{
    OLED_Write_CMD(0x8D); //SET DCDC命令
    OLED_Write_CMD(0x10); //DCDC OFF
    OLED_Write_CMD(0xAE); //DISPLAY OFF
}

//OLED清屏
void OLED_Clear(void)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
      OLED_Write_CMD(0xB0 + cnt);
      OLED_Write_CMD(0x00);
      OLED_Write_CMD(0x10);

      for(count = 0; count < 128; count++)
      {
            OLED_Write_Date(0x00);
      }
    }
}

//OLED清行
void OLED_Clear_Row(unsigned char n)
{
    unsigned char count;

    OLED_Write_CMD(0xB0 + n);
    OLED_Write_CMD(0x00);
    OLED_Write_CMD(0x10);

    for(count = 0; count < 128; count++)
    {
      OLED_Write_Date(0x00);
    }
}

//OLED填满屏幕
void OLED_Fill(void)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
      OLED_Write_CMD(0xB0 + cnt); //设置页地址(0~7)
      OLED_Write_CMD(0x00); //设置显示位置—列低地址
      OLED_Write_CMD(0x10); //设置显示位置—列高地址

      for(count = 0; count < 128; count++)
      {
            OLED_Write_Date(0x01);
      }
    }
}

//指定位置显示一个字符
//x:0~127
//y:0~63
//chr:字符
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
{
    unsigned char offset = 0, cnt = 0;

    offset = chr - ' '; //计算偏移量

    if(x > 128 - 1)
    {
      x = 0;
      y = y + 2;
    }

    if(size == 16)
    {
      OLED_Set_Pos(x, y);

      for(cnt = 0; cnt < 8; cnt++)
      {
            OLED_Write_Date(F8x16);
      }

      OLED_Set_Pos(x, y + 1);

      for(cnt = 0; cnt < 8; cnt++)
      {
            OLED_Write_Date(F8x16);
      }
    }
    else
    {
      OLED_Set_Pos(x, y);

      for(cnt = 0; cnt < 6; cnt++)
      {
            OLED_Write_Date(F6x8);
      }
    }
}

unsigned int oled_pow(unsigned char m, unsigned char n)
{
    unsigned int result = 1;

    while(n--)
    {
      result *= m;
    }

    return result;
}

//指定位置显示一个数字
//x,y:起点坐标
//num:数值(0~4294967295)
//len:数字的位数
//size:字体大小
//mode:模式        0,填充模式;1,叠加模式
void OLED_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
{
    unsigned char cnt, temp;
    unsigned char show = 0;

    for(cnt = 0; cnt < len; cnt++)
    {
      temp = (num / oled_pow(10, len - cnt - 1)) % 10;

      if(show == 0 && cnt < (len - 1))
      {
            if(temp == 0)
            {
                OLED_ShowChar(x + (size / 2) * cnt, y, ' ', size);
                continue;
            }
            else
            {
                show = 1;
            }
      }

      OLED_ShowChar(x + (size / 2) * cnt, y, temp + '0', size);
    }
}

//指定位置显示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)
{
    unsigned char cnt = 0;

    while(chr != '\0')
    {
      OLED_ShowChar(x, y, chr, size);
      x += 8;

      if(x > 120)
      {
            x = 0;
            y += 2;
      }

      cnt++;
    }
}

//显示汉字
void OLED_ShowCHinese(unsigned char x, unsigned char y, unsigned char no)
{
    unsigned char cnt, addr = 0;

    OLED_Set_Pos(x, y);

    for(cnt = 0; cnt < 16; cnt++)
    {
      OLED_Write_Date(Hzk);
      addr++;
    }

    OLED_Set_Pos(x, y + 1);

    for(cnt = 0; cnt < 16; cnt++)
    {
      OLED_Write_Date(Hzk);
      addr++;
    }
}

//显示图片
/*
        @brief                        显示图片
        @param                        x0:起始列地址
                                        y0:起始页地址
                                        x1:终止列地址
                                        y1:终止页地址
                                        BMP[]:存放图片代码的数组
        @retval                        无
*/
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y; //定义变量

    if(y1 % 8 == 0)
    {
      y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
      y = y1 / 8 + 1;
    }

    for(y = y0; y < y1; y++) //从起始页开始,画到终止页
    {
      OLED_Set_Pos(x0, y); //在页的起始列开始画

      for(x = x0; x < x1; x++) //画x1 - x0 列
      {
            OLED_Write_Date(BMP); //画图片的点
      }
    }
}

//显示动图
/*
        @brief                        显示动图
        @param                        x0:起始列地址
                                y0:起始页地址
                                x1:终止列地址
                                y1:终止页地址
                                k: 帧个数
                                m: 单帧数组大小
                                BMP[]:存放动图代码的数组
        @retval                        无
*/
void OLED_DrawGIF(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y, i; //定义变量

    if(y1 % 8 == 0)
    {
      y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
      y = y1 / 8 + 1;
    }

    for (i = 0; i < k; i++) //从第一帧开始画
    {
      j = 0;

      for(y = y0; y < y1; y++) //从起始页开始,画到终止页
      {
            OLED_Set_Pos(x0, y); //在页的起始列开始画

            for(x = x0; x < x1; x++) //画x1 - x0 列
            {
                OLED_Write_Date(GIF); //画图片的点
            }
      }

      //Delay_Ms(80);
    }
}


#if defined SW_SPI || defined HW_SPI
//SPI驱动方式DC、RES引脚初始化
void OLED_SPI_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    //根据GPIO组初始化GPIO时钟
    RCC_AHBPeriphClk_Enable( RCC_OLED_DC | RCC_OLED_RES, ENABLE);
    //GPIO_DC初始化设置
    GPIO_InitStructure.Pins = PIN_OLED_DC;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(PORT_OLED_DC, &GPIO_InitStructure);
    //GPIO_RES初始化设置
    GPIO_InitStructure.Pins = PIN_OLED_RES;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.IT = GPIO_IT_NONE;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(PORT_OLED_RES, &GPIO_InitStructure);

    OLED_RES_LOW;
    Delay_Ms(200);
    OLED_RES_HIGH;
}
#endif


//OLED初始化
void OLED_Init(void)
{
    #ifdef SW_I2C
    I2C_Init();
    #endif
    #ifdef HW_I2C
    I2C_Init();
    #endif
    #ifdef SW_SPI
    SPI_Initial();
    OLED_SPI_GPIO_Init();
    #endif
    #ifdef HW_SPI
    SPI_Initial();
    OLED_SPI_GPIO_Init();
    #endif
    Delay_Ms(200);
    OLED_Write_CMD(0xAE); //display off
    OLED_Write_CMD(0x00); //set low column address
    OLED_Write_CMD(0x10); //set high column address
    OLED_Write_CMD(0x40); //set start line address
    OLED_Write_CMD(0xB0); //set page address
    OLED_Write_CMD(0x81); //contract control
    OLED_Write_CMD(0xFF); //128
    OLED_Write_CMD(0xA1); //set segment remap
    OLED_Write_CMD(0xA6); //normal / reverse
    OLED_Write_CMD(0xA8); //set multiplex ratio(1 to 64)
    OLED_Write_CMD(0x3F); //1/32 duty
    OLED_Write_CMD(0xC8); //Com scan direction
    OLED_Write_CMD(0xD3); //set display offset
    OLED_Write_CMD(0x00); //
    OLED_Write_CMD(0xD5); //set osc division
    OLED_Write_CMD(0x80); //
    OLED_Write_CMD(0xD8); //set area color mode off
    OLED_Write_CMD(0x05); //
    OLED_Write_CMD(0xD9); //Set Pre-Charge Period
    OLED_Write_CMD(0xF1); //
    OLED_Write_CMD(0xDA); //set com pin configuartion
    OLED_Write_CMD(0x12); //
    OLED_Write_CMD(0xDB); //set Vcomh
    OLED_Write_CMD(0x30); //
    OLED_Write_CMD(0x8D); //set charge pump enable
    OLED_Write_CMD(0x14); //
    OLED_Write_CMD(0xAF); //turn on oled panel
}运行测试测试代码



duo点 发表于 2022-8-8 09:30

屏幕上的几个图标是啥意思啊

zhouminjie 发表于 2022-8-8 16:39

duo点 发表于 2022-8-8 09:30
屏幕上的几个图标是啥意思啊

几个车标

weifeng90 发表于 2022-8-8 21:14

软件模拟效率不高啊

zhouminjie 发表于 2022-8-8 23:36

weifeng90 发表于 2022-8-8 21:14
软件模拟效率不高啊

速度?

updownq 发表于 2022-8-16 20:30

模拟spi可以。

hearstnorman323 发表于 2022-8-20 16:25

有硬件iic或者spi驱动的吗   

zhouminjie 发表于 2022-8-22 23:26

hearstnorman323 发表于 2022-8-20 16:25
有硬件iic或者spi驱动的吗

有的,硬件SPI https://bbs.21ic.com/icview-3244756-1-1.html;硬件I2C https://bbs.21ic.com/icview-3244752-1-1.html

wwk1996 发表于 2022-8-23 06:31

OLED的驱动芯片是?

zhouminjie 发表于 2022-8-31 21:04

wwk1996 发表于 2022-8-23 06:31
OLED的驱动芯片是?

内部芯片是SSD1306

abotomson 发表于 2022-9-8 09:02

这个模拟spi的速度快吗   

bartonalfred 发表于 2022-9-8 15:42

CW32F030CxTx 价格可以吗   
页: [1]
查看完整版本: 【CW32F030CxTx StartKit测评】4.软件模拟SPI驱动OLED