STM32:IIC详解
文章目录
- 1.IIC历史
- 2.IIC优劣势
- 3.IIC时序
- 3.1 初始化IIC
- 3.2 产生IIC起始信号
- 3.3 产生IIC停止信号
- 3.4 等待ACK
- 3.5 产生ACK / 不产生ACK
- 3.6 IIC发送一个字节
- 3.7 IIC读取一个字节
- 3.8 IIC发送多字节
- 3.9 IIC读取多字节
- 4 IIC问题
- 4.1 STM32 HAL库的IIC设备地址没有左移
1.IIC历史
IIC (Inter-Integrated Circuit), 通常发音为: I-squared-C,是多主、多从、分组交换、单工的串行总线,通常用于处理器或微控制器与低速外围器件的短距离通信连接。这个通信协议是由(NXP半导体的前生)飞利浦公司发明,并在2006年开始,I2C协议的实施不要许可费,但仍要收取I2C从机分配地址的费用。
Philips Semiconductors(现为 NXP Semiconductors)开发了一种简单的用于高效 IC 间控制的双向 2 线总线,称为 Inter-IC 或 I2C总线。只需要两条总线线:一条串行数据线 (SDA) 和一条串行数据线时钟(SCL)。串行、8 位定向、双向数据传输可以是 在标准模式下以高达 100 kbit/s 的速度制造,在快速模式下高达 400 kbit/s,向上在增强型快速模式(Fm+) 中高达 1 Mbit/s,在高速模式下高达 3.4 Mbit/s。超快速模式是一种单向模式,数据传输速度高达 5 Mbit/s。
I2C 总线是事实上的世界标准,现在已在 1000 多种不同的由 50 多家公司制造的 IC。此外,多功能 I2C 总线用于各种控制架构,如系统管理总线 (SMBus)、电源管理总线 (PMBus)、智能平台管理接口 (IPMI)、显示器数据通道 (DDC) 和高级电信计算架构 (ATCA)。
IIC 协议规范英文版(NXP):https://www.nxp.com/docs/en/user-guide/UM10204.pdf
2.IIC优劣势
优点:硬件资源节约,协议设计精巧,易用,多用与传输命令等,使用广泛易移植。
缺点:传输速率较慢。
3.IIC时序
3.1 初始化IIC
//初始化IIC
void PCA9557_Init(void)
{
RCC->APB2ENR |= 1<<4;//先使能外设IO PORTC时钟
GPIOC->CRH &= 0XFFFF00FF;//PC10/11 推挽输出
GPIOC->CRH |= 0X00003300;
GPIOC->ODR |= 1<<10; //PC10 输出高
GPIOC->ODR |= 1<<11; //PC11 输出高
}
3.2 产生IIC起始信号
//产生IIC起始信号
void PCA9557_Start(void)
{
PCA9557_SDA_OUT(); //sda线输出
PCA9557_SDA=1;
PCA9557_SCL=1;
delay_us(4);
PCA9557_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
PCA9557_SCL=0;//钳住I2C总线,准备发送或接收数据
}
3.3 产生IIC停止信号
//产生IIC停止信号
void PCA9557_Stop(void)
{
PCA9557_SDA_OUT();//sda线输出
PCA9557_SCL=0;
PCA9557_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
PCA9557_SCL=1;
PCA9557_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
3.4 等待ACK
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t PCA9557_Wait_Ack(void)
{
uint8_t ucErrTime=0;
PCA9557_SDA_IN(); //SDA设置为输入
PCA9557_SDA=1;delay_us(1);
PCA9557_SCL=1;delay_us(1);
while(PCA9557_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
PCA9557_Stop();
return 1;
}
}
PCA9557_SCL=0;//时钟输出0
return 0;
}
3.5 产生ACK / 不产生ACK
//产生ACK应答
void PCA9557_ACK(void)
{
PCA9557_SCL=0;
PCA9557_SDA_OUT();
PCA9557_SDA=0;
delay_us(2);
PCA9557_SCL=1;
delay_us(2);
PCA9557_SCL=0;
}
//不产生ACK应答
void PCA9557_NAck(void)
{
PCA9557_SCL=0;
PCA9557_SDA_OUT();
PCA9557_SDA=1;
delay_us(2);
PCA9557_SCL=1;
delay_us(2);
PCA9557_SCL=0;
}
3.6 IIC发送一个字节
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
uint8_t PCA9557_Send_Byte(uint8_t txd)
{
uint8_t t;
PCA9557_SDA_OUT();
PCA9557_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if (txd&0x80) PCA9557_SDA = 1;
else PCA9557_SDA = 0;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
PCA9557_SCL=1;
delay_us(2);
PCA9557_SCL=0;
delay_us(2);
}
t=PCA9557_Wait_Ack(); //这里固定只要写字节就需要等待应答
return (t);
}
3.7 IIC读取一个字节
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t PCA9557_Read_Byte(uint8_t ack)
{
uint8_t i,receive=0;
PCA9557_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
PCA9557_SCL=0;
delay_us(2);
PCA9557_SCL=1;
receive<<=1;
if(PCA9557_READ_SDA)receive |= 1;
delay_us(1);
}
if (!ack)
PCA9557_NAck();//发送nACK
else
PCA9557_ACK(); //发送ACK
return receive;
}
3.8 IIC发送多字节
uint8_t PCA9557Write(uint8_t addr, uint8_t len, uint8_t *pData)
{
uint8_t i;
PCA9557_Start();
if(PCA9557_Send_Byte(PCA9557_ADDR_WRITE)==1)
{
PCA9557_Stop();
return 0;
}
if(PCA9557_Send_Byte(addr)==1)
{
PCA9557_Stop();
return 0;
}
for(i = 0; i < len; i++)
{
if(PCA9557_Send_Byte(pData[i])==1)
{
PCA9557_Stop();
return 0;
}
}
PCA9557_Stop();
return 1;
}
3.9 IIC读取多字节
uint8_t PCA9557Read(uint8_t addr,uint8_t len,uint8_t *pData)
{
uint8_t i;
PCA9557_Start();
if(PCA9557_Send_Byte(PCA9557_ADDR_WRITE)==1)
{
PCA9557_Stop();
return 0;
}
if(PCA9557_Send_Byte(addr)==1)
{
PCA9557_Stop();
return 0;
}
PCA9557_Start();
if(PCA9557_Send_Byte(PCA9557_ADDR_READ)==1)
{
PCA9557_Stop();
return 0;
}
for(i=0; i<len-1; i++)
{
pData[i] = PCA9557_Read_Byte(1);
}
pData[len-1] = PCA9557_Read_Byte(0);
PCA9557_Stop();
return 1;
}
4 IIC问题
4.1 STM32 HAL库的IIC设备地址没有左移
#define I2C_7BIT_ADD_WRITE(__ADDRESS__) ((uint8_t)((__ADDRESS__) & (uint8_t)(~I2C_OAR1_ADD0)))
#define I2C_7BIT_ADD_READ(__ADDRESS__) ((uint8_t)((__ADDRESS__) | I2C_OAR1_ADD0))
底层中并没有把设备的地址左移,而是直接把最低位改为“0”或“1”,需要自己把器件地址的左移了一位。
stat = HAL_I2C_Mem_Read(&hi2c1,(PCA9557_I2C_BUS_ADDR << 1),PCA9557_INPUT_REG,1,&io_statu,1,0xFF);
if(stat != 0 )
{
LOG_INFO("io %d Read Error STAT %d\r\n",io,stat);
}