单片机GD32F303RCT6 (Macos环境)开发 (十九)—— 软件模拟i2c的使用

软件模拟i2c的使用

1、直接上代码,移植时修改使用的管脚即可,频率差不多在94KHZ。
我使用的是PB10 PB11 这两个管脚

#define swi2c_sda_high gpio_bit_set(GPIOB,GPIO_PIN_11)
#define swi2c_sda_low  gpio_bit_reset(GPIOB,GPIO_PIN_11)
#define swi2c_sda_read gpio_input_bit_get(GPIOB,GPIO_PIN_11)

#define swi2c_scl_high gpio_bit_set(GPIOB,GPIO_PIN_10)
#define swi2c_scl_low  gpio_bit_reset(GPIOB,GPIO_PIN_10)


void swi2c_config(void)
{   /* enable GPIOB clock */
    rcu_periph_clock_enable(RCU_GPIOB);
    /*PB10 PB11配置成输出 */
	gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
	gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    swi2c_sda_high;
    swi2c_scl_high;    
}

/**
 * SDA PB11 input mode
*/
static void swi2c_sda_config_input_mode(void)
{
    gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
}

/**
 * SDA PB11 output mode
*/
static void swi2c_sda_config_output_mode(void)
{
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
}

//开始信号
void swi2c_start(void)
{
	swi2c_sda_config_output_mode(); //SDA定义为输出
	swi2c_sda_high; 
	swi2c_scl_high;
	delay_1us(4);
	swi2c_sda_low;  
	delay_1us(4);
	swi2c_scl_low; 
} 


void swi2c_stop(void)//停止信号
{
	swi2c_sda_config_output_mode(); //SDA定义为输出
	swi2c_scl_low;
	swi2c_sda_low; 
	delay_1us(4);
	swi2c_scl_high; 
	delay_1us(4);
	swi2c_sda_high; //发送 I2C 总线结束信号
	delay_1us(4); 
}

uint8_t swi2c_wait_ack(void)//等待应答信号:0-应答;1-非应答
{
    uint8_t uc_time = 0;
	swi2c_sda_high;
	swi2c_sda_config_input_mode(); //SDA定义为输入 
	delay_1us(4); 
	swi2c_scl_high;
	delay_1us(1);

    while (swi2c_sda_read)
    {
        uc_time++;
        if (uc_time > 250)
        {
            swi2c_stop();
            return 1;
        }
    }
	delay_1us(4);
	swi2c_scl_low;
	return 0; 
} 

void swi2c_ack(void)//产生 ACK 应答
{ 
	swi2c_scl_low;
	swi2c_sda_config_output_mode();
	swi2c_sda_low;
	delay_1us(2);
	swi2c_scl_high;
	delay_1us(2);
	swi2c_scl_low;
}

void swi2c_no_ack(void)//产生 NACK 非应答
{ 
	swi2c_scl_low;
	swi2c_sda_config_output_mode();
	swi2c_sda_high;
	delay_1us(2);
	swi2c_scl_high;
	delay_1us(2);
	swi2c_scl_low;
} 

//IIC 发送一个字节
void swi2c_write_byte(uint8_t txd)
{ 
	uint8_t t; 
	swi2c_sda_config_output_mode(); 
	swi2c_scl_low;            //拉低时钟开始数据传输
	for(t = 0;t < 8;t++)
	{ 
        if((txd<<t) & 0x80)//表示数据是1
			swi2c_sda_high;
		else
			swi2c_sda_low;
		delay_1us(4); 
		swi2c_scl_high;
		delay_1us(4); 
		swi2c_scl_low;
		//delay_us(2);
	}
} 

//读一个字节
uint8_t swi2c_read_byte(void)
{ 
	uint8_t i,receive = 0;
	
	swi2c_sda_config_input_mode();
	for(i = 0;i < 8;i++ )
	{  
		delay_1us(4);
		swi2c_scl_high;
		receive <<= 1;
		if(swi2c_sda_read == 1)
		{
			receive ++;
		}
		delay_1us(4); 
		swi2c_scl_low;
	}
	return receive;
} 

//只写地址
uint8_t write_device_addr(uint8_t addr)
{
	uint8_t read_ack = 0;
	swi2c_start();
	swi2c_write_byte(addr);
	read_ack = swi2c_wait_ack();
	swi2c_stop();
	
	return(read_ack);
}

//在总线上搜寻挂载的器件地址
void swi2c_search_device_addr(void)
{
	uint8_t result = 0;
	uint8_t j = 0;
	for(j = 0;j < 128; j++)
	{
		if((j % 16) == 0)
		{
			printf("\r\n");
		}
		result = write_device_addr(j << 1);
		if(result == 0)
		{
			printf(" %X ",j << 1);//%X 十六进制输出,大写;%x 小写
		}
		else
		{
			printf(" -- ");
		}
	}
    printf("\r\n");
}

//读指定器件的指定位置中的一个字节
uint8_t swi2c_device_read_one_byte(uint8_t device_addr,uint8_t reg_addr)
{
	uint8_t dat;

	swi2c_start();
	swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
	swi2c_wait_ack();//等待应答

	swi2c_write_byte(reg_addr);//写位置
	swi2c_wait_ack();

	swi2c_start();
	swi2c_write_byte((device_addr) | 0x01);//读写位改为读
	swi2c_wait_ack();

	dat = swi2c_read_byte();
	swi2c_no_ack();//读一个字节结束
	swi2c_stop();
	
	return dat;
}

void swi2c_device_write_one_byte(uint8_t device_addr,uint8_t reg_addr,uint8_t data)//写指定器件的指定位置中的一个字节
{
	swi2c_start();
	swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
	swi2c_wait_ack();//等待应答

	swi2c_write_byte(reg_addr);//写位置
	swi2c_wait_ack();

	swi2c_write_byte(data);//写数据
	swi2c_wait_ack();

	swi2c_stop();
}

//连续读指定器件的指定位置中的多个字节
void swi2c_device_read_bytes(uint8_t device_addr,uint8_t reg_addr,uint8_t *buf,uint8_t len)
{
	swi2c_start();
	swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
	swi2c_wait_ack();//等待应答

	swi2c_write_byte(reg_addr);//写位置
	swi2c_wait_ack();

	swi2c_start();
	swi2c_write_byte((device_addr) | 0x01);//读写位改为读
	swi2c_wait_ack();

	while(len > 1)
	{
		*buf++ = swi2c_read_byte();
		swi2c_ack();
		len--;
	}
	*buf = swi2c_read_byte();//循环体结束指针已经指向最后一个字节存放位置
	swi2c_no_ack();//读一个字节结束
	swi2c_stop();	
}

//连续写指定器件的指定位置中的多个字节
void swi2c_device_write_bytes(uint8_t device_addr,uint8_t reg_addr,uint8_t *buf,uint8_t len)
{
	while(len > 0)
	{
		swi2c_start();
		swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
		swi2c_wait_ack();//等待应答

		swi2c_write_byte(reg_addr++);//写位置
		swi2c_wait_ack();

		swi2c_write_byte(*buf++);//写数据
		swi2c_wait_ack();

		swi2c_stop();//发送完结束信号后,器件才会把数据进行擦写操作,搬运到非易失区,这段时间器件不再响应主机操作
		delay_1ms(10);//eeprom连续写时必须加,否则下个字节写入失败
		len--;
	}
}

void swi2c_device_write_data(uint8_t device_addr,uint8_t *reg_addr,
                                    uint16_t reg_len,uint8_t *buf,uint8_t len)
{
    swi2c_start();
	swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
	swi2c_wait_ack();//等待应答

	while(reg_len)
    {
	    swi2c_write_byte(*reg_addr++);//写位置
	    swi2c_wait_ack();
        reg_len--;
    }

    while(len)
    {
	    swi2c_write_byte(*buf++);//写位置
	    swi2c_wait_ack();
        len--;
    }
    swi2c_stop();
}

void swi2c_device_read_data(uint8_t device_addr,uint8_t *reg_addr,
                                    uint16_t reg_len,uint8_t *buf,uint8_t len)
{
	swi2c_start();
	swi2c_write_byte(device_addr);//写地址,7位地址左移,低位补0
	swi2c_wait_ack();//等待应答

    while (reg_len/* condition */)
    {
        /* code */
        swi2c_write_byte(*reg_addr++);//写位置
        swi2c_wait_ack();
        reg_len--;
    }
    
	swi2c_start();
	swi2c_write_byte((device_addr) | 0x01);//读写位改为读
	swi2c_wait_ack();

	while(len > 1)
	{
		*buf++ = swi2c_read_byte();
		swi2c_ack();
		len--;
	}
	*buf = swi2c_read_byte();//循环体结束指针已经指向最后一个字节存放位置
	swi2c_no_ack();//读一个字节结束
	swi2c_stop();	
}

.h文件

void swi2c_config(void);
void swi2c_search_device_addr(void);

//针对eeprom使用
void swi2c_device_write_bytes(uint8_t device_addr,uint8_t reg_addr,uint8_t *buf,uint8_t len);
void swi2c_device_read_bytes(uint8_t device_addr,uint8_t reg_addr,uint8_t *buf,uint8_t len);

void swi2c_device_read_data(uint8_t device_addr,uint8_t *reg_addr,
                                    uint16_t reg_len,uint8_t *buf,uint8_t len);
void swi2c_device_write_data(uint8_t device_addr,uint8_t *reg_addr,
                                    uint16_t reg_len,uint8_t *buf,uint8_t len);

main.c 调用

    swi2c_config();
    swi2c_search_device_addr();

    unsigned char data[7] = {0x4B,0x4D,0x3E,0x4C,0x5A,0x88,0xEB};

    swi2c_device_write_data(0xA0,0,1,data,7);
    delay_1ms(200);

    unsigned char r_data[7];
    // swi2c_device_read_bytes(0xA0,0,r_data,7);
    // print_register_value(r_data,7);

    swi2c_device_read_data(0xA0,0,1,r_data,7);
    print_register_value(r_data,7);

    swi2c_device_read_data(0x20,0,1,r_data,1);
    print_register_value(r_data,1);

2、swi2c_search_device_addr会打印出挂在总线上的设备地址。如图:
在这里插入图片描述
3、eeprom里写入几个数据,然后读出来
在这里插入图片描述
在这里插入图片描述
数据一致。
4、在这里插入图片描述

5、代码路径:https://gitee.com/xiaoguo-tec_0/gd32-iap-code.git

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值