Intern IC BUS, Philips公司开发. 同步, 半双工, 带数据应答机制的通信协议. 支持总线挂载多个设备, 支持多主多从模式, 支持一主多从模式.
同步时序: 软件更加的容易模拟, 不必要主机从机发送接收数据那么严格, 也可以被中断打断.
多主多从: 所有设备都可以跳出来宣布, 我是主机, 在总线冲突时, I2C协议会进行仲裁, 胜利的一方取的总线控制权, 失败的一方变为从机.
一主多从: 主机权利: SCL完全控制权, 空闲状态下, 主机可以主动发起对SDA的控制, 只有从机发送数据, 从机应答时, 主机才会转交SDA的控制权给从机.
空闲状态下, SCL和SDA都是高电平.
防止和终止信号冲突, 要先拉高SDA, 再拉高SCL信号进入空闲状态.
数据发送和接收的时候, 是高位先行, 即: 0b1010 1100, 实际发送顺序是先发送1->0->1->0->1->1->0->0.
在SCL高电平的时候就会读取完成, 所以需要再低电平的时候将SDA设置为数据位, 再拉高SCL.
主机可以慢慢发, 只要大于从机接收速度就行, 一位发一年都可以, 接收也是, 只要控制SCL就有绝对的控制权.
接收, 主机要释放SDA的控制权. 由从机掌控. 在SCL为高电平的时候, 主机读取.
在从机发送数据后, 主机接收到了需要发送应答信号, 如果为0表示应答, 如果为1表示非应答.
应答: 让从机发送继续.
非应答: 让从机停止发送.
主机发送数据后, 需要读取从机的应答信号. 如果为0表示应答, 如果为1表示非应答.
应答: 从机收到了.
非应答: 从机没收到.
起始信号->从机地址+写位(0为写1为读)->接收应答->发送写入地址->接收应答->发送数据->接收应答->停止信号.
起始信号-> 从机地址+写位(0为写1为读)->接收应答->发送读入地址->接收应答->起始信号->从机地址+读位->接收应答->读取数据->发送非应答->停止信号
头文件
#ifndef __MYI2C_H
#define __MYI2C_H#include "stm32f10x.h" // Device header
#include "delay.h"
#include "bitBand.h"#define SCL 12
#define SDA 11
void myI2C_Init(void);
void I2C_StartSignal(void);
void I2C_StopSignal(void);
void I2C_SendByte(uint8_t data);
uint8_t I2C_receiveByte(void);
void I2C_SendAck(uint8_t ack);
uint8_t I2C_ReceiveAck(void);#endif
源文件
#include "myI2C.h"
//配置开漏输出模式, 让PA11作为SDA, PA12作为SCL
void myI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//初始全是0
}//起始信号
void I2C_StartSignal(void)
{
PAout(SDA) = 1; //顺序不能错,要和停止信号区分开
Delay_us(5);
PAout(SCL) = 1;
Delay_us(5);
PAout(SDA) = 0;
Delay_us(5);
PAout(SCL) = 0;
Delay_us(5);
}//终止信号
void I2C_StopSignal(void)
{
PAout(SDA) = 0;
Delay_us(5);
PAout(SCL) = 1;
Delay_us(5);
PAout(SDA) = 1;
Delay_us(5);
}//发送一字节数据,高位先行
void I2C_SendByte(uint8_t data)
{
PAout(SCL) = 0;
for(uint8_t i = 0; i < 8; ++i)
{
PAout(SDA) = (data >> (7-i)) & 0x01;
Delay_us(5);
PAout(SCL) = 1;
Delay_us(5);
PAout(SCL) = 0;
Delay_us(5);
}
}//接收一字节数据
uint8_t I2C_receiveByte(void)
{
uint8_t data = 0;
PAout(SCL) = 0;
PAin(SDA) = 1; //释放SDA
Delay_us(5);//等待从机放数据
for(uint8_t i = 0; i < 8; ++i)
{
PAout(SCL) = 1;
Delay_us(5);
data |= PAin(SDA);
if(i != 7)
{
data = (data | PAin(SDA)) << 1;
}
Delay_us(5);
PAout(SCL) = 0;
Delay_us(5);
}
return data;
}//发送应答信号,0应答,1非应答
void I2C_SendAck(uint8_t ack)
{
PAout(SCL) = 0;
PAout(SDA) = ack;
Delay_us(5);
PAout(SCL) = 1;
Delay_us(5);
PAout(SCL) = 0;
}
//接收应答信号,0应答,1非应答
uint8_t I2C_ReceiveAck(void)
{
uint8_t ack = 0;
PAout(SDA) = 1; //必须释放SDA,开漏等于1就是释放
PAout(SCL) = 0;
PAout(SCL) = 1;
Delay_us(5);
ack = PAin(SDA);
Delay_us(5);
PAout(SCL) = 0;
return ack;
}