• IIC 通信协议之stm32 驱动OLED


    前言

        使用stm32 驱动4 Pin 的OLED, 现在网上开源的资料多的是,但是为了锻炼自己使用第一手资料的能力,今天我还是从数据手册开始,从头造一波轮子,同时也是为了加深自己对 IIC 协议的理解 ,本系列内容我会从单片机和linux两个板子做一些OLED的 验证,希望后面大家在学习IIC 相关内容的时候,可以少走一些弯路。为了大家学习方面,这些文章也会在我的微信公众号同步,方便大家随时查看,欢迎大家扫码关注。
    
    • 1

    在这里插入图片描述

    1. 查阅数据手册

       在我们实际工作中,需要我们从头造轮子的机会其实并不多,但是作为搞嵌入式这个行业的,阅读一些简单的数据手册的能力还是要有的,万一碰到碰到一个比较棘手的问题,说不定就需要我们不得不去看芯片的参考手册了。本篇我就以4Pin 的OLED 使用的SSD1306为例,通过stm32 实现对他的驱动。
    
      我们 打开他的数据手册,首先不要被全英文的吓到了,现在网上各种的翻译软件都比较给力。再说了,这个手册其实并不需要从头到尾一字不落的读完,首先我们看一下芯片的简单说明,了解芯片的大致的特性。
      
      其次:根据自己选择的芯片,使用的通讯方式,着重去看相对应的章节,比如我们本次使用的就是4 Pin的 IIC通信的,所以我们直接去找参考手册中介绍IIC 的.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    a. IIC 从机地址是有SA0 决定的,并且R/W# 决定是读取数据还是写入数据。R/W# = 0 为写模式,R/W# = 1 为读数据。

    IIC 通信协议:
    在这里插入图片描述
    S 信号 和 P 信号:
    在这里插入图片描述
    ACK 信号 和NAck 信号:
    在这里插入图片描述
    数据传输的时候要保证SDA 总线上数据稳定:
    在这里插入图片描述
    发送数据还是命令室友D/C# 引脚决定的: D/C# = 1 发送data , D/C# = 0 发送的是命令;

    在这里插入图片描述

    在这里插入图片描述在这里插入图片描述

    在这里插入图片描述

    SSD 1306 显存大小: 128列 * 64行

    在这里插入图片描述
    如果要驱动SSD1306 首先要实现IIC 总线的驱动代码,即IIC 协议的实现,如果使用的是stm32单片机 来驱动的话,其实可以直接使用硬件IIC,当然了软件IIC也是可以的,本篇我们就先以软件模拟IIC来驱动他。

    2. 软件模拟IIC 实现

    2.1

    #define SCL_Pin GPIO_Pin_6
    #define SDA_Pin GPIO_Pin_7
    #define IIC_GPIO_Port GPIOB
    
    
    #define OLED_SCLK_LOW   GPIO_ResetBits(GPIOB,GPIO_Pin_6)
    #define OLED_SCLK_HIGH  GPIO_SetBits(GPIOB,GPIO_Pin_6)
    #define OLED_SDA_LOW    GPIO_ResetBits(GPIOB,GPIO_Pin_7)
    #define OLED_SDA_HIGH   GPIO_SetBits(GPIOB,GPIO_Pin_7)
    
    void OLED_Config(void)
    {              
      GPIO_InitTypeDef GPIO_InitStructure;
      RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); 
         
      GPIO_InitStructure.GPIO_Pin = SCL_Pin|SDA_Pin;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(IIC_GPIO_Port, &GPIO_InitStructure);
    }
    
    /*IIC  起始信号*/
    void i2c_start()
    {
        OLED_SCLK_HIGH;
    
        OLED_SDA_HIGH;
        OLED_SDA_LOW;
    
        OLED_SCLK_LOW;
    }
    
    /*IIC 停止信号*/
    void i2c_stop()
    {
        OLED_SCLK_HIGH;
    
        OLED_SDA_LOW;
        OLED_SDA_HIGH;
        
    }
    
    /*等待应答:提供一个scl 时钟周期*/
    void i2c_wait_ack(void)
    {
        OLED_SCLK_HIGH;
        OLED_SCLK_LOW;
    }
    
    void Write_IIC_Byte(unsigned char IIC_Byte)
    {
    	unsigned char i;
    	unsigned char m,da;
    	da=IIC_Byte;
    	OLED_SCLK_LOW;
    	for(i=0;i<8;i++)		
    	{
    		m=da;
    		m=m&0x80;
    		if(m==0x80)
    		{
    			OLED_SDA_HIGH;
    		}
    		else 
    			OLED_SDA_LOW;
    		da=da<<1;
    		OLED_SCLK_HIGH;
    		OLED_SCLK_LOW;
    	}
    }
    
    /**********************************************
    // IIC Write Command
    **********************************************/
    void Write_IIC_Command(unsigned char IIC_Command)
    {
    	i2c_start();
    	Write_IIC_Byte(0x78);       //Slave address,SA0=0
    	i2c_wait_ack();	
    	Write_IIC_Byte(0x00);		//write command
    	i2c_wait_ack();	
    	Write_IIC_Byte(IIC_Command); 
    	i2c_wait_ack();	
    	i2c_stop();
    }
    
    /**********************************************
    // IIC Write Data
    **********************************************/
    void Write_IIC_Data(unsigned char IIC_Data)
    {
    	i2c_start();
    	Write_IIC_Byte(0x78);			//D/C#=0; R/W#=0
    	i2c_wait_ack();	
    	Write_IIC_Byte(0x40);			//write data
    	i2c_wait_ack();	
    	Write_IIC_Byte(IIC_Data);
    	i2c_wait_ack();	
    	i2c_stop();
    }
    
    void OLED_WR_Byte(unsigned dat,unsigned cmd)
    {
    	if(cmd)
    	{
    		Write_IIC_Data(dat);
    	}
    	else 
    	{
    		Write_IIC_Command(dat);	
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    3. OLED 设备驱动程序

    
    void OLED_Init(void)
    { 
     	OLED_Config();
     	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);	
    
    	delay_ms(100);
    	OLED_WR_Byte(0xAE,OLED_CMD);//--display off
    	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  
    	OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
    	OLED_WR_Byte(0x81,OLED_CMD); // contract control
    	OLED_WR_Byte(0xFF,OLED_CMD);//--128   
    	OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap 
    	OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
    	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    	OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
    	OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
    	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
    	OLED_WR_Byte(0x00,OLED_CMD);//
    	
    	OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
    	OLED_WR_Byte(0x80,OLED_CMD);//
    	
    	OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
    	OLED_WR_Byte(0x05,OLED_CMD);//
    	
    	OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
    	OLED_WR_Byte(0xF1,OLED_CMD);//
    	
    	OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
    	OLED_WR_Byte(0x12,OLED_CMD);//
    	
    	OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
    	OLED_WR_Byte(0x30,OLED_CMD);//
    	
    	OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
    	OLED_WR_Byte(0x14,OLED_CMD);//
    	
    	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
    }  
    
    
    void OLED_Set_Pos(unsigned char x, unsigned char y)
    {
    	OLED_WR_Byte(0xb0+y,OLED_CMD);
    	OLED_WR_Byte(((x&0xF0) >> 4)|0x10, OLED_CMD);
    	OLED_WR_Byte( (x&0x0F), OLED_CMD);
    }
    
    void OLED_Display_On(void)
    {
    	OLED_WR_Byte(0x8D,OLED_CMD);  //设置电荷泵
    	OLED_WR_Byte(0x14,OLED_CMD);  //开启电荷泵
    	OLED_WR_Byte(0xAF,OLED_CMD);  //OLED唤醒
    }
    
    void OLED_Display_Off(void)
    {
    	OLED_WR_Byte(0x8D,OLED_CMD);	//设置电荷泵
    	OLED_WR_Byte(0x10,OLED_CMD);    //关闭电荷泵
    	OLED_WR_Byte(0xAE,OLED_CMD);    //关闭屏幕显示
    }
    				    
    void OLED_Clear(void)
    {
    	u8 i = 0, n = 0; 
    	
    	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(0,OLED_DATA);
    	}
    }
    
    
    void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 Char_size)
    {
    	unsigned char c = 0, i = 0;
    	c = chr - ' ';
    	
    	if(x > 128 - 1)
    	{
    		x = 0;
    		y += 2;
    	}
    	
    	if(Char_size == 8)
    	{
    	    OLED_Set_Pos(x,y);
    		for(i = 0; i < 8; i++)
    		 OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);		//显示低字节
    		
    		OLED_Set_Pos(x,y+1);
    		for(i = 0; i < 8; i++ )
    		  OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);   //显示高字节
    	}
    	else 
    	{
    		 字体为6号
    		OLED_Set_Pos(x,y);
    		for(i=0;i<6;i++)
    			OLED_WR_Byte(F6x8[c][i],OLED_DATA);
    	}
    }
    
    void OLED_ShowString(u8 x, u8 y,u8* chr, u8 Char_size)
    {
    	while(*chr != '\0')
    	{
    		OLED_ShowChar(x,y,*chr, Char_size);
    		x+=Char_size;
    		
    		if(x > 120)
    		{
    			x = 0;
    			y += 2;
    		}	
    		chr++;
    	}
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
  • 相关阅读:
    STM32MP157F-DK2 使用体验
    笔试强训2
    Canmv K210开发板案例——人脸识别
    学习集合工具类CollectionUtils——List对象案例
    (附源码)计算机毕业设计SSM基于框架的动漫设计
    Hadoop基础入门
    selenium-webdriver-Chrome新驱动地址(Chrome115及以上版本)
    机器人入门(二)
    关于如何设置好记且复杂度高的密码
    jdbc-升级 水果库存系统 BaseDao 添加 执行复杂查询方法
  • 原文地址:https://blog.csdn.net/xiaolz88/article/details/134490150