打印
[MM32软件]

【MM32 eMiniBoard测评报告】续调OLED驱动

[复制链接]
763|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yinwuqing110 于 2020-5-5 15:10 编辑

    今天是五一假的最后一天了,明天又开始准备忙碌的工作了。今天来分享一下使用GPIO来模拟IIC通讯,尝试驱动一块0.96吋oled模块。想必大伙对这种oled屏都比较熟悉,这类屏成本低,有黑白与彩色版,有IIC接口与SPI接口版,我选择的是彩色IIC接口版,不知道是模拟的时序有问题,还是GPIO口存在复用功能的原因,移植好的驱动程序驱动不了该模块,确认该屏硬件没问题。
    OLED显示屏的正面与背面如下图所示:


     部分代码如下:
/*
     延时1us
*/
void delay(unsigned char num)
{
     uint8_t i = 10;
     while (num--)
     {
         while (i--);
      }
}

/*
     延时1ms
*/
void delay_ms(unsigned int ms)
{
      unsigned int x, y;
      for (x = ms; x > 0; x--)
      {
        for (y = 12000; y > 0; y--);
       }
}
/**********************************************
//IIC Start
**********************************************/
void IIC_Start()
{
    OLED_SCLK_Set();   
    OLED_SDIN_Set();   
    delay(1);   
    OLED_SDIN_Clr();   
    delay(1);   
    OLED_SCLK_Clr();   
    delay(1);   
}

/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop()
{
    OLED_SCLK_Set();
    OLED_SDIN_Clr();
    OLED_SDIN_Set();
}
/**********************************************
//IIC Ack
**********************************************/
void IIC_Wait_Ack()
{
    OLED_SCLK_Set();
    OLED_SCLK_Clr();
}
/**********************************************
// IIC Write byte
**********************************************/
void Write_IIC_Byte(unsigned char IIC_Byte)
{
    unsigned char i;
    unsigned char m,da;
               
    da=IIC_Byte;
    OLED_SCLK_Clr();
   for(i=0;i<8;i++)               
   {
      m=da;
      m=m&0x80;
      if(m==0x80)
      {
         OLED_SDIN_Set();
       }
     else
       {
         OLED_SDIN_Clr();
        }
         da=da<<1;
         OLED_SCLK_Set();
         OLED_SCLK_Clr();
      }
}
/**********************************************
IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
     IIC_Start();
     Write_IIC_Byte(IIC_Address);      //Slave address,SA0=0
     IIC_Wait_Ack();        
     Write_IIC_Byte(0x00);                                                        //write command
     IIC_Wait_Ack();        
     Write_IIC_Byte(IIC_Command);
     IIC_Wait_Ack();        
     IIC_Stop();
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
     if(cmd)
     {
       Write_IIC_Data(dat);
     }
    else
     {
       Write_IIC_Command(dat);
      }
  }
/********************************************
//开启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_On(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(1,OLED_DATA);                         //更新显示
        }
      }               
}
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size2)
{                 
    uint8_t t,temp;
    uint8_t enshow=0;                                                   
    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+(size2/2)*t,y,' ',size2);
         continue;
         }
        else
        {
          enshow=1;
         }
       }
     OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
    }
  }
//显示一个字符号串
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
{
    unsigned char j=0;
    while (chr[j]!='\0')
    {               
       OLED_ShowChar(x,y,chr[j],Char_Size);
       x+=8;
      if(x>120)
      {
        x=0;
        y+=2;
       }
       j++;
     }
   }
void OLED_DrawBattery(char level)
{
      switch(level)
      {
        case 0:
        OLED_DrawBMP(104,0,128,1,Battery_0);
        break;
        case 1:
        OLED_DrawBMP(104,0,128,1,Battery_25);
        break;
        case 2:
        OLED_DrawBMP(104,0,128,1,Battery_50);
        break;
        case 3:
        OLED_DrawBMP(104,0,128,1,Battery_75);
        break;
        case 4:
        OLED_DrawBMP(104,0,128,1,Battery_100);
        break;
        }
     }
/*
    [url=home.php?mod=space&uid=247401]@brief[/url]          初始化OLED与单片机的IO接口
*/
static void OLED_GPIO_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC,ENABLE);

        GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_15|GPIO_Pin_14;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_Init(GPIOC, &GPIO_InitStructure);
        OLED_SCLK_Set();    //设置PC14(SCL)为高电平
        OLED_SDIN_Set();    //设置PC15(SDA)为高电平
}

//初始化SSD1306                                            
void OLED_Init(void)
{
       OLED_GPIO_Init();   //GPIO口初始化
       delay_ms(200);  //延时,由于单片机上电初始化比OLED模块快,所以必须加上延迟,等待OLED上电复位完成
       OLED_WR_Byte(0xAE, OLED_CMD);   //关闭显示
       OLED_WR_Byte(0x00, OLED_CMD);   //设置地列地址
       OLED_WR_Byte(0x10, OLED_CMD);   //设置高列地址
       OLED_WR_Byte(0x40, OLED_CMD);   //设置起始行地址
       OLED_WR_Byte(0xB0, OLED_CMD);   //设置页地址
       OLED_WR_Byte(0x81, OLED_CMD);   // 对比度设置,可设置亮度
       OLED_WR_Byte(0xFF, OLED_CMD);   //  265
       OLED_WR_Byte(0xA1, OLED_CMD);   //设置段(SEG)的起始映射地址;column的127地址是SEG0的地址
       OLED_WR_Byte(0xA6, OLED_CMD);   //正常显示;0xa7逆显示
       OLED_WR_Byte(0xA8, OLED_CMD);   //设置驱动路数(16~64)
       OLED_WR_Byte(0x3F, OLED_CMD);   //64duty
       OLED_WR_Byte(0xC8, OLED_CMD);   //重映射模式,COM[N-1]~COM0扫描
       OLED_WR_Byte(0xD3, OLED_CMD);   //设置显示偏移
       OLED_WR_Byte(0x00, OLED_CMD);   //无偏移
       OLED_WR_Byte(0xD5, OLED_CMD);   //设置震荡器分频
       OLED_WR_Byte(0x80, OLED_CMD);   //使用默认值
       OLED_WR_Byte(0xD9, OLED_CMD);   //设置Pre-Charge Period
       OLED_WR_Byte(0xF1, OLED_CMD);   //使用官方推荐值
       OLED_WR_Byte(0xDA, OLED_CMD);   //设置com pin configuartion
       OLED_WR_Byte(0x12, OLED_CMD);   //使用默认值
       OLED_WR_Byte(0xDB, OLED_CMD);   //设置Vcomh,可调节亮度(默认)
       OLED_WR_Byte(0x40, OLED_CMD);   //使用官方推荐值
       OLED_WR_Byte(0x8D, OLED_CMD);   //设置OLED电荷泵
       OLED_WR_Byte(0x14, OLED_CMD);   //开启显示
       OLED_WR_Byte(0xAF, OLED_CMD);   //开启OLED面板显示
       OLED_Clear(CLEAR_ALL);        //清屏
       OLED_Set_Pos(0, 0);  //设置数据写入的起始行、列
}  
void TEST_OLED(void)
{               
      OLED_ShowString(16, 2, (unsigned char *)"HELLO EEWORLD",NUM_SIZE);
      OLED_DrawBattery(BATTERY_LEVEL_0);
      delay_ms(300);                                       
      OLED_DrawBattery(BATTERY_LEVEL_25);
      delay_ms(300);                                       
      OLED_DrawBattery(BATTERY_LEVEL_50);
      delay_ms(300);                                       
      OLED_DrawBattery(BATTERY_LEVEL_75);
      delay_ms(300);
      OLED_DrawBattery(BATTERY_LEVEL_100);
      delay_ms(500);
}
       此次工程包含流水灯与串口打印功能的实现,因此全编译的时候会报空间不足的错误,如果将调用OLED驱动程序部分注释掉,程序可以编译通过,后面在Keil中将IRAM1的Size由原来的0x2000改为0x4000,编译通过了还是未能驱动OLED屏。


       接线参考的电路原理图如下:

       串口1是通过USB线直接转换出的,因此只需在Keil的软件中设置烧录后自动重启,打开串口工具就能正常打印了。


    工程对应的实物连线与LED灯的状态如下:

     部分代码附上:
/********************************************************************************************************
**函数信息 :UartInit_Loop(void)                     
**功能描述 :初始化串口
**输入参数 :无
**输出参数 :无
********************************************************************************************************/
void UartInit_Loop(void)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    UART_InitTypeDef UART_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);        //使能UART1
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);  //开启GPIOA时钟

    //UART 初始化设置
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1);

    UART_InitStructure.UART_BaudRate = 115200;//串口波特率
    UART_InitStructure.UART_WordLength = UART_WordLength_8b;//字长为8位数据格式
    UART_InitStructure.UART_StopBits = UART_StopBits_1;//一个停止位
    UART_InitStructure.UART_Parity = UART_Parity_No;//无奇偶校验位
    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;//无硬件数据流控制
    UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;        //收发模式
   
    UART_Init(UART1, &UART_InitStructure); //初始化串口1
    UART_Cmd(UART1, ENABLE);                    //使能串口1
   
    //UART1_TX   GPIOA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2
   
    //UART1_RX          GPIOA.10初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  
   
    printf("Welcome to https://www.21ic.com/\r\n");
}

/********************************************************************************************************
**函数信息 :uart_send(UART_TypeDef* UARTx,char c)                    
**功能描述 :串口发送字节
**输入参数 :UART_TypeDef* UARTx ,选择UART1、UART2、UART3
**输入参数 :char c ,串口发送的字节
**输出参数 :无
********************************************************************************************************/
void uart_send(UART_TypeDef* UARTx,char c)
{
   UART_SendData(UARTx,(uint16_t)c);  
    while(1)
    {
      if(UART_GetITStatus(UARTx, UART_IT_TXIEN))
      {
       UART_ClearITPendingBit(UARTx, UART_IT_TXIEN);
       break;
      }
    }
}

/********************************************************************************************************
**函数信息 :unsigned char inbyte(UART_TypeDef* UARTx)               
**功能描述 :串口接收函数
**输入参数 :UART_TypeDef* UARTx ,选择UART1、UART2、UART3
**输出参数 :unsigned char 串口接收返回的字节
********************************************************************************************************/
unsigned char inbyte(UART_TypeDef* UARTx)
{
   unsigned char temp;
   while(1)
  {
    if(UART_GetITStatus(UARTx, UART_IT_RXIEN))
    {
     UART_ClearITPendingBit(UARTx, UART_IT_RXIEN);  //清除接受中断位
     break;
    }
  }
    temp = (uint8_t)UART_ReceiveData(UARTx);
    if(temp==0xd)//清除错误接收的数据
    {
       return 0;               
    }
    return temp;
}
/********************************************************************************************************
**函数信息 :void Uart1RxTest(UART_TypeDef* UARTx)               
**功能描述 :串口接收函数测试
**输入参数 :UART_TypeDef* UARTx ,选择UART1、UART2、UART3
**输出参数 :无
********************************************************************************************************/
void Uart1RxTest(UART_TypeDef* UARTx)
{
    unsigned char temp;
    temp = inbyte(UARTx);
   if(temp!=0)
   {
     printf("您输入的数据为:%c\r\n",temp);        
    }                        
}
#include "delay.h"
#include "sys.h"
#include "led.h"
#include "uart_loop.h"
#include "oled.h"

#define TIME 280
/********************************************************************************************************
**函数信息 :int main (void)                          
**功能描述 :开机后,ARMLED闪动
**输入参数 :
**输出参数 :
********************************************************************************************************/
int main(void)
{
        //OLED_Init();
    LED_Init();                                   //初始化与LED连接的硬件接口
        //OLED_ShowString(0, 2, (unsigned char *)"Set Time:",NUM_SIZE);
    while(1)              //无限循环
{
   //TEST_OLED();
   UartInit_Loop();  //UART1的发送,可以通过串口软件打印:Welcome to https://www.21ic.com/
   //uart_send(UART1,0x55);
   //Uart1RxTest(UART1);//UART1的接收,在串口软件中输入字符,可以通过打印验证接收的数据是否正确
   LED1_ON();
   LED2_ON();
   LED3_ON();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_ON();
   LED3_ON();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_OFF();
   LED3_ON();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_OFF();
   LED3_OFF();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_OFF();
   LED3_OFF();
   LED4_OFF();
   delay_ms(TIME);
   LED1_OFF();
   LED2_OFF();
   LED3_OFF();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_OFF();
   LED3_ON();
   LED4_ON();
   delay_ms(TIME);
   LED1_OFF();
   LED2_ON();
   LED3_ON();
   LED4_ON();
   delay_ms(TIME);
   LED1_ON();
   LED2_ON();
   LED3_ON();
   LED4_ON();
  }
}
#define IIC_Address                                                0x78

#define Max_Column                                                128
#define Max_Row                                                     64
#define        Brightness                                             0xFF

/*--------------------引脚定义--------------------------*/
#define         OLED_SCLK_Set()         GPIO_SetBits(GPIOC, GPIO_Pin_14)                   //PC14(SCL)输出高
#define         OLED_SCLK_Clr()         GPIO_ResetBits(GPIOC,GPIO_Pin_14)                  //PC14(SCL)输出低
#define         OLED_SDIN_Set()         GPIO_SetBits(GPIOC, GPIO_Pin_15)                    //PC15(SDA)输出高
#define         OLED_SDIN_Clr()         GPIO_ResetBits(GPIOC, GPIO_Pin_15)                  //PC15(SDA)输出高
#define         OLED_READ_SDIN()        GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15)   //读取PC15(SDA)电平

//-----------------OLED IIC端口定义----------------                                             
#define OLED_SDA                                                        PC14                       //I2C数据
#define OLED_SCL                                                        PC15                        //I2C时钟
#define OLED_RES                                                        4                              //I2CRES
#define OLED_EN                                                          5                              //I2CEN

#define OLED_CMD                                                        0                              //写命令
#define OLED_DATA                                                       1                              //写数据

//-----------------OLED 横向位置定义----------------                                             
#define LINE_HIGHT                                                       0                             //第一行
#define LINE_MIDDLE                                                     3                             //第二行
#define LINE_LOW                                                          6                             //第三行

//-----------------OLED 电池电量定义----------------  
#define BATTERY_LEVEL_0                          0                                        //空电量
#define BATTERY_LEVEL_25                        1                                        //25%电量
#define BATTERY_LEVEL_50                        2                                        //50%电量
#define BATTERY_LEVEL_75                        3                                        //75%电量
#define BATTERY_LEVEL_100                      4                                        //100%电量

//-----------------OLED 字号大小定义----------------
#define NUM_SIZE                                                        16  //普通数字和汉字的大小
#define SET_SIZE                                                         24  //设置界面的字体大小

typedef enum
{
    CLEAR_ALL,                                                                                                    //全部清屏
    CLEAR_HIGTH,                                                                                                //清除顶部行
    CLEAR_MIDDLE,                                                                                              //清除中间行
    CLEAR_LOW,                                                                                                   //清除底部行
    CLEAR_PASSWORD                                                                                          //清除密码行
}clear_mode_t;
    有心的坛友们可以谈谈您的见解,代码中有没有不妥的地方,有的话烦请回帖指明,谢谢啦,此次分享就到这里啦,后会有期。

使用特权

评论回复
沙发
nawu| | 2020-6-1 16:37 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
板凳
qcliu| | 2020-6-1 16:38 | 只看该作者
调试的时候需要注意什么呢

使用特权

评论回复
地板
tfqi| | 2020-6-1 16:38 | 只看该作者
小视频很漂亮

使用特权

评论回复
5
wiba| | 2020-6-1 16:38 | 只看该作者
楼主辛苦了

使用特权

评论回复
6
zljiu| | 2020-6-1 16:39 | 只看该作者
好羡慕楼主呀

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

1023

帖子

7

粉丝