NO.10 1.3寸OLED顯示屏IIC通信(12864驅動芯片)


  對於單片機來說,有一個良好的人機交互界面是很重要的。那么我們常用的單片機顯示設備有什么呢?OLED屏是一個不錯選擇。

  

  OLED能顯示我們的相對應的信息,使得我們的電子設計頓時高大上許多。

  OLED是啥呢?OLED跟LED差不多,簡單點說,就是一個個小小的LED組合起來,控制每一個小小的LED燈像素的亮滅來進行顯示,這樣就可以顯示任意字符了。

  對於OLED來說,單位面積內像素點的個數直接決定了我們顯示屏的分辨率。像我在某寶購買的OLED顯示屏,是1.3寸內有128*64個像素點。

  顯然我們是不可能搞這么多的GPIO口來控制這個OLED的,那么我們該如何控制呢?

  前人早就為我們想好的解決措施,人家造了專門控制這種OLED的芯片方便我們使用,我們只需要按照芯片所需要的通信協議跟芯片通信就可以操作這個OLED了。

  那么我們應該用什么通信協議呢?這個芯片支持IICSPI兩種通信方式,這里我們主要使用講解IIC。

   I2C 總線是一種串行數據總線,只有二根信號線,一根是雙向的數據線SDA,另一根是時鍾線SCL,兩條線可以掛多個設備。 IIC設備(絕大多數)里有個固化的地址,只有在兩條線上傳輸的值等於IIC設備的固化地址時,其才會作出響應。通常我們為了方便把IIC設備分為主設備和從設備,基本上誰控制時鍾線(即控制SCL的電平高低變換)誰就是主設備

  我們操作OLED顯示屏就是屬於最簡單的一種IIC應用。我們只需要向OLED發送數據,而不需要接收OLED發送的數據。

  IIC的具體通信協議我就不詳細說了,主要分為起始信號,應答信號,結束信號,同時向設備發送數據或者接收數據。

  對於我們的MSP432中,其實是自帶了IIC的庫,但是為了我們方便理解(才不是我老是發送不了數據呢),我們使用軟件模擬IIC。

  那什么又是軟件模擬IIC呢?就是拿GPIO口當做SCL和SDA,自己來控制傳輸線的高低電平!

  我們萬能的某寶賣家寫了利用51和STM32操作OLED例程,我們只需要對其修改下就可以運用到我們的MSP432中。

  下面我列出所有核心函數:

//起始信號
void I2C_Start(void)
{
    GPIO_write(CONFIG_GPIO_SDA,1);
    GPIO_write(CONFIG_GPIO_SCL,1);
    GPIO_write(CONFIG_GPIO_SDA,0);
    GPIO_write(CONFIG_GPIO_SCL,0);
}

//結束信號
void I2C_Stop(void)
{
    GPIO_write(CONFIG_GPIO_SDA,0);
    GPIO_write(CONFIG_GPIO_SCL,1);
    GPIO_write(CONFIG_GPIO_SDA,0);
}

//等待信號響應
void I2C_WaitAck(void) //測數據信號的電平
{
    GPIO_write(CONFIG_GPIO_SDA,1);
    GPIO_write(CONFIG_GPIO_SCL,1);
    GPIO_write(CONFIG_GPIO_SCL,0);
}

//寫入一個字節
void Send_Byte(uint8_t dat)
{
  uint8_t i;
  for(i=0;i<8;i++)
  {
    GPIO_write(CONFIG_GPIO_SCL,0);//將時鍾信號設置為低電平
    if(dat&0x80)//將dat的8位從最高位依次寫入
    {
        GPIO_write(CONFIG_GPIO_SDA,1);
    }
    else
    {
        GPIO_write(CONFIG_GPIO_SDA,0);
    }
    GPIO_write(CONFIG_GPIO_SCL,1);
    GPIO_write(CONFIG_GPIO_SCL,0);
    dat<<=1;
  }
}

//發送一個字節
//向SSD1306寫入一個字節。
//mode:數據/命令標志 0,表示命令;1,表示數據;
void OLED_WR_Byte(uint8_t dat,uint8_t mode)
{
  I2C_Start();
  Send_Byte(0x78);
  I2C_WaitAck();
  if(mode){Send_Byte(0x40);}
  else{Send_Byte(0x00);}
  I2C_WaitAck();
  Send_Byte(dat);
  I2C_WaitAck();
  I2C_Stop();
}

//坐標設置
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
  OLED_WR_Byte(0xb0+y,OLED_CMD);
  OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
  OLED_WR_Byte((x&0x0f),OLED_CMD);
}

//開啟OLED顯示
void OLED_Display_On(void)
{
  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
  OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
  OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//關閉OLED顯示
void OLED_Display_Off(void)
{
  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
  OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
  OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}
//清屏函數,清完屏,整個屏幕是黑色的!和沒點亮一樣!!!
void OLED_Clear(void)
{
  uint8_t i,n;
  for(i=0;i<8;i++)
  {
    OLED_WR_Byte (0xb0+i,OLED_CMD);    //設置頁地址(0~7)
    OLED_WR_Byte (0x00,OLED_CMD);      //設置顯示位置—列低地址
    OLED_WR_Byte (0x10,OLED_CMD);      //設置顯示位置—列高地址
    for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
  } //更新顯示
}

//在指定位置顯示一個字符,包括部分字符
//x:0~127
//y:0~63
//sizey:選擇字體 6x8  8x16
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t sizey)
{
  uint8_t c=0,sizex=sizey/2;
  uint16_t i=0,size1;
  if(sizey==8)size1=6;
  else size1=(sizey/8+((sizey%8)?1:0))*(sizey/2);
  c=chr-' ';//得到偏移后的值
  OLED_Set_Pos(x,y);
  for(i=0;i<size1;i++)
  {
    if(i%sizex==0&&sizey!=8) OLED_Set_Pos(x,y++);
    if(sizey==8) OLED_WR_Byte(asc2_0806[c][i],OLED_DATA);//6X8字號
    else if(sizey==16) OLED_WR_Byte(asc2_1608[c][i],OLED_DATA);//8x16字號
    //      else if(sizey==xx) OLED_WR_Byte(asc2_xxxx[c][i],OLED_DATA);//用戶添加字號
    else return;
  }
}
//m^n函數
uint32_t oled_pow(uint8_t m,uint8_t n)
{
  uint32_t result=1;
  while(n--)result*=m;
  return result;
}
//顯示數字
//x,y :起點坐標
//num:要顯示的數字
//len :數字的位數
//sizey:字體大小
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t sizey)
{
  uint8_t t,temp,m=0;
  uint8_t enshow=0;
  if(sizey==8)m=2;
  for(t=0;t<len;t++)
  {
    temp=(num/oled_pow(10,len-t-1))%10;
    if(enshow==0&&t<(len-1))
    {
      if(temp==0)
      {
        OLED_ShowChar(x+(sizey/2+m)*t,y,' ',sizey);
        continue;
      }else enshow=1;
    }
    OLED_ShowChar(x+(sizey/2+m)*t,y,temp+'0',sizey);
  }
}
//顯示一個字符號串
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t sizey)
{
  uint8_t j=0;
  while (chr[j]!='\0')
  {
    OLED_ShowChar(x,y,chr[j++],sizey);
    if(sizey==8)x+=6;
    else x+=sizey/2;
  }
}
//顯示漢字

void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t no,uint8_t sizey)
{
  uint16_t i,size1=(sizey/8+((sizey%8)?1:0))*sizey;
  for(i=0;i<size1;i++)
  {
    if(i%sizey==0) OLED_Set_Pos(x,y++);
    if(sizey==16) OLED_WR_Byte(Hzk[no][i],OLED_DATA);//16x16字號
    //      else if(sizey==xx) OLED_WR_Byte(xxx[c][i],OLED_DATA);//用戶添加字號
    else return;
  }
}


//顯示圖片
//x,y顯示坐標
//sizex,sizey,圖片長寬
//BMP:要顯示的圖片
void OLED_DrawBMP(uint8_t x,uint8_t y,uint8_t sizex, uint8_t sizey,uint8_t BMP[])
{
  uint16_t j=0;
  uint8_t i,m;
  sizey=sizey/8+((sizey%8)?1:0);
  for(i=0;i<sizey;i++)
  {
    OLED_Set_Pos(x,i+y);
    for(m=0;m<sizex;m++)
    {
      OLED_WR_Byte(BMP[j++],OLED_DATA);
    }
  }
}


//初始化SSD1306
void OLED_Init(void)
{
  sleep(1);
  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_Clear();
  OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}

  程序有點長,大家直接復制就可以使用。

  那我們該如何調用呢?非常簡單。比如我們顯示某種字符,我們這樣作為按鍵的回調函數,當我們按下按鍵就可以顯示某種字符。

void gpioButtonFxn1(uint_least8_t index)
{
    /* Clear the GPIO interrupt and toggle an LED */
    GPIO_toggle(CONFIG_GPIO_LED_1);
    OLED_Clear();
    OLED_ShowString(10,4,"2020/05/28",8);
    OLED_ShowString(10,6,"DerekChen",8);
    sleep(1);
}

  哈!非常簡單是不是。那么我們要顯示中文呢?中文有點復雜,你需要先生成中文的字符點陣數據,然后就可以照樣顯示了!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM