51单片机(IIC协议OLED屏)
一、IIC协议
1、IIC协议概述
1.1、概述:IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双 工同步通信方式
1.2、特点:简单性和有效性。 由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降 低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件
多主控(multimastering) 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当 然,在任何时间点上只能有一个主控。
1.3、构成:IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控 器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线 的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。
2、时序分析
IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。
起始信号
void IIC_Start()
{
scl = 0;
sda = 1;
scl = 1;
_nop_();
sda = 0;
_nop_();
}
终止信号
void IIC_Stop()
{
sda = 0;
scl = 1;
_nop_();
sda = 1;
_nop_();
}
应答信号
发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字 节; 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功
char IIC_ACK()
{
char flag;
sda = 1;//就在时钟脉冲9期间释放数据线
_nop_();
scl = 1;
_nop_();
flag = sda;
_nop_();
scl = 0;
_nop_();
return flag;
}
数据发送
void IIC_Send_Byte(char dataSend)
{
int i;
for(i = 0;i<8;i++){
scl = 0;//scl拉低,让sda做好数据准备
sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
_nop_();//发送数据建立时间
scl = 1;//scl拉高开始发送
_nop_();//数据发送时间
scl = 0;//发送完毕拉低
_nop_();//
dataSend = dataSend << 1;
}
}
二、oled屏
1、OLED写命令
写命令/数据的代码逻辑思路
1. start()
2. 写入 b0111 1000 0x78
3. ACK
4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
5. ACK
6. 写入指令/数据
7. ACK
8. STOP
void Oled_Write_Cmd(char dataCmd)
{
// 1. start()
IIC_Start();
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
//6. 写入指令/数据
IIC_Send_Byte(dataCmd);
//7. ACK
IIC_ACK();
//8. STOP
IIC_Stop();
}
void Oled_Write_Data(char dataData)
{
// 1. start()
IIC_Start();
// 2. 写入从机地址 b0111 1000 0x78
IIC_Send_Byte(0x78);
// 3. ACK
IIC_ACK();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
IIC_Send_Byte(0x00);
// 5. ACK
IIC_ACK();
///6. 写入指令/数据
IIC_Send_Byte(dataData);
//7. ACK
IIC_ACK();
//8. STOP
IIC_Stop();
}
2、OLED的寻址模式
如何显示一个点?
有三种寻址模式,分别位页地址模式,水平地址模式和垂直地址模式,可以通过一下表格进行配置 内存管理
页地址模式
水平地址模式
垂直地址模式
列地址选择
如果写入0x08(b00001000)会显示什么呢 ?一个字节负责一个Page的一列显示
三、代码实现
#include "reg52.h"
#include "intrins.h"
sbit scl=P0^1;
sbit sda=P0^3;
void IIC_start()
{
scl=0;//防止雪花
sda=1;
scl=1;
_nop_();
sda=0;
_nop_();
}
void IIC_stop()
{
scl=0;
sda=0;
scl=1;
_nop_();
sda=1;
_nop_();
}
char IIC_ACK()
{
char flag;
sda=1;//就在时钟脉冲9期间释放数据线
_nop_();
scl=1;
_nop_();
flag=sda;
_nop_();
scl=0;
_nop_();
return flag;
}
void IIC_Send_Byte(char dataSend)
{
int i;
for(i=0;i<8;i++){
scl=0;//scl拉低做好数据准备
sda=dataSend & 0x80; //1000 0000获得dataSend的最高位,给sda
_nop_();//发送数据建立时间
scl=1;//scl拉高开始传数据
_nop_();
scl=0;//发送完毕拉低
_nop_();
dataSend= dataSend << 1;
}
}
void Oled_Write_Data(char dataData)
{
//1.start
IIC_start();
//2.写入从机地址 0x78
IIC_Send_Byte(0x78);
//3.ACK
IIC_ACK();
//4.control byte: 0000 0000 写入命令 0100 0000 写入数据
IIC_Send_Byte(0x40);
//5.ACK
IIC_ACK();
//6.写入指令或数据
IIC_Send_Byte(dataData);
//7.ACK
IIC_ACK();
//8.STOP
IIC_stop();
}
void Oled_Write_Cmd(char dataCmd)
{
//1.start
IIC_start();
//2.写入从机地址 0x78
IIC_Send_Byte(0x78);
//3.ACK
IIC_ACK();
//4.control byte: 0000 0000 写入命令 0100 0000 写入数据
IIC_Send_Byte(0x00);
//5.ACK
IIC_ACK();
//6.写入指令或数据
IIC_Send_Byte(dataCmd);
//7.ACK
IIC_ACK();
//8.STOP
IIC_stop();
}
void Oled_Init(void){
Oled_Write_Cmd(0xAE);//--display off
Oled_Write_Cmd(0x00);//---set low column address
Oled_Write_Cmd(0x10);//---set high column address
Oled_Write_Cmd(0x40);//--set start line address
Oled_Write_Cmd(0xB0);//--set page address
Oled_Write_Cmd(0x81); // contract control
Oled_Write_Cmd(0xFF);//--128
Oled_Write_Cmd(0xA1);//set segment remap
Oled_Write_Cmd(0xA6);//--normal / reverse
Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
Oled_Write_Cmd(0x3F);//--1/32 duty
Oled_Write_Cmd(0xC8);//Com scan direction
Oled_Write_Cmd(0xD3);//-set display offset
Oled_Write_Cmd(0x00);//
Oled_Write_Cmd(0xD5);//set osc division
Oled_Write_Cmd(0x80);//
Oled_Write_Cmd(0xD8);//set area color mode off
Oled_Write_Cmd(0x05);//
Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
Oled_Write_Cmd(0xF1);//
Oled_Write_Cmd(0xDA);//set com pin configuartion
Oled_Write_Cmd(0x12);//
Oled_Write_Cmd(0xDB);//set Vcomh
Oled_Write_Cmd(0x30);//
Oled_Write_Cmd(0x8D);//set charge pump enable
Oled_Write_Cmd(0x14);//
Oled_Write_Cmd(0xAF);//--turn on oled panel
}
void Oled_Clear() //清屏函数
{
int i;
int j;
for(i=0;i<8;i++){
Oled_Write_Cmd(0xB0+ i);//page0--page7
//每个page从0列
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
//到127列,依次写入0,每写入数据,列地址自动偏移
for(j=0;j<128;j++){
Oled_Write_Data(0);
}
}
}
void Oled_Show_Image(unsigned char *image)//显示一张图
{
unsigned int i;
unsigned int j;
for(i=0;i<8;i++){
Oled_Write_Cmd(0xB0+ i);//page0--page7
//每个page从0列
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
//到127列,依次写入0,每写入数据,列地址自动偏移
for(j=128*i;j<128 * (i+1);j++){
Oled_Write_Data(image[j]);
}
}
}
void main()
{
//1.OLED初始化
Oled_Init();
//2.选择一个位置
//2.1确认页寻址模式
Oled_Write_Cmd(0x20);
Oled_Write_Cmd(0x02);
Oled_Clear();//清屏函数
//显示一条线
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
Oled_Write_Data(0x08);
while(1);//不让程序退出
}