首先I2C是同步半双工的通信方式,需要两条线即可,SCL时钟线,同步时钟由主机产生,SDA数据线用来发送接收数据,任何时候只能一台主机发送数据。
在编程的时候碰到了很多问题,其中一项就是等待从机的应答程序
开始的时候编写如下
void iic_wait_ack() 错误 void iic_wait_ack() 正确的程序
{ {
SCL=1; iic_sda_in();
iic_sda_in(); SCL=1;
delay_us(2); delay_us(2);
SDA=1; SDA=1;
while(SDAIN); while(SDAIN);
SCL=0; SCL=0;
} }
程序运行的时候总是死在红色部分的程序,后来用debug单步调试,慢慢的发现了规律,以下为个人观点,不敢保证一定正确:
1.SDAIN只会在SCL为低电平的时候拉低
2.且在SCL保持高电平的时候,SDAIN比保持低电平
3.在SCL变为高电平之后,SDAIN才会拉高

上图为I2C通信的时序图
其中起始信号:在SCL为高电平时,SDA电平由高变低,且高电平SDA高电平持续时间要大于4.7us,在SCL由高电平变低时,SDA的低电平持续时间要大于4us
终止信号:在SCL为高电平期间,SDA电平由低变高,且高电平持续时间大于4.7us,低电平持续时间大于4us
起始信号和终止信号无论何时都是由主设备产生的
数据帧:在SCL为高电平期间,SDA的电平保持稳定
主向从写数据

期间地址为八位:其中高四位为固定位,bit3—bit1为可变位,最低位为操作位 为0时为写操作,为1时是读操作
地址发送完成之后,等待从器件的应答信号,切记此时要将SDA配置为上拉输入模式

应答信号即为,SCL保持高电平时 SDA一直为低电平。
然后再发送要写入数据的地址,收到应答再写入数据,最后是停止信号
首先先配置IO口,PB10为SCL,PB.11为SDA
#include
void gpio_init()
{
RCC->APB2ENR|=1<<2;
GPIOA->CRH&=0xfffff00f;
GPIOA->CRH|=0x000008b0;
GPIOA->ODR|=3<<9;
RCC->APB2ENR|=1<<3;
GPIOB->CRH&=0xfffff0ff;
GPIOB->CRH|=0x00000300;
GPIOB->ODR|=1<<10;
}
void iic_sda_out()//此时为主器件发送数据
{
RCC->APB2ENR|=1<<3;
GPIOB->CRH&=0xffff0fff;
GPIOB->CRH|=0x00003000;
GPIOB->ODR|=1<<11;
}
void iic_sda_in()//此时为主器件接收数据或等待从器件发送应答信号
{
RCC->APB2ENR|=1<<3;
GPIOB->CRH&=0xffff0fff;
GPIOB->CRH|=0x00008000;
GPIOB->ODR|=1<<11;
}
然后是为头文件iic.h,如下
#define iic_write 0xa0
#define iic_read 0xa1
void iic_start(void);//起始信号
void iic_end(void);//终止信号
void iic_senddata(u8 data);//主器件发送一个字节数据
void iic_master_ack(void);//主机应答信号
void iic_master_nack(void);//主机非应答信号
void iic_wait_ack(void);//等待从机应答信号
void iic_master_write(u8 type,u8 address,u8 data);
u8 iic_master_read(u8 address);
u8 iic_readdata(void);
iic.c文件如下
#include
#include"gpio.h"
#include"delay.h"
#include"iic.h"
#include"sys.h"
void iic_start()
{
iic_sda_out();
SDA=0;
SCL=1;
SDA=1;
delay_us(5);
SDA=0;
delay_us(5);
}
void iic_end(void)
{
iic_sda_out();
SCL=0;
SDA=0;
delay_us(2);
SCL=1;
delay_us(5);
SDA=1;
delay_us(5);
SDA=0;
}
void iic_senddata(u8 data)
{
int i;
iic_sda_out();
for(i=7;i>=0;i--)
{
SCL=0;
SDA=(data>>i);
SCL=1;
delay_us(5);
SCL=0;
}
}
void iic_master_ack()
{
iic_sda_out();
SCL=0;
SDA=0;
SCL=1;
delay_us(5);
SCL=0;
SDA=1;
}
void iic_master_nack()
{
iic_sda_out();
SCL=0;
SDA=1;
SCL=1;
delay_us(5);
SCL=0;
}
void iic_wait_ack()
{
iic_sda_in();
SDA=1;
delay_us(1);
SCL=1;
delay_us(1);
while(SDAIN);
SCL=0;
}
void iic_master_write(u8 type,u8 address,u8 data)
{
iic_start();
iic_senddata(type);
iic_wait_ack();
iic_senddata(address);
iic_wait_ack();
iic_senddata(data);
iic_wait_ack();
iic_end();
delay_ms(10);
}
u8 iic_master_read(u8 address)
{
u8 dcb;
iic_start();
iic_senddata(0xa0);
iic_wait_ack();
iic_senddata(address);
iic_wait_ack();
iic_start();
iic_senddata(0xa1);
iic_wait_ack();
dcb=iic_readdata();
iic_master_nack();
iic_end();
return dcb;
}
u8 iic_readdata()
{
int k;
u8 receive;
iic_sda_in();
for(k=7;k>=0;k--)
{
SCL=0;
delay_us(2);
SCL=1;
receive<<=1;
if(SDAIN==1)
receive|=1<<0;
else
receive&=~(1<<0);
}
return receive;
}
主程序
#include
#include"delay.h"
#include"usart1.h"
#include"sys.h"
#include"gpio.h"
#include"iic.h"
int main()
{
u8 temp;
Stm32_Clock_Init(9);
delay_init(72);
gpio_init();
usart1_init();
iic_master_write(iic_write,0x00,0xdf);
temp=iic_master_read(0x00);
USART1->DR=temp;
while(1);
}