805721366 发表于 2022-7-26 23:49

[N32L43X评测] 4.模拟SPI驱动OLED

<p><span style="font-family:宋体;">前两篇<a href="https://bbs.eeworld.com.cn/thread-1212206-1-1.html" target="_blank"> 2.模拟I2C驱动OLED</a>&nbsp;<a href="https://bbs.eeworld.com.cn/thread-1212208-1-1.html" target="_blank"> 3.硬件I2C驱动OLED</a>介绍了I2C总线驱动OLED显示屏,此篇介绍SPI串行总线驱动OLED显示屏。</span></p>

<p><span style="font-family:宋体;">SPI是一种高速、全双工、同步串行总线,SPI与I2C对比:</span></p>

<p><span style="font-family:宋体;">&nbsp;&nbsp;&nbsp;&nbsp;SPI是全双工,I2C是半双工</span></p>

<p><span style="font-family:宋体;">&nbsp;&nbsp;&nbsp;&nbsp;SPI只能有一个主机,I2C支持多主机多从机</span></p>

<p><span style="font-family:宋体;">&nbsp;&nbsp;&nbsp;&nbsp;I2C占用更少的GPIO,更节省资源<br />
&nbsp;&nbsp;&nbsp;&nbsp;SPI的数据位宽更灵活,可以根据需要选择多位数数据宽度<br />
&nbsp;&nbsp;&nbsp;&nbsp;SPI协议没有响应机制,主机无法得知从机是否接收到所发的数据,需采用一些方法来防止数据丢帧<br />
&nbsp;&nbsp;&nbsp;&nbsp;SPI协议可以做到非常高的速率,每一个SCLK都可以进行数据的传输,通过引入CRC等校验方法,可以即高速传输数据,又能保持数据的准确度<br />
&nbsp;&nbsp;&nbsp;&nbsp;I2C通过器件地址来选择从机,从机数量的增加不会导致GPIO的增加,而SPI通过CS片选信号选择从机,每增加一个从机就要多占用一个GPIO;也可以通过加入译码器来实现多从机控制<br />
&nbsp;&nbsp;&nbsp;&nbsp;SPI、I2C都应用于板内器件短距离通讯</span></p>

<p><span style="font-family:宋体;">SPI总线详解可参考:</span></p>

<p></p>

<p><span style="font-family:宋体;">此篇主要介绍GPIO软件模拟实现SPI来驱动0.96寸OLED显示屏</span></p>

<p><span style="color:#f39c12;"><strong><span style="font-family:宋体;">硬件连接</span></strong></span></p>

<p><span style="font-family:宋体;">GND&nbsp;&mdash;&mdash;&nbsp; GND</span></p>

<p><span style="font-family:宋体;">VCC&nbsp;&mdash;&mdash;&nbsp; 3.3V</span></p>

<p><span style="font-family:宋体;">DO&nbsp; &mdash;&mdash;&nbsp; PB13<br />
DI&nbsp; &mdash;&mdash;&nbsp; PB15<br />
RES&nbsp;&mdash;&mdash;&nbsp; PC7<br />
DC &nbsp;&mdash;&mdash;&nbsp; PC6<br />
CS&nbsp; &mdash;&mdash;&nbsp; PB12</span></p>

<p class="imagemiddle" style="text-align: center;"></p>

<p><span style="color:#f39c12;"><strong><span style="font-family:宋体;">软件代码</span></strong></span></p>

<p><span style="font-family:宋体;">SPI代码:</span></p>

<pre>
<code>//SPI初始化
void SPI_Initial(void)
{
    GPIO_InitType GPIO_InitStructure;

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

    //GPIO_CS初始化设置
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin = PIN_SPI_CS;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(PORT_SPI_CS, &amp;GPIO_InitStructure);
    //GPIO_SCK初始化设置
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin = PIN_SPI_SCK;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(PORT_SPI_SCK, &amp;GPIO_InitStructure);
    //GPIO_MISO初始化设置
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin = PIN_SPI_MISO;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
    GPIO_InitPeripheral(PORT_SPI_MISO, &amp;GPIO_InitStructure);
    //GPIO_MOSI初始化设置
    GPIO_InitStruct(&amp;GPIO_InitStructure);
    GPIO_InitStructure.Pin = PIN_SPI_MOSI;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
    GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(PORT_SPI_MOSI, &amp;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 &lt; 8; cnt++)
    {
      SPI_SCK_LOW;                                 //时钟 - 低
      Delay_Us(1);

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

      TxData &lt;&lt;= 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 &lt; 8; cnt++)
    {
      SPI_SCK_LOW;                                 //时钟 - 低
      Delay_Us(1);

      RxData &lt;&lt;= 1;

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

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

    return RxData;
}</code></pre>

<p><span style="font-family:宋体;">OLED代码变更部分:</span></p>

<pre>
<code>//向设备写控制命令
static void OLED_Write_CMD(unsigned char cmd)
{
    #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_I2C
    unsigned char tx_buf = {0x00, cmd};
    i2c_master_send(tx_buf, BUF_SIZE);
    #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 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_I2C
    unsigned char tx_buf = {0x40, date};
    i2c_master_send(tx_buf, BUF_SIZE);
    #endif
    #ifdef SW_SPI
    OLED_CS_LOW;
    OLED_DC_HIGH;
    SPI_WriteByte(date);
    OLED_CS_HIGH;
    #endif
}

//OLED初始化
void OLED_Init(void)
{
    #ifdef SW_I2C
    I2C_Initial();
    #endif
    #ifdef SW_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
}</code></pre>

<p><span style="font-family:宋体;"><span style="color:#f39c12;"><strong>运行测试</strong></span></span></p>

<p>945d66b1b2b119468f1569493968fdf4</p>

<p><span style="color:#f39c12;"><strong><span style="font-family:宋体;">测试代码</span></strong></span></p>

<p></p>

<p>&nbsp;</p>

freebsder 发表于 2022-7-27 18:43

<p>谢谢分享,期待后续。</p>
页: [1]
查看完整版本: [N32L43X评测] 4.模拟SPI驱动OLED