/****************************************************************************** IIC通信原理介绍 I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式(SCL、 SDA)串行总线,用于连接微控制器及其外围设备。SDA(serial data line)表示串行数据线,SCL(serial clock line)为串行时钟线,SDA是双线数据线,既可以接收也可以发送数据。在使用时,SDA和SCL都必须利用外部上拉电阻连接到VCC(当总线空闲时可以保持高电平)。IIC总线快速模式下可以达到400kHZ的传输速率。每一个IIC器件都有一个唯一的可识别的地址。 ST(START):IIC数据传输开始于一个起始信号。起始信号定义为当SCL为高电平 (HIGH)时SDA从高电平(HIGH)跳为低电平(LOW); SAD(Slave Adress):起始信号传输后,下一个Byte前7bit表示从机的地址,第8 位表示发送or接收数 据,若从机接收到前7bit,并与自己的地址比较,若匹配,则从机认为主机选中自己,需要 进行通信; ACK(Acknowledge):主从机接收到数据后会发送一个应答信号; SP(STOP):停止信号定义为当SCL线为高电平时,SDA从低电平(LOW)跳变为高电平(HIGH)。 以上仅仅是针对IIC通信的简单的介绍,更加详细的介绍请参考IIC的datesheet。 *****************************************************************************/ /****************************************************************************** 说明: 1、本文件主要功能是:用普通IO口模拟IIC实现通信功能 2、本文件版权属思飞工作室 3、使用方法:在需要使用IIC的工程中,包含IIC.h和IIC.c文件 4、由于使用普通IO口模拟IIC,所以此处理论上可以用任意IO口来模拟 ******************************************************************************/ #include "IIC.h" #include "delay.h" /************************************************************************** 函数名称:IIC_Init(void) 功能:初始化IIC使用的引脚。此处使用stm32的PB10/PB11来模拟IIC通信 参数:无 返回值 : 无 说明:端口配置需要使用CNFx[1:0],MODEx[1:0]相关位含义列出如下: MODEx[1:0] 00:输入模式(复位后的状态) 01:输出模式,最大速度10MHz 10:输出模式,最大速度2MHz 11:输出模式,最大速度50MHz CNFx[1:0] 在输入模式(MODE[1:0]=00): 00:模拟输入模式 01:浮空输入模式(复位后的状态) 10:上拉/下拉输入模式 11:保留 在输出模式(MODE[1:0]>00): 00:通用推挽输出模式 01:通用开漏输出模式 10:复用功能推挽输出模式 11:复用功能开漏输出模式 ***************************************************************************/ void IIC_Init(void) { RCC->APB2ENR|=1<<3; //先使能外设IO PORTB时钟 GPIOB->CRH&=0XFFFF00FF; //PB10,11 推挽输出, CNFx[1:0],MODEx[1:0],配置0011表示推挽输出 GPIOB->CRH|=0X00003300; GPIOB->ODR|=3<<10; //PB10 11输出高 } /************************************************************************** 函数名称:IIC_Start(void) 功 能:产生IIC起始信号(ST) 参 数:无 返回值 : 无 说明:IIC起始信号定义为当SCL为高电平(HIGH)时,SDA线从高到低的跳变 ***************************************************************************/ void IIC_Start(void) { SDA_OUT(); //SDA线输出 IIC_SDA=1; //SDA为高电平输出 delay_us(1); IIC_SCL=1; //SCL为高电平 delay_us(6); IIC_SDA=0; //START:when CLK is high,DATA change form high to low delay_us(6); IIC_SCL=0; //钳住I2C总线,准备发送或接收数 据,避免在SCL为高电平期间,再次发出起始信号或者停止信号 delay_us(2); } /************************************************************************** 函数名称: IIC_Stop(void) 功 能: 产生IIC停止信号(SP) 参 数:None 返回值 : None 说明 :IIC停止信号定义为当SCL为高电平(HIGH)时,SDA线从低到高的跳变 ***************************************************************************/ void IIC_Stop(void) { SDA_OUT(); //SDA线输出 IIC_SDA=0; //SDA为低电平 delay_us(2); IIC_SCL=1; //SCL为高电平 delay_us(6); IIC_SDA=1; //STOP:when CLK is high DATA change form low to high delay_us(4); } /************************************************************************** 函数名称: IIC_Wait_Ack(void) 功能: 等待应答信号到来 参数:无 返回值 : [1,接收应答失败;0,接收应答成功] 说明:IIC通信时,接收到数据,从机(Slave)会发出ACK信号,一般是一个低电平 信号出现在SDA线上,若主机检测到,则认为此次通信成功 注意:应答信号是在SCL为高电平器件,SDA一直为低电平表示应答 ***************************************************************************/ u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(2); IIC_SCL=1;delay_us(2); while(READ_SDA) //若超过250个时钟周期没有收到应 答信号,认为通信失败,返回1,并结束此次通信 { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0; //时钟输出0 return 0; //若通信成功,则返回0 } /************************************************************************** 函数名称: IIC_Ack() 功 能: 产生应答信号 ,主机从从机读取数据后,向从机发出应答信号 参 数:None 返回值 : None 说明: 主机在接收到从机的数据后,向从机返回一个应答信号,继续通信,在SCL为 高电平器件,SDA一直保持低电平表示应答 ***************************************************************************/ void IIC_Ack(void) { IIC_SCL=0; //SCL初始为低 SDA_OUT(); IIC_SDA=0; //SDA输出低 delay_us(2); IIC_SCL=1; //SCL输出高电平 delay_us(2); IIC_SCL=0; //SCL再输出低电平,保证在SCL为高电平 期间,SDA始终保持高电平,表示应答信号 } /************************************************************************** 函数名称: IIC_NAck() 功能: 不产生应答信号,主机接收到从机的数据后,不发出应答信号 参 数:None 返回值 : None 说明:在IIC中SCL为高电平器件,SDA也保持高电平则表示不应答,NACK ***************************************************************************/ void IIC_NAck(void) { IIC_SCL=0; //SCL输出低电平 SDA_OUT(); IIC_SDA=1; //SDA输出高电平 delay_us(2); IIC_SCL=1; //SCL输出高电平 delay_us(2); IIC_SCL=0; //SCL再次输出低电平,保证SCL为高时, SDA一直为高 } /************************************************************************** 函数名称: IIC_Send_Byte() 功能: 主机发送一个字节数据到从机 参 数:需要发送的数据 返回值 : 无 说明: IIC每次传输的数据位8bit, ***************************************************************************/ void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); //SDA输出 IIC_SCL=0; //拉低时钟开始数据传输 for(t=0;t<8;t++) //循环八次,把txd中的8bit数据通过SDA线输出,但同 时SCL时钟也必须翻转 { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } /************************************************************************** 函数名称: IIC_Read_Byte() 功能: IIC接收一个字节的内容 参数:ack表示是否返回ACK信号,若ack=1,则发送ACK(),否则发送NACK() 返回值 : 返回读出的8bit的数据 ***************************************************************************/ u8 IIC_Read_Byte(unsigned char ack) { u8 i,receive=0; SDA_IN(); //SDA设置为输入 for(i=0;i<8;i++) //根据SDA电平的高低,决定receive的8bit信息 { 第 4 页 IIC.txt IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(2); } IIC_SCL = 0; if (!ack) IIC_NAck(); //发送nACK else IIC_Ack(); //发送ACK return receive; //返回读到的数据 } /************************************************************************** 函数名称:IIC_ReadReg(u8 DeviceAddr,u8ReadAddr) 功能:在指定的器件地址中指定的寄存器地址中读出一个数据 参 数:DeviceAddr:从器件的地址 ReadAddr:寄存器的地址 返回值 : 读到的数据 说明:读取数据流程如下 master(主机):ST SAD+W SUB SR SAD+R NMAK SP slave(从机) : SAK SAK SAK DATA 其中各符号含义如下: ST :起始信号 SAD+W:从器件地址+Write SAK:从机应答信号 SUB:寄存器地址 SR :重新启动 DATA :数据 NMAK :主机无应答 SP :停止信号 **************************************************************************/ u8 IIC_ReadReg(u8 DeviceAddr,u8 ReadAddr) { u8 temp=0; IIC_Start(); //起始信号 IIC_Send_Byte(DeviceAddr); //发送器件地址 IIC_Wait_Ack(); //等待应答 IIC_Send_Byte(ReadAddr); //发送寄存器地址 IIC_Wait_Ack(); //等待应答 IIC_Start(); //重新启动IIC总线 IIC_Send_Byte(DeviceAddr+1); //发送器件地址 IIC_Wait_Ack(); //再次等待应答 temp=IIC_Read_Byte(0); //只读取1byte的数据,无需主机应答 IIC_Stop(); //产生一个停止条件 return temp;
|