目前只实现了主设备模式,一般也只用到主设备模式,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接口驱动
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 创建时间 : 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[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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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/7
- SYS_DeviceClockEnable(DEV_GPIOB, TRUE); //使能GPIOB时钟
- SYS_GPIOx_OneInit(GPIOB, 6, AF_OD, SPEED_25M); //PB6
- SYS_GPIOx_OneInit(GPIOB, 7, AF_OD, SPEED_25M); //PB7
- SYS_GPIOx_SetAF(GPIOB, 6, AF4_I2C1); //AF4
- SYS_GPIOx_SetAF(GPIOB, 7, AF4_I2C1); //AF4
- #elif(IIC_CH1_IO_SELECT==IIC_CH1_PB8_9) //I2C1 使用PB8/9
- SYS_DeviceClockEnable(DEV_GPIOB, TRUE); //使能GPIOB时钟
- SYS_GPIOx_OneInit(GPIOB, 8, AF_OD, SPEED_25M); //PB8
- SYS_GPIOx_OneInit(GPIOB, 9, AF_OD, SPEED_25M); //PB9
- SYS_GPIOx_SetAF(GPIOB, 8, AF4_I2C1); //AF4
- SYS_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,PB11
- SYS_DeviceClockEnable(DEV_GPIOB, TRUE); //使能GPIOB时钟
- SYS_GPIOx_OneInit(GPIOB, 10, AF_OD, SPEED_25M); //PB10
- SYS_GPIOx_OneInit(GPIOB, 11, AF_OD, SPEED_25M); //PB11
- SYS_GPIOx_SetAF(GPIOB, 10, AF4_I2C2); //AF4
- SYS_GPIOx_SetAF(GPIOB, 11, AF4_I2C2); //AF4
- #elif(IIC_CH2_IO_SELECT==IIC_CH2_PF0_1) //PF0,PF1
- SYS_DeviceClockEnable(DEV_GPIOF, TRUE); //使能GPIOF时钟
- SYS_GPIOx_OneInit(GPIOF, 0, AF_OD, SPEED_25M); //PF0
- SYS_GPIOx_OneInit(GPIOF, 1, AF_OD, SPEED_25M); //PF1
- SYS_GPIOx_SetAF(GPIOF, 0, AF4_I2C2); //AF4
- SYS_GPIOx_SetAF(GPIOF, 1, AF4_I2C2); //AF4
- #elif(IIC_CH2_IO_SELECT==IIC_CH2_PH4_5) //PH4,PH5
- SYS_DeviceClockEnable(DEV_GPIOH, TRUE); //使能GPIOH时钟
- SYS_GPIOx_OneInit(GPIOH, 4, AF_OD, SPEED_25M); //PH4
- SYS_GPIOx_OneInit(GPIOH, 5, AF_OD, SPEED_25M); //PH5
- SYS_GPIOx_SetAF(GPIOH, 4, AF4_I2C2); //AF4
- SYS_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,PH8
- 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
- #elif(IIC_CH3_IO_SELECT==IIC_CH3_PA8_PC9) //PA8,PC9
- SYS_DeviceClockEnable(DEV_GPIOA, TRUE); //使能GPIOA时钟
- SYS_DeviceClockEnable(DEV_GPIOC, TRUE); //使能GPIOC时钟
- SYS_GPIOx_OneInit(GPIOA, 8, AF_OD, SPEED_25M); //PA8
- SYS_GPIOx_OneInit(GPIOC, 9, AF_OD, SPEED_25M); //PC9
- SYS_GPIOx_SetAF(GPIOA, 8, AF4_I2C3); //AF4
- SYS_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,PD13
- SYS_DeviceClockEnable(DEV_GPIOD, TRUE); //使能GPIOD时钟
- SYS_GPIOx_OneInit(GPIOD, 12, AF_OD, SPEED_25M); //PD12
- SYS_GPIOx_OneInit(GPIOD, 13, AF_OD, SPEED_25M); //PD13
- SYS_GPIOx_SetAF(GPIOD, 12, AF4_I2C4); //AF4
- SYS_GPIOx_SetAF(GPIOD, 13, AF4_I2C4); //AF4
- #elif(IIC_CH4_IO_SELECT==IIC_CH4_PF14_15) //PF14,PF15
- SYS_DeviceClockEnable(DEV_GPIOF, TRUE); //使能GPIOF时钟
- SYS_GPIOx_OneInit(GPIOF, 14, AF_OD, SPEED_25M); //PF14
- SYS_GPIOx_OneInit(GPIOF, 15, AF_OD, SPEED_25M); //PF15
- SYS_GPIOx_SetAF(GPIOF, 14, AF4_I2C4); //AF4
- SYS_GPIOx_SetAF(GPIOF, 15, AF4_I2C4); //AF4
- #elif(IIC_CH4_IO_SELECT==IIC_CH4_PH11_12) //PH11,PH12
- SYS_DeviceClockEnable(DEV_GPIOH, TRUE); //使能GPIOH时钟
- SYS_GPIOx_OneInit(GPIOH, 11, AF_OD, SPEED_25M); //PH11
- SYS_GPIOx_OneInit(GPIOH, 12, AF_OD, SPEED_25M); //PH12
- SYS_GPIOx_SetAF(GPIOH, 11, AF4_I2C4); //AF4
- SYS_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; //使能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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 返回 : 无
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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
- * 依赖 : 底层宏定义
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 时间 : 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接口驱动
- * 作者 : <a href="mailto:cp1300@139.com">cp1300@139.com</a>
- * 创建时间 : 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, //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;
- }
复制代码
|