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

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

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

正在上传…重新上传取消

下面简要说明一下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接口驱动* 作者         :   cp1300@139.com* 创建时间       :   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;           //操作超时,单位usu16 Speed_KHz;            //通讯速度,单位KHzbool isMasterMode;       //是否为主设备模式-目前只支持主设备模式
}IIC_HANDLE;//IIC外设结构指针
static const  I2C_TypeDef * const I2C_TYPE_BUFF[IIC_CH_COUNT] = {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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           2020-02-15
* 最后修改时间 :  2020-02-15
* 说明    :           速度只是个大概的计算值,设置为100KHz时会严格的按照SMBUS时序,其余的不保证SMBUS兼容,正常情况下只要时钟速度符合要求,时钟上升沿到来前数据以及稳定的切换了即可保证通讯稳定性
*************************************************************************************************************************/
bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
{SYS_DEV_CLOCK DevClock;IIC_HANDLE *pHandle;switch(ch){case IIC_CH1 :   //IIC1{RCC->DCKCFGR2 &= ~(0x3 << 16); //清除设置,使用APB1作为时钟源DevClock = DEV_I2C1;//初始化I2C IO
#if(IIC_CH1_IO_SELECT==IIC_CH1_PB6_7) //I2C1  使用PB6/7SYS_DeviceClockEnable(DEV_GPIOB, TRUE);              //使能GPIOB时钟SYS_GPIOx_OneInit(GPIOB, 6, AF_OD, SPEED_25M);       //PB6SYS_GPIOx_OneInit(GPIOB, 7, AF_OD, SPEED_25M);     //PB7SYS_GPIOx_SetAF(GPIOB, 6, AF4_I2C1);               //AF4SYS_GPIOx_SetAF(GPIOB, 7, AF4_I2C1);               //AF4
#elif(IIC_CH1_IO_SELECT==IIC_CH1_PB8_9)   //I2C1  使用PB8/9SYS_DeviceClockEnable(DEV_GPIOB, TRUE);              //使能GPIOB时钟SYS_GPIOx_OneInit(GPIOB, 8, AF_OD, SPEED_25M);       //PB8SYS_GPIOx_OneInit(GPIOB, 9, AF_OD, SPEED_25M);     //PB9SYS_GPIOx_SetAF(GPIOB, 8, AF4_I2C1);               //AF4SYS_GPIOx_SetAF(GPIOB, 9, AF4_I2C1);               //AF4
#else#error("无效的I2C1 IO选择");
#endif //IIC_CH1_IO_SELECT}break;case IIC_CH2   :   //IIC2{RCC->DCKCFGR2 &= ~(0x3 << 18); //清除设置,使用APB1作为时钟源DevClock = DEV_I2C2;//初始化I2C IO
#if(IIC_CH2_IO_SELECT==IIC_CH2_PB10_11)   //使用PB10,PB11SYS_DeviceClockEnable(DEV_GPIOB, TRUE);                //使能GPIOB时钟SYS_GPIOx_OneInit(GPIOB, 10, AF_OD, SPEED_25M);      //PB10SYS_GPIOx_OneInit(GPIOB, 11, AF_OD, SPEED_25M);       //PB11SYS_GPIOx_SetAF(GPIOB, 10, AF4_I2C2);             //AF4SYS_GPIOx_SetAF(GPIOB, 11, AF4_I2C2);              //AF4
#elif(IIC_CH2_IO_SELECT==IIC_CH2_PF0_1)   //PF0,PF1SYS_DeviceClockEnable(DEV_GPIOF, TRUE);                //使能GPIOF时钟SYS_GPIOx_OneInit(GPIOF, 0, AF_OD, SPEED_25M);       //PF0SYS_GPIOx_OneInit(GPIOF, 1, AF_OD, SPEED_25M);     //PF1SYS_GPIOx_SetAF(GPIOF, 0, AF4_I2C2);               //AF4SYS_GPIOx_SetAF(GPIOF, 1, AF4_I2C2);               //AF4
#elif(IIC_CH2_IO_SELECT==IIC_CH2_PH4_5)   //PH4,PH5SYS_DeviceClockEnable(DEV_GPIOH, TRUE);                //使能GPIOH时钟SYS_GPIOx_OneInit(GPIOH, 4, AF_OD, SPEED_25M);       //PH4SYS_GPIOx_OneInit(GPIOH, 5, AF_OD, SPEED_25M);     //PH5SYS_GPIOx_SetAF(GPIOH, 4, AF4_I2C2);               //AF4SYS_GPIOx_SetAF(GPIOH, 5, AF4_I2C2);               //AF4
#else#error("无效的I2C2 IO选择");
#endif //IIC_CH2_IO_SELECT}break;           case IIC_CH3    :   //IIC3{RCC->DCKCFGR2 &= ~(0x3 << 20); //清除设置,使用APB1作为时钟源DevClock = DEV_I2C3;//初始化I2C IO
#if(IIC_CH3_IO_SELECT==IIC_CH3_PH7_8)     //PH7,PH8SYS_DeviceClockEnable(DEV_GPIOH, TRUE);                //使能GPIOH时钟SYS_GPIOx_OneInit(GPIOH, 7, AF_OD, SPEED_25M);       //PH7SYS_GPIOx_OneInit(GPIOH, 8, AF_OD, SPEED_25M);     //PH8SYS_GPIOx_SetAF(GPIOH, 7, AF4_I2C3);               //AF4SYS_GPIOx_SetAF(GPIOH, 8, AF4_I2C3);               //AF4
#elif(IIC_CH3_IO_SELECT==IIC_CH3_PA8_PC9) //PA8,PC9SYS_DeviceClockEnable(DEV_GPIOA, TRUE);                //使能GPIOA时钟SYS_DeviceClockEnable(DEV_GPIOC, TRUE);              //使能GPIOC时钟SYS_GPIOx_OneInit(GPIOA, 8, AF_OD, SPEED_25M);       //PA8SYS_GPIOx_OneInit(GPIOC, 9, AF_OD, SPEED_25M);     //PC9SYS_GPIOx_SetAF(GPIOA, 8, AF4_I2C3);               //AF4SYS_GPIOx_SetAF(GPIOC, 9, AF4_I2C3);               //AF4
#else#error("无效的I2C3 IO选择");
#endif //IIC_CH3_IO_SELECT          }break;         case IIC_CH4    :   //IIC4{RCC->DCKCFGR2 &= ~(0x3 << 22); //清除设置,使用APB1作为时钟源DevClock = DEV_I2C4;//初始化I2C IO
#if(IIC_CH4_IO_SELECT==IIC_CH4_PD12_13)       //PD12,PD13SYS_DeviceClockEnable(DEV_GPIOD, TRUE);              //使能GPIOD时钟SYS_GPIOx_OneInit(GPIOD, 12, AF_OD, SPEED_25M);      //PD12SYS_GPIOx_OneInit(GPIOD, 13, AF_OD, SPEED_25M);       //PD13SYS_GPIOx_SetAF(GPIOD, 12, AF4_I2C4);             //AF4SYS_GPIOx_SetAF(GPIOD, 13, AF4_I2C4);              //AF4
#elif(IIC_CH4_IO_SELECT==IIC_CH4_PF14_15) //PF14,PF15SYS_DeviceClockEnable(DEV_GPIOF, TRUE);              //使能GPIOF时钟SYS_GPIOx_OneInit(GPIOF, 14, AF_OD, SPEED_25M);      //PF14SYS_GPIOx_OneInit(GPIOF, 15, AF_OD, SPEED_25M);       //PF15SYS_GPIOx_SetAF(GPIOF, 14, AF4_I2C4);             //AF4SYS_GPIOx_SetAF(GPIOF, 15, AF4_I2C4);              //AF4
#elif(IIC_CH4_IO_SELECT==IIC_CH4_PH11_12) //PH11,PH12SYS_DeviceClockEnable(DEV_GPIOH, TRUE);              //使能GPIOH时钟SYS_GPIOx_OneInit(GPIOH, 11, AF_OD, SPEED_25M);      //PH11SYS_GPIOx_OneInit(GPIOH, 12, AF_OD, SPEED_25M);       //PH12SYS_GPIOx_SetAF(GPIOH, 11, AF4_I2C4);             //AF4SYS_GPIOx_SetAF(GPIOH, 12, AF4_I2C4);              //AF4
#else#error("无效的I2C4 IO选择");
#endif //IIC_CH4_IO_SELECT          }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(DevClock, TRUE);                   //使能时钟SYS_DeviceReset(DevClock);                                //外设复位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;                              //使能IICpHandle->isMasterMode = TRUE;                            //主设备模式Delay_US(1);return TRUE;
}/*************************************************************************************************************************
* 函数    :           static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)
* 功能    :           检查是否有NACK中断状态,如果有则清除掉
* 参数    :           pHandle:句柄;TimeOutUs:超时时间,单位us
* 返回    :           IIC_ERROR
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 返回    :           无
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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寄存器//主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送STARTError = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,TRUE, pHandle->TimeOutUs);if(Error != IIC_OK){goto end_loop;}//发送后续数据if(ReadByteNum > 255)  //如果超过255字节,则需要分多次读取-后续还有数据,需要重载{ReadCount = 255;    //本次需要读取的数据长度255IIC_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
* 依赖    :           底层宏定义
* 作者    :           cp1300@139.com
* 时间    :           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寄存器//主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送STARTError = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,FALSE,  pHandle->TimeOutUs);if(Error != IIC_OK){goto end_loop;}//发送后续数据if(WriteByteNum > 255)           //如果超过255字节,则需要分多次写入-后续还有数据,需要重载{ReadCount = 255;            //本次需要写入的数据长度255IIC_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
* 依赖            :   底层宏定义
* 作者            :   cp1300@139.com
* 时间            :   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
* 依赖            :   底层宏定义
* 作者            :   cp1300@139.com
* 时间            :   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;                                //计算高电平时间(低电平时间一样),单位nsif(time_temp < 20) time_temp = 20;time_temp = 1*1000 / time_temp;                          //单位nsuart_printf("IIC时钟计算->APB1周期:%dns\t", time_temp);if(Speed_KHz == 100)  //如果是100,则按照SMBUS时序{//主时钟分频系数固定为3time_temp *= 3;            //主时钟周期uart_printf("IIC时钟周期:%dns\t", time_temp);tTime |= (3-1) << 28;  //PRESC,时钟预分配//计算数据建立时间,要求最小250nstemp = 250 / time_temp;if(temp > 15) temp = 15;if(temp < 1) temp = 1;tTime |= temp << 20;//计算数据保持时间,要求至少300nstemp = 300 / time_temp;if(temp > 15) temp = 15;if(temp < 1) temp = 1;tTime |= temp << 16;//计算高电平周期5ustemp = 5*1000 / time_temp;if(temp > 255) temp = 255;if(temp < 1) temp = 1;tTime |= temp << 8;//计算低电平周期5ustemp = 5*1000 / time_temp;if(temp > 255) temp = 255;if(temp < 1) temp = 1;tTime |= temp << 0;}else if(Speed_KHz < 100){//主时钟分频系数固定为6time_temp *= 6;          //主时钟周期uart_printf("IIC时钟周期:%dns\t", time_temp);tTime |= (6-1) << 28;  //PRESC,时钟预分配//计算数据建立时间,要求最小250nstemp = 250 / time_temp;if(temp > 15) temp = 15;tTime |= temp << 20;//计算数据保持时间,要求至少300nstemp = 300 / time_temp;if(temp > 15) temp = 15;tTime |= temp << 16;//计算高电平周期Th ustemp = Th*1000 / time_temp;if(temp > 255) temp = 255;if(temp < 1) temp = 1;tTime |= temp << 8;//计算低电平周期 Th ustemp = Th*1000 / time_temp;if(temp > 255) temp = 255;if(temp < 1) temp = 1;tTime |= temp << 0;}else //>100{//主时钟分频系数固定为2time_temp *= 2;            //主时钟周期uart_printf("IIC时钟周期:%dns\t", time_temp);tTime |= (2-1) << 28;  //PRESC,时钟预分配//计算数据建立时间,随便给100nstemp = 100 / time_temp;if(temp > 15) temp = 15;tTime |= temp << 20;//计算数据保持时间,给100nstemp = 100 / time_temp;if(temp > 15) temp = 15;tTime |= temp << 16;//计算高电平周期Th ustemp = Th*1000 / time_temp;if(temp > 255) temp = 255;if(temp < 1) temp = 1;tTime |= temp << 8;//计算低电平周期 Th ustemp = 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接口驱动* 作者         :   cp1300@139.com* 创建时间       :   2020-02-09* 最后修改时间  :   2020-02-09* 详细:
*************************************************************************************************************/
#ifndef __STM32F7_IIC_H_
#define __STM32F7_IIC_H_
#include "system.h" //16位整形数高低对调
#ifndef SWAP16
#define SWAP16(x)   (((x & 0xff00) >> 8) | ((x & 0xff) << 8))
#endif  //SWAP16//硬件IO口选择
//I2C1 IO定义与选择
#define IIC_CH1_PB6_7               0                   //PB6,PB7
#define IIC_CH1_PB8_9               1                   //PB8,PB9
#define IIC_CH1_IO_SELECT           IIC_CH1_PB6_7       //选择I2C1 的IO    //I2C2 IO定义与选择
#define IIC_CH2_PB10_11             0                   //PB10,PB11
#define IIC_CH2_PF0_1               1                   //PF0,PF1
#define IIC_CH2_PH4_5               2                   //PH4,PH5
#define IIC_CH2_IO_SELECT           IIC_CH2_PB10_11     //选择I2C2 的IO//I2C3 IO定义与选择
#define IIC_CH3_PH7_8               0                   //PH7,PH8
#define IIC_CH3_PA8_PC9             1                   //PA8,PC9
#define IIC_CH3_IO_SELECT           IIC_CH3_PH7_8       //选择I2C3 的IO//I2C4 IO定义与选择
#define IIC_CH4_PD12_13             0                   //PD12,PD13
#define IIC_CH4_PF14_15             1                   //PF14,PF15
#define IIC_CH4_PH11_12             2                   //PH11,PH12
#define IIC_CH4_IO_SELECT           IIC_CH4_PD12_13     //选择I2C4 的IO//IIC硬件接口选择
typedef enum
{IIC_CH1    =      0,  //IIC1IIC_CH2   =      1,  //IIC2IIC_CH3   =      2,  //IIC3IIC_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;}
}

STM32F7 硬件IIC驱动相关推荐

  1. MSP430杂谈--AD7745硬件IIC驱动与模拟IIC驱动

    和上一篇AD7793类似,项目中也涉及到利用AD7745读取电容值,来测环境湿度.编写了基于MSP430的AD7745的硬件IIC驱动和模拟IIC驱动,分享给大家. AD7745硬件IIC驱动完整版下 ...

  2. 基于Stm32f103硬件iic驱动LM75A温度传感器

    这是LM75A温度传感器的概述,本文主要介绍基于Stm32f103的硬件iic驱动LM75A温度传感器. 这是我所使用的硬件电路,很简单. 对于该传感器的使用,主要是读取温度值,查看数据手册我们知道需 ...

  3. STM32 驱动 GY-302 光照传感器 BH1750 模块(软件IIC与硬件IIC驱动)

    1.特别说明 ​ 要是不想看原理和过程,直接下拉找代码吧,都是测试过的,很稳定,有硬件I2C驱动的,也有软件模拟I2C驱动的,基于STM32F103系列和STM32F4系列实现,基于标准库实现,条理清 ...

  4. 12. STM32——硬件IIC驱动OLED屏幕显示

    STM32--硬件IIC驱动OLED屏幕显示 OLED屏幕 OLED屏幕特点 OLED屏幕接线说明 OLED屏幕显存 OLED屏幕原理 OLED屏幕常用指令 OLED屏幕字模软件的使用 写命令 写数据 ...

  5. 基于STM32F103RC硬件IIC驱动18位AD MCP3421驱动开发

    基于STM32 HAL库硬件IIC 驱动18位AD MCP3421 最近用到小信号采集,发现关于该芯片的STM32 HAL 库驱动比较少.就写了一个基于STM32F103RCT6测试Demo .在此分 ...

  6. 【STM32】 硬件IIC 驱动SSD1302(0.96 OLED模块) -- 3/4 OLED的命令表 学习

    书接上回 文章1:[STM32] 硬件IIC 驱动SSD1306(0.96 OLED模块) – 1/4 库函数 学习 文章2:[STM32] 硬件IIC 驱动SSD1302(0.96 OLED模块) ...

  7. 基于GR551x芯片的硬件IIC驱动QMA7981

    基于GR551x芯片的硬件IIC驱动QMA7981 实现的函数 qma7981_init(); int16_t get_accel_x() int16_t get_accel_y() int16_t ...

  8. HC32F460开发之硬件IIC驱动AT24C64

    文章目录 前言 一.AT24C64 二.驱动步骤 1.宏定义 2.代码实现 总结 前言 在嵌入式设计中,E2PROM存储芯片常被应用于需要掉电存储,且容量不大的场合.对比flash存储芯片,E2PRO ...

  9. STM32F103使用硬件IIC驱动SHT30温湿度传感器

    文章目录 前言 一.SHT30温湿度传感器原理图 二.代码部分 1.SHT30.c文件 2.SHT30.h文件 总结 前言 SHT30是一种常见的温湿度传感器,该传感器广泛应用于各种场景,小米的温湿度 ...

  10. HC32L136/HC32L176开发之硬件IIC驱动AT24C64

    文章目录 前言 一.AT24C64 二.驱动步骤 1.宏定义 2.代码实现 前言 在嵌入式设计中,E2PROM存储芯片常被应用于需要掉电存储,且容量不大的场合.对比flash存储芯片,E2PROM具有 ...

最新文章

  1. eclipse 创建maven web项目
  2. shortcut icon 修改浏览器标签网站图标
  3. ubuntu6.06容易死机的一种解决方法
  4. Google Cloud大规模宕机;中国正式进入 5G 商用元年!苹果发布SwiftUI |开发者周刊...
  5. OFFICE技术讲座:JDK绘制旋转斜体字体的效果
  6. MyBatis(三)------MyBatis的核心组件
  7. c语言多字符和宽字符,2.1.5 多字节字符和宽字符
  8. 个人热爱软件编程,我对自己做了一个总结
  9. android 6.1 app闪退,手机软件闪退怎么办 具体解决方法【图文】
  10. 键盘是计算机最常用的输入设备之一
  11. 入门了解开发板是做什么用的?
  12. Python程序写诗【训练1分钟】古诗生成
  13. aws上传找不到endpoint url或者The Aws Access Key Id you provided does not exist in our recordss
  14. Zephyr events
  15. 水仙花数c之和语言程序,水仙花数C语言的
  16. 安装Properties Editor
  17. 十三、添加RD 会话主机角色
  18. 用户画像(profile v.s. persona)
  19. ExpandableListView 模拟QQ好友分组 小实例
  20. Java解决时间小时时差问题

热门文章

  1. python处理word页码_使用Python向Word文档添加页码
  2. MOT:A Higher Order Metric for Evaluating Multi-object Tracking
  3. BUUCTF WEB PIAPIAPIA1
  4. 洛谷P4315 月下“毛景树”(树剖+线段树)
  5. 汽车研发企业试验数据管理系统建设研究
  6. 老博客里的另一篇文章《自由与爱情》,实际是一句名言的中英文对照~
  7. 模数转换器(ADC)
  8. BeautyGAN论文翻译
  9. 一个让Google、Facebook、Amazon都羡慕的平台,为什么说阿里妈妈是数字营销的未来...
  10. 关于SOC、态势感知,5种常见的关联分析模型