使用的是GD32f470单片机,由于每个单片机的主频不同,使用时需要利用逻辑分析仪查看波形,看是否会出现开始,结束,写数据,读数据,响应,不响应等信号。
正常的写时序
正常的读时序
特别注意的是:
- 写时序:最后是自己主动发出停止信号,而读时序最后是从机不响应才结束的,不需要发停止信号
- 如果代码执行后,逻辑分析仪不出现上面的时序,则需要调整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);
}