前言
所用芯片STM32H750VBT6,采用hal库实现。
1.IIC简介
在 I2C 总线中,每个设备都必须有一个唯一的地址,用于识别该设备。地址位的数目由总线规范给出。在数据传输过程中,发送主机先将设备地址发送到总线上,接收设备会检测该地址并发送应答信号。若该设备在总线上没有被发现,则总线会终止数据传输。
I2C 数据传输格式包括写操作和读操作两种。在写操作中,发送主机会先向设备发送一个字节的控制信息,然后跟随数据字节。在读操作中,发送主机先向设备发送控制信息,并向设备发出读地址,接着由设备向发送主机返回数据。具体的数据传输格式会随着不同的设备而有所不同。
在软件实现 I2C 总线时,需要使用相应的 I2C 库函数,通过开启 I2C 模块并设置 I2C 总线参数来实现数据传输。而在硬件实现中,需要使用 I2C 接口芯片或 MCU 的硬件 I2C 模块来实现数据传输。软件 I2C 适用于资源受限的嵌入式系统,硬件 I2C 适用于需要高速、高可靠性和高精度的应用。
2.代码(可移植)
myiic.c文件
#include "i2c_2.h"
#include "tim.h"
#include "sys.h"
#define DELAY_TIME 20
void SCCB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟
//PB8,PB9初始化设置
GPIO_InitStructure.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; //PB8、PB9 推挽输出
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8|GPIO_PIN_9,GPIO_PIN_SET);
}
//配置SDA引脚为输入模式
void SDA_Input_Mode2(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = SDA2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//配置SDA引脚为输出模式
void SDA_Output_Mode2(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = SDA2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//SDA引脚输出
void SDA_Output2(uint16_t val )
{
if(val)
HAL_GPIO_WritePin(GPIOB, SDA2, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOB, SDA2, GPIO_PIN_RESET);
}
//SCL引脚输出
void SCL_Output2( uint16_t val )
{
if(val)
HAL_GPIO_WritePin(GPIOB, SCL2, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOB, SCL2, GPIO_PIN_RESET);
}
//读取SDA引脚状态
uint8_t SDA_Input2(void)
{
return HAL_GPIO_ReadPin(GPIOB, SDA2);
}
//I2C总线启动信号
void SCCB_Start(void)
{
SDA_Output2(1);
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
SDA_Output2(0);
delay_us(DELAY_TIME);
SCL_Output2(0);
delay_us(DELAY_TIME);
}
//I2C总线停止信号
void SCCB_Stop(void)
{
SCL_Output2(0);
delay_us(DELAY_TIME);
SDA_Output2(0);
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
SDA_Output2(1);
delay_us(DELAY_TIME);
}
//等待应答
unsigned char I2CWaitAck2(void)
{
unsigned short cErrTime = 5;
SDA_Input_Mode2();
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
while(SDA_Input2())
{
cErrTime--;
delay_us(DELAY_TIME);
if (0 == cErrTime)
{
SDA_Output_Mode2();
SCCB_Stop();
return ERROR;
}
}
SCL_Output2(0);
SDA_Output_Mode2();
delay_us(DELAY_TIME);
return SUCCESS;
}
//发送应答
void I2CSendAck2(void)
{
SDA_Output2(0);
delay_us(DELAY_TIME);
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
SCL_Output2(0);
delay_us(DELAY_TIME);
}
//发送非应答
void SCCB_No_Ack(void)
{
SDA_Output2(1);
delay_us(DELAY_TIME);
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
SCL_Output2(0);
delay_us(DELAY_TIME);
}
//发送一个字节数据
u8 SCCB_WR_Byte(u8 cSendByte)
{
unsigned char i = 8;
while (i--)
{
SCL_Output2(0);
delay_us(DELAY_TIME);
SDA_Output2(cSendByte & 0x80);
delay_us(DELAY_TIME);
cSendByte += cSendByte;
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
}
SCL_Output2(0);
delay_us(DELAY_TIME);
return 0;
}
//接收一个字节数据
unsigned char SCCB_RD_Byte(void)
{
unsigned char i = 8;
unsigned char cR_Byte = 0;
SDA_Input_Mode2();
while (i--)
{
cR_Byte += cR_Byte;
SCL_Output2(0);
delay_us(DELAY_TIME);
delay_us(DELAY_TIME);
SCL_Output2(1);
delay_us(DELAY_TIME);
cR_Byte |= SDA_Input2();
}
SCL_Output2(0);
delay_us(DELAY_TIME);
SDA_Output_Mode2();
return cR_Byte;
}
//写寄存器
//返回值:0,成功;1,失败.
u8 SCCB_WR_Reg(u8 reg,u8 data)
{
// u8 res=0;
// SCCB_Start(); //启动SCCB传输
// if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID
// delay_us(100);
// if(SCCB_WR_Byte(reg))res=1; //写寄存器地址
// delay_us(100);
// if(SCCB_WR_Byte(data))res=1; //写数据
// SCCB_Stop();
// return res;
u8 res=0;
SCCB_Start(); //启动SCCB传输
if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID
I2CWaitAck2();
if(SCCB_WR_Byte(reg))res=1; //写寄存器地址
I2CWaitAck2();
if(SCCB_WR_Byte(data))res=1; //写数据
I2CWaitAck2();
SCCB_Stop();
return res;
}
//读寄存器
//返回值:读到的寄存器值
u8 SCCB_RD_Reg(u8 reg)
{
// u8 val=0;
// SCCB_Start(); //启动SCCB传输
// SCCB_WR_Byte(SCCB_ID); //写器件ID
// delay_us(4);
// SCCB_WR_Byte(reg); //写寄存器地址
// delay_us(100);
// SCCB_Stop();
// delay_us(100);
// //设置寄存器地址后,才是读
// SCCB_Start();
// SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令
// delay_us(100);
// val=SCCB_RD_Byte(); //读取数据
// SCCB_No_Ack();
// SCCB_Stop();
// return val;
u8 val=0;
SCCB_Start(); //启动SCCB传输
SCCB_WR_Byte(SCCB_ID); //写器件ID
I2CWaitAck2();
SCCB_WR_Byte(reg); //写寄存器地址
I2CWaitAck2();
SCCB_Stop();
delay_us(100);
//设置寄存器地址后,才是读
SCCB_Start();
SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令
I2CWaitAck2();
val=SCCB_RD_Byte(); //读取数据
SCCB_No_Ack();
SCCB_Stop();
return val;
}
myiic.h文件
#ifndef __I2C_2_H
#define __I2C_2_H
#include "main.h"
#include "sys.h"
//I2C总线引脚定义
#define SCL2 GPIO_PIN_8
#define SDA2 GPIO_PIN_9
//接口函数
#define SCCB_ID 0X60 //OV2640的ID
///
void SCCB_Init(void);
void SCCB_Start(void);
void SCCB_Stop(void);
void SCCB_No_Ack(void);
u8 SCCB_WR_Byte(u8 cSendByte);
u8 SCCB_RD_Byte(void);
u8 SCCB_WR_Reg(u8 reg,u8 data);
u8 SCCB_RD_Reg(u8 reg);
unsigned char I2CWaitAck2(void);
void I2CSendAck2(void);
#endif
PS.调试有问题可以留言,不同型号的芯片可能操作上有小变化。