[N32G430] 软件模拟i2c的使用

[复制链接]
 楼主| 菜鸟的第一步 发表于 2024-10-30 16:45 | 显示全部楼层 |阅读模式
软件模拟i2c的使用

1、i2c硬件资源不太够的时候,可以用普通gpio模拟i2c的功能,
话不多说,直接上代码,使用时直接替换对应管脚即可。

2、代码如下:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include "main.h"
  4. #include "type.h"
  5. #include "SwI2c.h"
  6. #include "n32g430.h"
  7. #include "timer.h"

  8. // PB8 scl PB9 sda
  9. #define SWI2C1_SCL_PORT     GPIOB
  10. #define SWI2C1_SCL_PIN      GPIO_PIN_8
  11. #define SWI2C1_SCL_CLOCK    RCC_AHB_PERIPH_GPIOB
  12. #define SWI2C1_SCL_HIGH         GPIO_Pins_Set(SWI2C1_SCL_PORT,SWI2C1_SCL_PIN)
  13. #define SWI2C1_SCL_LOW                 GPIO_Pins_Reset(SWI2C1_SCL_PORT,SWI2C1_SCL_PIN)


  14. #define SWI2C1_SDA_PORT     GPIOB
  15. #define SWI2C1_SDA_PIN      GPIO_PIN_9
  16. #define SWI2C1_SDA_CLOCK    RCC_AHB_PERIPH_GPIOB
  17. #define SWI2C1_SDA_HIGH         GPIO_Pins_Set(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
  18. #define SWI2C1_SDA_LOW                GPIO_Pins_Reset(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
  19. #define SWI2C1_SDA_READ     GPIO_Input_Pin_Data_Get(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
  20. #define BIT0     0x01
  21. #define BIT7     0x80

  22. void SwI2c_Gpio_Init(void)
  23. {
  24.         /* Define a structure of type GPIO_InitType */
  25.         GPIO_InitType GPIO_InitStructure;
  26.        
  27.         /* Enable LED related GPIO peripheral clock */
  28.         RCC_AHB_Peripheral_Clock_Enable(SWI2C1_SCL_CLOCK);
  29.     RCC_AHB_Peripheral_Clock_Enable(SWI2C1_SDA_CLOCK);
  30.        
  31.         /* Assign default value to GPIO_InitStructure structure */
  32.         GPIO_Structure_Initialize(&GPIO_InitStructure);
  33.        
  34.         /* Select the GPIO pin to control */
  35.         GPIO_InitStructure.Pin                  = SWI2C1_SCL_PIN;
  36.         /* Set pin mode to general push-pull output */
  37.         GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_OUT_OD;
  38.     GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
  39.         /* Set the pin drive current to 4MA*/
  40.         GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
  41.         GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
  42.         /* Initialize GPIO */
  43.         GPIO_Peripheral_Initialize(SWI2C1_SCL_PORT, &GPIO_InitStructure);

  44.         /* Select the GPIO pin to control */
  45.         GPIO_InitStructure.Pin                  = SWI2C1_SDA_PIN;
  46.         /* Set pin mode to general push-pull output */
  47.         GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_OUT_OD;
  48.     GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
  49.         /* Set the pin drive current to 4MA*/
  50.         GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
  51.         GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
  52.         /* Initialize GPIO */
  53.         GPIO_Peripheral_Initialize(SWI2C1_SDA_PORT, &GPIO_InitStructure);

  54.         SWI2C1_SDA_HIGH;
  55.         SWI2C1_SCL_HIGH;
  56. }

  57. static void SwI2c_Delay_Us(int32_t us)
  58. {
  59.         TIM3_Delay_Us(us);
  60. }

  61. static void SwI2c_Sda_Config_Output_mode(void)
  62. {
  63.         GPIO_InitType GPIO_InitStructure;

  64.         /* Assign default value to GPIO_InitStructure structure */
  65.         GPIO_Structure_Initialize(&GPIO_InitStructure);

  66.         /* Select the GPIO pin to control */
  67.         GPIO_InitStructure.Pin                  = SWI2C1_SDA_PIN;
  68.         /* Set pin mode to general push-pull output */
  69.         GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_OUT_OD;
  70.     GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
  71.         /* Set the pin drive current to 4MA*/
  72.         GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
  73.         GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
  74.         /* Initialize GPIO */
  75.         GPIO_Peripheral_Initialize(SWI2C1_SDA_PORT, &GPIO_InitStructure);

  76.         SWI2C1_SDA_HIGH;
  77. }


  78. static void SwI2c_Sda_Config_Input_mode(void)
  79. {
  80.         GPIO_InitType GPIO_InitStructure;
  81.        
  82.         /* Assign default value to GPIO_InitStructure structure */
  83.         GPIO_Structure_Initialize(&GPIO_InitStructure);

  84.         /* Select the GPIO pin to control */
  85.         GPIO_InitStructure.Pin                  = SWI2C1_SDA_PIN;
  86.         /* Set pin mode to general push-pull output */
  87.         GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_INPUT;
  88.     GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
  89.         /* Set the pin drive current to 4MA*/
  90.         GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
  91.         GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
  92.         /* Initialize GPIO */
  93.         GPIO_Peripheral_Initialize(SWI2C1_SDA_PORT, &GPIO_InitStructure);
  94. }

  95. void SwI2c_Start(void)
  96. {
  97.     SWI2C1_SCL_LOW;                                                         //  先使SCL = 0, 为SDA上的电平改变做准备
  98.     SwI2c_Delay_Us(2);
  99.     SWI2C1_SDA_HIGH;                                                         //  SDA = 1
  100.     SwI2c_Sda_Config_Output_mode();                      //  sda OD 初始化输出1,
  101.     SWI2C1_SDA_HIGH;                                                         //  SDA = 1, 此时SDA的电平变化对通讯双方没有影响
  102.     SwI2c_Delay_Us(2);
  103.     SWI2C1_SCL_HIGH;                                                         //  SCL = 1
  104.     SwI2c_Delay_Us(2);
  105.     SWI2C1_SDA_LOW;                                                         //  SDA=0,产生下降沿,启动IIC通讯
  106.     SwI2c_Delay_Us(2);
  107.     SWI2C1_SCL_LOW;                                                                //  SCL=0,为SDA上的电平改变做准备
  108.     SwI2c_Delay_Us(2);
  109. }

  110. void SwI2c_Stop(void)                                                        //停止信号
  111. {
  112.         SWI2C1_SCL_LOW ;                                                         //  先使SCL = 0, 为SDA上的电平改变做准备
  113.     SwI2c_Delay_Us(2);
  114.     SWI2C1_SDA_HIGH;                                                         //  SDA = 1
  115.     SwI2c_Sda_Config_Output_mode();                      //  sda OD 初始化输出1,
  116.     SWI2C1_SDA_HIGH;
  117.     SwI2c_Delay_Us(2);
  118.     SWI2C1_SDA_LOW;                                                         //  SDA=0,此时SDA的电平变化对通讯双方没有影响
  119.     SwI2c_Delay_Us(2);
  120.     SWI2C1_SCL_HIGH;                                                         //  SCL=1
  121.     SwI2c_Delay_Us(2);
  122.     SWI2C1_SDA_HIGH;                                                         //  SDA=1,结束IIC通讯
  123.     SwI2c_Delay_Us(2);                                //  SDA在结束后维持在高电平,如果有干扰脉冲产生而使得SDA
  124.                                                                //  变低,则干扰过后会恢复高电平. 此时SCL如果因干扰而处于
  125.     return;   
  126. }

  127. MI_U8 SwI2c_Wait_Ack(void)                                                //等待应答信号:0-应答;1-非应答
  128. {
  129.     MI_U8 uc_time = 0;
  130.         SwI2c_Sda_Config_Input_mode();                                 //SDA定义为输入

  131.         SWI2C1_SDA_HIGH;
  132.         SwI2c_Delay_Us(1);
  133.         SWI2C1_SCL_HIGH;
  134.         SwI2c_Delay_Us(1);

  135.     while (SWI2C1_SDA_READ)
  136.     {
  137.         uc_time++;
  138.         if (uc_time > 250)
  139.         {
  140.             SwI2c_Stop();
  141.             return 1;
  142.         }
  143.     }
  144.         SWI2C1_SCL_LOW;
  145.         return 0;
  146. }

  147. void SwI2c_Ack(void)                                        //产生 ACK 应答
  148. {
  149.     SWI2C1_SDA_HIGH;                                         //  SDA = 1
  150.     SwI2c_Sda_Config_Output_mode();     //  sda OD 初始化输出1,
  151.     SWI2C1_SDA_HIGH;                                         //  SDA输出高电平
  152.     SWI2C1_SDA_LOW;                                         //  清SDA="0",CPU发低电平确认信号,
  153.     SwI2c_Delay_Us(2);
  154.     SWI2C1_SCL_HIGH;                                         //  置SCL="1", 产生上升沿,发送一位确认数据
  155.     SwI2c_Delay_Us(2);
  156.     SWI2C1_SCL_LOW;                                         //  清SCL="0",为SDA上的电平改变做准备
  157.     return;
  158. }

  159. void Swi2c_No_Ack(void)                                        //产生 NACK 非应答
  160. {
  161.     SWI2C1_SDA_HIGH;                                         //  SDA = 1
  162.     SwI2c_Sda_Config_Output_mode();            //  sda OD 初始化输出1,
  163.     SWI2C1_SDA_HIGH;                                         //  置SDA=1, CPU发"高电平非应答确认"信号
  164.     SwI2c_Delay_Us(2);
  165.     SWI2C1_SCL_HIGH;                                         //  置SCL="1", 产生上升沿,发送一位确认数据
  166.     SwI2c_Delay_Us(2);
  167.     SWI2C1_SCL_LOW;                                         //  清SCL="0",为SDA上的电平改变做准备
  168.     return;
  169. }

  170. //IIC 发送一个字节
  171. MI_BOOL SwI2c_Write_Byte(MI_U8 data)
  172. {
  173.     MI_U8 m;                        //  SDA = 1
  174.     SwI2c_Sda_Config_Output_mode(); //  sda OD 初始化输出1,

  175.     for (m = 0; m < 8; m++)
  176.     {
  177.         SWI2C1_SCL_LOW;                         //  SCL=0,为SDA上的电平改变做准备
  178.         SwI2c_Delay_Us(2);

  179.         if (data & BIT7)                         //  由最高位开始发送
  180.         {
  181.             SWI2C1_SDA_HIGH;
  182.         }
  183.         else
  184.         {
  185.             SWI2C1_SDA_LOW;
  186.         }

  187.         SwI2c_Delay_Us(2);
  188.         SWI2C1_SCL_HIGH;                         //  SCL="1",产生上升沿,发送一位数据
  189.         SwI2c_Delay_Us(2);
  190.         data <<= 1;
  191.     }

  192.     SWI2C1_SCL_LOW;                                 //  清SCL="0",产生下降沿, 器件使SDA="0"
  193.     SwI2c_Delay_Us(2);
  194.     SwI2c_Sda_Config_Input_mode();         //  SDA改为输入,准备接收确认应答

  195.     SWI2C1_SCL_HIGH;                                 //  SCL="1",让CPU在此期间读取SDA上的信号

  196.     for (m = 0; m < 8; m++)
  197.     {
  198.         SwI2c_Delay_Us(2);

  199.         if (SWI2C1_SDA_READ == 0)
  200.         {
  201.             SWI2C1_SCL_LOW;                 //  清SCL="0",为SDA上的电平改变做准备
  202.             SwI2c_Delay_Us(2);
  203.             return MI_TRUE;                         //  收到正确的低电平应答
  204.         }
  205.     }

  206.         SWI2C1_SCL_LOW;                                 //  清SCL="0",为SDA上的电平改变做准备
  207.     return MI_FALSE;  
  208. }

  209. //读一个字节
  210. MI_U8 SwI2c_Read_Byte()
  211. {
  212.     MI_U8 m, data;

  213.     SwI2c_Sda_Config_Input_mode();         //  SDA改为输入,准备接收数据
  214.     data = 0;

  215.     for (m = 0; m < 8; m++)
  216.     {
  217.         SWI2C1_SCL_LOW;                         //  SCL="0",产生下降沿, 器件串出一位数据
  218.         SwI2c_Delay_Us(2);
  219.         SWI2C1_SCL_HIGH;                         //  置SCL="1", 让CPU在此期间读取SDA上的信号
  220.         SwI2c_Delay_Us(2);
  221.         data <<= 1;

  222.         if (SWI2C1_SDA_READ)
  223.         {
  224.             data |= BIT0;
  225.         }
  226.         else
  227.         {
  228.             data &= (~BIT0);
  229.         }
  230.     }

  231.     SWI2C1_SCL_LOW;                                 //  清SCL="0",为SDA上的电平改变做准备
  232.     return data;
  233. }

  234. MI_BOOL SwI2c_Device_Write_Data(MI_U8 device_addr,MI_U8 *reg_addr,
  235.                                     uint16_t reg_len,const MI_U8 *buf,MI_U8 len)
  236. {
  237.            SwI2c_Start();
  238.     SwI2c_Write_Byte(device_addr);

  239.         while (reg_len != 0)
  240.     {
  241.         if (SwI2c_Write_Byte(*reg_addr++) == MI_FALSE) //  发送一字节数据
  242.         {
  243.             SwI2c_Stop();
  244.             return MI_FALSE;
  245.         }
  246.         reg_len--;
  247.     }

  248.     while (len != 0)
  249.     {
  250.         if (SwI2c_Write_Byte( *buf++) == MI_FALSE) //  发送一字节数据
  251.         {
  252.             SwI2c_Stop();
  253.             return MI_FALSE;
  254.         }
  255.         len--;
  256.     }

  257.     SwI2c_Stop();
  258.     return MI_TRUE;
  259. }

  260. MI_U8 SwI2c_Device_Read_Data(MI_U8 device_addr,MI_U8 *reg_addr,
  261.                                     uint16_t reg_len, MI_U8 *buf,MI_U8 len)
  262. {
  263.     SwI2c_Start();
  264.     SwI2c_Write_Byte(device_addr);
  265.     while (reg_len != 0)
  266.     {
  267.         if (SwI2c_Write_Byte(*reg_addr++) == MI_FALSE) //  发送一字节数据
  268.         {
  269.             SwI2c_Stop();
  270.             return MI_FALSE;
  271.         }
  272.         reg_len--;
  273.     }

  274.     SwI2c_Start();
  275.     SwI2c_Write_Byte(device_addr + 1);

  276.     while (1)
  277.     {
  278.         *buf++ = SwI2c_Read_Byte();                                        //  接收一字节
  279.         len--;

  280.         if (0 == len)
  281.         {
  282.             break;
  283.         }
  284.         SwI2c_Ack();                                                                 //  未读完,CPU发低电平确认应答信号,以便读取下8位数据
  285.     }

  286.     Swi2c_No_Ack();                                                                 //  已读完所有的数据,CPU发"高电平非应答确认"信号
  287.     SwI2c_Stop();
  288.     return MI_TRUE;
  289. }

.h文件

  1. #ifndef _SWI2C_H__
  2. #define _SWI2C_H__


  3. void SwI2c_Gpio_Init(void);
  4. MI_U8 SwI2c_Device_Read_Data(MI_U8 device_addr,MI_U8 *reg_addr,
  5.                                     uint16_t reg_len,MI_U8 *buf,MI_U8 len);
  6. MI_BOOL SwI2c_Device_Write_Data(MI_U8 device_addr,MI_U8 *reg_addr,
  7.                                     uint16_t reg_len,const MI_U8 *buf,MI_U8 len);                       
  8. #endif  //_SWI2C_H__

3、延时函数换成自己的即可,楼主为了准确性,延时函数开了一个定时器。
prescaler = 64-1 ,1us加一次,Period设置最大,最大延时数就是这个。
相应代码如下

  1. void TIM3_Init(void)
  2. {
  3.     RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_TIM3);
  4.     TIM_TimeBaseInitType TIM_TimeBaseStructure;
  5.     TIM_Base_Struct_Initialize(&TIM_TimeBaseStructure);
  6.     TIM_TimeBaseStructure.Period    = 65535-1;
  7.     TIM_TimeBaseStructure.Prescaler = 64-1;
  8.     TIM_TimeBaseStructure.ClkDiv    = 0;
  9.     TIM_TimeBaseStructure.CntMode   = TIM_CNT_MODE_UP;

  10.     TIM_Base_Initialize(TIM3, &TIM_TimeBaseStructure);  
  11.     TIM_Base_Reload_Mode_Set(TIM2, TIM_PSC_RELOAD_MODE_IMMEDIATE);
  12. }

  13. void TIM3_Delay_Us(__IO uint32_t us)
  14. {
  15.     TIM_Base_Count_Set(TIM3,0);
  16.     TIM_On(TIM3);
  17.     while(TIM_Base_Count_Get(TIM3) < us);
  18.     TIM_Off(TIM3);
  19. }



申小林一号 发表于 2024-10-30 20:34 | 显示全部楼层
软件一致性很强
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

459

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部