新金宝

依然范特西╮ 提交于 2020-02-18 03:38:18

目前只实现了主设备模式,一般也只用到主设备模式,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; //操作超时,单位us
u16 Speed_KHz; //通讯速度,单位KHz
bool isMasterMode; //是否为主设备模式-目前只支持主设备模式
}IIC_HANDLE;

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

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

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

/*************************************************************************************************************************

  • 函数 : bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)

  • 功能 : 硬件IIC初始化

  • 参数 : ch:IIC通道;Speed_KHz:速度10-1000(如果速度是100,则按照SMBUS时序);TimeOutUs:操作超时us(0:自定计算超时)

  • 返回 : IIC_ERROR

  • 依赖 : 底层宏定义

  • 作者 : 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 DevCloce;
    IIC_HANDLE *pHandle;

    switch(ch)
    {
    case IIC_CH1 : //IIC1
    {
    RCC->DCKCFGR2 &= ~(0x3 << 16); //清除设置,使用APB1作为时钟源
    DevCloce = DEV_I2C1;
    }break;
    case IIC_CH2 : //IIC2
    {
    RCC->DCKCFGR2 &= ~(0x3 << 18); //清除设置,使用APB1作为时钟源
    DevCloce = DEV_I2C2;
    }break;
    case IIC_CH3 : //IIC3
    {
    RCC->DCKCFGR2 &= ~(0x3 << 20); //清除设置,使用APB1作为时钟源
    DevCloce = DEV_I2C3;
    //初始化IO口
    SYS_DeviceClockEnable(DEV_GPIOH, TRUE); //使能GPIOH时钟
    SYS_GPIOx_OneInit(GPIOH, 7, AF_OD, SPEED_25M); //PH7
    SYS_GPIOx_OneInit(GPIOH, 8, AF_OD, SPEED_25M); //PH8
    SYS_GPIOx_SetAF(GPIOH, 7, AF4_I2C3); //AF4
    SYS_GPIOx_SetAF(GPIOH, 8, AF4_I2C3); //AF4
    }break;
    case IIC_CH4 : //IIC4
    {
    RCC->DCKCFGR2 &= ~(0x3 << 22); //清除设置,使用APB1作为时钟源
    DevCloce = DEV_I2C4;
    }break;
    default:
    {
    DEBUG(“初始化IIC失败:无效的IIC通道%d\r\n”, ch);
    return FALSE;
    }
    }
    pHandle = &sg_IIC_Handle[ch]; //获取相关通道的句柄
    if(pHandle == NULL)
    {
    DEBUG(“初始化IIC失败:无效的IIC句柄\r\n”);
    return FALSE;
    }

    SYS_DeviceClockEnable(DevCloce, TRUE); //使能时钟
    SYS_DeviceReset(DevCloce); //外设复位
    pHandle->I2Cx = (I2C_TypeDef )I2C_TYPE_BUFF[ch]; //外设指针
    pHandle->I2Cx->CR1 = 0;
    Delay_US(1);
    pHandle->I2Cx->CR1 |= 2<<8; //设置噪声滤波器,关闭所有中断
    pHandle->I2Cx->CR2 = 0;
    pHandle->I2Cx->OAR1 = 0;
    pHandle->I2Cx->OAR2 = 0;
    if(Speed_KHz > 1000) Speed_KHz = 1000;
    if(Speed_KHz < 10) Speed_KHz = 10;
    pHandle->Speed_KHz = Speed_KHz; //记录速度
    if(TimeOutUs == 0) //需要自动计算超时时间,时钟周期
    10*10
    {
    TimeOutUs = 1000/Speed_KHz; //时钟周期
    TimeOutUs *= 10; //字节周期
    TimeOutUs *= 10; //超时时间为10个字节时间
    }
    if(TimeOutUs < 3) TimeOutUs = 3;
    pHandle->TimeOutUs = TimeOutUs; //记录通讯超时时间
    uart_printf(“IIC超时时间:%dus\r\n”, pHandle->TimeOutUs);
    pHandle->I2Cx->TIMINGR = IIC_CalculationTiming(pHandle->Speed_KHz);//0x40912732; //时序
    pHandle->I2Cx->CR1 |= BIT0; //使能IIC
    pHandle->isMasterMode = TRUE; //主设备模式
    Delay_US(1);

    return TRUE;
    }

/*************************************************************************************************************************

  • 函数 : static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)

  • 功能 : 检查是否有NACK中断状态,如果有则清除掉

  • 参数 : pHandle:句柄;TimeOutUs:超时时间,单位us

  • 返回 : IIC_ERROR

  • 依赖 : 底层宏定义

  • 作者 : 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寄存器
    //主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送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

  • 依赖 : 底层宏定义

  • 作者 : 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寄存器
    //主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送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
  • 依赖 : 底层宏定义
  • 作者 : 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; //计算高电平时间(低电平时间一样),单位ns

    if(time_temp < 20) time_temp = 20;
    time_temp = 11000 / 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 = Th1000 / 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接口驱动
  • 作者 : cp1300@139.com
  • 创建时间 : 2020-02-09
  • 最后修改时间 : 2020-02-09
  • 详细:
    *************************************************************************************************************/
    #ifndef _STM32F7_IIC_H
    #define _STM32F7_IIC_H
    #include “system.h”

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

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

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

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

#endif //_STM32F7_IIC_H

//初始化

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

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

//触摸屏IIC写寄存器接口
bool TP_FT5336_IIC_WriteReg(u16 SlaveAddr, u8 RegAddr, u8 *pDataBuff, u16 ByteNum)
{
if(IIC_MasterWriteReg(FT5336_IIC_CH, SlaveAddr, RegAddr, TRUE, pDataBuff, ByteNum) == IIC_OK)
{
return TRUE;
}
else
{
return FALSE;
}
}
 
————————————————
版权声明:本文为CSDN博主「cp1300」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cp1300/article/details/104347417

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!