• 软件模拟IIC通信(STM32)


            在学习STM32的时候,难免会使用IIC通信,在STM32中有硬件IIC和软件模拟IIC,ST公司为了防止专利,就直自己设计一套硬件IIC,但是个人感觉不太好用哎,还是喜欢软件模拟IIC,有两个原因:

    ①:硬件IIC通信接口只有两个:I2C1和I2C2。接口太少了。

    ②:得是固定的那几个接口,不太灵活。

    下面我给大家完整介绍一下IIC通信过程:

            IIC软件模拟通信一共需要两根线,SDA和SCL,SDA是数据线,专门传输数据;SCL是时钟线,专门是控制作用。

            IIC通信是半双工通信(就一根数据线),通信过程是双向的,MCU既可以发送给外设控制信号,外设也可以发送数据给MCU。所以就得控制SDA是输出模式还是输入模式。

    IIC通信的大致流程:

    1、发送开始信号;

    2、发送控制指令;(控制指令可以是多个)

    3、等待应答;(MCU向外设发送控制指令,发送完成后,外设给一个应答信号)

    4、停止信号;

    下面是控制信号时序:

    (1) 起始信号和停止信号:

            开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。

            结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
    (2) 数据的有效性:
            IIC 总线进行数据传送时,SDA 数据线上的数据必须在时钟线 SCL 的高电平期间保持稳定,数据线的电平状态只有在 SCL 线的时钟信号为低电平时才能改变。
    (3) 数据传输
            发送到 SDA 线上的每个字节必须为 8 位,每次传输可以发送的字节数量可以不受限
    制,但每个字节传输结束后必须跟一个应答信号,首先传送的是数据的最高位。
    (4) 应答信号:
            发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 对于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号。

     IIC.H文件:

    1. //先把IIC.H文件粘出来,多理解一下这个宏定义
    2. #ifndef __OLED_H
    3. #define __OLED_H
    4. #include "stm32f10x.h"
    5. #define IIC_GPIO GPIOB
    6. #define IIC_SCL GPIO_Pin_6
    7. #define IIC_SDA GPIO_Pin_7
    8. #define IIC_GPIO_RCC RCC_APB2Periph_GPIOB
    9. /*设置输入输出状态*/ /* 8:表示输入;3:表示输出;28:每位GPIO由CRL4位数据控制 */
    10. #define IIC_SDA_IN() {IIC_GPIO->CRL&=0x0FFFFFFF;IIC_GPIO->CRL|=8<<28;}//CRL是GPIO低八位控制寄存器,每4位控制因为GPIO模式以及速度
    11. #define IIC_SDA_OUT() {IIC_GPIO->CRL&=0x0FFFFFFF;IIC_GPIO->CRL|=3<<28;}//CRL是GPIO低八位控制寄存器,每4位控制因为GPIO模式以及速度
    12. //控制SDA,SCL的输出电平
    13. #define IIC_SCL_State(n) GPIO_WriteBit(IIC_GPIO,IIC_SCL,(BitAction)n)
    14. #define IIC_SDA_State(n) GPIO_WriteBit(IIC_GPIO,IIC_SDA,(BitAction)n)
    15. #define IIC_SDA_Read GPIO_ReadInputDataBit(IIC_GPIO,IIC_SDA)//读取SDA的值
    16. void IIC_Start(void);
    17. void IIC_Stop(void);
    18. void IIC_Send_Byte(u8 dat);
    19. void IIC_NAck(void);//不产生ACK应答
    20. void IIC_Ack(void);//产生ACK应答
    21. u8 IIC_WaitAck(void);
    22. u8 CT_IIC_Read_Byte(unsigned char ack);
    23. #endif

    IIC.C文件

    1. #include "iic.h"
    2. #include "delay.h" // Device header
    3. void IIC_Init()
    4. {
    5. GPIO_InitTypeDef GPIO_InitStructure;
    6. RCC_APB2PeriphClockCmd(IIC_GPIO_RCC,ENABLE);
    7. GPIO_InitStructure.GPIO_Pin = IIC_SCL|IIC_SDA; // 端口配置
    8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
    9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
    10. GPIO_Init(IIC_GPIO, &GPIO_InitStructure); //根据设定参数初始化GPIO
    11. GPIO_SetBits(IIC_GPIO,GPIO_Pin_7|GPIO_Pin_6); //拉高IIC数据线和时钟线
    12. }
    13. //起始信号
    14. void IIC_Start(void)
    15. {
    16. IIC_SDA_OUT();
    17. IIC_SDA_State(1);
    18. IIC_SCL_State(1);
    19. IIC_SDA_State(0);
    20. IIC_SCL_State(0);
    21. }
    22. //结束信号
    23. void IIC_Stop(void)
    24. {
    25. IIC_SDA_OUT();
    26. IIC_SCL_State(1);
    27. IIC_SDA_State(0);
    28. IIC_SDA_State(1);
    29. }
    30. //等待应答信号到来
    31. //返回值:1,接收应答失败
    32. // 0,接收应答成功
    33. u8 IIC_WaitAck(void)
    34. {
    35. u8 ucErrTime=0;
    36. IIC_SDA_IN(); //SDA设置为输入
    37. IIC_SDA_State(1);
    38. IIC_SCL_State(1);
    39. while(IIC_SDA_Read) //等待SDA=0,产生应答信号
    40. {
    41. ucErrTime++;
    42. if(ucErrTime>250)
    43. {
    44. IIC_Stop();
    45. return 1;
    46. }
    47. }
    48. IIC_SCL_State(0);
    49. return 0;
    50. }
    51. //IIC写入一个字节
    52. void IIC_Send_Byte(u8 dat)
    53. {
    54. u8 i;
    55. IIC_SDA_OUT();
    56. for(i=0;i<8;i++)
    57. {
    58. IIC_SCL_State(0);//将时钟信号设置为低电平
    59. if(dat&0x80)//将dat的8位从最高位依次写入
    60. {
    61. IIC_SDA_State(1);
    62. }
    63. else
    64. {
    65. IIC_SDA_State(0);
    66. }
    67. IIC_SCL_State(1);//将时钟信号设置为高电平
    68. IIC_SCL_State(0);//将时钟信号设置为低电平
    69. dat<<=1;
    70. }
    71. }
    72. //产生ACK应答
    73. void IIC_Ack(void)
    74. {
    75. IIC_SCL_State(0);//SCL低
    76. IIC_SDA_OUT();
    77. IIC_SDA_State(0);//SDA低
    78. IIC_SCL_State(1);//SCL高
    79. IIC_SCL_State(0);//SCL低
    80. }
    81. //不产生ACK应答
    82. void IIC_NAck(void)
    83. {
    84. IIC_SCL_State(0);//SCL低
    85. IIC_SDA_OUT();
    86. IIC_SDA_State(1);//SDA高
    87. IIC_SCL_State(1);//SCL高
    88. IIC_SCL_State(0);//SCL低
    89. }
    90. //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
    91. //MCU读取数据完成后,可以发送应答,可以不发送应答
    92. u8 CT_IIC_Read_Byte(unsigned char ack)
    93. {
    94. u8 i,receive=0;
    95. IIC_SDA_IN();//SDA设置为输入
    96. delay_us(30);
    97. for(i=0;i<8;i++ )
    98. {
    99. IIC_SCL_State(0) ;
    100. IIC_SCL_State(1);//SCL高
    101. receive<<=1;
    102. if( IIC_SDA_Read )receive++;
    103. }
    104. if (!ack)IIC_NAck();//发送nACK
    105. else IIC_Ack(); //发送ACK
    106. return receive;
    107. }

  • 相关阅读:
    STM32入门笔记(02):SPI 通信之xxx(SPL库函数版)
    VW ware安装Ubuntu虚拟机及环境配置
    python爬虫(数据获取——selenium)
    Python学习笔记 - 函数和lambda表达式 (1)
    【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程
    测试面试-经典测试场景
    修改npm源--多种方式
    Go编程快闪之 logrus日志库
    使用Java客户端发送消息和消费的应用
    二维数组的最小路径和问题
  • 原文地址:https://blog.csdn.net/m0_58832575/article/details/125478913