#I2C协议#OLED显示
USART多用于板间通信,比如和上位机通信。
IIC多用于板内通信,比如和EEPROM、OLED通信。
我们在使用STM32的IIC时,多半用软件模拟,IIC的硬件部分不太稳定,故不推荐。
IIC的连线图则是MCU(主机)和IIC协议的传感器(从机)和触摸屏(从机)和EEPROM(从机)等连接在SDA总线和SCL总线上。注:两根总线上附有上拉电阻。
SDA:数据总线
SCL:时间总线
IIC总线可以并联多个器件,每一个设备的可以作为主机或者从机,但是一般情况下MCU作为主机。
每一个设备都有对应的唯一一个地址。
IIC总线组成“线与”的关系,任何一个器件都可以拉低电平。
IIC总线时序有起始信号、数据传输和停止信号
IIC开始和结束时序图分析:
起始信号:当SCL为高电平期间,SDA由高到低的跳变;
停止信号:当SCL为高电平期间,SDA由低到高的跳变;
IIC发送数据的时序图分析:
应答位为图中A部分(ACK),ACK简称应答位,表示接收方已经成功接受了该字节。(低电平)
当应答位为NACK,一般表示接收方未能正确接受该字节。(高电平)
数据在时钟信号的上升沿到来之前就需准备好,在下降沿到来之前必须稳定。
IIC协议在此处与OLED连接只需了解MCU通过IIC协议发送命令或数据给OLED。
程序部分:
起始信号和停止信号时序图上图已经演示过,下面是起始和停止的软件部分。
/*-----------------------------------------------
函数名:产生IIC起始信号
参 数:无
返回值:无
------------------------------------------------*/
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住IIC总线,准备发送或者接受数据
}
/*-----------------------------------------------
函数名:产生IIC停止信号
参 数:无
返回值;无
------------------------------------------------*/
void IIC_Stop(void)
{
SDA_OUT();//sda先输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送IIC总线结束信号
delay_us(4);
}
完成起始信号后,SCL和SDA都为低电平。
根据上图,我们给程序延时4us。
数据发送和数据接收:
程序部分:
for(mask=0x80;mask!=0;mask>>=1)
{
if((mask&dat) == 0)
{
IIC_SDA=0;
}
else
IIC_SDA=1;
delay_us(4);
IIC_SCL=1;
delay_us(4);
IIC_SCL=0;
}
比如发送0b10101101
第一次
10101101和10000000相与不等于0 IIC_SDA=1
第二次
10101101和01000000相与等于0 IIC_SDA=0
第三次
10101101和00100000相与等于1 IIC_SDA=1
..........
应答部分程序:
SDA_IN();
IIC_SDA=1;
delay_us(4);
IIC_SCL=1;
ack=READ_SDA;
delay_us(4);
IIC_SCL=0;
return ack;
总的写入从机程序:
/*-----------------------------------------------
函数名;写入1字节
参 数;写入的字节
返回值:ack为0,表示写入成功;ack为1,表示写入失败
------------------------------------------------*/
u8 IICWrite(u8 dat)
{
u8 ack;
u8 mask;
SDA_OUT();
for(mask=0x80;mask!=0;mask>>=1)
{
if((mask&dat) == 0)
{
IIC_SDA=0;
}
else
IIC_SDA=1;
delay_us(4);
IIC_SCL=1;
delay_us(4);
IIC_SCL=0;
}
SDA_IN();
IIC_SDA=1;
delay_us(4);
IIC_SCL=1;
ack=READ_SDA;
delay_us(4);
IIC_SCL=0;
return ack;
}
与OLED的通信:
主机通过总线向从机写入从机地址,观察应答位ACK。
如果ACK返回0,表示正确写入说明从机确实存在。
如果ACK返回0,表示未正确写入说明从机确实不存在。
OLED从机地址共7位,其中高六位确定:011110x(根据oled数据手册得到)
0111100->0x3c 0111101->0x3D
起始信号——发送字节——停止信号判断是否存在从机部分
/*-----------------------------------------------
函数名:验证从机是否存在
参 数:从机的地址
返回值:ack为0,表示从机存在;ack为1,表示从机不存在
------------------------------------------------*/
u8 IICAdress(u8 addr)
{
u8 ack;
IIC_Start();
ack=IICWrite(addr<<1);
IIC_Stop();
return ack;
}
//配置PA7的输入输出模式
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
OLED相关引脚的初始化部分:
/*-----------------------------------------------
函数名:OLED相关引脚初始化
参 数:无
返回值:无
------------------------------------------------*/
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}