前言
这是本人在学习 stm32 过程中总结的代码,希望能对新手有所帮助,若有谬误之处,恳请各位指正。
基本说明
此代码为基于 stm32f1 固件库的对 I2C 的封装,可以方便地配置和使用
硬件 I2C1
、
硬件 I2C1 重映射
或
硬件 I2C2
,同时可以配置
软件模拟 I2C
,可以选择模拟 I2C 的引脚。
使用说明
本 I2C 代码除了使用了固件库,还使用了GPIO、系统定时器、串口(可选)的内容,因此有以下三个头文件:
#include “GPIOpinInit.h” // 为了用寄存器操作IO,模拟I2C操控引脚需要
#include “SysTick.h” // 模拟I2C延时需要
#include “USARTcontrol.h” // 总线出错报告需要
相关代码见我的前三篇文章:
STM32f103 GPIO应用代码
STM32f103 SysTick应用代码
STM32f103 串口应用代码
为节省程序空间,采用条件编译,启用 I2C 只需在h文件中启用对应的宏,如:
示例1、仅开启软件模拟I2C
//#define I2C1D//硬件I2C1
//#define I2C1R//硬件I2C1重映射
//#define I2C2D//硬件I2C2
#define I2CS//软件模拟I2C
示例2、仅开启硬件I2C1重映射
//#define I2C1D//硬件I2C1
#define I2C1R//硬件I2C1重映射
//#define I2C2D//硬件I2C2
//#define I2CS//软件模拟I2C
示例3、同时开启硬件I2C2和软件模拟I2C
//#define I2C1D//硬件I2C1
//#define I2C1R//硬件I2C1重映射
#define I2C2D//硬件I2C2
#define I2CS//软件模拟I2C
允许同时定义硬件和软件模拟 I2C ,但两者不能有引脚冲突,且3个硬件 I2C 只能有一个开启
硬件 I2C 的引脚见 stm32 数据手册,软件 I2C 的引脚可以修改,见h文件的如下 3 行:
//IO操作函数
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA
此处选择了 B8 为 SCL,B9 为 SDA,需要更换引脚时修改这 3 行即可。
在修改好宏定义之后,只需调用:
void I2C_Config(void);//I2C初始化
就能完成所启用的 I2C 的初始化。
初始化后要使用 I2C 通信,则可调用以下的函数:
//操作函数
u8 I2C_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data);//硬件I2C写1字节
u8 I2C_Read_1Byte(u8 DeviceAddr,u8 ReadAddr);//硬件I2C读1字节
u8 I2C_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n);//硬件I2C写n字节
u8 I2C_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n);//硬件I2C读n字节
u8 I2CS_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data);//模拟I2C写1字节
u8 I2CS_Read_1Byte(u8 DeviceAddr,u8 ReadAddr);//模拟I2C读1字节
u8 I2CS_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n);//模拟I2C写n字节
u8 I2CS_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n);//模拟I2C读n字节
其中 DeviceAddr 为设备地址,WriteAddr 为读地址,ReadAddr 为写地址,Data 为要写入的字节,DataArray 为要写入或存放读取信息的数组,n 为数组长度。
I2C_ 开头的是硬件 I2C 操作函数,I2CS_ 开头的是模拟 I2C 操作函数。
注意
①当 I2C 出现通信错误时,将调用如下的错误处理函数:
void I2C_ERROR(u8 errNum);//I2C错误处理函数
该函数默认将 I2C 的错误信息打印至串口(打印到哪个串口将取决于串口那边 printf 的重定向,见之前串口的文章),可以帮助查找是通信的哪一步出现了问题。
除此之外,它还会对来自硬件 I2C 的错误进行计数,当达到一定次数后,会试图重启 I2C 总线。经测试,这样做后即使在通信中拔插 I2C 总线,也基本都能恢复通信(模拟 I2C 是直接操作引脚电平,所以不需要这样重启,但硬件 I2C 不这样做的话在断线重连后经常不能自行恢复)。
当然,以上功能都是可选的,不需要则可以注释掉,也可添加其他功能,如出错则点亮板载 LED 等。
②当使用硬件 I2C1 重映射时,涉及到一些重映射时钟和 GPIO 重映射功能的使能,如果项目需要失能这些时钟的操作,则务必放在 I2C_Config(void); 函数之前,否则会将设置好的重映射取消掉。
③对于模拟 I2C,采用的是最一般的方式对所用的引脚进行的配置,直接将 SCL 和 SDA 均配置为一般的开漏,因此外接上拉电阻是必须的。另外 stm32 的某些引脚有特殊功能,不能用普通的方法配置,因此一般不要将它们作为模拟 I2C 的引脚,如一定要用,则要查清楚它们的配置方法。
可以参考以下内容:
STM32引脚使用选择注意
代码
h文件:
#ifndef _I2CCONTROL_H
#define _I2CCONTROL_H
#include "stm32f10x.h"
#include "GPIOpinInit.h"//实际为了用寄存器操作IO,模拟I2C操控引脚需要
#include "SysTick.h"//模拟I2C延时需要
#include "USARTcontrol.h"//总线出错报告需要
/*************宏定义操作区*************/
//#define I2C1D//硬件I2C1
//#define I2C1R//硬件I2C1重映射
//#define I2C2D//硬件I2C2
//#define I2CS//软件模拟I2C
/*************************************/
#ifdef I2C1D//若定义硬件I2C1
#define I2Cx I2C1
#define I2Cx_Clock RCC_APB1Periph_I2C1//I2C时钟
#define I2Cx_SCL_PIN GPIO_Pin_6//I2C SCL GPIO引脚
#define I2Cx_SDA_PIN GPIO_Pin_7//I2C SDA GPIO引脚
#endif
#ifdef I2C1R//若定义硬件I2C1重映射
#define I2Cx I2C1
#define I2Cx_Clock RCC_APB1Periph_I2C1//I2C时钟
#define I2Cx_SCL_PIN GPIO_Pin_8//I2C SCL GPIO引脚
#define I2Cx_SDA_PIN GPIO_Pin_9//I2C SDA GPIO引脚
#endif
#ifdef I2C2D//若定义硬件I2C2
#define I2Cx I2C2
#define I2Cx_Clock RCC_APB1Periph_I2C2//I2C时钟
#define I2Cx_SCL_PIN GPIO_Pin_10//I2C SCL GPIO引脚
#define I2Cx_SDA_PIN GPIO_Pin_11//I2C SDA GPIO引脚
#endif
#if (defined I2C1D)||(defined I2C1R)||(defined I2C2D)//若定义硬件I2C
//配置宏定义
#define I2C_Speed 400000//快速模式400k
#define I2Cx_MCU_Addr_7 0X55//单片机本身的7位I2C地址
#define I2Cx_GPIO_Clock RCC_APB2Periph_GPIOB//I2C GPIO时钟
#define I2Cx_SCL_PORT GPIOB//I2C SCL GPIO端口
#define I2Cx_SDA_PORT GPIOB//I2C SDA GPIO端口
//操作函数
u8 I2C_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data);//硬件I2C写1字节
u8 I2C_Read_1Byte(u8 DeviceAddr,u8 ReadAddr);//硬件I2C读1字节
u8 I2C_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n);//硬件I2C写n字节
u8 I2C_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n);//硬件I2C读n字节
#endif
#ifdef I2CS//若定义软件模拟I2C
/****************************修改SCL和SDA引脚需正确修改以下宏****************************/
//配置宏定义
#define SCL_GPIO_Clock RCC_APB2Periph_GPIOB//SCL GPIO时钟
#define SCL_GPIO_Port GPIOB //SCL GPIO端口
#define SCL_GPIO_Pin GPIO_Pin_8 //SCL GPIO引脚
#define SDA_GPIO_Clock RCC_APB2Periph_GPIOB//SDA GPIO时钟
#define SDA_GPIO_Port GPIOB //SDA GPIO端口
#define SDA_GPIO_Pin GPIO_Pin_9 //SDA GPIO引脚
//SDA IO方向设置
//若SDA引脚号n为0-7,则用都用CRL,&=右边的16位数从右向左从0开始数第n位为0,<<右边=4*n;
//若SDA引脚号n为8-15,则用都用CRH,&=右边的16位数从右向左从0开始数第n-8位为0,<<右边=4*n-32;
//SDA输入设置,<<左边=4为浮空输入,=8为上下拉输入
#define SDA_IN() {SDA_GPIO_Port->CRH&=0XFFFFFF0F;SDA_GPIO_Port->CRH|=4<<4;}
//SDA输出设置,<<左边=3为50MHz推挽输出,=7为50MHz开漏输出
#define SDA_OUT() {SDA_GPIO_Port->CRH&=0XFFFFFF0F;SDA_GPIO_Port->CRH|=7<<4;}
//具体内容参考:STM32F10xxx参考手册-8.2节-GPIO寄存器描述
//IO操作函数
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA
/***************************************************************************************/
//操作函数
void I2CS_Start(void); //发送I2C开始信号
void I2CS_Stop(void);//发送I2C停止信号
void I2CS_Send_Byte(u8 txd);//I2C发送一个字节
u8 I2CS_Read_Byte(u8 ack);//I2C读取一个字节
u8 I2CS_Wait_Ack(void);//I2C等待ACK信号
void I2CS_Ack(void); //I2C发送ACK信号
void I2CS_NAck(void);//I2C不发送ACK信号
u8 I2CS_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data);//模拟I2C写1字节
u8 I2CS_Read_1Byte(u8 DeviceAddr,u8 ReadAddr);//模拟I2C读1字节
u8 I2CS_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n);//模拟I2C写n字节
u8 I2CS_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n);//模拟I2C读n字节
#endif
void I2C_Config(void);//I2C初始化
void I2C_ERROR(u8 errNum);//I2C错误处理函数
#endif
c文件:
#include "I2Ccontrol.h"
#define I2C_Wait_Short 0x1000//硬件I2C短等待时间
#define I2C_Wait_Long 0xA000//硬件I2C长等待时间
uint32_t I2C_Wait = 0;//硬件I2C等待变量
u8 I2C_ERROR_Count = 0;//I2C错误计数变量
void I2C_Config(void)//I2C初始化
{
GPIO_InitTypeDef GPIO_InitStructure;//创建GPIO初始化结构体
#if (defined I2C1D)||(defined I2C1R)||(defined I2C2D)//若定义硬件I2C
I2C_InitTypeDef I2C_InitStructure;//创建I2C初始化结构体
RCC_APB1PeriphClockCmd( I2Cx_Clock,ENABLE );//使能与I2C有关的时钟
RCC_APB2PeriphClockCmd( I2Cx_GPIO_Clock,ENABLE );//使能与I2C GPIO有关的时钟
#ifdef I2C1R//若定义硬件I2C1重映射
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO,ENABLE );//使能重映射时钟,注意之后不得取消使能
GPIO_PinRemapConfig( GPIO_Remap_I2C1,ENABLE );//使能重映射
#endif
GPIO_InitStructure.GPIO_Pin = I2Cx_SCL_PIN;//SCL引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//开漏输出
GPIO_Init( I2Cx_SCL_PORT,&GPIO_InitStructure );//GPIO初始化
GPIO_InitStructure.GPIO_Pin = I2Cx_SDA_PIN;//SDA引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//开漏输出
GPIO_Init( I2Cx_SDA_PORT,&GPIO_InitStructure );//GPIO初始化
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//配置为普通I2C模式
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//高电平数据稳定,低电平数据变化SCL时钟线的占空比
I2C_InitStructure.I2C_OwnAddress1 =I2Cx_MCU_Addr_7;//单片机本身的7位或10位I2C地址
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//使能应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位或10位寻址模式
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;//通信速率
I2C_Init(I2Cx, &I2C_InitStructure);//I2C初始化
I2C_Cmd(I2Cx, ENABLE);//使能I2C
#endif
#ifdef I2CS//若定义软件模拟I2C
RCC_APB2PeriphClockCmd( SCL_GPIO_Clock,ENABLE );//使能SCL GPIO时钟
RCC_APB2PeriphClockCmd( SDA_GPIO_Clock,ENABLE );//使能SDA GPIO时钟
GPIO_InitStructure.GPIO_Pin = SCL_GPIO_Pin;//SCL引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_Init( SCL_GPIO_Port,&GPIO_InitStructure );//SCL GPIO初始化
GPIO_InitStructure.GPIO_Pin = SDA_GPIO_Pin;//SDA引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_Init( SDA_GPIO_Port,&GPIO_InitStructure );//SDA GPIO初始化
GPIO_SetBits( SCL_GPIO_Port,SCL_GPIO_Pin );//SCL输出高
GPIO_SetBits( SDA_GPIO_Port,SDA_GPIO_Pin );//SCL输出高
#endif
}
#if (defined I2C1D)||(defined I2C1R)||(defined I2C2D)//若定义硬件I2C
u8 I2C_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data)//硬件I2C写1字节
{
I2C_GenerateSTART( I2Cx,ENABLE );//发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(1);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Transmitter );//发送设备地址(8位以及写)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//检查EV6(写)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(2);return 0; } }
I2C_SendData( I2Cx,WriteAddr );//写8位寄存器地址
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(3);return 0; } }
I2C_SendData( I2Cx,Data );//写8位数据
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(4);return 0; } }
I2C_GenerateSTOP( I2Cx,ENABLE );//发送结束符
return 1;
}
u8 I2C_Read_1Byte(u8 DeviceAddr,u8 ReadAddr)//硬件I2C读1字节
{
I2C_Wait = I2C_Wait_Long;//等待时间复位
while(I2C_GetFlagStatus( I2Cx,I2C_FLAG_BUSY ))
{ if((I2C_Wait--) == 0){ I2C_ERROR(21);return 0; } }
I2C_GenerateSTART( I2Cx,ENABLE );//发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(22);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Transmitter );//发送设备地址(8位以及写)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//检查EV6(写)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(23);return 0; } }
I2C_Cmd( I2Cx,ENABLE );//Clear EV6 by setting again the PE bit
I2C_SendData( I2Cx,ReadAddr );//写8位寄存器地址
I2C_Wait=I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(24);return 0; } }
I2C_GenerateSTART( I2Cx,ENABLE );//二次发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(25);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Receiver );//发送设备地址(8位以及读)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))//检查EV6(读)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(26);return 0; } }
I2C_Wait = I2C_Wait_Long;//等待时间复位
I2C_AcknowledgeConfig( I2Cx,DISABLE );//不使能应答
I2C_GenerateSTOP( I2Cx,ENABLE );//发送结束符
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))//检查EV7并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(27);return 0; } }
I2C_AcknowledgeConfig( I2Cx,ENABLE );//为之后的通讯使能应答
return I2C_ReceiveData(I2Cx);//取出数据
}
u8 I2C_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n)//硬件I2C写n字节
{
I2C_Wait = I2C_Wait_Long;//等待时间复位
while(I2C_GetFlagStatus( I2Cx,I2C_FLAG_BUSY ))
{ if((I2C_Wait--) == 0){ I2C_ERROR(41);return 0; } }
I2C_GenerateSTART( I2Cx,ENABLE );//发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(42);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Transmitter );//发送设备地址(8位以及写)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//检查EV6(写)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(43);return 0; } }
I2C_Cmd( I2Cx,ENABLE );//Clear EV6 by setting again the PE bit
I2C_SendData( I2Cx,WriteAddr );//写8位寄存器地址
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(44);return 0; } }
for(; n>0; n--)
{
I2C_SendData( I2Cx,*DataArray );//写8位数据
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(45);return 0; } }
DataArray++;
}
I2C_GenerateSTOP( I2Cx,ENABLE );//发送结束符
return 1;
}
u8 I2C_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n)//硬件I2C读n字节
{
I2C_Wait = I2C_Wait_Long;//等待时间复位
while(I2C_GetFlagStatus( I2Cx,I2C_FLAG_BUSY ))
{ if((I2C_Wait--) == 0){ I2C_ERROR(61);return 0; } }
I2C_GenerateSTART( I2Cx,ENABLE );//发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(62);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Transmitter );//发送设备地址(8位以及写)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//检查EV6(写)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(63);return 0; } }
I2C_Cmd( I2Cx,ENABLE );//Clear EV6 by setting again the PE bit
I2C_SendData( I2Cx,ReadAddr );//写8位寄存器地址
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//检查EV8并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(64);return 0; } }
I2C_GenerateSTART( I2Cx,ENABLE );//二次发送起始符
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))//检查EV5并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(65);return 0; } }
I2C_Send7bitAddress( I2Cx,DeviceAddr,I2C_Direction_Receiver );//发送设备地址(8位以及读)
I2C_Wait = I2C_Wait_Short;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))//检查EV6(读)并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(66);return 0; } }
for(; n>0; n--)
{
if(n == 1)
{
I2C_AcknowledgeConfig( I2Cx,DISABLE );//不使能应答
I2C_GenerateSTOP( I2Cx,ENABLE );//发送结束符
}
I2C_Wait = I2C_Wait_Long;//等待时间复位
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))//检查EV7并清除
{ if((I2C_Wait--) == 0){ I2C_ERROR(67);return 0; } }
*DataArray = I2C_ReceiveData(I2Cx);
DataArray++;
}
I2C_AcknowledgeConfig( I2Cx,ENABLE );//为之后的通讯使能应答
return 1;
}
#endif
#ifdef I2CS//若定义软件模拟I2C
void I2CS_Start(void)//产生I2C起始信号
{
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;//钳住I2C总线,准备发送或接收数据
}
void I2CS_Stop(void)//产生IIC停止信号
{
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;//发送I2C总线结束信号
Delay_us(4);
}
u8 I2CS_Wait_Ack(void)//等待应答信号到来,返回值:1接收应答失败,0接收应答成功
{
u8 ucErrTime = 0;
SDA_IN(); //SDA设置为输入
IIC_SDA = 1;
Delay_us(1);
IIC_SCL = 1;
Delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime > 250)
{
I2CS_Stop();
return 1;
}
}
IIC_SCL = 0;//时钟输出0
return 0;
}
void I2CS_Ack(void)//产生ACK应答
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 0;
Delay_us(2);
IIC_SCL = 1;
Delay_us(2);
IIC_SCL = 0;
}
void I2CS_NAck(void)//不产生ACK应答
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 1;
Delay_us(2);
IIC_SCL = 1;
Delay_us(2);
IIC_SCL = 0;
}
void I2CS_Send_Byte(u8 txd)//I2C发送一个字节,返回从机有无应答:1有应答,0无应答
{
u8 t;
SDA_OUT();
IIC_SCL = 0;//拉低时钟开始数据传输
for(t = 0; t < 8; t++)
{
//IIC_SDA=(txd&0x80)>>7;
if((txd & 0x80) >> 7){ IIC_SDA = 1; }
else{ IIC_SDA = 0; }
txd <<= 1;
Delay_us(2);//对TEA5767这三个延时都是必须的
IIC_SCL = 1;
Delay_us(2);
IIC_SCL = 0;
Delay_us(2);
}
}
u8 I2CS_Read_Byte(u8 ack)//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
{
u8 i,receive = 0;
SDA_IN();//SDA设置为输入
for(i = 0; i < 8; i++ )
{
IIC_SCL = 0;
Delay_us(2);
IIC_SCL = 1;
receive <<= 1;
if(READ_SDA){ receive++; }
Delay_us(1);
}
if(!ack){ I2CS_NAck(); }//发送nACK
else{ I2CS_Ack(); }//发送ACK
return receive;
}
u8 I2CS_Write_1Byte(u8 DeviceAddr,u8 WriteAddr,u8 Data)//模拟I2C写1字节
{
I2CS_Start();//启动总线
I2CS_Send_Byte(DeviceAddr & 0xFE);//发送设备地址(写)
if(I2CS_Wait_Ack()){ I2C_ERROR(11);return 0; }
I2CS_Send_Byte(WriteAddr);//发送寄存器地址
if(I2CS_Wait_Ack()){ I2C_ERROR(12);return 0; }
I2CS_Send_Byte(Data);//发送数据
if(I2CS_Wait_Ack()){ I2C_ERROR(13);return 0; }
I2CS_Stop();//产生一个停止条件
return 1;
}
u8 I2CS_Read_1Byte(u8 DeviceAddr,u8 WriteAddr)//模拟I2C读1字节
{
u8 Data = 0;
I2CS_Start();//启动总线
I2CS_Send_Byte(DeviceAddr & 0xFE);//发送设备地址(写)
if(I2CS_Wait_Ack()){ I2C_ERROR(31);return 0; }
I2CS_Send_Byte(WriteAddr);//发送寄存器地址
if(I2CS_Wait_Ack()){ I2C_ERROR(32);return 0; }
I2CS_Start();//二次启动总线
I2CS_Send_Byte(DeviceAddr | 0x01);//发送设备地址(读)
if(I2CS_Wait_Ack()){ I2C_ERROR(33);return 0; }
Data = I2CS_Read_Byte(0);//读取数据且不应答
I2CS_Stop();//产生一个停止条件
return Data;
}
u8 I2CS_Write_nByte(u8 DeviceAddr,u8 WriteAddr,u8 *DataArray,u8 n)//模拟I2C写n字节
{
I2CS_Start();//启动总线
I2CS_Send_Byte(DeviceAddr & 0xFE);//发送设备地址(写)
if(I2CS_Wait_Ack()){ I2C_ERROR(51);return 0; }
I2CS_Send_Byte(WriteAddr);//发送寄存器地址
if(I2CS_Wait_Ack()){ I2C_ERROR(52);return 0; }
for(; n>0; n--)
{
I2CS_Send_Byte(*DataArray);//发送数据
if(I2CS_Wait_Ack()){ I2C_ERROR(53);return 0; }
DataArray++;
}
I2CS_Stop();//产生一个停止条件
return 1;
}
u8 I2CS_Read_nByte(u8 DeviceAddr,u8 ReadAddr,u8 *DataArray,u8 n)//模拟I2C读n字节
{
I2CS_Start();//启动总线
I2CS_Send_Byte(DeviceAddr & 0xFE);//发送设备地址(写)
if(I2CS_Wait_Ack()){ I2C_ERROR(71);return 0; }
I2CS_Send_Byte(ReadAddr);//发送寄存器地址
if(I2CS_Wait_Ack()){ I2C_ERROR(72);return 0; }
I2CS_Start();//二次启动总线
I2CS_Send_Byte(DeviceAddr|0x01);//发送设备地址(读)
if(I2CS_Wait_Ack()){ I2C_ERROR(73);return 0; }
for(; n>1; n--)
{
*DataArray = I2CS_Read_Byte(1);//读取数据且应答
DataArray++;
}
*DataArray = I2CS_Read_Byte(0);//读取数据且不应答
I2CS_Stop();//产生一个停止条件
return 1;
}
#endif
void I2C_ERROR(u8 errNum)//I2C错误处理函数
{
printf("I2C_err%d\n", errNum);//串口打印错误信息
//PCout(13) = 0;//点亮板载LED
#if (defined I2C1D)||(defined I2C1R)||(defined I2C2D)//若定义硬件I2C
I2C_ERROR_Count++;
if(((u8)(errNum / 10) % 2) == 0){
I2C_GenerateSTOP( I2Cx,ENABLE );//若来自硬件I2C错误则发送硬件I2C结束符
}
if(I2C_ERROR_Count >= 8){//多次错误则重启I2C总线
I2C_ERROR_Count = 0;
I2C_DeInit(I2Cx);
I2C_Config();
printf("I2C_RESTART\n");//串口打印I2C重启信息
}
#endif
}