IIC
一、用于连接CPU和外围电路的总线,一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。I2C总线上允许连接多个微处理器以及各种外围设备,为了进行通讯,每个接到I2C总线的设备都有一个唯一的地址。
二、信号。IIC在每次传输完一字节后需要应答信号。
- 在进行通信时,主控要告诉外设开始和结束,分别对应开始和结束信号。
- IIC在每次传输完一字节后需要应答信号。
- 在驱动大部分外设时,传输一个字节数据需要SCL==1。此时SDA上的数据认为是可信的。这也就是说如果对于信号没有特殊的要求,这部分不需要改写。这里直接用的正点原子的,就是初始化和配置端口需要自己改。
#include "myiic.h" #include "delay.h" // //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK STM32F407开发板 //IIC 驱动代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //创建日期:2014/5/6 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2014-2024 //All rights reserved // //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟 //GPIOB8,B9初始化设置 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;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 IIC_SCL=1; IIC_SDA=1; } //产生IIC起始信号 void IIC_Start(void) { SDA_OUT(); //sda线输出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop(void) { SDA_OUT();//sda线输出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号 delay_us(4); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//时钟输出0 return 0; } //产生ACK应答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不产生ACK应答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }
三、我们有了驱动的信号,就可以做读或者写数据了。这是我们就要看好数据手册了。主要是他需要的地址信息(每个外设唯一)和信号指令(读或者写)。
- 先来看24c02的首地址,A2/1/0都是地址位。 当24c02的三个A管脚都置零时,如果写数据则首地址位是1010 0000,如果读数据也就是1010 0001。
- 在看fdc2214的首地址。他是这么说的“当ADDR pin设置为低时,FDC I2C地址为0x2A;当ADDR pin设置为高时,FDC I2C地址为0x2B”。
- 然后我们看他们给我们读取或写入数据的规则。拿24c02读数据来说: 他说我们需要先写一个起始条件,然后器件地址接应答,在进入读模式读取8位数据,最后停止。
//在AT24CXX指定地址读出一个数据 //ReadAddr:开始读数的地址 //返回值 :读到的数据 u8 AT24CXX_ReadOneByte(u16 ReadAddr) { u8 temp=0; IIC_Start(); if(EE_TYPE>AT24C16) { IIC_Send_Byte(0XA0); //发送写命令 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr>>8);//发送高地址 }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr%256); //发送低地址 IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0XA1); //进入接收模式 IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop();//产生一个停止条件 return temp; }
- 在看fdc2214 我们需要写入器件地址和寄存器地址,在进行数据读取,最后无应答和停止信号,只不过这个寄存器是16位的。我们需要读两次。其实和上面24c02的代码是很像的。
u16 FDC_Read(u8 reg) //核心代码 //不需修改 { u16 res; FDC_IIC_Start(); FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|0);//发送器件地址+写命令 FDC_IIC_Wait_Ack(); //等待应答 FDC_IIC_Send_Byte(reg); //写寄存器地址 FDC_IIC_Wait_Ack(); //等待应答 FDC_IIC_Start(); FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|1);//发送器件地址+读命令 FDC_IIC_Wait_Ack(); //等待应答 res=FDC_IIC_Read_Byte(1)<<8;//读取数据,发送ACK res|=FDC_IIC_Read_Byte(0);//读取数据,发送nACK FDC_IIC_Stop(); //产生一个停止条件 return res; }
结束了。