IIC总线:I2C 总线是一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
IIC总线定义:时钟线:sck 数据线:sda
软件模拟:通过软件控制普通 GPIO 引脚高低点评切换,来模拟总线时序,从而达到与总线协议一样的效果。
缺点:与使用硬件 IIC 相比,会占用更多的 CPU 时间。
实验目的:
本实现通过软件模拟 IIC 时序,编写 IIC 发送接收函数。
硬件电路:
本实验的蜂鸣器为有源蜂鸣器
PB8 --> IIC_SCL
PB9 --> IIC_SDA
主要库函数调用:
#define
sda_in()
{GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}
#define sda_out()
{GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;}
#define iic_sda_out(x) PBout(9)=x
#define iic_scl_out(x) PBout(8)=x
#define iix_read_sda() PBin(9)
代码:
//iic.c
#include "iic.h"
#include "base.h"
#include "delay.h"
//***********************************************************************
//移植修改部分
#define sda_in()
{GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}
#define sda_out()
{GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;}
#define iic_sda_out(x) PBout(9)=x
#define iic_scl_out(x) PBout(8)=x
#define iix_read_sda() PBin(9)
static void iic_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,
ENABLE);
GPIO_InitStructure.GPIO_Pin
= GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType
= GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed
= GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd
= GPIO_PuPd_UP;
GPIO_Init(GPIOB,
&GPIO_InitStructure);
}
static void iic_gpio_deinit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin
= GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType
= GPIO_OType_OD;
GPIO_InitStructure.GPIO_Speed
= GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd
= GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,
&GPIO_InitStructure);
}
//***********************************************************************
void iic_init(void)
{
iic_gpio_init();
iic_scl_out(1);
iic_sda_out(1);
}
void iic_deinit(void)
{
iic_gpio_deinit();
iic_scl_out(1);
iic_sda_out(1);
}
void iic_start(void)
{
sda_out();
iic_sda_out(1);
iic_scl_out(1);
delay_us(4);
iic_sda_out(0);
delay_us(4);
iic_scl_out(0);
}
void iic_stop(void)
{
sda_out();
iic_scl_out(0);
iic_sda_out(0);
delay_us(4);
iic_scl_out(1);
iic_sda_out(1);
delay_us(4);
}
uint8_t
iic_wait_ack(void)
{
uint8_t
ucerrtime = 0;
sda_in();
iic_sda_out(1);
delay_us(1);
iic_scl_out(1);
delay_us(1);
while(iix_read_sda())
{
ucerrtime++;
if(ucerrtime > 250)
{
iic_stop();
return 1;
}
}
iic_scl_out(0);
return 0;
}
void iic_ack(void)
{
iic_scl_out(0);
sda_out();
iic_sda_out(0);
delay_us(2);
iic_scl_out(1);
delay_us(2);
iic_scl_out(0);
}
void iic_nack(void)
{
iic_scl_out(0);
sda_out();
iic_sda_out(1);
delay_us(2);
iic_scl_out(1);
delay_us(2);
iic_scl_out(0);
}
void iic_send_byte(uint8_t
txd)
{
uint8_t t;
sda_out();
iic_scl_out(0);
for(t = 0; t < 8; t++)
{
iic_sda_out((txd
& 0x80) >> 7);
txd <<= 1;
delay_us(2);
iic_scl_out(1);
delay_us(2);
iic_scl_out(0);
delay_us(2);
}
}
uint8_t
iic_read_byte(unsigned char ack)
{
unsigned char i, receive = 0;
sda_in();
for(i = 0; i < 8; i++ )
{
iic_scl_out(0);
delay_us(2);
iic_scl_out(1);
receive <<= 1;
if(iix_read_sda())
receive++;
delay_us(1);
}
if (!ack)
iic_nack();
else
iic_ack();
return
receive;
}
//iic.h
#ifndef _IIC_H
#define _IIC_H
#include "stdint.h"
void iic_init(void);
void iic_deinit(void);
void iic_start(void);
void iic_stop(void);
void iic_send_byte(uint8_t
txd);
uint8_t
iic_read_byte(unsigned char ack);
uint8_t
iic_wait_ack(void);
void iic_ack(void);
void iic_nack(void);
void iic_write_one_byte(uint8_t
daddr, uint8_t addr, uint8_t data);
uint8_t
iic_read_one_byte(uint8_t daddr, uint8_t addr);
#endif
编译运行:result
如下,编译无报错,无警告。
总结:
理论上gpio操作加上精确的延时,能模拟出任何总线的时序
iic.c将移植部分单独摘取出来,方便未来的移植开发
公众号:
关注公众号 InsertingAll,获取嵌入式学习资源,学习更多嵌入式知识
作者: 嵌入式搬砖, 来源:面包板社区
链接: https://mbb.eet-china.com/blog/uid-me-3894741.html
版权声明:本文为博主原创,未经本人允许,禁止转载!
文章评论(0条评论)
登录后参与讨论