软件模拟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