STM32F7 硬件IIC驱动
目前只实现了主设备模式,一般也只用到主设备模式,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驱动相关推荐
- MSP430杂谈--AD7745硬件IIC驱动与模拟IIC驱动
和上一篇AD7793类似,项目中也涉及到利用AD7745读取电容值,来测环境湿度.编写了基于MSP430的AD7745的硬件IIC驱动和模拟IIC驱动,分享给大家. AD7745硬件IIC驱动完整版下 ...
- 基于Stm32f103硬件iic驱动LM75A温度传感器
这是LM75A温度传感器的概述,本文主要介绍基于Stm32f103的硬件iic驱动LM75A温度传感器. 这是我所使用的硬件电路,很简单. 对于该传感器的使用,主要是读取温度值,查看数据手册我们知道需 ...
- STM32 驱动 GY-302 光照传感器 BH1750 模块(软件IIC与硬件IIC驱动)
1.特别说明 要是不想看原理和过程,直接下拉找代码吧,都是测试过的,很稳定,有硬件I2C驱动的,也有软件模拟I2C驱动的,基于STM32F103系列和STM32F4系列实现,基于标准库实现,条理清 ...
- 12. STM32——硬件IIC驱动OLED屏幕显示
STM32--硬件IIC驱动OLED屏幕显示 OLED屏幕 OLED屏幕特点 OLED屏幕接线说明 OLED屏幕显存 OLED屏幕原理 OLED屏幕常用指令 OLED屏幕字模软件的使用 写命令 写数据 ...
- 基于STM32F103RC硬件IIC驱动18位AD MCP3421驱动开发
基于STM32 HAL库硬件IIC 驱动18位AD MCP3421 最近用到小信号采集,发现关于该芯片的STM32 HAL 库驱动比较少.就写了一个基于STM32F103RCT6测试Demo .在此分 ...
- 【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模块) ...
- 基于GR551x芯片的硬件IIC驱动QMA7981
基于GR551x芯片的硬件IIC驱动QMA7981 实现的函数 qma7981_init(); int16_t get_accel_x() int16_t get_accel_y() int16_t ...
- HC32F460开发之硬件IIC驱动AT24C64
文章目录 前言 一.AT24C64 二.驱动步骤 1.宏定义 2.代码实现 总结 前言 在嵌入式设计中,E2PROM存储芯片常被应用于需要掉电存储,且容量不大的场合.对比flash存储芯片,E2PRO ...
- STM32F103使用硬件IIC驱动SHT30温湿度传感器
文章目录 前言 一.SHT30温湿度传感器原理图 二.代码部分 1.SHT30.c文件 2.SHT30.h文件 总结 前言 SHT30是一种常见的温湿度传感器,该传感器广泛应用于各种场景,小米的温湿度 ...
- HC32L136/HC32L176开发之硬件IIC驱动AT24C64
文章目录 前言 一.AT24C64 二.驱动步骤 1.宏定义 2.代码实现 前言 在嵌入式设计中,E2PROM存储芯片常被应用于需要掉电存储,且容量不大的场合.对比flash存储芯片,E2PROM具有 ...
最新文章
- eclipse 创建maven web项目
- shortcut icon 修改浏览器标签网站图标
- ubuntu6.06容易死机的一种解决方法
- Google Cloud大规模宕机;中国正式进入 5G 商用元年!苹果发布SwiftUI |开发者周刊...
- OFFICE技术讲座:JDK绘制旋转斜体字体的效果
- MyBatis(三)------MyBatis的核心组件
- c语言多字符和宽字符,2.1.5 多字节字符和宽字符
- 个人热爱软件编程,我对自己做了一个总结
- android 6.1 app闪退,手机软件闪退怎么办 具体解决方法【图文】
- 键盘是计算机最常用的输入设备之一
- 入门了解开发板是做什么用的?
- Python程序写诗【训练1分钟】古诗生成
- aws上传找不到endpoint url或者The Aws Access Key Id you provided does not exist in our recordss
- Zephyr events
- 水仙花数c之和语言程序,水仙花数C语言的
- 安装Properties Editor
- 十三、添加RD 会话主机角色
- 用户画像(profile v.s. persona)
- ExpandableListView 模拟QQ好友分组 小实例
- Java解决时间小时时差问题
热门文章
- python处理word页码_使用Python向Word文档添加页码
- MOT:A Higher Order Metric for Evaluating Multi-object Tracking
- BUUCTF WEB PIAPIAPIA1
- 洛谷P4315 月下“毛景树”(树剖+线段树)
- 汽车研发企业试验数据管理系统建设研究
- 老博客里的另一篇文章《自由与爱情》,实际是一句名言的中英文对照~
- 模数转换器(ADC)
- BeautyGAN论文翻译
- 一个让Google、Facebook、Amazon都羡慕的平台,为什么说阿里妈妈是数字营销的未来...
- 关于SOC、态势感知,5种常见的关联分析模型