国民技术N32G430开发笔记(21)- 软件模拟i2c的使用

软件模拟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);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值