前言

一、项目涉及的内容

项目简介

二、模块实操

1. IIC模块

1.1 IIC协议格式

1.2 开始信号与停止信号

1.3 写数据

1.3.1 硬件IIC代码编写

1.3.2 软件模拟IIC代码编写

2. OLED板块


前言

本篇文章对使用IIC协议操作OLED屏幕进行了总结。


一、项目涉及的内容

本项实验的硬件组成有STM32F103C8T6开发板、OLED模块(0.96寸4针IIC接口OLED显示屏),设计到的软件模块有GPIO、IIC、系统定时器SysTick,接下来对这些主要模块进行讲解,回顾项目的重点,希望大家也能有所收获。

项目简介

通过IIC进行数据传输,操控OLED屏幕显示文字、图像。


二、模块实操

1. IIC模块

IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。一个 I2C 总线只需要使用两条总线线路就可以进行通讯,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。数据线SDA即用来表示数据,时钟线SCL用于数据收发同步。SCL由主机产生,SDA由主机或者从机产生。

I2C是同步串行通信,同时它属于半双工,也就是说同一时间SDA只能由一个设备发送信号。IIC是以字节的方式进行传输的,每次传输8位(一个字节)。

接口是通过SCL与SDA连接到IIC总线上的,接口可以下述4种模式中的一种运行:
● 从发送器模式
● 从接收器模式
● 主发送器模式
● 主接收器模式
模块默认地工作于从模式。接口在生成起始条件后自动地从从模式切换到主模式;当仲裁丢
失(两个及以上的主设备要同时通信时的优先级判定)或产生停止信号时,则从主模式切换到从模式。主模式时, I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。

1.1 IIC协议格式

IIC的通讯方式简单概括一下就是:

  1. 主机产生开始信号,跟在起始条件后的1或2个字节是地址(7位模式为1个字节, 10位模式为2个字节),字节的最后一位确定读/写。从机I2C接口能识别它自己的地址(7位或10位)和广播呼叫地址,I2C中与从机地址相同的从机开始与主机进行通讯。
  2. 第二个字节传送从机寄存器地址,也就是我们要往哪个地址传输内容,分为数据寄存器和命令寄存器两种,跟名字一样传输到对应寄存器表示要传输数据/命令。
  3. 第三个字节开始才是传送给从机数据看命令是多少或者数据是多少,主机每发送完一
    个字节数据,都要等待从机的应答信号(ACK),重复这个过程,可以向从机传输 N 个数据,
    这个 N 没有大小限制。

图片来源:零死角玩转 STM32F103—霸道>>>第24章  I2C—读写EEPROM>>>图 24-4 I2C 通讯复合格式


1.2 开始信号与停止信号

I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答
信号。
开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据
结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号可以不要。

 图片来源:STM32F1xx中文参考手册>>>24 I2C接口>>>24.3 I2C功能描述>>>24.3.1 模式选择

IIC通讯中,数据和地址都是按8位/字节进行传输,高位在前。跟在起始条件后的1或2个字节是地址(7位模式为1个字节, 10位模式为2个字节)。地址只在主模式发送。我们选择的是7位地址模式,可以看到我们发送起始信号之后,在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。


1.3 写数据

IIC通讯有软件的方式和硬件的方式两种,软件方式引脚定义会更灵活,硬件方式引脚是固定的,但通讯的效率会更高一些。

1.3.1 硬件IIC代码编写

STM32的IIC片上外设专门负责实现IIC通讯协议,只要配置好这个外设,它就会自动根据协议的要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器就能完成数据收发。

stm32F1系列的硬件I2C外设默认情况下使用PB6和PB7两个引脚。

图片来源:STM32F1xx中文参考手册 >>> 8.3.9 I2C1 复用功能重映射

我们分别配置GPIO口与I2C外设的结构体成员,然后调用库函数GPIO_Init()与 I2C_Init() 把结构体的配置写入到寄存器中。

//硬件IIC初始化配置
void I2C_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;I2C_InitTypeDef I2C_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE);      //PB6 --SCL; PB7 --SDAGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOB, &GPIO_InitStructure); //初始化GPIO配置I2C_DeInit( I2C1);    //复位初始化IIC的配置I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能响应I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //指定地址长度,可为7或10I2C_InitStructure.I2C_ClockSpeed = 400000; //设置SCL时钟频率,400kHzI2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,可选low/high = 2:0或16:9I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //指定工作模式为IIC,可选IIC模式及SMBUS模式I2C_InitStructure.I2C_OwnAddress1 = 0X10; //自身的IIC设备地址I2C_Init( I2C1, &I2C_InitStructure); //初始化IIC外设配置I2C_Cmd( I2C1, ENABLE); //使能IIC外设
}

IIC主机发送符合协议内容的数据出去之后,IIC外设会产生相应的时间EVx,也就是IIC外设的相关寄存器会自动置0或置1,我们只要去读取对应的寄存器就可以确定事件是否发生。

 图片来源:STM32F1xx中文参考手册>>>24 I2C接口>>>24.3.1 模式选择>>>图245 主发送器传送序列图

图片来源:STM32F1xx中文参考手册>>>24 I2C接口>>>24.3.3 I2C主模式>>>图242 I2C的功能框图

我们要发送数据,要做的有以下几件事情:

  1. 先检查IIC总线是否有被其他的主机使用
  2. 如果IIC总线处于空闲状态,我们就使用库函数I2C_GenerateSTART()产生IIC起始信号
  3. 发送从机地址
  4. 发送寄存器地址,确定是要输入命令还是数据内容
  5. 往命令/数据寄存器发送数据内容
  6. 关闭IIC总线,让IIC总线重新进入空闲状态等待被主机激活

其中除了一开始检查IIC总线是否处于繁忙状态外,每一件事情我们都要去检测对应的EVx事件是否发生,等待每一个事情触发事件后我们继续进行下一步,检测的函数以及对应的事件都已经在文件stm32f10x_i2c.h与stm32f10x_i2c.c中定义好了。

//硬件IIC写字节
void I2C_WriteByte(uint8_t addr, uint8_t data)
{//先检查I2C总线是否繁忙while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY)); //开启I2C1I2C_GenerateSTART( I2C1, ENABLE);   while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //检查是否应答,等待触发EV5//发送器件地址I2C_Send7bitAddress( I2C1, Slave_Address, I2C_Direction_Transmitter);  while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待触发EV6//发送寄存器地址I2C_SendData( I2C1, addr);    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); //等待触发EV8//发送数据I2C_SendData( I2C1, data);   while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) ); //等待触发EV8_2//关闭I2C1总线I2C_GenerateSTOP( I2C1, ENABLE);
}

1.3.2 软件模拟IIC代码编写

我们也可以直接控制STM32的两个GPIO引脚分别用作SCL及SDA,然后按照上述信号的时序要求,遵循通讯协议,控制引脚的输出(如果是接收数据则是读取SDA电平),就可以实现IIC通讯。直接控制 GPIO 引脚电平产生通讯时序,由 CPU 控制每个时刻的引脚状态,这种方式称之为“软件模拟协议”方式。

我们进行初始化配置的时候,就不需要配置IIC外设了,只需要对GPIO口进行配置就可以了。

//软件模拟IIC协议初始化
void OLED_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);//PB0 --SCL; PB1 --SDA//I2C支持多个主设备与多个从设备连接在同一根总线上,如果不开漏输出,会出现短路现象GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init( GPIOB, &GPIO_InitStructure);OLED_SCLK_Set(); //初始化时钟总线高电平OLED_SDIN_Set(); 初始化总线高电平
}

软件模拟IIC时序的话,就要按照IIC协议规定的方式严格执行控制引脚的输出。

 图片来源:STM32F1xx中文参考手册>>>24 I2C接口>>>24.3 I2C功能描述>>>24.3.1 模式选择

//模拟I2C起始信号
static void OLED_IIC_Start(void)
{OLED_SCLK_Set(); //时钟总线高电平OLED_SDIN_Set(); //数据总线高电平us_delay(1);OLED_SDIN_Clr();us_delay(1);OLED_SCLK_Clr();us_delay(1);
}//模拟I2C结束信号
static void OLED_IIC_Stop(void)
{OLED_SDIN_Clr(); //数据总线低电平us_delay(1);OLED_SCLK_Set(); //时钟总线高电平us_delay(1);OLED_SDIN_Set();us_delay(1);
}//模拟I2C读取从机应答信号
static unsigned char IIC_Wait_Ack(void)
{unsigned char ack;OLED_SCLK_Clr(); //时钟总线低电平us_delay(1);OLED_SDIN_Set(); //数据总线高电平us_delay(1); OLED_SCLK_Set(); //时钟总线高电平us_delay(1);if(OLED_READ_SDIN()) //无应答{ack = IIC_NO_ACK;}else{ack = IIC_ACK;}       OLED_SCLK_Clr(); //时钟线置低us_delay(1);    return ack;
}

传输时, SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“ 1”,为低电平时表示数据“0”。当 SCL为低电平时, SDA 的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。传输数据的时候,将SCL置低,然后设置SDA总线对应的引脚电平为高/低,SDA电平确定后再讲SCL置高,将8个位由高到低依次发送出去。

//IIC传输一个字节,传输时先从高位开始
static void Write_IIC_Byte(unsigned char IIC_Byte)
{unsigned char i; //定义变量for(i=0;i<8;i++){OLED_SCLK_Clr();  //时钟线置低us_delay(1);if(IIC_Byte & 0x80) //读取最高位OLED_SDIN_Set();  //最高位为1elseOLED_SDIN_Clr();  //最高位为0us_delay(1);OLED_SCLK_Set(); //时钟线置高,产生上升沿把数据送出去us_delay(1);IIC_Byte <<= 1; //数据左移一位}OLED_SCLK_Clr();  //时钟线置低us_delay(1);IIC_Wait_Ack();   //从机等待(这边应该是要监测返回值是否正确,当不应答时重新进行连接或者暂停传输数据)
}

2. OLED板块

我们用的0.96寸OLED显示屏芯片是SSD1306,这里我们将OLED屏幕作为从机,数据和应答都是通过SDA发送的,SSD1306的IIC通讯接口的数据总线是由SDAout和SDAin输入组成的,SDAin和SDAout绑定到了一起作为SDA,SDAout引脚可以不连接,当不连接时,应答信号将会被IIC总线忽略。

当主机通过开始条件初始化IIC通讯,并且广播的从机地址与我们要接收数据的从机地址相同时,发起开始信号的主机与从机双方将建立通讯,控制字节或数据字节开始通过SDA传输。

数据位的解读是基于D/C#位引脚的输入,如果 D/C#引脚是高,D[7:0]就被解读为写到图像显示数据 RAM( GDDRAM)中的显示数据,GDDRAM 列地址指针将会在每次数据写之后自动加 1。
如果是低, D[7:0]的输入就被解读为一个命令。然后数据输入就会被解码并写到相关的命令
寄存器中。

GDDRAM 是一个为映射静态 RAM 保存位模式来显示。该 RAM 的大小为 128 * 64 为, RAM
分为 8 页,从 PAFE0 到 PAGE7,用于单色 128 * 64 点阵显示,如下图所示

图片来源:SSD1306-OLED驱动芯片中文手册

SSD1306 中有三种不同的内存地址模式:页地址模式,水平地址模式,垂直地址模式。我们使用页地址模式,在页地址模式下,在显示 RAM 读写之后,列地址指针自动加一。如果列地址指针达到了列的结束地址,列地址指针重置为列开始地址并且页地址指针不会改变。用户需要设置新的页
和列地址来访问下一页 RAM 内容
。页地址模式下 PAGE 和列地址指针的移动模式参考下图

使用下面的步骤来定义开始 RAM 访问的位置:
1. 通过命令 B0h 到 B7h 来设置目标显示位置的页开始地址
2. 通过 00h~0Fh 来设置低开始列地址的指针
3. 通过命令 10h~1Fh 来设置高开始列地址
比如说,如果页地址设置为 B2h,低列地址是 03h 高列地址为 00h,那么就意味着开始列是
PAGE2 的 SEG3.RAM 访问指针的位置如下图所示。输出数据字节将写到 RAM 列 3 的位置。

OLED屏幕的初始化代码用到的指令都是芯片手册上有固定的格式,厂家一般会提供初始化代码,格式略有不同,这部分就不贴出来了。

下边代码分别是设置屏幕显示的位置、显示字符、显示字符串、显示中文、显示图片

  • 设置图像显示的起点行、列
//设置数据写入的起始行,列
//x:列的起始低地址与起始高地址
//y:起始页地址  0-7
void OLED_Set_Pos(unsigned char x, unsigned char y)
{OLED_WR_Byte( 0xb0+y, OLED_CMD); //写入页地址OLED_WR_Byte( x&0x0f, OLED_CMD); //写入列的地址,低半个字节OLED_WR_Byte( (x&0xf0)>>4 | 0x10, OLED_CMD); //写入列地址,高半个字节
}
  • 设置OLED屏幕显示ASCII码字符,有8*16与6*8两种字符大小
//显示字符
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr)
{unsigned char j = 0;chr = chr - ' '; //获取字符的偏移量if( x >= Max_Column)//让x限制在128列内x=0;   if( SIZE == 16) //字符8*16(高度是16,所以要分为两个上下两部分来发送){     OLED_Set_Pos(x,y); //上半段for(j=0; j<8; j++){OLED_WR_Byte( F8X16[chr*16+j],OLED_DAT);} OLED_Set_Pos(x,y+1); //下半段for(j=0; j<8; j++){OLED_WR_Byte( F8X16[chr*16+j+8],OLED_DAT);}           }else if( SIZE == 6) //字符6*8{OLED_Set_Pos(x,y);for(j=0; j<6; j++)OLED_WR_Byte( F6x8[chr*8][j],OLED_DAT);}       }   
  • 设置OLED屏幕显示字符串
//显示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr)
{unsigned char j=0;while(chr[j] != '\0') //判断是不是最后一个字符{if(SIZE == 16){if( x >= Max_Column){x=0;y=y+2;} OLED_ShowChar(x,y,chr[j]);  //显示字符x=x+8; //一个字符占8列}else if(SIZE == 6){if( x >= Max_Column){x=0;y=y+1;}OLED_ShowChar(x,y,chr[j]); //显示字符          x=x+6; //一个字符占6列          }j++; //下一个字符}}
  • 设置OLED屏幕显示中文字体
//显示文字
void OLED_ShowChinese(unsigned char x, unsigned char y, unsigned char list)
{unsigned char i=0;OLED_Set_Pos(x, y); //中文字体上半部分for(i=0;i<16;i++){OLED_WR_Byte( F16x16[list*32+i], OLED_DAT); //一个字体占用了16+16个字节,所以是list乘以32,下半部分就再加上16}OLED_Set_Pos(x,y+1); //中文字体下半部分for(i=0;i<16;i++){OLED_WR_Byte( F16x16[list*32+16+i], OLED_DAT);}
}
  • 设置OLED屏幕显示bmp格式图像
//显示图片
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{unsigned int j =0;unsigned char x,y;if(y1%8 == 0)y = y1/8;elsey = y1/8 +1;for(y=y0;y<y1;y++){OLED_SetPos(x0,y); for(x=x0;x<x1;x++){WriteDat(BMP[j++]);}}
}

STM32基于IIC协议的OLED模块的使用相关推荐

  1. STM32基于IIC通信协议的OLED模块使用(详解)

    目录 前言 一.项目内容 实验简介 二.IIC模块 1.IIC协议简介 2.物理层 3.协议层 4.硬件IIC代码配置 5.软件模拟IIC配置 1.起始信号与停止信号 2.从机应答信号 3.数据的有效 ...

  2. [STC89C52RC]基于IIC协议的OLED显示字符

    目录 起始信号 终止信号 应答信号 选择某一点点亮 页模式: 水平模式: 垂直模式: 选择行数 ​编辑  选择列数 清屏 Oled显示,我们会用到字模软件 显示图片 IIC协议 1.1 IIC协议概述 ...

  3. iic裸机与linux通信,基于IIC协议的Linux操作系统与裸机通信的方法

    主权项: 1.一种基于IIC协议的Linux操作系统与裸机通信的方法,其特征在于,包括如下步骤:步骤S1,对采用Linux系统的多个硬件和裸机同时挂载在总线上,包括:配置一个主机设备和多个从机设备,每 ...

  4. STM32模拟IIC协议驱动AD7991/AD7995/AD7999芯片

    STM32模拟IIC协议驱动AD7991/AD7995/AD7999芯片 AD7991/AD7995/AD7999是12位/10位/8位 4通道模拟输入的AD转换芯片 通信方式采用的是:I2C 芯片供 ...

  5. IIC协议与OLED

    1.认识OLED 1.OLED概述: OLED(Organic Light-Emitting Diode,有机发光二极管)是一种显示技术,利用有机材料的发光特性来产生光. OLED显示器由一系列有机材 ...

  6. 基于IIC协议的4脚OLED模块的单片机驱动控制(含驱动程序)

    文章目录 前言 OLED模块工作原理 iiciiciic总线协议 核心代码 51单片机 stm32单片机 总结 前言 嵌入式控制系统中,常常会遇到需要显示控制状态的控制场合,这种场景下往往不需要显示花 ...

  7. STM32驱动MPU6050基于IIC协议

    一. 简介 MPU6050是一款六轴陀螺仪,可以通过IIC协议输出三个方向上的加速度和角速度,在平衡车和飞控中较为常见.一般情况下,模块有用的引脚只有四个(其它的不常用),即VCC,GND,SDA和S ...

  8. STM32通过SPI协议驱动OLED屏

    坚持就是胜利 一.SPI协议介绍 01 简介 02 SPI物理层 03 SPI基本通讯过程 二.OLED显示器介绍 01 简介 02 接口定义 03 与STM32接线图 三.汉字取模软件介绍 01 下 ...

  9. STM32基于SPI接口的OLED数据显示

    文章目录 一.SPI简介 1.1 什么是SPI 1.2 SPI原理 1.3 SPI的连接方式 1.4 协议层 二.OLED 2.1 OLED原理 2.2 点阵编码原理与显示 三.OLED显示实验 3. ...

最新文章

  1. SAP MM初阶之ERS功能展示
  2. cppcheck编译安装命令
  3. 同步中断和异步中断区别
  4. 【AI视野·今日Robot 机器人论文速览 第十二期】Tue, 22 Jun 2021
  5. 去除dataframe中的空行_Python数据分析中的处理与数值加速技巧简介
  6. python读取pdf文件 pdfplumber_Python pdfprumber用于PDF表提取,pythonpdfplumber,表格
  7. [转载] python笔记
  8. PTA题---求两个有序序列中位数所体现的思想。
  9. 记一次PLC和脉冲型伺服电机的接线方法_20210915
  10. 用计算机计算2的31次方,2的31次方,用什么方法可以最快算出来呢
  11. xp下格式化linux分区的硬盘,Windows XP 下格式化和清理磁盘(转)
  12. 习惯养成app_如何培养优秀的开发人员沟通技巧,养成不良习惯
  13. 美团财报电话会:将专注于创造外卖和到店业务的协同效应
  14. Kali Linux使用MSF木马入侵安卓手机
  15. ISO8583报文(一)
  16. Python 实验1-8
  17. 记一次面试准备(续上)
  18. Android高级工程师面试题-字节跳动,含BATJM大厂
  19. MySQL安装版本Navicat连接报错2509解决方案
  20. c++基础知识点(6)类的继承,构造,析构顺序,虚继承等

热门文章

  1. 常用类-String类
  2. 比大厂更香的Offer:有钱,有氛围,也有生活
  3. python中集合运算_Python中的集合操作与集合运算
  4. 02-前端-javaScript
  5. 30岁985大学计算机硕士,985院校硕士的心酸,今年已经30岁了,却还拿着不到5000块的工资...
  6. Henry前端笔记之 vertical-align与line-hight与基线详解
  7. php502bad gateway,经验之谈:nginx php 502 bad gateway 解决方法
  8. ObjectARX学习
  9. Python爬虫系列(二):爬取中国大学排名丁香园-用户名和回复内容淘宝品比价
  10. 昇腾宠粉嘉年华︱爆款豪礼送不停!