打印
[N32G430]

软件模拟i2c的使用

[复制链接]
74|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
菜鸟的第一步|  楼主 | 2024-10-30 16:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
软件模拟i2c的使用

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

2、代码如下:

#include <stdio.h>
#include <stdint.h>
#include "main.h"
#include "type.h"
#include "SwI2c.h"
#include "n32g430.h"
#include "timer.h"

// PB8 scl PB9 sda
#define SWI2C1_SCL_PORT     GPIOB
#define SWI2C1_SCL_PIN      GPIO_PIN_8
#define SWI2C1_SCL_CLOCK    RCC_AHB_PERIPH_GPIOB
#define SWI2C1_SCL_HIGH         GPIO_Pins_Set(SWI2C1_SCL_PORT,SWI2C1_SCL_PIN)
#define SWI2C1_SCL_LOW                 GPIO_Pins_Reset(SWI2C1_SCL_PORT,SWI2C1_SCL_PIN)


#define SWI2C1_SDA_PORT     GPIOB
#define SWI2C1_SDA_PIN      GPIO_PIN_9
#define SWI2C1_SDA_CLOCK    RCC_AHB_PERIPH_GPIOB
#define SWI2C1_SDA_HIGH         GPIO_Pins_Set(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
#define SWI2C1_SDA_LOW                GPIO_Pins_Reset(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
#define SWI2C1_SDA_READ     GPIO_Input_Pin_Data_Get(SWI2C1_SDA_PORT,SWI2C1_SDA_PIN)
#define BIT0     0x01
#define BIT7     0x80

void SwI2c_Gpio_Init(void)
{
        /* Define a structure of type GPIO_InitType */
        GPIO_InitType GPIO_InitStructure;
       
        /* Enable LED related GPIO peripheral clock */
        RCC_AHB_Peripheral_Clock_Enable(SWI2C1_SCL_CLOCK);
    RCC_AHB_Peripheral_Clock_Enable(SWI2C1_SDA_CLOCK);
       
        /* Assign default value to GPIO_InitStructure structure */
        GPIO_Structure_Initialize(&GPIO_InitStructure);
       
        /* Select the GPIO pin to control */
        GPIO_InitStructure.Pin                  = SWI2C1_SCL_PIN;
        /* Set pin mode to general push-pull output */
        GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_OUT_OD;
    GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
        /* Set the pin drive current to 4MA*/
        GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
        GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
        /* Initialize GPIO */
        GPIO_Peripheral_Initialize(SWI2C1_SCL_PORT, &GPIO_InitStructure);

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

        SWI2C1_SDA_HIGH;
        SWI2C1_SCL_HIGH;
}

static void SwI2c_Delay_Us(int32_t us)
{
        TIM3_Delay_Us(us);
}

static void SwI2c_Sda_Config_Output_mode(void)
{
        GPIO_InitType GPIO_InitStructure;

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

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

        SWI2C1_SDA_HIGH;
}


static void SwI2c_Sda_Config_Input_mode(void)
{
        GPIO_InitType GPIO_InitStructure;
       
        /* Assign default value to GPIO_InitStructure structure */
        GPIO_Structure_Initialize(&GPIO_InitStructure);

        /* Select the GPIO pin to control */
        GPIO_InitStructure.Pin                  = SWI2C1_SDA_PIN;
        /* Set pin mode to general push-pull output */
        GPIO_InitStructure.GPIO_Mode            = GPIO_MODE_INPUT;
    GPIO_InitStructure.GPIO_Pull            = GPIO_PULL_UP;
        /* Set the pin drive current to 4MA*/
        GPIO_InitStructure.GPIO_Current         = GPIO_DS_4MA;
        GPIO_InitStructure.GPIO_Slew_Rate         = GPIO_SLEW_RATE_FAST;
        /* Initialize GPIO */
        GPIO_Peripheral_Initialize(SWI2C1_SDA_PORT, &GPIO_InitStructure);
}

void SwI2c_Start(void)
{
    SWI2C1_SCL_LOW;                                                         //  先使SCL = 0, 为SDA上的电平改变做准备
    SwI2c_Delay_Us(2);
    SWI2C1_SDA_HIGH;                                                         //  SDA = 1
    SwI2c_Sda_Config_Output_mode();                      //  sda OD 初始化输出1,
    SWI2C1_SDA_HIGH;                                                         //  SDA = 1, 此时SDA的电平变化对通讯双方没有影响
    SwI2c_Delay_Us(2);
    SWI2C1_SCL_HIGH;                                                         //  SCL = 1
    SwI2c_Delay_Us(2);
    SWI2C1_SDA_LOW;                                                         //  SDA=0,产生下降沿,启动IIC通讯
    SwI2c_Delay_Us(2);
    SWI2C1_SCL_LOW;                                                                //  SCL=0,为SDA上的电平改变做准备
    SwI2c_Delay_Us(2);
}

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

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

        SWI2C1_SDA_HIGH;
        SwI2c_Delay_Us(1);
        SWI2C1_SCL_HIGH;
        SwI2c_Delay_Us(1);

    while (SWI2C1_SDA_READ)
    {
        uc_time++;
        if (uc_time > 250)
        {
            SwI2c_Stop();
            return 1;
        }
    }
        SWI2C1_SCL_LOW;
        return 0;
}

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

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

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

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

        if (data & BIT7)                         //  由最高位开始发送
        {
            SWI2C1_SDA_HIGH;
        }
        else
        {
            SWI2C1_SDA_LOW;
        }

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

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

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

    for (m = 0; m < 8; m++)
    {
        SwI2c_Delay_Us(2);

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

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

//读一个字节
MI_U8 SwI2c_Read_Byte()
{
    MI_U8 m, data;

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

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

        if (SWI2C1_SDA_READ)
        {
            data |= BIT0;
        }
        else
        {
            data &= (~BIT0);
        }
    }

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

MI_BOOL SwI2c_Device_Write_Data(MI_U8 device_addr,MI_U8 *reg_addr,
                                    uint16_t reg_len,const MI_U8 *buf,MI_U8 len)
{
           SwI2c_Start();
    SwI2c_Write_Byte(device_addr);

        while (reg_len != 0)
    {
        if (SwI2c_Write_Byte(*reg_addr++) == MI_FALSE) //  发送一字节数据
        {
            SwI2c_Stop();
            return MI_FALSE;
        }
        reg_len--;
    }

    while (len != 0)
    {
        if (SwI2c_Write_Byte( *buf++) == MI_FALSE) //  发送一字节数据
        {
            SwI2c_Stop();
            return MI_FALSE;
        }
        len--;
    }

    SwI2c_Stop();
    return MI_TRUE;
}

MI_U8 SwI2c_Device_Read_Data(MI_U8 device_addr,MI_U8 *reg_addr,
                                    uint16_t reg_len, MI_U8 *buf,MI_U8 len)
{
    SwI2c_Start();
    SwI2c_Write_Byte(device_addr);
    while (reg_len != 0)
    {
        if (SwI2c_Write_Byte(*reg_addr++) == MI_FALSE) //  发送一字节数据
        {
            SwI2c_Stop();
            return MI_FALSE;
        }
        reg_len--;
    }

    SwI2c_Start();
    SwI2c_Write_Byte(device_addr + 1);

    while (1)
    {
        *buf++ = SwI2c_Read_Byte();                                        //  接收一字节
        len--;

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

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

.h文件

#ifndef _SWI2C_H__
#define _SWI2C_H__


void SwI2c_Gpio_Init(void);
MI_U8 SwI2c_Device_Read_Data(MI_U8 device_addr,MI_U8 *reg_addr,
                                    uint16_t reg_len,MI_U8 *buf,MI_U8 len);
MI_BOOL SwI2c_Device_Write_Data(MI_U8 device_addr,MI_U8 *reg_addr,
                                    uint16_t reg_len,const MI_U8 *buf,MI_U8 len);                       
#endif  //_SWI2C_H__

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

void TIM3_Init(void)
{
    RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_TIM3);
    TIM_TimeBaseInitType TIM_TimeBaseStructure;
    TIM_Base_Struct_Initialize(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.Period    = 65535-1;
    TIM_TimeBaseStructure.Prescaler = 64-1;
    TIM_TimeBaseStructure.ClkDiv    = 0;
    TIM_TimeBaseStructure.CntMode   = TIM_CNT_MODE_UP;

    TIM_Base_Initialize(TIM3, &TIM_TimeBaseStructure);  
    TIM_Base_Reload_Mode_Set(TIM2, TIM_PSC_RELOAD_MODE_IMMEDIATE);
}

void TIM3_Delay_Us(__IO uint32_t us)
{
    TIM_Base_Count_Set(TIM3,0);
    TIM_On(TIM3);
    while(TIM_Base_Count_Get(TIM3) < us);
    TIM_Off(TIM3);
}



使用特权

评论回复
沙发
申小林一号| | 2024-10-30 20:34 | 只看该作者
软件一致性很强

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

57

主题

436

帖子

1

粉丝