IIC 简介
IIC (Inter-Integrated Circuit) 总线是一种由 NXP (原PHILIPS) 公司开发的两线式串行通信总线,多用于主控制器和从设备之间的主从式通信,且任意时刻只能有一个主机。
IIC总线由数据线 SDA 和时钟线 SCL 构成,可实现在 CPU 与被控 IC 之间、IC 与 IC 之间双向通信,高速 IIC 总线一般可达 400kbps 以上。
为驱动实验室自研基于 DAC60502 的双通道电流输出模块,基于 ZYNQ 平台,将 PS 端的 GPIO 资源通过 EMIO 引出到 PL 端,使用 PL 端引脚资源连接 IIC 从设备,既使用 EMIO 模拟 IIC 实现。
模拟 IIC 示例代码
- iic_io.c
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#include "iic_io.h"
extern XGpioPs GpioPs;
void SCL_OUT(void)
{
XGpioPs_SetDirectionPin(&GpioPs, SCL_PIN, GPIO_MODEL_OUTPUT);
XGpioPs_SetOutputEnablePin(&GpioPs, SCL_PIN, GPIO_OUTPUT_ENABLE);
}
void SCL_HIGH(void)
{
XGpioPs_WritePin(&GpioPs, SCL_PIN, GPIO_SET);
}
void SCL_LOW(void)
{
XGpioPs_WritePin(&GpioPs, SCL_PIN, GPIO_RESET);
}
void SDA_IN(void)
{
XGpioPs_SetDirectionPin(&GpioPs, SDA_PIN, GPIO_MODEL_INPUT);
}
uint32_t SDA_READ(void)
{
return XGpioPs_ReadPin(&GpioPs, SDA_PIN);
}
void SDA_OUT(void)
{
XGpioPs_SetDirectionPin(&GpioPs, SDA_PIN, GPIO_MODEL_OUTPUT);
XGpioPs_SetOutputEnablePin(&GpioPs, SDA_PIN, GPIO_OUTPUT_ENABLE);
}
void SDA_HIGH(void)
{
XGpioPs_WritePin(&GpioPs, SDA_PIN, GPIO_SET);
}
void SDA_LOW(void)
{
XGpioPs_WritePin(&GpioPs, SDA_PIN, GPIO_RESET);
}
int IIC_Init(void)
{
SCL_OUT();
SCL_HIGH();
SDA_OUT();
SDA_HIGH();
return 0;
}
- iic_io.h
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#ifndef __IIC_IO_H__
#define __IIC_IO_H__
#include "xgpiops.h"
#define SCL_PIN 54
#define SDA_PIN 55
#define GPIO_RESET 0
#define GPIO_SET 1
#define GPIO_MODEL_INPUT 0
#define GPIO_MODEL_OUTPUT 1
#define GPIO_OUTPUT_DISABLE 0
#define GPIO_OUTPUT_ENABLE 1
void SCL_OUT(void);
void SCL_HIGH(void);
void SCL_LOW(void);
void SDA_IN(void);
uint32_t SDA_READ(void);
void SDA_OUT(void);
void SDA_HIGH(void);
void SDA_LOW(void);
int IIC_Init(void);
#endif
- iic_ctrl.c
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#include "iic_ctrl.h"
#include "sleep.h"
#define I2C_DELAY(...) usleep(__VA_ARGS__)
void IIC_Start(void)
{
SDA_OUT();
SDA_HIGH();
SCL_HIGH();
I2C_DELAY(4);
SDA_LOW();
I2C_DELAY(4);
SCL_LOW();
I2C_DELAY(2);
}
void IIC_Stop(void)
{
SDA_OUT();
SCL_LOW();
SDA_LOW();
I2C_DELAY(4);
SCL_HIGH();
SDA_HIGH();
I2C_DELAY(4);
}
// 1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_OUT();
SDA_HIGH();
I2C_DELAY(1);
SCL_HIGH();
I2C_DELAY(1);
SDA_IN();
while(SDA_READ())
{
ucErrTime++;
if(ucErrTime > 250)
{
IIC_Stop();
return 1;
}
}
SCL_LOW();
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
SCL_LOW();
SDA_OUT();
SDA_LOW();
I2C_DELAY(2);
SCL_HIGH();
I2C_DELAY(2);
SCL_LOW();
}
//不产生ACK应答
void IIC_NAck(void)
{
SCL_LOW();
SDA_OUT();
SDA_HIGH();
I2C_DELAY(2);
SCL_HIGH();
I2C_DELAY(2);
SCL_LOW();
}
void IIC_Write_Byte(uint8_t data)
{
uint8_t i = 0;
SDA_OUT();
SCL_LOW();
for(i=0; i<8; i++)
{
if(data & 0x80 )
SDA_HIGH();
else
SDA_LOW();
data <<= 1;
I2C_DELAY(2);
SCL_HIGH();
I2C_DELAY(2);
SCL_LOW();
I2C_DELAY(2);
}
}
// ack = 1,发送 ACK
// ack = 0,发送 nACK
uint8_t IIC_Read_Byte(uint8_t ack)
{
uint8_t i = 0, data = 0;
SDA_IN();
for(i = 0; i < 8; i++)
{
SCL_LOW();
I2C_DELAY(2);
SCL_HIGH();
data <<= 1;
if(SDA_READ())
data++;
I2C_DELAY(1);
}
if(!ack)
IIC_NAck();
else
IIC_Ack();
return data;
}
void IIC_Write_UINT16(uint8_t dev_addr, uint8_t wr_addr, uint16_t data)
{
IIC_Start();
IIC_Write_Byte(dev_addr);
IIC_Wait_Ack();
IIC_Write_Byte(wr_addr);
IIC_Wait_Ack();
IIC_Write_Byte(data >> 8);
IIC_Wait_Ack();
IIC_Write_Byte(data & 0xFF);
IIC_Wait_Ack();
IIC_Stop();
I2C_DELAY(800);
}
uint16_t IIC_Read_UINT16(uint8_t dev_addr, uint8_t rd_addr)
{
uint16_t data;
IIC_Start();
IIC_Write_Byte(dev_addr);
IIC_Wait_Ack();
IIC_Write_Byte(rd_addr);
IIC_Wait_Ack();
IIC_Start();
IIC_Write_Byte(dev_addr + 1);
IIC_Wait_Ack();
data = IIC_Read_Byte(1);
data = (data <<8 ) + IIC_Read_Byte(0);
IIC_Stop();
return data;
}
- iic_ctrl.h
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#ifndef __I2C_CTRL_H__
#define __I2C_CTRL_H__
#include "iic_io.h"
void IIC_Start(void);
void IIC_Stop(void);
uint8_t IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Write_Byte(uint8_t value );
uint8_t IIC_Read_Byte(uint8_t addr);
void IIC_Write_UINT16(uint8_t dev_addr, uint8_t wr_addr, uint16_t data);
uint16_t IIC_Read_UINT16(uint8_t dev_addr, uint8_t rd_addr);
#endif
用软件模拟 IIC,最大的好处就是方便移植,同一个代码兼容所有 MCU,任何一个微控制器 只要有 IO 口,就可以很快的移植过去,而且不需要特定的 IO 口。但是相对于硬件 IIC 来说,模拟 IIC 通信速率要慢得多。
- 自问:那为啥不用硬件 IIC
- 自答:就想用模拟的嘿嘿嘿