STM32F7 硬件IIC驱动

目前只实现了主设备模式,一般也只用到主设备模式,IIC如果不能使用硬件方式,读取大量数据的时候效率很大,由于只有1个字节的缓冲区,根本不能使用中断模式(实际使用过程中,IIC会造成100us以内间隔的中断,单片机根本扛不住的),所以建议数据少就直接阻塞,1个字节也就几十us,数据多直接用DMA,将线程阻塞,等待DMA传输完成,而不会阻塞CPU(上传的代码没有实现DMA部分,便于理解)。

目前已经做了完善的错误处理,读写操作前都会清除中断,遇到错误会软复位,所有位置均做了超时处理,防止程序卡死,目前只测试了几个字节的读写,大量的,长时间的读写均表现稳定,目前手上开发板没有eeprom,无法做大数据的连续读写,如果你在使用过程中遇到问题,欢迎指正。

 

下面是IIC读写的时序例子,可以先熟悉一下,这样比较容易上手,哪一个环节出了问题也要调试。

 

uploading.4e448015.gif正在上传…重新上传取消

 

 

下面简要说明一下STM32F7硬件IIC的驱动设计方式(建议先百度学习一下IIC的时序要求):

[基本的初始化]

1.初始化IIC时钟,IO(IIC IO必须设置为复用开漏输出,这个很重要)。

2.CR1先赋值为0,复位IIC.

3.除了TIMINGR寄存器需要自己计算设置好,其余寄存器全部复位为0

4.设置CR1使能IIC。

注意:上面说到,IIC的IO必须初始化为复用开漏输出,我之前初始化为复用推挽输出,测试很久,只要一启动传输,就会立马收到NACK,然后是STOP中断,数据根本没法发送,弄了好久才发现是这个IO初始化的问题。

 

[读取]

1.检查总线是否忙,如果忙的话可以根据你的需要时推出还是直接复位总线,清除中断状态。

2.设置CR2寄存器,从机地址,待写入数据长度(就是后面要发送的寄存器地址长度,通常1-2字节)软件STOP,写模式,启动传输。

3.等待TXIS置位,然后写入1字节的寄存器地址,如果寄存器地址有2个字节,重复上面过程。

4.等待TC置位,意味着上面的1-2字节的寄存器地址以及写完成了。

5.根据要读取的数据长度,去设置CR2寄存器,由于一次最大只能读取255字节(计数器只有8位),如果超出了就要分多次,这里有2种情况,如果当前是最后一包,则将自动STOP使能,RELOAD禁止;如果后面还有数据要发送,则将RELOAD使能自动STOP禁止;这2个是二选一的,而且RELOAD优先级高一些。

6.设置了从机地址,要读取的数据长度,之后设置方向为读取,然后启动START(从写切换到读要重新发送START,这个跟软件模拟是一模一样的)。

7.循环一个字节一个字节的读取数据,字节读取需要等待RXNE有效,只有读取RXDN寄存器,直到所有数据读取完成。

8.读取完成后等待STOP有效,因为前面在最后一包数据读取的时候使能了自动STOP,这个时候检查STOP是否生成。

9.需要注意的是,所有的操作都要给超时,并且是自己可控的,绝对不要有死循环,之所以一直说STM32的IIC死机很大程度上都是由于这个地方没有涉及好,我的程序设计方法是:一旦出现了异常,就复位IIC,如果不复位就会导致下次进去的时候IIC一直忙,因为还处于上一个通讯过程。

 

[写入]

1.检查总线是否忙,如果忙的话可以根据你的需要时推出还是直接复位总线,清除中断状态。

2.设置CR2寄存器,从机地址,待写入数据长度(就是后面要发送的寄存器地址长度,通常1-2字节)自动重载,写模式,启动传输。

3.等待TXIS置位,然后写入1字节的寄存器地址,如果寄存器地址有2个字节,重复上面过程。

4.等待TCR置位,意味着上面的1-2字节的寄存器地址以及写完成了,已经发生重载了,后续可以继续写入数据了。

5.根据要写入的数据长度,去设置CR2寄存器,由于一次最大只能读取255字节(计数器只有8位),如果超出了就要分多次,这里有2种情况,如果当前是最后一包,则将自动STOP使能,RELOAD禁止;如果后面还有数据要发送,则将RELOAD使能自动STOP禁止;这2个是二选一的,而且RELOAD优先级高一些。

6.设置了从机地址,要写入的数据长度,之后不用切换方向,不用启动START(都是写,不用重复发送START,这个与读取有区别)。

7.循环一个字节一个字节的写入,字节写入与步骤3一样,等待TXIS置位,就可以写入数据到TXDR。

8.等待STOP有效,因为前面在最后一包数据写入的时候使能了自动STOP,这个时候检查STOP是否生成。

9.需要注意的是,所有的操作都要给超时,跟读取一样。

 

注意:IIC的读取实际很短,超时也不用给很长,以标准的100KHz通讯时钟速度,一个字节大概在90us左右,超时可以给个5-10B时间,也就是450-900us左右。

 

好了说了这么多,具体的看代码中的注释,直接上代码(我只测试过I2C3,其余的理论上都是一样的,就是IO初始化部分有区别,别的通道没有硬件进行测试)

 

/*************************************************************************************************************
 * 文件名		:	stm32f7_iic.c
 * 功能			:	STM32F7 IIC接口驱动
 * 作者			:	[email protected]
 * 创建时间		:	2020-02-09
 * 最后修改时间	:	2020-02-09
 * 详细:			只支持主机模式,7bit地址,默认APB1为IIC提供时钟
					涉及到读写1字节的超时时间是us,其余的是ms
					注意:IIC的IO必须初始化为复用开漏输出。
*************************************************************************************************************/	
#include "stm32f7_iic.h"
#include "system.h" 
#include "dma.h"

#define IIC_FLAG_MASK  ((uint32_t)0x0001FFFF)	//中断标志掩码

//自动结束或自动重载设置(重载与自动结束只能二选一,或者都不选)
typedef enum 
{
	IIC_SOFTEND_MODE		=	0,	//手动结束,并不开启重载
	IIC_AUTOEND_MODE		=	1,	//自动结束,用于最后一次通讯完成后自动发送STOP结束通讯
	IIC_RELOAD_MODE			=	2,	//后续还有数据,本次通讯后自动重载,重新设置后继续通讯
}IIC_RELOAD_END_MODE;

//读写与开始模式控制
typedef enum
{
	IIC_NOSTART_WRITE		=	0,	//没有开始的写-用于后续数据的写入
	IIC_NOSTART_READ		=	1,	//没有开始的读-用于后续数据读取
	IIC_START_WRITE			=	2,	//生成开始写-用于通讯开始时,写地址或数据
	IIC_START_READ        	=	3,	//生成开始读-用于读取数据时切换到读取方向,并读取后续数据
}IIC_START_WR_MODE;

//IIC句柄
typedef struct
{
	IIC_CH_Type ch;			//当前通道
	I2C_TypeDef *I2Cx;		//当前通道外设结构体
	u32 TimeOutUs;			//操作超时,单位us
	u16 Speed_KHz;			//通讯速度,单位KHz
	bool isMasterMode;		//是否为主设备模式-目前只支持主设备模式
}IIC_HANDLE;

//IIC外设结构指针
static const  I2C_TypeDef * const I2C_TYPE_BUFF[4] = {I2C1,I2C2,I2C3,I2C4};
//IIC通道句柄定义
static IIC_HANDLE sg_IIC_Handle[IIC_CH_COUNT];

//发送NAK
static __inline void IIC_SendNAK(IIC_HANDLE *pHandle) {pHandle->I2Cx->CR2 |= BIT15;}
//发送STOP
static __inline void IIC_SendStop(IIC_HANDLE *pHandle) {pHandle->I2Cx->CR2 |= BIT14;}
//发送START
static __inline void IIC_SendStart(IIC_HANDLE *pHandle) {pHandle->I2Cx->CR2 |= I2C_CR2_START;}
//获取中断状态
static __inline u32 IIC_GetISR(IIC_HANDLE *pHandle) {return pHandle->I2Cx->ISR & IIC_FLAG_MASK;}
//清除中断状态
static __inline void IIC_ClearISR(IIC_HANDLE *pHandle, u32 IsrFlag) {pHandle->I2Cx->ICR = IsrFlag & IIC_FLAG_MASK;}
//复位CR2寄存器
static __inline void IIC_ClearCR2(IIC_HANDLE *pHandle) {pHandle->I2Cx->CR2 = 0;}

static void IIC_SoftReset(IIC_HANDLE *pHandle);//硬件IIC软复位(会使能IIC)
static u32 IIC_CalculationTiming(u16 Speed_KHz);//硬件IIC时序计算


/*************************************************************************************************************************
* 函数	:			bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
* 功能	:			硬件IIC初始化
* 参数	:			ch:IIC通道;Speed_KHz:速度10-1000(如果速度是100,则按照SMBUS时序);TimeOutUs:操作超时us(0:自定计算超时)
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			速度只是个大概的计算值,设置为100KHz时会严格的按照SMBUS时序,其余的不保证SMBUS兼容,正常情况下只要时钟速度符合要求,
					时钟上升沿到来前数据以及稳定的切换了即可保证通讯稳定性
*************************************************************************************************************************/
bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
{
	SYS_DEV_CLOCK DevCloce;
	IIC_HANDLE *pHandle;
	
	switch(ch)
	{
		case IIC_CH1	:	//IIC1
		{
			RCC->DCKCFGR2 &= ~(0x3 << 16);	//清除设置,使用APB1作为时钟源
			DevCloce = DEV_I2C1;
		}break;
		case IIC_CH2	:	//IIC2
		{
			RCC->DCKCFGR2 &= ~(0x3 << 18);	//清除设置,使用APB1作为时钟源
			DevCloce = DEV_I2C2;
		}break;			
		case IIC_CH3	:	//IIC3
		{
			RCC->DCKCFGR2 &= ~(0x3 << 20);	//清除设置,使用APB1作为时钟源
			DevCloce = DEV_I2C3;
			//初始化IO口
			SYS_DeviceClockEnable(DEV_GPIOH, TRUE);				//使能GPIOH时钟
			SYS_GPIOx_OneInit(GPIOH, 7, AF_OD, SPEED_25M);		//PH7
			SYS_GPIOx_OneInit(GPIOH, 8, AF_OD, SPEED_25M);		//PH8
			SYS_GPIOx_SetAF(GPIOH, 7, AF4_I2C3);				//AF4
			SYS_GPIOx_SetAF(GPIOH, 8, AF4_I2C3);				//AF4
		}break;			
		case IIC_CH4	:	//IIC4
		{
			RCC->DCKCFGR2 &= ~(0x3 << 22);	//清除设置,使用APB1作为时钟源
			DevCloce = DEV_I2C4;
		}break;
		default:
		{
			DEBUG("初始化IIC失败:无效的IIC通道%d\r\n", ch);
			return FALSE;
		}
	}
	pHandle = &sg_IIC_Handle[ch];							//获取相关通道的句柄
	if(pHandle == NULL)
	{
		DEBUG("初始化IIC失败:无效的IIC句柄\r\n");
		return FALSE;
	}
	
	SYS_DeviceClockEnable(DevCloce, TRUE);					//使能时钟
	SYS_DeviceReset(DevCloce);								//外设复位
	pHandle->I2Cx = (I2C_TypeDef *)I2C_TYPE_BUFF[ch];		//外设指针
	pHandle->I2Cx->CR1 = 0;
	Delay_US(1);						
	pHandle->I2Cx->CR1 |= 2<<8;								//设置噪声滤波器,关闭所有中断
	pHandle->I2Cx->CR2 = 0;
	pHandle->I2Cx->OAR1 = 0;
	pHandle->I2Cx->OAR2 = 0;
	if(Speed_KHz > 1000) Speed_KHz = 1000;
	if(Speed_KHz < 10) Speed_KHz = 10;
	pHandle->Speed_KHz = Speed_KHz;							//记录速度
	if(TimeOutUs == 0)										//需要自动计算超时时间,时钟周期*10*10 
	{
		TimeOutUs = 1000/Speed_KHz;							//时钟周期
		TimeOutUs *= 10;									//字节周期
		TimeOutUs *= 10;									//超时时间为10个字节时间
	}
	if(TimeOutUs < 3) TimeOutUs = 3;
	pHandle->TimeOutUs = TimeOutUs;							//记录通讯超时时间
	uart_printf("IIC超时时间:%dus\r\n", pHandle->TimeOutUs);
	pHandle->I2Cx->TIMINGR = IIC_CalculationTiming(pHandle->Speed_KHz);//0x40912732;					//时序
	pHandle->I2Cx->CR1 |= BIT0;								//使能IIC
	pHandle->isMasterMode = TRUE;							//主设备模式
	Delay_US(1);

	return TRUE;
}


/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)
* 功能	:			检查是否有NACK中断状态,如果有则清除掉
* 参数	:			pHandle:句柄;TimeOutUs:超时时间,单位us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			如果有NACK中断,则先产生停止位,然后清清除掉所有的中断,并复位IIC,然后返回错误
*************************************************************************************************************************/  
static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)
{
	IIC_ERROR Error = IIC_OK;
	
	if(IIC_GetISR(pHandle) & IIC_FLAG_NACKF)	//接收到否定应答标志
	{
		uart_printf("NACK : IIC_isWaitTXIS:CR1=0x%X\t", pHandle->I2Cx->CR1);
		uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
		uart_printf("ISR=0x%X\r\n", IIC_GetISR(pHandle));
		//主设备下,如果没有开启自动产生停止位,则手动产生停止位
		if(pHandle->isMasterMode && ((pHandle->I2Cx->CR2 & I2C_CR2_AUTOEND) == 0))
		{
			IIC_SendStop(pHandle);			//send stop
		}
		//等待STOP标志有效
		while((IIC_GetISR(pHandle) & IIC_FLAG_STOPF) == 0)
		{
			if(TimeOutUs == 0)				//检查是否超时了
			{
				break;
			}			
			TimeOutUs --;
			Delay_US(1);						//延时
		}
		//清除相关的中断标志
		IIC_ClearISR(pHandle, IIC_FLAG_NACKF | IIC_FLAG_STOPF);	//清除NACK,STOP标志
		pHandle->I2Cx->CR2 = 0;										//清除CR2寄存器
		IIC_SoftReset(pHandle);									//执行软复位
		if(TimeOutUs == 0)						//没有超时,就是硬件通讯出错了
		{
			DEBUG("IIC发送stop超时\r\n");
			Error = IIC_TIMEOUT;				//超时
		}
		else
		{
			Error = IIC_HAL_ERROR;				//底层错误
		}
	}
	
	return Error;
}



/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_isWaitTXIS(IIC_HANDLE *pHandle, u32 TimeOutUs)
* 功能	:			等待TXIS中断有效(等待 I2C_TXDR 发送寄存器为空)
* 参数	:			pHandle:句柄;TimeOutUs:超时时间,单位us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			会先检查NACK中断,如果有NACK会直接退出的
*************************************************************************************************************************/  
static IIC_ERROR IIC_isWaitTXIS(IIC_HANDLE *pHandle, u32 TimeOutUs)
{
	//等待TXIS中断有效
	while((IIC_GetISR(pHandle) & IIC_FLAG_TXIS) == 0)
	{
		//有NACK中断,进行处理
		if(IIC_isCheckNackFlag(pHandle, TimeOutUs) != IIC_OK)	
		{
			DEBUG("检测到NAK错误\r\n");
			return IIC_NACK;
		}
		
		if(TimeOutUs == 0)					//检查是否超时了
		{
			uart_printf("IIC_isWaitTXIS:CR1=0x%X\t", pHandle->I2Cx->CR1);
			uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
			uart_printf("ISR=0x%X\r\n", IIC_GetISR(pHandle));
			
			IIC_SoftReset(pHandle);		//执行软复位
			return IIC_TIMEOUT;				//超时
		}			
		TimeOutUs --;
		Delay_US(1);						//延时
	}
	
	return IIC_OK;
}


/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_isWaitSTOPF(IIC_HANDLE *pHandle, u32 TimeOutUs)
* 功能	:			等待STOP中断有效(等待 发送结束 )
* 参数	:			pHandle:句柄;TimeOutUs:超时时间,单位us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			会先检查NACK中断,如果有NACK会直接退出的
*************************************************************************************************************************/  
static IIC_ERROR IIC_isWaitSTOPF(IIC_HANDLE *pHandle, u32 TimeOutUs)
{
	//等待STOPF中断有效
	while((IIC_GetISR(pHandle) & IIC_FLAG_STOPF) == 0)
	{
		//有NACK中断,进行处理
		if(IIC_isCheckNackFlag(pHandle, 5) != IIC_OK)	
		{
			return IIC_HAL_ERROR;
		}
		
		if(TimeOutUs == 0)					//检查是否超时了
		{
			IIC_SoftReset(pHandle);		//执行软复位
			DEBUG("IIC等待结束超时\r\n");
			return IIC_TIMEOUT;				//超时
		}			
		TimeOutUs --;
		Delay_US(1);						//延时
	}
	
	return IIC_OK;
}


/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_isWaitFlag(IIC_HANDLE *pHandle,u32 Flag,bool isWaitFlagSet, u32 TimeOutUs)
* 功能	:			等待中断有效
* 参数	:			pHandle:句柄;Flag:需要等待的flag,见IIC_FLAG_xxx;isWaitFlagSet:TRUE:等待标志有效,FALSE:等待标志复位;TimeOutUs:超时时间,单位Us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			
*************************************************************************************************************************/  
static IIC_ERROR IIC_isWaitFlag(IIC_HANDLE *pHandle,u32 Flag,bool isWaitFlagSet, u32 TimeOutUs)
{
	if(isWaitFlagSet)	//需要等待标志有效
	{
		while((IIC_GetISR(pHandle) & Flag) == 0)
		{
			if(TimeOutUs == 0)					//检查是否超时了
			{
				return IIC_TIMEOUT;				//超时
			}			
			TimeOutUs --;
			Delay_US(1);						//延时
		}
	}
	else				//需要等待标志复位
	{
		while((IIC_GetISR(pHandle) & Flag) != 0)
		{
			if(TimeOutUs == 0)					//检查是否超时了
			{
				return IIC_TIMEOUT;				//超时
			}			
			TimeOutUs --;
			Delay_US(1);						//延时
		}
	}

	return IIC_OK;
}




/*************************************************************************************************************************
* 函数	:			static void IIC_MasterTransConfig(IIC_HANDLE *pHandle, u16 SlaveAddr, u8 TransByteCount, IIC_RELOAD_END_MODE EndorReload, IIC_START_WR_MODE StartAndWR)
* 功能	:			主设备传输配置(配置CR2寄存器)
* 参数	:			pHandle:句柄;SlaveAddr:从机地址;TransByteCount:需要传输的数据长度;EndorReload:自动结束还是重载设置,见IIC_RELOAD_END_MODE;StartAndWR:开始读写控制,见StartAndWR
* 返回	:			无
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-16
* 说明	: 			在往 NBYTE 中设置最后一次传输的字节数前,必须把 RELOAD 位清零便于后续自动发送STOP。
					当 RELOAD 位置 1 时,AUTOEND 位将不起作用;
*************************************************************************************************************************/  
static void IIC_MasterTransConfig(IIC_HANDLE *pHandle, u16 SlaveAddr, u8 TransByteCount, IIC_RELOAD_END_MODE EndorReload, IIC_START_WR_MODE StartAndWR)
{
	u32 temp = 0;
	
	//先读取CR2寄存器的值
	temp = pHandle->I2Cx->CR2;
	//清除掉相关的字节
	temp &= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND |I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
	//生成配置数据
	temp |= (u32)(((u32)SlaveAddr & I2C_CR2_SADD) | (((u32)TransByteCount << 16 ) & I2C_CR2_NBYTES));
	//重载与自动结束只能二选一
	if(EndorReload == IIC_AUTOEND_MODE) 	
	{
		temp |= I2C_CR2_AUTOEND;	//自动结束模式
	}
	else if(EndorReload == IIC_RELOAD_MODE)
	{
		temp |= I2C_CR2_RELOAD;		//NBYTES 重载模式
	}
	
	switch(StartAndWR)
	{
		case IIC_NOSTART_WRITE	:	break;	//没有开始的写-默认就是这样的
		case IIC_NOSTART_READ	:			//没有开始的读-用于后续数据读取
		{
			temp |= I2C_CR2_RD_WRN;			//读取
		}break;
		case IIC_START_WRITE	:			//生成开始写
		{
			temp |= I2C_CR2_START;			//启动传输
		}break;
		case IIC_START_READ		:			//生成开始读	
		{
			temp |= I2C_CR2_RD_WRN;			//读取
			temp |= I2C_CR2_START;			//启动传输
		}break;
		default:break;
	}
	
	//uart_printf("准备写入CR2=0x%X\t", temp);
	//更新到寄存器
	pHandle->I2Cx->CR2 = temp;  //测试
	//uart_printf("ISR=0x%X\r\n", pHandle->I2Cx->ISR);
	Delay_US(100);
}


/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_SendByte(IIC_HANDLE *pHandle, u8 data, u32 TimeOutUs)
* 功能	:			IIC发送一字节
* 参数	:			pHandle:句柄;data:待发送数据;TimeOutUs:超时时间,单位us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-16
* 说明	: 			TXIS有效后才会进行数据发送,不会检查数据发送是否完成。
					如果最后一个字节发送完成后不会再触发TXIS中断,只会触发TC中断
*************************************************************************************************************************/  
static IIC_ERROR IIC_SendByte(IIC_HANDLE *pHandle, u8 data, u32 TimeOutUs)
{
	IIC_ERROR Error = IIC_isWaitTXIS(pHandle, TimeOutUs);				//先等待可以发数据
	if(Error != IIC_OK) return Error;
	pHandle->I2Cx->TXDR = data;											//写数据到发送寄存器-不会等待数据发送完成	

	return IIC_OK;
}

/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_MasterRequestWriteAddr(IIC_HANDLE *pHandle, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, 
						bool isRead,u32 TimeOutUs)
* 功能	:			主设备请求发送从机地址与目标寄存器地址
* 参数	:			pHandle:句柄;SlaveAddr:从机地址;RegAddr:寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,
						FALSE:16bit寄存器地址;isRead:TRUE:这个操作是读取寄存器;否则是写入寄存器;TimeOutUs:超时时间,单位us
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-16
* 说明	: 			
*************************************************************************************************************************/  
static IIC_ERROR IIC_MasterRequestWriteAddr(IIC_HANDLE *pHandle, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,bool isRead, u32 TimeOutUs)
{	
	IIC_ERROR Error;
	
	//uart_printf("WriteAddr1:CR1=0x%X\t", pHandle->I2Cx->CR1);
	//uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
	//uart_printf("ISR=0x%X\r\n", pHandle->I2Cx->ISR);
	//传输配置,并启动传输,发送起始序列,开始IIC传输了
	//如果是读取则使能软件结束,如果是写则是自动重载
	IIC_MasterTransConfig(pHandle, SlaveAddr, (is8bitRegAddr==FALSE)?2:1, (isRead)?IIC_SOFTEND_MODE:IIC_RELOAD_MODE, IIC_START_WRITE);	//传输相关配置-写,并启动传输
	
	//开始发送寄存器地址
	if(is8bitRegAddr==FALSE)												//寄存器地址是16位的,IIC通常是MSB高位在前,需要进行高低位对调
	{
		Error = IIC_SendByte(pHandle, RegAddr>>8, TimeOutUs);	//先发送MSB-非最后一字节
		if(Error != IIC_OK)
		{
			DEBUG("IIC发送寄存器地址MSB失败\r\n");
			return Error;
		}	
	}
	Error = IIC_SendByte(pHandle, RegAddr & 0xFF, TimeOutUs);	//再发送LSB-最后一字节
	if(Error != IIC_OK)
	{
		DEBUG("IIC发送寄存器地址LSB失败\r\n");
		return Error;
	}
	//等待全部数据发送完成
	if(isRead)	//读取方向-非重载,等待数据传输完成 当 RELOAD=0、AUTOEND=0 且 NBYTES 数据传输完成时,该标志由硬件置 1。
	{
		if(IIC_isWaitFlag(pHandle, IIC_FLAG_TC, TRUE, TimeOutUs) != IIC_OK)
		{
			return IIC_TIMEOUT;
		}
	}	
	else //写入方向,等待重载
	{
		if(IIC_isWaitFlag(pHandle, IIC_FLAG_TCR, TRUE, TimeOutUs) != IIC_OK)
		{
			return IIC_TIMEOUT;
		}
	}
	
	return Error;
}


/*************************************************************************************************************************
* 函数	:			static IIC_ERROR IIC_WaitRxOneByte(IIC_HANDLE *pHandle, u8 *pData, u32 TimeOutUs)
* 功能	:			等待接收一字节数据
* 参数	:			pHandle:句柄;pData:接收的字节数据缓冲区;TimeOutUs:超时时间,单位ms
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			当RXNE有效后读取一条数据,否则可能会超时
*************************************************************************************************************************/  
static IIC_ERROR IIC_WaitRxOneByte(IIC_HANDLE *pHandle, u8 *pData, u32 TimeOutUs)
{
	while((IIC_GetISR(pHandle) & IIC_FLAG_RXNE) == 0)	//等待RXNE有效
	{
		if(TimeOutUs == 0)					//检查是否超时了
		{
			DEBUG("IIC等待接收超时\r\n");
			return IIC_TIMEOUT;				//超时
		}			
		TimeOutUs --;
		Delay_US(1);						//延时
	}
		
	*pData = pHandle->I2Cx->RXDR;							//读取收到的数据
	return IIC_OK;
}


/*************************************************************************************************************************
* 函数	:			IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, 
						u8 *pDataBuff, u16 ReadByteNum)
* 功能	:			IIC读取寄存器(可以读取1个或者多个寄存器)
* 参数	:			ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要读取的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
						pDataBuff:接收的字节数据缓冲区;ReadByteNum:要读取的寄存器数量;
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-15
* 最后修改时间 : 	2020-02-15
* 说明	: 			读取的数据都是小端模式,如果是16bit的寄存器,请读取偶数个数据,并且需要另外进行高低字节对调最后组成16bit数据
					可能的意外:比如一个异常的操作可能会导致IIC一直忙,此处检测到忙后会直接复位IIC,但是必须在应用层做好重入保护,比如
						增加信号量
					通过测试发现:在函数进入的时候,清除掉中断状态,之后几乎检测不到IIC忙的情况,当然在结束的时候如果检测到忙会主动发送一个STOP,在
						操作失败的情况下,会对IIC进行软复位,确保本次的错误不会影响下一次的IIC操作。
*************************************************************************************************************************/
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 ReadByteNum)
{
	IIC_ERROR Error = IIC_OK;
	u16 ReadCount;
	IIC_HANDLE *pHandle;
	
	if(ch > (IIC_CH_COUNT-1) || pDataBuff == NULL || ReadByteNum == 0)
	{
		DEBUG("IIC错误:无效的参数\r\n");
		return IIC_PARAMETER_ERROR;
	}
	pHandle = &sg_IIC_Handle[ch];					//获取相关通道的句柄
	
	if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)			//忙
	{
		IIC_SoftReset(pHandle);
		DEBUG("IIC错误:总线忙,复位总线,可能直接打断别的地方IIC操作,做好保护措施\r\n");
		if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)		//忙
		{
			DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
			Error = IIC_BUSY;
			goto end_loop;
		}
	}
	IIC_ClearISR(pHandle, IIC_GetISR(pHandle));		//复位错误状态
	IIC_ClearCR2(pHandle);							//复位CR2寄存器
	//主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送START
	Error = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,TRUE, pHandle->TimeOutUs);
	if(Error != IIC_OK)
	{
		goto end_loop;
	}
	//发送后续数据
	if(ReadByteNum > 255)	//如果超过255字节,则需要分多次读取-后续还有数据,需要重载
	{
		ReadCount = 255;	//本次需要读取的数据长度255
		IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_START_READ);	//传输相关配置-读取,并启动传输		
	}
	else
	{
		ReadCount = ReadByteNum;	//记录本次需要读取的数据长度-最后一次通讯后自动结束
		IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_START_READ);	//传输相关配置-读取,并启动传输
	}
	
	//循环等待接收数据完成
	do
	{
		//uart_printf("读ISR=0x%X\r\n", pHandle->I2Cx->ISR);
		Error = IIC_WaitRxOneByte(pHandle, pDataBuff,  pHandle->TimeOutUs);					//接收1字节数据
		if(Error != IIC_OK)
		{
			goto end_loop;
		}
	
		pDataBuff ++;
		ReadCount --;
		ReadByteNum --;
		if((ReadByteNum > 0) && (ReadCount == 0))	//还有数据要读取
		{
			if(ReadByteNum > 255)	//如果超过255字节,则需要分多次读取
			{
				ReadCount = 255;	//本次需要读取的数据长度255-后续还有数据,需要重载
				IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_READ);	//传输相关配置-读取,不启动传输			
			}
			else
			{
				ReadCount = ReadByteNum;	//记录本次需要读取的数据长度-最后一次通讯,自动结束
				IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_READ);	//传输相关配置-读取,不启动传输			
			}
		}
	}while(ReadByteNum);
	//读取完成了,等待STOP位有效
	Error = IIC_isWaitSTOPF(pHandle, pHandle->TimeOutUs);
	if(Error != IIC_OK) 
	{
		Error = IIC_STOP_ERROR;
		goto end_loop;
	}	
	IIC_ClearISR(pHandle, IIC_GetISR(pHandle));		//复位错误状态
	
end_loop:	
	IIC_ClearCR2(pHandle);							//复位CR2寄存器	
	if(Error != IIC_OK)
	{
		IIC_SoftReset(pHandle);						//IIC软复位,清除掉错误
		DEBUG("[IIC R错误]:%d\r\n", Error);
	}
	if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)			//忙,注:奇怪的是高速读取的时候,此处可能会检测到忙,TC标志有效,正常此处不应该有TC标志的
	{
		IIC_SendStop(pHandle);						//总线忙,发送一个STOP,释放掉总线
		DEBUG("IIC R结束:总线忙 ISR=0x%X\r\n", pHandle->I2Cx->ISR);		
	}
	
	return Error;
}


/*************************************************************************************************************************
* 函数	:			IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, 
						u8 *pDataBuff, u16 WriteByteNum)
* 功能	:			IIC写寄存器(可以写1个或者多个寄存器)
* 参数	:			ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要写入的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
						pDataBuff:写入的字节数据缓冲区;WriteByteNum:要写入的寄存器数量;
* 返回	:			IIC_ERROR
* 依赖	:			底层宏定义
* 作者	:			[email protected]
* 时间	:			2020-02-16
* 最后修改时间 : 	2020-02-16
* 说明	: 			写入的数据都是小端模式,如果是16bit的寄存器,请写入偶数个数据,并且需要提前进行高低字节对调最后组成高字节在前的数据buff
					可能的意外:比如一个异常的操作可能会导致IIC一直忙,此处检测到忙后会直接复位IIC,但是必须在应用层做好重入保护,比如
						增加信号量
					通过测试发现:在函数进入的时候,清除掉中断状态,之后几乎检测不到IIC忙的情况,当然在结束的时候如果检测到忙会主动发送一个STOP,在
						操作失败的情况下,会对IIC进行软复位,确保本次的错误不会影响下一次的IIC操作。
*************************************************************************************************************************/
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum)
{
	IIC_ERROR Error = IIC_OK;
	u16 ReadCount;
	IIC_HANDLE *pHandle;
	
	if(ch > (IIC_CH_COUNT-1) || pDataBuff == NULL || WriteByteNum == 0)
	{
		DEBUG("IIC错误:无效的参数\r\n");
		return IIC_PARAMETER_ERROR;
	}
	pHandle = &sg_IIC_Handle[ch];					//获取相关通道的句柄
	
	if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)		//忙
	{
		DEBUG("IIC错误:总线忙,复位总线,可能直接打断别的地方IIC操作,做好保护措施\r\n");
		IIC_SoftReset(pHandle);
		
		if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)	//忙
		{
			DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
			Error = IIC_BUSY;
			goto end_loop;
		}
	}
	
	IIC_ClearISR(pHandle, IIC_GetISR(pHandle));		//复位错误状态
	IIC_ClearCR2(pHandle);						//复位CR2寄存器
	//主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送START
	Error = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,FALSE,  pHandle->TimeOutUs);
	if(Error != IIC_OK)
	{
		goto end_loop;
	}
	//发送后续数据
	if(WriteByteNum > 255)			//如果超过255字节,则需要分多次写入-后续还有数据,需要重载
	{
		ReadCount = 255;			//本次需要写入的数据长度255
		IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_WRITE);	//传输相关配置-不启动传输的写入		
	}
	else
	{
		ReadCount = WriteByteNum;	//记录本次需要写入的数据长度-最后一次通讯后自动结束
		IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_WRITE);	//传输相关配置-不启动传输的写入
	}

	//循环发送数据
	do
	{
		Error = IIC_SendByte(pHandle, *pDataBuff, pHandle->TimeOutUs);	//发送一字节 
		if(Error != IIC_OK) 
		{
			goto end_loop;
		}

		ReadCount --;
		WriteByteNum --;
		pDataBuff ++;
		
		if((WriteByteNum > 0) && (ReadCount == 0))	//还有数据要读取
		{
			if(WriteByteNum > 255)	//如果超过255字节,则需要分多次读取
			{
				ReadCount = 255;	//本次需要写入的数据长度255-后续还有数据,需要重载
				IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_WRITE);	//传输相关配置-不启动传输的写入
			}
			else
			{
				ReadCount = WriteByteNum;	//记录本次需要写入的数据长度-最后一次通讯,自动结束
				IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_WRITE);	//传输相关配置-不启动传输的写入		
			}
		}
	}while(WriteByteNum);	

	//写入完成了,等待STOP位有效
	Error = IIC_isWaitSTOPF(pHandle, pHandle->TimeOutUs);
	if(Error != IIC_OK) 
	{
		Error = IIC_STOP_ERROR;
		goto end_loop;
	}
	IIC_ClearISR(pHandle, IIC_GetISR(pHandle));		//复位错误状态
	
end_loop:	
	IIC_ClearCR2(pHandle);							//复位CR2寄存器	
	if(Error != IIC_OK)
	{
		IIC_SoftReset(pHandle);						//IIC软复位,清除掉错误
		DEBUG("[IIC W错误]:%d\r\n", Error);
	}
	
	if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)			//忙,注:奇怪的是高速读取的时候,此处可能会检测到忙,TC标志有效,正常此处不应该有TC标志的
	{
		IIC_SendStop(pHandle);						//总线忙,发送一个STOP,释放掉总线
		DEBUG("IIC W结束:总线忙 ISR=0x%X\r\n", pHandle->I2Cx->ISR);		
	}
	return Error;
}


/*************************************************************************************************************************
* 函数			:	void IIC_SoftReset(IIC_HANDLE *pHandle)
* 功能			:	硬件IIC软复位(会使能IIC)
* 参数			:	ch:IIC通道
* 返回			:	IIC_ERROR
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2020-02-15
* 最后修改时间 	: 	2020-02-15
* 说明			: 	IIC软复位;必须使 PE 保持低电平持续至少 3 个 APB 时钟周期,才能成功执行软件复位。
*************************************************************************************************************************/
static void IIC_SoftReset(IIC_HANDLE *pHandle)
{
	pHandle->I2Cx->CR1 &= ~BIT0;		//关闭IIC后执行软复位
	Delay_US(1);	
	if(pHandle->I2Cx->CR1 & BIT0)
	{
		DEBUG("IIC软复位失败\r\n");
	}
	pHandle->I2Cx->CR1 |= BIT0;			//重新启动
	Delay_US(1);	
}



/*************************************************************************************************************************
* 函数			:	static u32 IIC_CalculationTiming(u16 Speed_KHz)
* 功能			:	硬件IIC时序计算
* 参数			:	ch:IIC通道;Speed_KHz:速度10-1000
* 返回			:	IIC_ERROR
* 依赖			:	底层宏定义
* 作者			:	[email protected]
* 时间			:	2020-02-15
* 最后修改时间 	: 	2020-02-15
* 说明			: 	IIC使用的是APB1时钟,将IIC总线时钟控制在100KHz	
					APB1时钟:最大不能超过45MHz	
					如果速度是100,则按照SMBUS时序
*************************************************************************************************************************/
static u32 IIC_CalculationTiming(u16 Speed_KHz)
{
	u32 tTime = 0;
	u32 temp;
	u32 time_temp = SYS_GetAPB1ClockSpeed() / 1000000;		//先获取APB1时钟速度
	u32 Th = 1000/Speed_KHz/2;								//计算高电平时间(低电平时间一样),单位ns
	
	if(time_temp < 20) time_temp = 20;
	time_temp = 1*1000 / time_temp;							//单位ns
	uart_printf("IIC时钟计算->APB1周期:%dns\t", time_temp);
	if(Speed_KHz == 100)	//如果是100,则按照SMBUS时序
	{
		//主时钟分频系数固定为3
		time_temp *= 3;			//主时钟周期
		uart_printf("IIC时钟周期:%dns\t", time_temp);
		tTime |= (3-1) << 28;	//PRESC,时钟预分配
		//计算数据建立时间,要求最小250ns
		temp = 250 / time_temp;
		if(temp > 15) temp = 15;
		if(temp < 1) temp = 1;
		tTime |= temp << 20;
		//计算数据保持时间,要求至少300ns
		temp = 300 / time_temp;
		if(temp > 15) temp = 15;
		if(temp < 1) temp = 1;
		tTime |= temp << 16;
		//计算高电平周期5us
		temp = 5*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 8;
		//计算低电平周期5us
		temp = 5*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 0;
	}
	else if(Speed_KHz < 100)
	{
		//主时钟分频系数固定为6
		time_temp *= 6;			//主时钟周期
		uart_printf("IIC时钟周期:%dns\t", time_temp);
		tTime |= (6-1) << 28;	//PRESC,时钟预分配
		//计算数据建立时间,要求最小250ns
		temp = 250 / time_temp;
		if(temp > 15) temp = 15;
		tTime |= temp << 20;
		//计算数据保持时间,要求至少300ns
		temp = 300 / time_temp;
		if(temp > 15) temp = 15;
		tTime |= temp << 16;
		//计算高电平周期Th us
		temp = Th*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 8;
		//计算低电平周期 Th us
		temp = Th*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 0;
	}
	else //>100
	{
		//主时钟分频系数固定为2
		time_temp *= 2;			//主时钟周期
		uart_printf("IIC时钟周期:%dns\t", time_temp);
		tTime |= (2-1) << 28;	//PRESC,时钟预分配
		//计算数据建立时间,随便给100ns
		temp = 100 / time_temp;
		if(temp > 15) temp = 15;
		tTime |= temp << 20;
		//计算数据保持时间,给100ns
		temp = 100 / time_temp;
		if(temp > 15) temp = 15;
		tTime |= temp << 16;
		//计算高电平周期Th us
		temp = Th*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 8;
		//计算低电平周期 Th us
		temp = Th*1000 / time_temp;
		if(temp > 255) temp = 255;
		if(temp < 1) temp = 1;
		tTime |= temp << 0;
	}
	
	uart_printf("时序寄存器结果为:0x%X\r\n", tTime);
	
	return tTime;
}

/*************************************************************************************************************
 * 文件名		:	stm32f7_iic.h
 * 功能			:	STM32F7 IIC接口驱动
 * 作者			:	[email protected]
 * 创建时间		:	2020-02-09
 * 最后修改时间	:	2020-02-09
 * 详细:			
*************************************************************************************************************/		
#ifndef __STM32F7_IIC_H_
#define __STM32F7_IIC_H_
#include "system.h" 


//IIC硬件接口选择
typedef enum
{
	IIC_CH1	=		0,	//IIC1
	IIC_CH2	=		1,	//IIC2
	IIC_CH3	=		2,	//IIC3
	IIC_CH4	=		3,	//IIC4
}IIC_CH_Type;
#define IIC_CH_COUNT	4	//4个IIC


//中断状态
#define IIC_FLAG_TXE                    BIT0	//发送数据寄存器为空(发送器); 当 I2C_TXDR 寄存器为空时,该位由硬件置 1。下一个待发送的数据写入 I2C_TXDR 寄存器时,该位被清零。
#define IIC_FLAG_TXIS                   BIT1	//发送中断状态(发送器); 当 I2C_TXDR 寄存器为空时,该位由硬件置 1,待发送的数据必须写入 I2C_TXDR 寄存器。
#define IIC_FLAG_RXNE                   BIT2	//接收数据寄存器不为空(接收器); 当接收到的数据已复制到 I2C_RXDR 寄存器且准备就绪可供读取时,该位由硬件置 1。读取I2C_RXDR 时,将清零该位。
#define IIC_FLAG_ADDR                   BIT3	//地址匹配(从模式); 接收到的地址与使能的从设备地址之一匹配时,该位由硬件置 1。该位由软件清零,方法是将ADDRCF 位置 1。
#define IIC_FLAG_NACKF                  BIT4	//接收到否定应答标志; 传输完字节后接收到 NACK 时,该标志由硬件置 1。该标志由软件清零,方法是将 NACKCF位置 1。
#define IIC_FLAG_STOPF                  BIT5	//停止位检测标志; 当在总线上检测到停止位,且外设也参与本次传输时,该标志由硬件置 1
#define IIC_FLAG_TC                     BIT6	//传输完成(主模式); 当 RELOAD=0、AUTOEND=0 且 NBYTES 数据传输完成时,该标志由硬件置 1。当 START位或 STOP 位置 1 时,该标志由软件清零。
#define IIC_FLAG_TCR                    BIT7	//传输完成等待重载; 当 RELOAD=1 且 NBYTES 数据传输完成时,该标志由硬件置 1。当 NBYTES 写入一个非零值时,该标志由软件清零。
#define IIC_FLAG_BERR                   BIT8	//总线错误; 当检测到错位的起始位或停止位,而外设也参与传输时,该标志由硬件置 1。在从模式下的地址阶段,该标志不会置 1。该标志由软件清零,方法是将 BERRCF 位置 1。
#define IIC_FLAG_ARLO                   BIT9	//仲裁丢失; 发生仲裁丢失时,该标志由硬件置 1。该标志由软件清零,方法是将 ARLOCF 位置 1。
#define IIC_FLAG_OVR                    BIT10	//上溢/下溢(从模式); 在从模式下且 NOSTRETCH=1 时,如果发生上溢/下溢错误,该标志由硬件置 1。该标志由软件清零,方法是将 OVRCF 位置 1。
#define IIC_FLAG_PECERR                 BIT11	//接收期间的 PEC 错误; 当接收到的 PEC 与 PEC 寄存器的内容不匹配时,该标志由硬件置 1。接收到错误的 PEC 后,将自动发送 NACK。该标志由软件清零,方法是将 PECCF 位置 1。
#define IIC_FLAG_TIMEOUT                BIT12	//超时或 tLOW 检测标志; 发生超时或延长时钟超时时,该标志由硬件置 1。该位由软件清零,方法是将 TIMEOUTCF 位置 1。
#define IIC_FLAG_ALERT                  BIT13	//SMBus 报警; 当 SMBHEN=1(SMBus 主机配置)、ALERTEN=1 且在 SMBA 引脚上检测到 SMBALERT 事件(下降沿)时,该标志由硬件置 1。该位由软件清零,方法是将 ALERTCF 位置 1。
#define IIC_FLAG_BUSY                   BIT15	//总线繁忙; 该标志用于指示总线上正在进行通信。当检测到起始位时,该位由硬件置 1。当检测到停止位或 PE = 0 时,该位由硬件清零。
#define IIC_FLAG_DIR                    BIT16	//传输方向(从模式); 该标志在发生地址匹配事件时 (ADDR=1) 更新。;0:写;1:读

//通讯错误状态
typedef enum
{
	IIC_OK					=	0,	//没有错误
	IIC_PARAMETER_ERROR		=	1,	//参数错误
	IIC_TIMEOUT				=	2,	//超时错误,也可能是底层错误
	IIC_HAL_ERROR			=	3,	//底层错误
	IIC_STOP_ERROR			=	4,	//等待结束错误
	IIC_BUSY				=	5,	//硬件忙
	IIC_NACK				=	6,	//收到NACK了
}IIC_ERROR;


bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs);	//硬件IIC初始化
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 ReadByteNum);	//IIC读取寄存器(可以读取1个或者多个寄存器)
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum);	//IIC写寄存器(可以写1个或者多个寄存器)


#endif //__STM32F7_IIC_H_

//初始化

IIC_Init(IIC_CH3, 200, 0);	//硬件IIC初始化

//调用


//触摸屏IIC读取寄存器接口
bool TP_FT5336_IIC_ReadReg(u16 SlaveAddr, u8 RegAddr, u8 *pDataBuff, u16 ByteNum)
{
	if(IIC_MasterReadReg(FT5336_IIC_CH, SlaveAddr, RegAddr, TRUE, pDataBuff, ByteNum) == IIC_OK)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

//触摸屏IIC写寄存器接口
bool TP_FT5336_IIC_WriteReg(u16 SlaveAddr, u8 RegAddr, u8 *pDataBuff, u16 ByteNum)
{
	if(IIC_MasterWriteReg(FT5336_IIC_CH, SlaveAddr, RegAddr, TRUE, pDataBuff, ByteNum) == IIC_OK)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}
发布了143 篇原创文章 · 获赞 370 · 访问量 81万+

猜你喜欢

转载自blog.csdn.net/cp1300/article/details/104347417