STM32 OLED显示屏--SPI通信知识汇总

简介: 在此功能中,采用的是四线SPI,我们在开发过程中,应该去寻扎数据手册里面的通信时序图,才能使得单片机利用四线SPI和OLED进行通信的功能操作。

一、SPI时序通信

 在此功能中,采用的是四线SPI,我们在开发过程中,应该去寻扎数据手册里面的通信时序图,才能使得单片机利用四线SPI和OLED进行通信的功能操作。

小结:在时序通信的过程张,高位先发,且当时钟线SCLK为低电平的时候,就可以吧数据放到数据线上面将其数据发(SDIN)。

二、SPI的分类

1.软件SPI

对于软件SPI的定义是:采用高低电平模拟SPI通信时序来完成发送或者接收数据。对于软件SPIeryan1,这样的通信方式在于移植灵活,引脚使用灵活,只需要任意四个I/O口就可以进行功能的完成,但是其缺点也很明显,那就是速度比较慢。

2.硬件SPI

对于硬件SPI的解释是,由单片机内部自主控制数据的发送与接收,硬件SPI的优点是速度快,单片机可以自主控制,我们只要完成将数据给到单片机,就可以了。缺点是:接口引脚相对固定,没有那么灵活。但是这是开发过程中最为经典的一部分,往下都是介绍硬件SPI.

三、硬件SPI

1、SPI的特性

 

硬件SPI特性有:是一个三线全双工的同步传输,可以带有或者不带第三条双线数据线的双线单工同步传输、可以进行8或者16位传输帧格式选择,能搞实现主机或者从机操作,并且支持多个主控模式,可以进行编程的时钟极性和相位,可以进行数据顺序编程,MSB在前或者LSB在前,可以触发中断的专用发送和收发标准,支持可靠通信的硬件CRC。

2、对四大引脚进行解释说明

MISO引脚:主设备输入,从设备输出引脚,该引脚的功能是在从机模式下发送数据,在主机模式下接收数据;

MOSI引脚:主设备输出,从设备输入引脚,该引脚的功能时在主机模式下发送数据,在从机模式下接收数据;

SCK引脚:是串口时钟引脚,该引脚的功能时主设备输出,从设备的输入;

NSS引脚:名为片选引脚,该功能是从设备选择,这一一个可以进行选择的引脚,用来选择主机、从机设备,可以让主设备进行单独的和特定的设备进行通信,并且可以避免和数据线发生冲突,片选引脚,我们一般不采用硬件的片选,我们可以进行手动对片选的控制。

2345_image_file_copy_30.jpg

3、库函数初始化

初始化引脚--

2345_image_file_copy_31.jpg

需要关闭PA15的JTAG功能,因为PA15复位I/O口不可用--

2345_image_file_copy_32.jpg

GPIO_InitTypeDef  GPIO_InitStructure;
  GPIO_InitTypeDef  SPI2_SCLK_MOSI;
  SPI_InitTypeDef   SPI_InitStruct;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//JTAG默认为下载接口 我们需要通过复用为普通的IO
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//使其生效为普通IO口

初始化结构体

SPI2_SCLK_MOSI.GPIO_Mode = GPIO_Mode_AF_PP;    //设置功能模式为推挽复用功能
  SPI2_SCLK_MOSI.GPIO_Pin  = GPIO_Pin_13|GPIO_Pin_15;  //设置引脚配置
  SPI2_SCLK_MOSI.GPIO_Speed= GPIO_Speed_50MHz;   //设置速度
  GPIO_Init(GPIOB, &SPI2_SCLK_MOSI);      //初始化spi2的sclk_mosi方面
  SPI2_SCLK_MOSI.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //设置为上升沿
  SPI2_SCLK_MOSI.GPIO_Pin  = GPIO_Pin_14;   //
  SPI2_SCLK_MOSI.GPIO_Speed= GPIO_Speed_50MHz;  //a072O+45
  GPIO_Init(GPIOB, &SPI2_SCLK_MOSI); 
  SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//2分频最大速度36M
  SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//第二个时钟边沿的上升沿采样
  SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;//CLK空闲时为高电平
  SPI_InitStruct.SPI_CRCPolynomial = 7;//STM32的SPI带硬件ecc
  SPI_InitStruct.SPI_DataSize   = SPI_DataSize_8b;//8位数据位
  SPI_InitStruct.SPI_Direction  = SPI_Direction_2Lines_FullDuplex;//全双工模式
  SPI_InitStruct.SPI_FirstBit   = SPI_FirstBit_MSB;//高位在前先发
  SPI_InitStruct.SPI_Mode       = SPI_Mode_Master;//主机模式
  SPI_InitStruct.SPI_NSS        = SPI_NSS_Hard;//CS硬件管理 我们自己控制
  SPI_Init(SPI2,&SPI_InitStruct);
  SPI_Cmd(SPI2, ENABLE);

完整代码

OLED.c

#include "oled.h"
#include "stdlib.h"
#include "oledfont.h" 
#include "delay.h"
u8 OLED_GRAM[144][8];
u8 SPI2_Send_Byte(u8 data)
{
  while(!(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==SET));//获取发送完成标志位
  SPI_I2S_SendData(SPI2, data);//spi发送一个字节
  while(!(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)==SET));//获取接收完成标志位
  return SPI_I2S_ReceiveData(SPI2);//spi读一个字节
}
void OLED_WR_Byte(u8 dat,u8 cmd)
{ 
  u8 i;       
  if(cmd)
    OLED_DC_Set();
  else
    OLED_DC_Clr();
  OLED_CS_Clr();
  SPI2_Send_Byte(dat); 
  OLED_CS_Set();
  OLED_DC_Set();      
}
//更新显存到OLED 
void OLED_Refresh(void)
{
  u8 i,n;
  for(i=0;i<8;i++)
  {
     OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
     OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
     OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
     for(n=0;n<128;n++)
     OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
  }
}
//清屏函数
void OLED_Clear(void)
{
  u8 i,n;
  for(i=0;i<8;i++)
  {
     for(n=0;n<128;n++)
      {
       OLED_GRAM[n][i]=0;//清除所有数据
      }
  }
  OLED_Refresh();//更新显示
}
//配置写入数据的起始位置
void OLED_WR_BP(u8 x,u8 y)
{
  OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址
  OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);//设置列起始地址
  OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//显示单个字符
//x:0~127
//y:0~7
//chr:传入的字符
void OLED_ShowChar(u8 x,u8 y,char chr,u8 size)//chr = 'A'      chr = 65
{
  int i;
  u8 j;
  u8 page = size/8;//计算字符12 16 24 大小所需要显示页数
  if(size%8 !=0) page+=1;//有余数 说明还要加多一页
  for(j=0;j<page;j++)
  {
    OLED_WR_BP(x,y+j);//先确定开始位置
    for(i=0;i<size/2;i++)
    {
      if(size == 12) OLED_WR_Byte(F12x6[(chr-32)*12+i + j*size/2],OLED_DATA);
      else if(size == 16) OLED_WR_Byte(F16x8[(chr-32)*16+i + j*size/2],OLED_DATA);
      else if(size == 24) OLED_WR_Byte(F24x12[(chr-32)*36+i + j*size/2],OLED_DATA);
    }
  }
}
//显示一串字符
void OLED_Show_String(u8 x,u8 y,char *ch,u8 size)
{
  u8 page = size/8;//计算字符12 16 24 大小所需要显示页数
  if(size%8 !=0) page+=1;//有余数 说明还要加多一页
  while(*ch != 0)
  {
    OLED_ShowChar(x,y,*ch,size);
    x = x+size/2;//移动位置 显示下一个字符
    ch++;
    if(x>127)
    {
      x = 0;//坐标归0  下一行显示
      y+=page;//页数相加
    }
  }
}
//OLED显示一个汉字
void OLED_Show_One_Chinese(u8 x,u8 y,char ch[])
{
  u16 i = 0;
  int k;
  while(1)
  {
    if((ch[0] == GB2312_font[i][0])  &&  (ch[1]==GB2312_font[i][1]))
    {
      OLED_WR_BP(x,y);
      for(k=0;k<16;k++)
      {
        OLED_WR_Byte(GB2312_16x16[i*32+k],OLED_DATA);//发送字符数据的上半部分
      }
      OLED_WR_BP(x,y+1);//页数+1 改变显示位置
      for(;k<32;k++)
      {
        OLED_WR_Byte(GB2312_16x16[i*32+k],OLED_DATA);//发送字符数据的上半部分
      }
      break;
    }
    i++;
    if(i >= (sizeof(GB2312_font)/3)) break;//超过字库大小  说明没有搜索到相应的汉字
  }
}
//13397813910
//OLED的初始化
//BMP[]:要写入的图片数组
void OLED_ShowPicture(u8 x0,u8 y0,u8 x1,u8 y1,u8 BMP[])
{
  u16 j=0;
  u8 x=0,y=0;
  if(y%8==0)y=0;
  else y+=1;
  for(y=y0;y<y1;y++)
   {
     OLED_WR_BP(x0,y);
     for(x=x0;x<x1;x++)
     {
       OLED_WR_Byte(BMP[j],OLED_DATA);
       j++;
     }
   }
}
//实现滚动的功能
void OLED_ScrollDisplay(u8 num,u8 space)
{
  u8 i,n,t=0,m=0,r;
  while(1)
  {
    if(m==0)
    {
      OLED_Show_One_Chinese(10,2,6); //写入一个汉字保存在OLED_GRAM[][]数组中
      t++;
    }
    if(t==num)
      {
        for(r=0;r<16*space;r++)      //显示间隔
         {
          for(i=0;i<144;i++)
            {
              for(n=0;n<8;n++)
              {
                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
              }
            }
           OLED_Refresh();
         }
        t=0;
      }
    m++;
    if(m==16){m=0;}
    for(i=0;i<144;i++)   //实现左移
    {
      for(n=0;n<8;n++)
      {
        OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
      }
    }
    OLED_Refresh();
  }
}
void OLED_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  GPIO_InitTypeDef  SPI2_SCLK_MOSI;
  SPI_InitTypeDef   SPI_InitStruct;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//JTAG默认为下载接口 我们需要通过复用为普通的IO
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//使其生效为普通IO口
  //PA4---复位   PA15 命令/数据
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能A端口时钟,开启时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_15;  //配置引脚
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //速度50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);    //初始化GPIOD3,6
  GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_15); //设置空闲时间为高电平
  //PB7---片选线
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能A端口时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;   //设置引脚配置引脚7
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);    //初始化GPIOD3,6
  GPIO_SetBits(GPIOB,GPIO_Pin_7);   //空闲默认高电平
  SPI2_SCLK_MOSI.GPIO_Mode = GPIO_Mode_AF_PP;    //设置功能模式为推挽复用功能
  SPI2_SCLK_MOSI.GPIO_Pin  = GPIO_Pin_13|GPIO_Pin_15;  //设置引脚配置
  SPI2_SCLK_MOSI.GPIO_Speed= GPIO_Speed_50MHz;   //设置速度
  GPIO_Init(GPIOB, &SPI2_SCLK_MOSI);      //初始化spi2的sclk_mosi方面
  SPI2_SCLK_MOSI.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //设置为上升沿
  SPI2_SCLK_MOSI.GPIO_Pin  = GPIO_Pin_14;   //
  SPI2_SCLK_MOSI.GPIO_Speed= GPIO_Speed_50MHz;  //a072O+45
  GPIO_Init(GPIOB, &SPI2_SCLK_MOSI); 
  SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//2分频最大速度36M
  SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//第二个时钟边沿的上升沿采样
  SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;//CLK空闲时为高电平
  SPI_InitStruct.SPI_CRCPolynomial = 7;//STM32的SPI带硬件ecc
  SPI_InitStruct.SPI_DataSize   = SPI_DataSize_8b;//8位数据位
  SPI_InitStruct.SPI_Direction  = SPI_Direction_2Lines_FullDuplex;//全双工模式
  SPI_InitStruct.SPI_FirstBit   = SPI_FirstBit_MSB;//高位在前先发
  SPI_InitStruct.SPI_Mode       = SPI_Mode_Master;//主机模式
  SPI_InitStruct.SPI_NSS        = SPI_NSS_Hard;//CS硬件管理 我们自己控制
  SPI_Init(SPI2,&SPI_InitStruct);
  SPI_Cmd(SPI2, ENABLE);
  Delay_ms(200);
  OLED_RST_Clr();//复位
  Delay_ms(200);
  OLED_RST_Set();
  OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
  OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
  OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
  OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
  OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
  OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
  OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
  OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
  OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
  OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
  OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
  OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
  OLED_WR_Byte(0x00,OLED_CMD);//-not offset
  OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
  OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
  OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
  OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
  OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
  OLED_WR_Byte(0x12,OLED_CMD);
  OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
  OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
  OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
  OLED_WR_Byte(0x02,OLED_CMD);//
  OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
  OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
  OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
  OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
  OLED_WR_Byte(0xAF,OLED_CMD);
  OLED_Clear();
}
相关文章
|
4月前
STM32CubeMX OLED驱动
STM32CubeMX OLED驱动
68 10
|
5月前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
575 1
|
4月前
|
监控
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
stm32f407探索者开发板(十八)——串口通信实验讲解(USART_RX_STA流程图详解)
331 0
|
4月前
stm32f407探索者开发板(十六)——串行通信原理讲解-UART
stm32f407探索者开发板(十六)——串行通信原理讲解-UART
278 0
|
4月前
|
传感器 编解码 API
【STM32开发入门】温湿度监测系统实战:SPI LCD显示、HAL库应用、GPIO配置、UART中断接收、ADC采集与串口通信全解析
SPI(Serial Peripheral Interface)是一种同步串行通信接口,常用于微控制器与外围设备间的数据传输。SPI LCD是指使用SPI接口与微控制器通信的液晶显示屏。这类LCD通常具有较少的引脚(通常4个:MISO、MOSI、SCK和SS),因此在引脚资源有限的系统中非常有用。通过SPI协议,微控制器可以向LCD发送命令和数据,控制显示内容和模式。
167 0
|
4月前
|
数据安全/隐私保护
STM32F103C8T6实现简易密码锁(CubeMax配置)(一),Oled显示。
项目功能:实现设置密码,登陆密码,后期还可以通过E2PROM实现掉电不丢失数据。通过Oled的显示去判断我们是否设置或者登陆成功。
|
6月前
|
存储 缓存 芯片
STM32标准库SPI通信协议与W25Q64-2
STM32标准库SPI通信协议与W25Q64
|
6月前
|
存储 芯片
STM32标准库SPI通信协议与W25Q64-1
STM32标准库SPI通信协议与W25Q64
|
6月前
STM32--SPI通信与W25Q64(2)
STM32--SPI通信与W25Q64(2)
|
6月前
|
存储 传感器 芯片
STM32--SPI通信与W25Q64(1)
STM32--SPI通信与W25Q64(1)
200 0