• stm32之软件模拟IIC


            在之前的文章中分析过在52上的IIC时序,也测试过stm32的自带IIC功能,这里大致写下如何模拟stm32上的IIC。实验硬件基于stm32f103c8t6

    废话不多说,先直接上代码。

    一、源码

    头文件

    1. #include "stdint.h"
    2. #include "gpio.h"
    3. #ifndef __IIC_H__
    4. #define __IIC_H__
    5. /*********************引脚和主频根据情况更换*****************************/
    6. #define CPU_FREQUENCY_MHZ 72 // STM32时钟主频
    7. #define IIC_GPIO_PORT GPIOB // GPIO端口
    8. #define IIC_SCL_PIN GPIO_PIN_9 // 连接到SCL时钟线的GPIO引脚
    9. #define IIC_SDA_PIN GPIO_PIN_8 // 连接到SDA数据线的GPIO引脚
    10. #define IIC_SCL_1() IIC_GPIO_PORT->BSRR = IIC_SCL_PIN // SCL = 1
    11. #define IIC_SCL_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U // SCL = 0
    12. #define IIC_SDA_1() IIC_GPIO_PORT->BSRR = IIC_SDA_PIN // SDA = 1
    13. #define IIC_SDA_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U // SDA = 0
    14. #define IIC_SDA_READ() (IIC_GPIO_PORT->IDR & IIC_SDA_PIN) // 读SDA引脚
    15. #define IIC_SCL_READ() (IIC_GPIO_PORT->IDR & IIC_SCL_PIN) // 读SCL引脚
    16. void iic_start(void); //开始信号
    17. void iic_stop(void); //停止信号
    18. void iic_send_byte(uint8_t _ucByte); //发送一个字节
    19. void iic_ack(void); //应答信号
    20. uint8_t iic_read_byte(void); //读取一个字节
    21. uint8_t iic_wait_ack(void); //等待应答信号
    22. #endif

    .m文件

    1. #include "iic.h"
    2. void delay_us(__IO uint32_t delay) {
    3. int last, curr, val;
    4. int temp;
    5. while (delay != 0) {
    6. temp = delay > 900 ? 900 : delay;
    7. last = SysTick->VAL;
    8. curr = last - CPU_FREQUENCY_MHZ * temp;
    9. if (curr >= 0){
    10. do{
    11. val = SysTick->VAL;
    12. }
    13. while ((val < last) && (val >= curr));
    14. } else{
    15. curr += CPU_FREQUENCY_MHZ * 1000;
    16. do{
    17. val = SysTick->VAL;
    18. }
    19. while ((val <= last) || (val > curr));
    20. }
    21. delay -= temp;
    22. }
    23. }
    24. // IIC 至少0.6us,1us即可
    25. void iic_delay_1us(void) {
    26. delay_us(1);
    27. }
    28. // SCL保持高电平期间,SDA由高电平变成低电平,产生一个下降沿。
    29. void iic_start(void) {
    30. IIC_SDA_1();
    31. IIC_SCL_1();
    32. iic_delay_1us();
    33. IIC_SDA_0();
    34. iic_delay_1us();
    35. }
    36. // SCL保持高电平期间,SDA由低电平变成高电平,产生一个上升沿。
    37. void iic_stop(void){
    38. IIC_SDA_0();
    39. IIC_SCL_1();
    40. iic_delay_1us();
    41. IIC_SDA_1();
    42. iic_delay_1us();
    43. }
    44. void iic_send_byte(uint8_t _ucByte){
    45. uint8_t i;
    46. for (i = 0; i < 8; i++) {
    47. IIC_SCL_0();
    48. if (_ucByte & 0x80){
    49. IIC_SDA_1();
    50. } else{
    51. IIC_SDA_0();
    52. }
    53. iic_delay_1us();
    54. IIC_SCL_1();
    55. iic_delay_1us();
    56. IIC_SCL_0();
    57. // 发送最后一位
    58. if (i == 7) {
    59. IIC_SDA_1(); // 释放总线
    60. }
    61. _ucByte <<= 1; // 左移一个bit
    62. iic_delay_1us();
    63. }
    64. }
    65. uint8_t iic_read_byte(void){
    66. uint8_t i;
    67. uint8_t value = 0;
    68. for (i = 0; i < 8; i++) {
    69. value <<= 1;
    70. IIC_SCL_1();
    71. iic_delay_1us();
    72. if (IIC_SDA_READ()) {
    73. value++;
    74. }
    75. IIC_SCL_0();
    76. iic_delay_1us();
    77. }
    78. return value;
    79. }
    80. uint8_t iic_wait_ack(void){
    81. uint8_t result;
    82. IIC_SCL_1();
    83. iic_delay_1us();
    84. result = IIC_SDA_READ() ? 1 :0;
    85. IIC_SCL_0();
    86. iic_delay_1us();
    87. return result;
    88. }
    89. void iic_ack(void){
    90. IIC_SDA_0();
    91. iic_delay_1us();
    92. IIC_SCL_1();
    93. iic_delay_1us();
    94. IIC_SCL_0();
    95. iic_delay_1us();
    96. IIC_SDA_1();
    97. }
    98. uint8_t iic_CheckDevice(uint8_t _Address){
    99. uint8_t ucAck;
    100. if (IIC_SDA_READ() && IIC_SCL_READ()){
    101. iic_start(); /* 发送启动信号 */
    102. /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
    103. iic_send_byte(_Address | 0);
    104. ucAck = iic_wait_ack();
    105. iic_stop();
    106. return ucAck;
    107. }
    108. return 1;
    109. }
    110. void Device_WriteData(uint8_t DeciveAddr,uint8_t *Data,int size){
    111. int count=size;
    112. uint8_t *pData=Data;
    113. //起始信号
    114. iic_start();
    115. //发送器件地址
    116. iic_send_byte(DeciveAddr);
    117. //等待应答
    118. iic_wait_ack();
    119. while(count--)
    120. {
    121. //发送数据
    122. iic_send_byte(*pData++);
    123. //等待应答
    124. iic_wait_ack();
    125. }
    126. //结束信号
    127. iic_stop();
    128. }

    二、CubeMX配置

     实验用了PB8和PB9,设置的都是开漏输出式,因为在这个模式下有如下说明:

    在配置成开漏输出时,可以读IDR寄存器。 

    三、源码分析

    整个源码的格式写法和之前手撸51中的IIC的类似,包括start,stop, ack等的时序。

    这里是直接操作的寄存器来输出/输入数据。输出时比如SDA,设置BSRR,读取状态时,比如SDA中的ack,直接读取IDR寄存器。

    1. #define IIC_SCL_1() IIC_GPIO_PORT->BSRR = IIC_SCL_PIN // SCL = 1
    2. #define IIC_SCL_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U // SCL = 0
    3. #define IIC_SDA_1() IIC_GPIO_PORT->BSRR = IIC_SDA_PIN // SDA = 1
    4. #define IIC_SDA_0() IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U // SDA = 0
    5. #define IIC_SDA_READ() (IIC_GPIO_PORT->IDR & IIC_SDA_PIN) // 读SDA引脚
    6. #define IIC_SCL_READ() (IIC_GPIO_PORT->IDR & IIC_SCL_PIN) // 读SCL引脚

    其它地方就没什么分析的了,和51的代码都一致,O(∩_∩)O哈哈~

    完整代码

  • 相关阅读:
    [入门一]C# webApi创建、与发布、部署、api调用
    实例分割计算指标TP,FP,FN,F1(附代码)
    苹果M1芯片VM虚拟机安装ubuntu22.04浏览器中文乱码
    六级易混词整理
    Linux基本指令一
    [华为北向网管NCE开发教程(6)消息订阅
    C语言中编译时出现警告C4013(C语言不加函数原型产生的潜在错误)
    微信小程序快速入门【四】
    SPOJ DQUERY D-query(主席树维护区间 求区间内不同数字的个数)
    python学习之几种经典排序算法
  • 原文地址:https://blog.csdn.net/TSC1235/article/details/133347780