32单片机利用GPIO模拟IIC时序

使用的是GD32f470单片机,由于每个单片机的主频不同,使用时需要利用逻辑分析仪查看波形,看是否会出现开始,结束,写数据,读数据,响应,不响应等信号。

正常的写时序

S:start开始,W:7位地址+W    A:ack响应   P:stop停止

正常的读时序

S、Sr:start开始,AW、AR都是设备地址,N:nack 不响应
特别注意的是:

  • 写时序:最后是自己主动发出停止信号,而读时序最后是从机不响应才结束的,不需要发停止信号
  • 如果代码执行后,逻辑分析仪不出现上面的时序,则需要调整delay_ns延时的时间
  • 程序中delay_1ns(int x)是延时函数,表示延时x ns 需要自行实现
头文件:
#ifndef __SOFT_I2C_H__
#define __SOFT_I2C_H__

#include "gd32f4xx.h"
#include "systick.h"

#define SCL_PORT_RCU	RCU_GPIOB
#define SCL_PORT			GPIOB
#define SCL_PIN				GPIO_PIN_6

#define SDA_PORT_RCU	RCU_GPIOB
#define SDA_PORT			GPIOB
#define SDA_PIN				GPIO_PIN_7

#define SDA(bit) gpio_bit_write(SDA_PORT, SDA_PIN, bit ? SET : RESET)
#define SCL(bit) gpio_bit_write(SCL_PORT, SCL_PIN, bit ? SET : RESET)

#define SDA_OUT() gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SDA_PIN)
#define SDA_IN()  gpio_mode_set(SDA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SDA_PIN)
//#define SDA_OUT() 0
//#define SDA_IN()  0  //逻辑分析仪测试用的 
#define SDA_STATE() gpio_input_bit_get(SDA_PORT, SDA_PIN)
void SoftI2C0_init();
// 写数据流程
uint8_t SoftI2C0_write(uint8_t address, uint8_t reg, uint8_t* data, uint32_t len);
// 读数据流程
uint8_t SoftI2C0_read(uint8_t address, uint8_t reg, uint8_t* data, uint32_t len);

#endif
C文件
#include "Soft_I2C0.h"


static void start();//开始信号
static void stop();//结束信号
static void send(uint8_t data);//发送数据信号
static uint8_t wait_ack();//等待从设备响应

static uint8_t recv();// 接收数据,从设备发送数据,主设备去读取
static void send_ack();// 主设备发送响应
static void send_nack();// 主设备发送空响应


void SoftI2C0_init() {
    // 软实现:cpu模拟出来
    // 硬实现:通过电路实现
    /**************** GPIO初始化 *****************/
    // PB6:SCL ,SCL一直是由主设备控制,从设备重来都没有控制
    // 内部芯片电路上拉
    rcu_periph_clock_enable(SCL_PORT_RCU);
    gpio_mode_set(SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SCL_PIN);
    gpio_output_options_set(SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_MAX, SCL_PIN);
    // PB7:SDA
    rcu_periph_clock_enable(SDA_PORT_RCU);
    gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SDA_PIN);
    gpio_output_options_set(SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_MAX, SDA_PIN);

    // I2C初始化:硬实现
    // 软实现:cpu模拟出来

}

// 写数据流程
uint8_t SoftI2C0_write(uint8_t address, uint8_t reg, uint8_t* data, uint32_t len) {
    // 地址规定:address
    // 0101011R/W
    // address: 00101011 ****
    // address: 0101011W	0
    // address: 0101011R	1
    uint8_t addr = address << 1;

    // 开始
    start();
    // 发送设备写的地址。读和写地址,1byte,b0是1-读,0-写
    send(addr);
    // 等待响应
    if(wait_ack()) return 1; // 发送设备地址失败

    // 发送寄存器地址
    send(reg);
    // 等待响应
    if(wait_ack()) return 2; // 发送寄存器地址失败

    // n个数据,n个byte
    uint32_t i;
    for(i = 0; i < len; i++) {
        // 发送数据
        send(data[i]);
        // 等待响应
        if(wait_ack()) return 3; // 发送数据失败
    }
    // 停止
    stop();
    // 成功
    return 0;
}

uint8_t SoftI2C0_read(uint8_t address, uint8_t reg, uint8_t* data, uint32_t len) {
		uint8_t waddr = address << 1; 				 // write 0
		uint8_t raddr = (address << 1) | 0x01; // read  1
		
		// 开始
		start();
		
		// 发送设备地址(写的地址)
		send(waddr);
		// 等待响应
    if(wait_ack()) return 1; // 发送设备地址失败
	
		// 发送寄存器地址
		send(reg);
		// 等待响应
    if(wait_ack()) return 2; // 发送寄存器地址失败
	
		// 开启
		start();
		
		// 发送设备地址(读的地址)
		send(raddr);
		// 等待响应
    if(wait_ack()) return 3; // 发送设备地址失败
		
		// 接收数据
		uint32_t i;
    for(i = 0; i < len; i++) {
        // 接收数据
				uint8_t d = recv();
        data[i] = d;
			
				// 如果是最后一条数据的时候,我们要发送NACK
				if(i == len - 1) {
					send_nack();
				} else {
					// 发送响应
					send_ack();
				}
    }
		
		// 停止
		stop();
		return 0;
}

static void start() {
    SDA_OUT();// 表示,现在是主设备控制SDA线,现在的模式是输出模式
    // 100k 400k
    // SDA高电平,持续输出
    SDA(1);
    delay_1us(5);

    // SCL高电平,持续输出
    SCL(1);
    delay_1us(5);

    // SDA低电平,持续输出
    SDA(0);
    delay_1us(5);

    // SCL低电平,持续输出
    SCL(0);
    delay_1us(5);
}
static void stop() {
    SDA_OUT();

    SDA(0);
    SCL(0);

    SCL(1);
    delay_1us(5);

    SDA(1);
    delay_1us(5);
}
static void send(uint8_t data) {
    SDA_OUT();
    // byte: data
    // 10101010:高位
    // 0: & 1000 0000 = 1000 0000 > 0; 10101010 << 1
    //  01010100
    // 1: & 1000 0000 = 0 ; 01010100 << 1
    // 1010 1000
    // 2: & 1000 0000

    uint8_t i;
    for(i = 0; i < 8; i++) {
        // 发送1个bit
        // 1 0
        if(data & 0x80) {
            SDA(1);
        } else {
            SDA(0);
        }

        SCL(1);
        delay_1us(5);
        SCL(0);
        delay_1us(5);

        data <<= 1;
    }
}
static uint8_t wait_ack() {
    uint8_t retry = 3;
    SDA_OUT();// 主动权在主设备手上

    SDA(1);
    SCL(0);
    delay_1us(5);

    SCL(1);
    //从设备控制SDA线,主设备需要读取状态
    SDA_IN();
    delay_1us(5);

    do {
        // 从设备将SDA由高变为低了
        // 主设备需要读
        if(SDA_STATE() == 0) {
            // 响应了,成功
						SCL(0);
						SDA_OUT();
						SDA(1);
            return 0;//表示成功
        } else {
					 delay_1us(5);
				}
    } while(retry --);

    if(SDA_STATE() == 0) {
        return 0;
    } else {
				// 没响应,失败,
				SCL(0);
				stop();
        return 1;
    }
}


static uint8_t recv() {
					// 	0000 0000 
				// i=0; state = 1
				//  1000 0000 
				// i=1; state = 1
				//  1100 0000 
				// i=2; state = 0
				//  1100 0000 
			
//				// SDA高低电平
//				if(SDA_STATE()) {
//						// 高电平
//						// i = 0;  << 8
//						// i = 1;  << 7
//					  // i = 2;  << 6
//						data |= 1 << (8 - i);
//				} else {
//						// 低电平
//						data &= ~(1 << (8 - i));
//				}
					// 0000 0000 
					// i=0; <<=1    0000 0000 
					// |= state 1;  0000 0001
					
					// i=1; <<=1    000 00010
					// |= state 1;  000 00011
					
					// i=2; <<=1    00 000110
					// |= state 1;  00 000111

					// i=3; <<=1    0 0001110
					// |= state 1;  0 0001111
		SDA_IN();
		// 接收数据,从设备发送数据,主设备去读取
		uint8_t data = 0;
		// 8bit ->byte
		uint8_t i;
		for(i = 0; i < 8; i++) {
				// 单个bit的操作
				SCL(0);
				delay_1us(5);
				SCL(1);
				delay_1us(5);
					data <<= 1;
					data |= SDA_STATE();
			delay_1us(5);
		}
		SCL(0);
		//delay_1us(5);
		
		return data;
}
static void send_ack() {
		SDA_OUT();
		// 主设备发送响应
		SDA(0);
		SCL(0);
		delay_1us(5);
		SDA(0);
		SCL(1);
		delay_1us(5);
		SDA(1);
		SCL(0);
	
}
static void send_nack() {
		SDA_OUT();
		// 主设备发送响应
		SDA(0);
		SCL(0);
		delay_1us(5);
		SDA(1);
		SCL(1);
		delay_1us(5);
		SDA(1);
		SCL(0);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驱蚊儿童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值