你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F7 硬件IIC驱动

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

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

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

20200216194202434.png


20200216194219185.png


20200216194234908.png



下面简要说明一下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初始化部分有区别,别的通道没有硬件进行测试)

  1. /*************************************************************************************************************
  2. * 文件名                :        stm32f7_iic.c
  3. * 功能                        :        STM32F7 IIC接口驱动
  4. * 作者                        :        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  5. * 创建时间                :        2020-02-09
  6. * 最后修改时间        :        2020-02-09
  7. * 详细:                        只支持主机模式,7bit地址,默认APB1为IIC提供时钟
  8.                                         涉及到读写1字节的超时时间是us,其余的是ms
  9.                                         注意:IIC的IO必须初始化为复用开漏输出。
  10. *************************************************************************************************************/        
  11. #include "stm32f7_iic.h"
  12. #include "system.h"
  13. #include "dma.h"

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

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

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

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

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

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

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


  57. /*************************************************************************************************************************
  58. * 函数        :                        bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
  59. * 功能        :                        硬件IIC初始化
  60. * 参数        :                        ch:IIC通道;Speed_KHz:速度10-1000(如果速度是100,则按照SMBUS时序);TimeOutUs:操作超时us(0:自定计算超时)
  61. * 返回        :                        IIC_ERROR
  62. * 依赖        :                        底层宏定义
  63. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  64. * 时间        :                        2020-02-15
  65. * 最后修改时间 :         2020-02-15
  66. * 说明        :                         速度只是个大概的计算值,设置为100KHz时会严格的按照SMBUS时序,其余的不保证SMBUS兼容,正常情况下只要时钟速度符合要求,
  67.                                         时钟上升沿到来前数据以及稳定的切换了即可保证通讯稳定性
  68. *************************************************************************************************************************/
  69. bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
  70. {
  71.         SYS_DEV_CLOCK DevClock;
  72.         IIC_HANDLE *pHandle;
  73.         
  74.         switch(ch)
  75.         {
  76.                 case IIC_CH1        :        //IIC1
  77.                 {
  78.                         RCC->DCKCFGR2 &= ~(0x3 << 16);        //清除设置,使用APB1作为时钟源
  79.                         DevClock = DEV_I2C1;
  80.                         //初始化I2C IO
  81. #if(IIC_CH1_IO_SELECT==IIC_CH1_PB6_7)        //I2C1        使用PB6/7
  82.                         SYS_DeviceClockEnable(DEV_GPIOB, TRUE);                                //使能GPIOB时钟
  83.                         SYS_GPIOx_OneInit(GPIOB, 6, AF_OD, SPEED_25M);                //PB6
  84.                         SYS_GPIOx_OneInit(GPIOB, 7, AF_OD, SPEED_25M);                //PB7
  85.                         SYS_GPIOx_SetAF(GPIOB, 6, AF4_I2C1);                                //AF4
  86.                         SYS_GPIOx_SetAF(GPIOB, 7, AF4_I2C1);                                //AF4
  87. #elif(IIC_CH1_IO_SELECT==IIC_CH1_PB8_9)        //I2C1        使用PB8/9
  88.                         SYS_DeviceClockEnable(DEV_GPIOB, TRUE);                                //使能GPIOB时钟
  89.                         SYS_GPIOx_OneInit(GPIOB, 8, AF_OD, SPEED_25M);                //PB8
  90.                         SYS_GPIOx_OneInit(GPIOB, 9, AF_OD, SPEED_25M);                //PB9
  91.                         SYS_GPIOx_SetAF(GPIOB, 8, AF4_I2C1);                                //AF4
  92.                         SYS_GPIOx_SetAF(GPIOB, 9, AF4_I2C1);                                //AF4
  93. #else
  94.                         #error("无效的I2C1 IO选择");
  95. #endif //IIC_CH1_IO_SELECT
  96.                 }break;
  97.                 case IIC_CH2        :        //IIC2
  98.                 {
  99.                         RCC->DCKCFGR2 &= ~(0x3 << 18);        //清除设置,使用APB1作为时钟源
  100.                         DevClock = DEV_I2C2;
  101.                         //初始化I2C IO
  102. #if(IIC_CH2_IO_SELECT==IIC_CH2_PB10_11)        //使用PB10,PB11
  103.                         SYS_DeviceClockEnable(DEV_GPIOB, TRUE);                                //使能GPIOB时钟
  104.                         SYS_GPIOx_OneInit(GPIOB, 10, AF_OD, SPEED_25M);                //PB10
  105.                         SYS_GPIOx_OneInit(GPIOB, 11, AF_OD, SPEED_25M);                //PB11
  106.                         SYS_GPIOx_SetAF(GPIOB, 10, AF4_I2C2);                                //AF4
  107.                         SYS_GPIOx_SetAF(GPIOB, 11, AF4_I2C2);                                //AF4
  108. #elif(IIC_CH2_IO_SELECT==IIC_CH2_PF0_1)        //PF0,PF1
  109.                         SYS_DeviceClockEnable(DEV_GPIOF, TRUE);                                //使能GPIOF时钟
  110.                         SYS_GPIOx_OneInit(GPIOF, 0, AF_OD, SPEED_25M);                //PF0
  111.                         SYS_GPIOx_OneInit(GPIOF, 1, AF_OD, SPEED_25M);                //PF1
  112.                         SYS_GPIOx_SetAF(GPIOF, 0, AF4_I2C2);                                //AF4
  113.                         SYS_GPIOx_SetAF(GPIOF, 1, AF4_I2C2);                                //AF4
  114. #elif(IIC_CH2_IO_SELECT==IIC_CH2_PH4_5)        //PH4,PH5
  115.                         SYS_DeviceClockEnable(DEV_GPIOH, TRUE);                                //使能GPIOH时钟
  116.                         SYS_GPIOx_OneInit(GPIOH, 4, AF_OD, SPEED_25M);                //PH4
  117.                         SYS_GPIOx_OneInit(GPIOH, 5, AF_OD, SPEED_25M);                //PH5
  118.                         SYS_GPIOx_SetAF(GPIOH, 4, AF4_I2C2);                                //AF4
  119.                         SYS_GPIOx_SetAF(GPIOH, 5, AF4_I2C2);                                //AF4                        
  120. #else
  121.                         #error("无效的I2C2 IO选择");
  122. #endif //IIC_CH2_IO_SELECT
  123.                 }break;                        
  124.                 case IIC_CH3        :        //IIC3
  125.                 {
  126.                         RCC->DCKCFGR2 &= ~(0x3 << 20);        //清除设置,使用APB1作为时钟源
  127.                         DevClock = DEV_I2C3;
  128.                         //初始化I2C IO
  129. #if(IIC_CH3_IO_SELECT==IIC_CH3_PH7_8)                //PH7,PH8
  130.                         SYS_DeviceClockEnable(DEV_GPIOH, TRUE);                                //使能GPIOH时钟
  131.                         SYS_GPIOx_OneInit(GPIOH, 7, AF_OD, SPEED_25M);                //PH7
  132.                         SYS_GPIOx_OneInit(GPIOH, 8, AF_OD, SPEED_25M);                //PH8
  133.                         SYS_GPIOx_SetAF(GPIOH, 7, AF4_I2C3);                                //AF4
  134.                         SYS_GPIOx_SetAF(GPIOH, 8, AF4_I2C3);                                //AF4
  135. #elif(IIC_CH3_IO_SELECT==IIC_CH3_PA8_PC9)        //PA8,PC9
  136.                         SYS_DeviceClockEnable(DEV_GPIOA, TRUE);                                //使能GPIOA时钟
  137.                         SYS_DeviceClockEnable(DEV_GPIOC, TRUE);                                //使能GPIOC时钟
  138.                         SYS_GPIOx_OneInit(GPIOA, 8, AF_OD, SPEED_25M);                //PA8
  139.                         SYS_GPIOx_OneInit(GPIOC, 9, AF_OD, SPEED_25M);                //PC9
  140.                         SYS_GPIOx_SetAF(GPIOA, 8, AF4_I2C3);                                //AF4
  141.                         SYS_GPIOx_SetAF(GPIOC, 9, AF4_I2C3);                                //AF4               
  142. #else
  143.                         #error("无效的I2C3 IO选择");
  144. #endif //IIC_CH3_IO_SELECT                        
  145.                 }break;                        
  146.                 case IIC_CH4        :        //IIC4
  147.                 {
  148.                         RCC->DCKCFGR2 &= ~(0x3 << 22);        //清除设置,使用APB1作为时钟源
  149.                         DevClock = DEV_I2C4;
  150.                         //初始化I2C IO
  151. #if(IIC_CH4_IO_SELECT==IIC_CH4_PD12_13)                //PD12,PD13
  152.                         SYS_DeviceClockEnable(DEV_GPIOD, TRUE);                                //使能GPIOD时钟
  153.                         SYS_GPIOx_OneInit(GPIOD, 12, AF_OD, SPEED_25M);                //PD12
  154.                         SYS_GPIOx_OneInit(GPIOD, 13, AF_OD, SPEED_25M);                //PD13
  155.                         SYS_GPIOx_SetAF(GPIOD, 12, AF4_I2C4);                                //AF4
  156.                         SYS_GPIOx_SetAF(GPIOD, 13, AF4_I2C4);                                //AF4
  157. #elif(IIC_CH4_IO_SELECT==IIC_CH4_PF14_15)        //PF14,PF15
  158.                         SYS_DeviceClockEnable(DEV_GPIOF, TRUE);                                //使能GPIOF时钟
  159.                         SYS_GPIOx_OneInit(GPIOF, 14, AF_OD, SPEED_25M);                //PF14
  160.                         SYS_GPIOx_OneInit(GPIOF, 15, AF_OD, SPEED_25M);                //PF15
  161.                         SYS_GPIOx_SetAF(GPIOF, 14, AF4_I2C4);                                //AF4
  162.                         SYS_GPIOx_SetAF(GPIOF, 15, AF4_I2C4);                                //AF4
  163. #elif(IIC_CH4_IO_SELECT==IIC_CH4_PH11_12)        //PH11,PH12
  164.                         SYS_DeviceClockEnable(DEV_GPIOH, TRUE);                                //使能GPIOH时钟
  165.                         SYS_GPIOx_OneInit(GPIOH, 11, AF_OD, SPEED_25M);                //PH11
  166.                         SYS_GPIOx_OneInit(GPIOH, 12, AF_OD, SPEED_25M);                //PH12
  167.                         SYS_GPIOx_SetAF(GPIOH, 11, AF4_I2C4);                                //AF4
  168.                         SYS_GPIOx_SetAF(GPIOH, 12, AF4_I2C4);                                //AF4                        
  169. #else
  170.                         #error("无效的I2C4 IO选择");
  171. #endif //IIC_CH4_IO_SELECT                        
  172.                 }break;
  173.                 default:
  174.                 {
  175.                         DEBUG("初始化IIC失败:无效的IIC通道%d\r\n", ch);
  176.                         return FALSE;
  177.                 }
  178.         }
  179.         pHandle = &sg_IIC_Handle[ch];                                                        //获取相关通道的句柄
  180.         if(pHandle == NULL)
  181.         {
  182.                 DEBUG("初始化IIC失败:无效的IIC句柄\r\n");
  183.                 return FALSE;
  184.         }
  185.         
  186.         SYS_DeviceClockEnable(DevClock, TRUE);                                        //使能时钟
  187.         SYS_DeviceReset(DevClock);                                                                //外设复位
  188.         pHandle->I2Cx = (I2C_TypeDef *)I2C_TYPE_BUFF[ch];                //外设指针
  189.         pHandle->I2Cx->CR1 = 0;
  190.         Delay_US(1);                                                
  191.         pHandle->I2Cx->CR1 |= 2<<8;                                                                //设置噪声滤波器,关闭所有中断
  192.         pHandle->I2Cx->CR2 = 0;
  193.         pHandle->I2Cx->OAR1 = 0;
  194.         pHandle->I2Cx->OAR2 = 0;
  195.         if(Speed_KHz > 1000) Speed_KHz = 1000;
  196.         if(Speed_KHz < 10) Speed_KHz = 10;
  197.         pHandle->Speed_KHz = Speed_KHz;                                                        //记录速度
  198.         if(TimeOutUs == 0)                                                                                //需要自动计算超时时间,时钟周期*10*10
  199.         {
  200.                 TimeOutUs = 1000/Speed_KHz;                                                        //时钟周期
  201.                 TimeOutUs *= 10;                                                                        //字节周期
  202.                 TimeOutUs *= 10;                                                                        //超时时间为10个字节时间
  203.         }
  204.         if(TimeOutUs < 3) TimeOutUs = 3;
  205.         pHandle->TimeOutUs = TimeOutUs;                                                        //记录通讯超时时间
  206.         uart_printf("IIC超时时间:%dus\r\n", pHandle->TimeOutUs);
  207.         pHandle->I2Cx->TIMINGR = IIC_CalculationTiming(pHandle->Speed_KHz);//0x40912732;                                        //时序
  208.         pHandle->I2Cx->CR1 |= BIT0;                                                                //使能IIC
  209.         pHandle->isMasterMode = TRUE;                                                        //主设备模式
  210.         Delay_US(1);

  211.         return TRUE;
  212. }


  213. /*************************************************************************************************************************
  214. * 函数        :                        static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)
  215. * 功能        :                        检查是否有NACK中断状态,如果有则清除掉
  216. * 参数        :                        pHandle:句柄;TimeOutUs:超时时间,单位us
  217. * 返回        :                        IIC_ERROR
  218. * 依赖        :                        底层宏定义
  219. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  220. * 时间        :                        2020-02-15
  221. * 最后修改时间 :         2020-02-15
  222. * 说明        :                         如果有NACK中断,则先产生停止位,然后清清除掉所有的中断,并复位IIC,然后返回错误
  223. *************************************************************************************************************************/  
  224. static IIC_ERROR IIC_isCheckNackFlag(IIC_HANDLE *pHandle, u32 TimeOutUs)
  225. {
  226.         IIC_ERROR Error = IIC_OK;
  227.         
  228.         if(IIC_GetISR(pHandle) & IIC_FLAG_NACKF)        //接收到否定应答标志
  229.         {
  230.                 uart_printf("NACK : IIC_isWaitTXIS:CR1=0x%X\t", pHandle->I2Cx->CR1);
  231.                 uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
  232.                 uart_printf("ISR=0x%X\r\n", IIC_GetISR(pHandle));
  233.                 //主设备下,如果没有开启自动产生停止位,则手动产生停止位
  234.                 if(pHandle->isMasterMode && ((pHandle->I2Cx->CR2 & I2C_CR2_AUTOEND) == 0))
  235.                 {
  236.                         IIC_SendStop(pHandle);                        //send stop
  237.                 }
  238.                 //等待STOP标志有效
  239.                 while((IIC_GetISR(pHandle) & IIC_FLAG_STOPF) == 0)
  240.                 {
  241.                         if(TimeOutUs == 0)                                //检查是否超时了
  242.                         {
  243.                                 break;
  244.                         }                        
  245.                         TimeOutUs --;
  246.                         Delay_US(1);                                                //延时
  247.                 }
  248.                 //清除相关的中断标志
  249.                 IIC_ClearISR(pHandle, IIC_FLAG_NACKF | IIC_FLAG_STOPF);        //清除NACK,STOP标志
  250.                 pHandle->I2Cx->CR2 = 0;                                                                                //清除CR2寄存器
  251.                 IIC_SoftReset(pHandle);                                                                        //执行软复位
  252.                 if(TimeOutUs == 0)                                                //没有超时,就是硬件通讯出错了
  253.                 {
  254.                         DEBUG("IIC发送stop超时\r\n");
  255.                         Error = IIC_TIMEOUT;                                //超时
  256.                 }
  257.                 else
  258.                 {
  259.                         Error = IIC_HAL_ERROR;                                //底层错误
  260.                 }
  261.         }
  262.         
  263.         return Error;
  264. }



  265. /*************************************************************************************************************************
  266. * 函数        :                        static IIC_ERROR IIC_isWaitTXIS(IIC_HANDLE *pHandle, u32 TimeOutUs)
  267. * 功能        :                        等待TXIS中断有效(等待 I2C_TXDR 发送寄存器为空)
  268. * 参数        :                        pHandle:句柄;TimeOutUs:超时时间,单位us
  269. * 返回        :                        IIC_ERROR
  270. * 依赖        :                        底层宏定义
  271. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  272. * 时间        :                        2020-02-15
  273. * 最后修改时间 :         2020-02-15
  274. * 说明        :                         会先检查NACK中断,如果有NACK会直接退出的
  275. *************************************************************************************************************************/  
  276. static IIC_ERROR IIC_isWaitTXIS(IIC_HANDLE *pHandle, u32 TimeOutUs)
  277. {
  278.         //等待TXIS中断有效
  279.         while((IIC_GetISR(pHandle) & IIC_FLAG_TXIS) == 0)
  280.         {
  281.                 //有NACK中断,进行处理
  282.                 if(IIC_isCheckNackFlag(pHandle, TimeOutUs) != IIC_OK)        
  283.                 {
  284.                         DEBUG("检测到NAK错误\r\n");
  285.                         return IIC_NACK;
  286.                 }
  287.                
  288.                 if(TimeOutUs == 0)                                        //检查是否超时了
  289.                 {
  290.                         uart_printf("IIC_isWaitTXIS:CR1=0x%X\t", pHandle->I2Cx->CR1);
  291.                         uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
  292.                         uart_printf("ISR=0x%X\r\n", IIC_GetISR(pHandle));
  293.                         
  294.                         IIC_SoftReset(pHandle);                //执行软复位
  295.                         return IIC_TIMEOUT;                                //超时
  296.                 }                        
  297.                 TimeOutUs --;
  298.                 Delay_US(1);                                                //延时
  299.         }
  300.         
  301.         return IIC_OK;
  302. }


  303. /*************************************************************************************************************************
  304. * 函数        :                        static IIC_ERROR IIC_isWaitSTOPF(IIC_HANDLE *pHandle, u32 TimeOutUs)
  305. * 功能        :                        等待STOP中断有效(等待 发送结束 )
  306. * 参数        :                        pHandle:句柄;TimeOutUs:超时时间,单位us
  307. * 返回        :                        IIC_ERROR
  308. * 依赖        :                        底层宏定义
  309. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  310. * 时间        :                        2020-02-15
  311. * 最后修改时间 :         2020-02-15
  312. * 说明        :                         会先检查NACK中断,如果有NACK会直接退出的
  313. *************************************************************************************************************************/  
  314. static IIC_ERROR IIC_isWaitSTOPF(IIC_HANDLE *pHandle, u32 TimeOutUs)
  315. {
  316.         //等待STOPF中断有效
  317.         while((IIC_GetISR(pHandle) & IIC_FLAG_STOPF) == 0)
  318.         {
  319.                 //有NACK中断,进行处理
  320.                 if(IIC_isCheckNackFlag(pHandle, 5) != IIC_OK)        
  321.                 {
  322.                         return IIC_HAL_ERROR;
  323.                 }
  324.                
  325.                 if(TimeOutUs == 0)                                        //检查是否超时了
  326.                 {
  327.                         IIC_SoftReset(pHandle);                //执行软复位
  328.                         DEBUG("IIC等待结束超时\r\n");
  329.                         return IIC_TIMEOUT;                                //超时
  330.                 }                        
  331.                 TimeOutUs --;
  332.                 Delay_US(1);                                                //延时
  333.         }
  334.         
  335.         return IIC_OK;
  336. }


  337. /*************************************************************************************************************************
  338. * 函数        :                        static IIC_ERROR IIC_isWaitFlag(IIC_HANDLE *pHandle,u32 Flag,bool isWaitFlagSet, u32 TimeOutUs)
  339. * 功能        :                        等待中断有效
  340. * 参数        :                        pHandle:句柄;Flag:需要等待的flag,见IIC_FLAG_xxx;isWaitFlagSet:TRUE:等待标志有效,FALSE:等待标志复位;TimeOutUs:超时时间,单位Us
  341. * 返回        :                        IIC_ERROR
  342. * 依赖        :                        底层宏定义
  343. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  344. * 时间        :                        2020-02-15
  345. * 最后修改时间 :         2020-02-15
  346. * 说明        :                        
  347. *************************************************************************************************************************/  
  348. static IIC_ERROR IIC_isWaitFlag(IIC_HANDLE *pHandle,u32 Flag,bool isWaitFlagSet, u32 TimeOutUs)
  349. {
  350.         if(isWaitFlagSet)        //需要等待标志有效
  351.         {
  352.                 while((IIC_GetISR(pHandle) & Flag) == 0)
  353.                 {
  354.                         if(TimeOutUs == 0)                                        //检查是否超时了
  355.                         {
  356.                                 return IIC_TIMEOUT;                                //超时
  357.                         }                        
  358.                         TimeOutUs --;
  359.                         Delay_US(1);                                                //延时
  360.                 }
  361.         }
  362.         else                                //需要等待标志复位
  363.         {
  364.                 while((IIC_GetISR(pHandle) & Flag) != 0)
  365.                 {
  366.                         if(TimeOutUs == 0)                                        //检查是否超时了
  367.                         {
  368.                                 return IIC_TIMEOUT;                                //超时
  369.                         }                        
  370.                         TimeOutUs --;
  371.                         Delay_US(1);                                                //延时
  372.                 }
  373.         }

  374.         return IIC_OK;
  375. }




  376. /*************************************************************************************************************************
  377. * 函数        :                        static void IIC_MasterTransConfig(IIC_HANDLE *pHandle, u16 SlaveAddr, u8 TransByteCount, IIC_RELOAD_END_MODE EndorReload, IIC_START_WR_MODE StartAndWR)
  378. * 功能        :                        主设备传输配置(配置CR2寄存器)
  379. * 参数        :                        pHandle:句柄;SlaveAddr:从机地址;TransByteCount:需要传输的数据长度;EndorReload:自动结束还是重载设置,见IIC_RELOAD_END_MODE;StartAndWR:开始读写控制,见StartAndWR
  380. * 返回        :                        无
  381. * 依赖        :                        底层宏定义
  382. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  383. * 时间        :                        2020-02-15
  384. * 最后修改时间 :         2020-02-16
  385. * 说明        :                         在往 NBYTE 中设置最后一次传输的字节数前,必须把 RELOAD 位清零便于后续自动发送STOP。
  386.                                         当 RELOAD 位置 1 时,AUTOEND 位将不起作用;
  387. *************************************************************************************************************************/  
  388. static void IIC_MasterTransConfig(IIC_HANDLE *pHandle, u16 SlaveAddr, u8 TransByteCount, IIC_RELOAD_END_MODE EndorReload, IIC_START_WR_MODE StartAndWR)
  389. {
  390.         u32 temp = 0;
  391.         
  392.         //先读取CR2寄存器的值
  393.         temp = pHandle->I2Cx->CR2;
  394.         //清除掉相关的字节
  395.         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));
  396.         //生成配置数据
  397.         temp |= (u32)(((u32)SlaveAddr & I2C_CR2_SADD) | (((u32)TransByteCount << 16 ) & I2C_CR2_NBYTES));
  398.         //重载与自动结束只能二选一
  399.         if(EndorReload == IIC_AUTOEND_MODE)         
  400.         {
  401.                 temp |= I2C_CR2_AUTOEND;        //自动结束模式
  402.         }
  403.         else if(EndorReload == IIC_RELOAD_MODE)
  404.         {
  405.                 temp |= I2C_CR2_RELOAD;                //NBYTES 重载模式
  406.         }
  407.         
  408.         switch(StartAndWR)
  409.         {
  410.                 case IIC_NOSTART_WRITE        :        break;        //没有开始的写-默认就是这样的
  411.                 case IIC_NOSTART_READ        :                        //没有开始的读-用于后续数据读取
  412.                 {
  413.                         temp |= I2C_CR2_RD_WRN;                        //读取
  414.                 }break;
  415.                 case IIC_START_WRITE        :                        //生成开始写
  416.                 {
  417.                         temp |= I2C_CR2_START;                        //启动传输
  418.                 }break;
  419.                 case IIC_START_READ                :                        //生成开始读        
  420.                 {
  421.                         temp |= I2C_CR2_RD_WRN;                        //读取
  422.                         temp |= I2C_CR2_START;                        //启动传输
  423.                 }break;
  424.                 default:break;
  425.         }
  426.         
  427.         //uart_printf("准备写入CR2=0x%X\t", temp);
  428.         //更新到寄存器
  429.         pHandle->I2Cx->CR2 = temp;  //测试
  430.         //uart_printf("ISR=0x%X\r\n", pHandle->I2Cx->ISR);
  431.         Delay_US(100);
  432. }


  433. /*************************************************************************************************************************
  434. * 函数        :                        static IIC_ERROR IIC_SendByte(IIC_HANDLE *pHandle, u8 data, u32 TimeOutUs)
  435. * 功能        :                        IIC发送一字节
  436. * 参数        :                        pHandle:句柄;data:待发送数据;TimeOutUs:超时时间,单位us
  437. * 返回        :                        IIC_ERROR
  438. * 依赖        :                        底层宏定义
  439. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  440. * 时间        :                        2020-02-15
  441. * 最后修改时间 :         2020-02-16
  442. * 说明        :                         TXIS有效后才会进行数据发送,不会检查数据发送是否完成。
  443.                                         如果最后一个字节发送完成后不会再触发TXIS中断,只会触发TC中断
  444. *************************************************************************************************************************/  
  445. static IIC_ERROR IIC_SendByte(IIC_HANDLE *pHandle, u8 data, u32 TimeOutUs)
  446. {
  447.         IIC_ERROR Error = IIC_isWaitTXIS(pHandle, TimeOutUs);                                //先等待可以发数据
  448.         if(Error != IIC_OK) return Error;
  449.         pHandle->I2Cx->TXDR = data;                                                                                        //写数据到发送寄存器-不会等待数据发送完成        

  450.         return IIC_OK;
  451. }

  452. /*************************************************************************************************************************
  453. * 函数        :                        static IIC_ERROR IIC_MasterRequestWriteAddr(IIC_HANDLE *pHandle, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
  454.                                                 bool isRead,u32 TimeOutUs)
  455. * 功能        :                        主设备请求发送从机地址与目标寄存器地址
  456. * 参数        :                        pHandle:句柄;SlaveAddr:从机地址;RegAddr:寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,
  457.                                                 FALSE:16bit寄存器地址;isRead:TRUE:这个操作是读取寄存器;否则是写入寄存器;TimeOutUs:超时时间,单位us
  458. * 返回        :                        IIC_ERROR
  459. * 依赖        :                        底层宏定义
  460. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  461. * 时间        :                        2020-02-15
  462. * 最后修改时间 :         2020-02-16
  463. * 说明        :                        
  464. *************************************************************************************************************************/  
  465. static IIC_ERROR IIC_MasterRequestWriteAddr(IIC_HANDLE *pHandle, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,bool isRead, u32 TimeOutUs)
  466. {        
  467.         IIC_ERROR Error;
  468.         
  469.         //uart_printf("WriteAddr1:CR1=0x%X\t", pHandle->I2Cx->CR1);
  470.         //uart_printf("CR2=0x%X\t", pHandle->I2Cx->CR2);
  471.         //uart_printf("ISR=0x%X\r\n", pHandle->I2Cx->ISR);
  472.         //传输配置,并启动传输,发送起始序列,开始IIC传输了
  473.         //如果是读取则使能软件结束,如果是写则是自动重载
  474.         IIC_MasterTransConfig(pHandle, SlaveAddr, (is8bitRegAddr==FALSE)?2:1, (isRead)?IIC_SOFTEND_MODE:IIC_RELOAD_MODE, IIC_START_WRITE);        //传输相关配置-写,并启动传输
  475.         
  476.         //开始发送寄存器地址
  477.         if(is8bitRegAddr==FALSE)                                                                                                //寄存器地址是16位的,IIC通常是MSB高位在前,需要进行高低位对调
  478.         {
  479.                 Error = IIC_SendByte(pHandle, RegAddr>>8, TimeOutUs);        //先发送MSB-非最后一字节
  480.                 if(Error != IIC_OK)
  481.                 {
  482.                         DEBUG("IIC发送寄存器地址MSB失败\r\n");
  483.                         return Error;
  484.                 }        
  485.         }
  486.         Error = IIC_SendByte(pHandle, RegAddr & 0xFF, TimeOutUs);        //再发送LSB-最后一字节
  487.         if(Error != IIC_OK)
  488.         {
  489.                 DEBUG("IIC发送寄存器地址LSB失败\r\n");
  490.                 return Error;
  491.         }
  492.         //等待全部数据发送完成
  493.         if(isRead)        //读取方向-非重载,等待数据传输完成 当 RELOAD=0、AUTOEND=0 且 NBYTES 数据传输完成时,该标志由硬件置 1。
  494.         {
  495.                 if(IIC_isWaitFlag(pHandle, IIC_FLAG_TC, TRUE, TimeOutUs) != IIC_OK)
  496.                 {
  497.                         return IIC_TIMEOUT;
  498.                 }
  499.         }        
  500.         else //写入方向,等待重载
  501.         {
  502.                 if(IIC_isWaitFlag(pHandle, IIC_FLAG_TCR, TRUE, TimeOutUs) != IIC_OK)
  503.                 {
  504.                         return IIC_TIMEOUT;
  505.                 }
  506.         }
  507.         
  508.         return Error;
  509. }


  510. /*************************************************************************************************************************
  511. * 函数        :                        static IIC_ERROR IIC_WaitRxOneByte(IIC_HANDLE *pHandle, u8 *pData, u32 TimeOutUs)
  512. * 功能        :                        等待接收一字节数据
  513. * 参数        :                        pHandle:句柄;pData:接收的字节数据缓冲区;TimeOutUs:超时时间,单位ms
  514. * 返回        :                        IIC_ERROR
  515. * 依赖        :                        底层宏定义
  516. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  517. * 时间        :                        2020-02-15
  518. * 最后修改时间 :         2020-02-15
  519. * 说明        :                         当RXNE有效后读取一条数据,否则可能会超时
  520. *************************************************************************************************************************/  
  521. static IIC_ERROR IIC_WaitRxOneByte(IIC_HANDLE *pHandle, u8 *pData, u32 TimeOutUs)
  522. {
  523.         while((IIC_GetISR(pHandle) & IIC_FLAG_RXNE) == 0)        //等待RXNE有效
  524.         {
  525.                 if(TimeOutUs == 0)                                        //检查是否超时了
  526.                 {
  527.                         DEBUG("IIC等待接收超时\r\n");
  528.                         return IIC_TIMEOUT;                                //超时
  529.                 }                        
  530.                 TimeOutUs --;
  531.                 Delay_US(1);                                                //延时
  532.         }
  533.                
  534.         *pData = pHandle->I2Cx->RXDR;                                                        //读取收到的数据
  535.         return IIC_OK;
  536. }


  537. /*************************************************************************************************************************
  538. * 函数        :                        IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
  539.                                                 u8 *pDataBuff, u16 ReadByteNum)
  540. * 功能        :                        IIC读取寄存器(可以读取1个或者多个寄存器)
  541. * 参数        :                        ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要读取的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
  542.                                                 pDataBuff:接收的字节数据缓冲区;ReadByteNum:要读取的寄存器数量;
  543. * 返回        :                        IIC_ERROR
  544. * 依赖        :                        底层宏定义
  545. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  546. * 时间        :                        2020-02-15
  547. * 最后修改时间 :         2020-02-15
  548. * 说明        :                         读取的数据都是小端模式,如果是16bit的寄存器,请读取偶数个数据,并且需要另外进行高低字节对调最后组成16bit数据
  549.                                         可能的意外:比如一个异常的操作可能会导致IIC一直忙,此处检测到忙后会直接复位IIC,但是必须在应用层做好重入保护,比如
  550.                                                 增加信号量
  551.                                         通过测试发现:在函数进入的时候,清除掉中断状态,之后几乎检测不到IIC忙的情况,当然在结束的时候如果检测到忙会主动发送一个STOP,在
  552.                                                 操作失败的情况下,会对IIC进行软复位,确保本次的错误不会影响下一次的IIC操作。
  553. *************************************************************************************************************************/
  554. IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 ReadByteNum)
  555. {
  556.         IIC_ERROR Error = IIC_OK;
  557.         u16 ReadCount;
  558.         IIC_HANDLE *pHandle;
  559.         
  560.         if(ch > (IIC_CH_COUNT-1) || pDataBuff == NULL || ReadByteNum == 0)
  561.         {
  562.                 DEBUG("IIC错误:无效的参数\r\n");
  563.                 return IIC_PARAMETER_ERROR;
  564.         }
  565.         pHandle = &sg_IIC_Handle[ch];                                        //获取相关通道的句柄
  566.         
  567.         if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)                        //忙
  568.         {
  569.                 IIC_SoftReset(pHandle);
  570.                 DEBUG("IIC错误:总线忙,复位总线,可能直接打断别的地方IIC操作,做好保护措施\r\n");
  571.                 if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)                //忙
  572.                 {
  573.                         DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
  574.                         Error = IIC_BUSY;
  575.                         goto end_loop;
  576.                 }
  577.         }
  578.         IIC_ClearISR(pHandle, IIC_GetISR(pHandle));                //复位错误状态
  579.         IIC_ClearCR2(pHandle);                                                        //复位CR2寄存器
  580.         //主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送START
  581.         Error = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,TRUE, pHandle->TimeOutUs);
  582.         if(Error != IIC_OK)
  583.         {
  584.                 goto end_loop;
  585.         }
  586.         //发送后续数据
  587.         if(ReadByteNum > 255)        //如果超过255字节,则需要分多次读取-后续还有数据,需要重载
  588.         {
  589.                 ReadCount = 255;        //本次需要读取的数据长度255
  590.                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_START_READ);        //传输相关配置-读取,并启动传输               
  591.         }
  592.         else
  593.         {
  594.                 ReadCount = ReadByteNum;        //记录本次需要读取的数据长度-最后一次通讯后自动结束
  595.                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_START_READ);        //传输相关配置-读取,并启动传输
  596.         }
  597.         
  598.         //循环等待接收数据完成
  599.         do
  600.         {
  601.                 //uart_printf("读ISR=0x%X\r\n", pHandle->I2Cx->ISR);
  602.                 Error = IIC_WaitRxOneByte(pHandle, pDataBuff,  pHandle->TimeOutUs);                                        //接收1字节数据
  603.                 if(Error != IIC_OK)
  604.                 {
  605.                         goto end_loop;
  606.                 }
  607.         
  608.                 pDataBuff ++;
  609.                 ReadCount --;
  610.                 ReadByteNum --;
  611.                 if((ReadByteNum > 0) && (ReadCount == 0))        //还有数据要读取
  612.                 {
  613.                         if(ReadByteNum > 255)        //如果超过255字节,则需要分多次读取
  614.                         {
  615.                                 ReadCount = 255;        //本次需要读取的数据长度255-后续还有数据,需要重载
  616.                                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_READ);        //传输相关配置-读取,不启动传输                        
  617.                         }
  618.                         else
  619.                         {
  620.                                 ReadCount = ReadByteNum;        //记录本次需要读取的数据长度-最后一次通讯,自动结束
  621.                                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_READ);        //传输相关配置-读取,不启动传输                        
  622.                         }
  623.                 }
  624.         }while(ReadByteNum);
  625.         //读取完成了,等待STOP位有效
  626.         Error = IIC_isWaitSTOPF(pHandle, pHandle->TimeOutUs);
  627.         if(Error != IIC_OK)
  628.         {
  629.                 Error = IIC_STOP_ERROR;
  630.                 goto end_loop;
  631.         }        
  632.         IIC_ClearISR(pHandle, IIC_GetISR(pHandle));                //复位错误状态
  633.         
  634. end_loop:        
  635.         IIC_ClearCR2(pHandle);                                                        //复位CR2寄存器        
  636.         if(Error != IIC_OK)
  637.         {
  638.                 IIC_SoftReset(pHandle);                                                //IIC软复位,清除掉错误
  639.                 DEBUG("[IIC R错误]:%d\r\n", Error);
  640.         }
  641.         if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)                        //忙,注:奇怪的是高速读取的时候,此处可能会检测到忙,TC标志有效,正常此处不应该有TC标志的
  642.         {
  643.                 IIC_SendStop(pHandle);                                                //总线忙,发送一个STOP,释放掉总线
  644.                 DEBUG("IIC R结束:总线忙 ISR=0x%X\r\n", pHandle->I2Cx->ISR);               
  645.         }
  646.         
  647.         return Error;
  648. }


  649. /*************************************************************************************************************************
  650. * 函数        :                        IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
  651.                                                 u8 *pDataBuff, u16 WriteByteNum)
  652. * 功能        :                        IIC写寄存器(可以写1个或者多个寄存器)
  653. * 参数        :                        ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要写入的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
  654.                                                 pDataBuff:写入的字节数据缓冲区;WriteByteNum:要写入的寄存器数量;
  655. * 返回        :                        IIC_ERROR
  656. * 依赖        :                        底层宏定义
  657. * 作者        :                        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  658. * 时间        :                        2020-02-16
  659. * 最后修改时间 :         2020-02-16
  660. * 说明        :                         写入的数据都是小端模式,如果是16bit的寄存器,请写入偶数个数据,并且需要提前进行高低字节对调最后组成高字节在前的数据buff
  661.                                         可能的意外:比如一个异常的操作可能会导致IIC一直忙,此处检测到忙后会直接复位IIC,但是必须在应用层做好重入保护,比如
  662.                                                 增加信号量
  663.                                         通过测试发现:在函数进入的时候,清除掉中断状态,之后几乎检测不到IIC忙的情况,当然在结束的时候如果检测到忙会主动发送一个STOP,在
  664.                                                 操作失败的情况下,会对IIC进行软复位,确保本次的错误不会影响下一次的IIC操作。
  665. *************************************************************************************************************************/
  666. IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum)
  667. {
  668.         IIC_ERROR Error = IIC_OK;
  669.         u16 ReadCount;
  670.         IIC_HANDLE *pHandle;
  671.         
  672.         if(ch > (IIC_CH_COUNT-1) || pDataBuff == NULL || WriteByteNum == 0)
  673.         {
  674.                 DEBUG("IIC错误:无效的参数\r\n");
  675.                 return IIC_PARAMETER_ERROR;
  676.         }
  677.         pHandle = &sg_IIC_Handle[ch];                                        //获取相关通道的句柄
  678.         
  679.         if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)                //忙
  680.         {
  681.                 DEBUG("IIC错误:总线忙,复位总线,可能直接打断别的地方IIC操作,做好保护措施\r\n");
  682.                 IIC_SoftReset(pHandle);
  683.                
  684.                 if(IIC_GetISR(pHandle) & IIC_FLAG_BUSY)        //忙
  685.                 {
  686.                         DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
  687.                         Error = IIC_BUSY;
  688.                         goto end_loop;
  689.                 }
  690.         }
  691.         
  692.         IIC_ClearISR(pHandle, IIC_GetISR(pHandle));                //复位错误状态
  693.         IIC_ClearCR2(pHandle);                                                //复位CR2寄存器
  694.         //主设备请求发送从机地址以及寄存器地址-会切换到写方向,并发送START
  695.         Error = IIC_MasterRequestWriteAddr(pHandle, SlaveAddr, RegAddr, is8bitRegAddr,FALSE,  pHandle->TimeOutUs);
  696.         if(Error != IIC_OK)
  697.         {
  698.                 goto end_loop;
  699.         }
  700.         //发送后续数据
  701.         if(WriteByteNum > 255)                        //如果超过255字节,则需要分多次写入-后续还有数据,需要重载
  702.         {
  703.                 ReadCount = 255;                        //本次需要写入的数据长度255
  704.                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_WRITE);        //传输相关配置-不启动传输的写入               
  705.         }
  706.         else
  707.         {
  708.                 ReadCount = WriteByteNum;        //记录本次需要写入的数据长度-最后一次通讯后自动结束
  709.                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_WRITE);        //传输相关配置-不启动传输的写入
  710.         }

  711.         //循环发送数据
  712.         do
  713.         {
  714.                 Error = IIC_SendByte(pHandle, *pDataBuff, pHandle->TimeOutUs);        //发送一字节
  715.                 if(Error != IIC_OK)
  716.                 {
  717.                         goto end_loop;
  718.                 }

  719.                 ReadCount --;
  720.                 WriteByteNum --;
  721.                 pDataBuff ++;
  722.                
  723.                 if((WriteByteNum > 0) && (ReadCount == 0))        //还有数据要读取
  724.                 {
  725.                         if(WriteByteNum > 255)        //如果超过255字节,则需要分多次读取
  726.                         {
  727.                                 ReadCount = 255;        //本次需要写入的数据长度255-后续还有数据,需要重载
  728.                                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_RELOAD_MODE, IIC_NOSTART_WRITE);        //传输相关配置-不启动传输的写入
  729.                         }
  730.                         else
  731.                         {
  732.                                 ReadCount = WriteByteNum;        //记录本次需要写入的数据长度-最后一次通讯,自动结束
  733.                                 IIC_MasterTransConfig(pHandle, SlaveAddr, ReadCount, IIC_AUTOEND_MODE, IIC_NOSTART_WRITE);        //传输相关配置-不启动传输的写入               
  734.                         }
  735.                 }
  736.         }while(WriteByteNum);        

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


  761. /*************************************************************************************************************************
  762. * 函数                        :        void IIC_SoftReset(IIC_HANDLE *pHandle)
  763. * 功能                        :        硬件IIC软复位(会使能IIC)
  764. * 参数                        :        ch:IIC通道
  765. * 返回                        :        IIC_ERROR
  766. * 依赖                        :        底层宏定义
  767. * 作者                        :        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  768. * 时间                        :        2020-02-15
  769. * 最后修改时间         :         2020-02-15
  770. * 说明                        :         IIC软复位;必须使 PE 保持低电平持续至少 3 个 APB 时钟周期,才能成功执行软件复位。
  771. *************************************************************************************************************************/
  772. static void IIC_SoftReset(IIC_HANDLE *pHandle)
  773. {
  774.         pHandle->I2Cx->CR1 &= ~BIT0;                //关闭IIC后执行软复位
  775.         Delay_US(1);        
  776.         if(pHandle->I2Cx->CR1 & BIT0)
  777.         {
  778.                 DEBUG("IIC软复位失败\r\n");
  779.         }
  780.         pHandle->I2Cx->CR1 |= BIT0;                        //重新启动
  781.         Delay_US(1);        
  782. }



  783. /*************************************************************************************************************************
  784. * 函数                        :        static u32 IIC_CalculationTiming(u16 Speed_KHz)
  785. * 功能                        :        硬件IIC时序计算
  786. * 参数                        :        ch:IIC通道;Speed_KHz:速度10-1000
  787. * 返回                        :        IIC_ERROR
  788. * 依赖                        :        底层宏定义
  789. * 作者                        :        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  790. * 时间                        :        2020-02-15
  791. * 最后修改时间         :         2020-02-15
  792. * 说明                        :         IIC使用的是APB1时钟,将IIC总线时钟控制在100KHz        
  793.                                         APB1时钟:最大不能超过45MHz        
  794.                                         如果速度是100,则按照SMBUS时序
  795. *************************************************************************************************************************/
  796. static u32 IIC_CalculationTiming(u16 Speed_KHz)
  797. {
  798.         u32 tTime = 0;
  799.         u32 temp;
  800.         u32 time_temp = SYS_GetAPB1ClockSpeed() / 1000000;                //先获取APB1时钟速度
  801.         u32 Th = 1000/Speed_KHz/2;                                                                //计算高电平时间(低电平时间一样),单位ns
  802.         
  803.         if(time_temp < 20) time_temp = 20;
  804.         time_temp = 1*1000 / time_temp;                                                        //单位ns
  805.         uart_printf("IIC时钟计算->APB1周期:%dns\t", time_temp);
  806.         if(Speed_KHz == 100)        //如果是100,则按照SMBUS时序
  807.         {
  808.                 //主时钟分频系数固定为3
  809.                 time_temp *= 3;                        //主时钟周期
  810.                 uart_printf("IIC时钟周期:%dns\t", time_temp);
  811.                 tTime |= (3-1) << 28;        //PRESC,时钟预分配
  812.                 //计算数据建立时间,要求最小250ns
  813.                 temp = 250 / time_temp;
  814.                 if(temp > 15) temp = 15;
  815.                 if(temp < 1) temp = 1;
  816.                 tTime |= temp << 20;
  817.                 //计算数据保持时间,要求至少300ns
  818.                 temp = 300 / time_temp;
  819.                 if(temp > 15) temp = 15;
  820.                 if(temp < 1) temp = 1;
  821.                 tTime |= temp << 16;
  822.                 //计算高电平周期5us
  823.                 temp = 5*1000 / time_temp;
  824.                 if(temp > 255) temp = 255;
  825.                 if(temp < 1) temp = 1;
  826.                 tTime |= temp << 8;
  827.                 //计算低电平周期5us
  828.                 temp = 5*1000 / time_temp;
  829.                 if(temp > 255) temp = 255;
  830.                 if(temp < 1) temp = 1;
  831.                 tTime |= temp << 0;
  832.         }
  833.         else if(Speed_KHz < 100)
  834.         {
  835.                 //主时钟分频系数固定为6
  836.                 time_temp *= 6;                        //主时钟周期
  837.                 uart_printf("IIC时钟周期:%dns\t", time_temp);
  838.                 tTime |= (6-1) << 28;        //PRESC,时钟预分配
  839.                 //计算数据建立时间,要求最小250ns
  840.                 temp = 250 / time_temp;
  841.                 if(temp > 15) temp = 15;
  842.                 tTime |= temp << 20;
  843.                 //计算数据保持时间,要求至少300ns
  844.                 temp = 300 / time_temp;
  845.                 if(temp > 15) temp = 15;
  846.                 tTime |= temp << 16;
  847.                 //计算高电平周期Th us
  848.                 temp = Th*1000 / time_temp;
  849.                 if(temp > 255) temp = 255;
  850.                 if(temp < 1) temp = 1;
  851.                 tTime |= temp << 8;
  852.                 //计算低电平周期 Th us
  853.                 temp = Th*1000 / time_temp;
  854.                 if(temp > 255) temp = 255;
  855.                 if(temp < 1) temp = 1;
  856.                 tTime |= temp << 0;
  857.         }
  858.         else //>100
  859.         {
  860.                 //主时钟分频系数固定为2
  861.                 time_temp *= 2;                        //主时钟周期
  862.                 uart_printf("IIC时钟周期:%dns\t", time_temp);
  863.                 tTime |= (2-1) << 28;        //PRESC,时钟预分配
  864.                 //计算数据建立时间,随便给100ns
  865.                 temp = 100 / time_temp;
  866.                 if(temp > 15) temp = 15;
  867.                 tTime |= temp << 20;
  868.                 //计算数据保持时间,给100ns
  869.                 temp = 100 / time_temp;
  870.                 if(temp > 15) temp = 15;
  871.                 tTime |= temp << 16;
  872.                 //计算高电平周期Th us
  873.                 temp = Th*1000 / time_temp;
  874.                 if(temp > 255) temp = 255;
  875.                 if(temp < 1) temp = 1;
  876.                 tTime |= temp << 8;
  877.                 //计算低电平周期 Th us
  878.                 temp = Th*1000 / time_temp;
  879.                 if(temp > 255) temp = 255;
  880.                 if(temp < 1) temp = 1;
  881.                 tTime |= temp << 0;
  882.         }
  883.         
  884.         uart_printf("时序寄存器结果为:0x%X\r\n", tTime);
  885.         
  886.         return tTime;
  887. }
复制代码


  1. /*************************************************************************************************************
  2. * 文件名                :        stm32f7_iic.h
  3. * 功能                        :        STM32F7 IIC接口驱动
  4. * 作者                        :        <a href="mailto:cp1300@139.com">cp1300@139.com</a>
  5. * 创建时间                :        2020-02-09
  6. * 最后修改时间        :        2020-02-09
  7. * 详细:                        
  8. *************************************************************************************************************/               
  9. #ifndef __STM32F7_IIC_H_
  10. #define __STM32F7_IIC_H_
  11. #include "system.h"

  12. //16位整形数高低对调
  13. #ifndef SWAP16
  14. #define SWAP16(x)   (((x & 0xff00) >> 8) | ((x & 0xff) << 8))
  15. #endif  //SWAP16

  16. //硬件IO口选择
  17. //I2C1 IO定义与选择
  18. #define IIC_CH1_PB6_7                                0                                        //PB6,PB7
  19. #define IIC_CH1_PB8_9                                1                                        //PB8,PB9
  20. #define IIC_CH1_IO_SELECT                        IIC_CH1_PB6_7                //选择I2C1 的IO        

  21. //I2C2 IO定义与选择
  22. #define IIC_CH2_PB10_11                                0                                        //PB10,PB11
  23. #define IIC_CH2_PF0_1                                1                                        //PF0,PF1
  24. #define IIC_CH2_PH4_5                                2                                        //PH4,PH5
  25. #define IIC_CH2_IO_SELECT                        IIC_CH2_PB10_11                //选择I2C2 的IO

  26. //I2C3 IO定义与选择
  27. #define IIC_CH3_PH7_8                                0                                        //PH7,PH8
  28. #define IIC_CH3_PA8_PC9                                1                                        //PA8,PC9
  29. #define IIC_CH3_IO_SELECT                        IIC_CH3_PH7_8                //选择I2C3 的IO

  30. //I2C4 IO定义与选择
  31. #define IIC_CH4_PD12_13                                0                                        //PD12,PD13
  32. #define IIC_CH4_PF14_15                                1                                        //PF14,PF15
  33. #define IIC_CH4_PH11_12                                2                                        //PH11,PH12
  34. #define IIC_CH4_IO_SELECT                        IIC_CH4_PD12_13                //选择I2C4 的IO

  35. //IIC硬件接口选择
  36. typedef enum
  37. {
  38.         IIC_CH1        =                0,        //IIC1
  39.         IIC_CH2        =                1,        //IIC2
  40.         IIC_CH3        =                2,        //IIC3
  41.         IIC_CH4        =                3,        //IIC4
  42. }IIC_CH_Type;
  43. #define IIC_CH_COUNT        4        //4个IIC


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

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


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


  75. #endif //__STM32F7_IIC_H_
复制代码


//初始化

  1. IIC_Init(IIC_CH3, 200, 0);        //硬件IIC初始化
复制代码


//调用

  1. //触摸屏IIC读取寄存器接口
  2. bool TP_FT5336_IIC_ReadReg(u16 SlaveAddr, u8 RegAddr, u8 *pDataBuff, u16 ByteNum)
  3. {
  4.         if(IIC_MasterReadReg(FT5336_IIC_CH, SlaveAddr, RegAddr, TRUE, pDataBuff, ByteNum) == IIC_OK)
  5.         {
  6.                 return TRUE;
  7.         }
  8.         else
  9.         {
  10.                 return FALSE;
  11.         }
  12. }

  13. //触摸屏IIC写寄存器接口
  14. bool TP_FT5336_IIC_WriteReg(u16 SlaveAddr, u8 RegAddr, u8 *pDataBuff, u16 ByteNum)
  15. {
  16.         if(IIC_MasterWriteReg(FT5336_IIC_CH, SlaveAddr, RegAddr, TRUE, pDataBuff, ByteNum) == IIC_OK)
  17.         {
  18.                 return TRUE;
  19.         }
  20.         else
  21.         {
  22.                 return FALSE;
  23.         }

复制代码



收藏 评论0 发布时间:2021-12-11 12:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版