基础认识
模拟IIC是模拟IIC通信时序,一些单片机有硬件IIC接口,如果没有硬件IIC可以通过普通GPIO模拟得到,这里将介绍如何实现模拟IIC
数据线:SDA
时钟线:SCL
注意:
1.只允许有一个主设备,总线上可以挂接多个从设备,
2.总线连线一般不超过2米
3.两线(SDA,SCL)的总线连接,两条总线都需要1~10K的上拉电阻
3.每个器件地址唯一(7位地址,最低位0为发送,1为读取),最多127个器件地址,每次通信之前需要先发送地址
起始/停止信号
起始信号:
当时钟线和数据线都为高电平时,IIC总线上的所有从设备都处于空闲状态。当时钟线和数据线都为高电平时,数据线从高电平到低电平的跳动,被定义为起始信号。
1 //IIC起始信号,SDA下降沿 2 void iic_start() 3 { 4 SDA_OUT_Mode(); //设置SDA为输出 5 SDA_Control(1); 6 SCL_Control(1); 7 delay_us(4); 8 SDA_Control(0);//下降沿 9 delay_us(4); 10 SCL_Control(0);//准备发送数据 11 }
停止信号:
当时钟线为高电平,数据线为低电平时,数据线从低到高的跳动,被定义为停止信号。
1 //IIC停止信号,SDA上升沿 2 void iic_stop() 3 { 4 SDA_OUT_Mode();//设置SDA为输出 5 SCL_Control(0); 6 SDA_Control(0); 7 delay_us(4); 8 SCL_Control(1); 9 delay_us(4); 10 SDA_Control(1);//上升沿 11 }
主机收发一个字节
主机发送数据信号:
时钟线高电平和数据线为低电平时,当时钟线拉低之后,IIC从设备会收到一个数据0;
时钟线高电平和数据线为高电平时,当时钟线拉低之后,IIC从设备会收到一个数据1。
编程思路:因为时钟线为高电平时,数据线是不能动作的,因为有可能会无触发为起始或者停止信号,所以必须先把时钟线拉低后再去改变数据线的电平,然后数据线电平不变的情况下,拉高时钟线然后再拉低时钟线,以发送数据0或1。
1 //IIC发送一个字节 2 void iic_write_byte(u8 c) 3 { 4 u8 i; 5 SDA_OUT_Mode();//设置SDA为输出 6 SCL_Control(0);//拉低时钟开始数据传输 7 for(i=0;i<8;i++) 8 { 9 SDA_Control((c&0x80)>>7);//数据变化 10 c<<=1; 11 delay_us(2); 12 SCL_Control(1);//时钟线产生下降沿 13 delay_us(2); 14 SCL_Control(0); 15 delay_us(2); 16 } 17 }
应答信号的获取:
当主设备发送一个字节完成后从设备会产生一个应答信号,读取是否有应答信号可以判断出主设备是否发送发出数据完成,并被从设备接收,但此判断非必须的,编程可以不考虑
1 //等待应答信号到来 2 //返回值:1,接收应答失败 3 // 0,接收应答成功 4 u8 iic_wait_ack(void) 5 { 6 u8 outTime=0; 7 SDA_OUT_Mode();//设置SDA为输出 8 SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化 9 SCL_Control(1);delay_us(1); 10 SDA_IN_Mode(); //设置SDA为输入 11 while(SDA_Read()) 12 { 13 outTime++; 14 if(outTime>250) 15 { 16 iic_stop(); 17 return 1; 18 } 19 } 20 SCL_Control(0);//时钟输出0 21 return 0; 22 }
主机接收数据信号:
1 //产生ACK应答 2 void iic_ack(void) 3 { 4 SCL_Control(0); 5 SDA_OUT_Mode(); 6 SDA_Control(0); 7 delay_us(2); 8 SCL_Control(1); 9 delay_us(2); 10 SCL_Control(0); 11 } 12 //不产生ACK应答 13 void iic_nack(void) 14 { 15 SCL_Control(0); 16 SDA_OUT_Mode(); 17 SDA_Control(1); 18 delay_us(2); 19 SCL_Control(1); 20 delay_us(2); 21 SCL_Control(0); 22 } 23 24 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK 25 u8 iic_read_byte(unsigned char ack) 26 { 27 unsigned char i,receive=0; 28 SCL_Control(0);//拉低时钟开始数据传输 29 SDA_OUT_Mode();//设置SDA为输出 30 SDA_Control(1); //此条后SDA电平已经无变化 31 SDA_IN_Mode(); //设置SDA为输入 32 for(i=0;i<8;i++ ) 33 { 34 SCL_Control(1);//锁定数据,让数据不变化 35 receive<<=1; 36 if(SDA_Read())receive++; //读取数据 37 delay_us(1); 38 SCL_Control(0);//释放锁定,开始下一个数据检测 39 delay_us(2); 40 } 41 /* 42 if (!ack) iic_nack();//发送nACK 43 else iic_ack(); //发送ACK 44 */ 45 return receive; 46 }
根据地址收发一个数据
器件地址(Device Addr):7位,最低位为0表示写数据,最低位为1表示读数据
数据地址(Register Addr):数据保存的地址
写:
读:
1 //根据地址写数据 2 void IIC_Write(u8 addr,uint8_t data) 3 { 4 iic_start(); //起始信号 5 iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写 6 iic_wait_ack(); //等待应答 7 iic_write_byte(addr); //发送数据地址 8 iic_wait_ack(); 9 iic_write_byte(data);//发送数据 10 iic_wait_ack(); 11 iic_stop();//产生一个停止条件 12 } 13 14 //根据地址读取数据 15 u8 IIC_Read(u8 addr) //读寄存器或读数据 16 { 17 u8 data; 18 iic_start();//起始信号 19 iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写 20 iic_wait_ack();//等待应答 21 iic_write_byte(addr); //发送数据地址 22 iic_wait_ack(); 23 iic_start(); 24 iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读 25 iic_wait_ack(); 26 data=iic_read_byte(0);//读取一个字节 27 iic_stop();//产生一个停止条件 28 return data; 29 }
工程整体
STM8L151K4T6单片机测试工程参考:
1 #include "stm8l15x.h" 2 #include "TCA8418.h" 3 4 #define I2C_SLAVE_ADDRESS7 0x68 5 6 //延时函数,大致延时, 7 //如果需要标准延时,请使用定时器 8 void delay_us(u8 n) 9 { 10 unsigned int x , y; 11 for(x = n; x > 0; x--); 12 for(y =30; y > 0 ; y--); 13 } 14 15 /* 16 SCL PC1 17 SDA PC0 18 */ 19 20 #define SCL_PORT GPIOC 21 #define SCL_PIN GPIO_Pin_1 22 23 #define SDA_PORT GPIOC 24 #define SDA_PIN GPIO_Pin_0 25 26 //SCL输出电平 27 void SCL_Control(u8 c){ 28 if(c==0) GPIO_WriteBit(SCL_PORT , SCL_PIN ,RESET); //低电平 29 else GPIO_WriteBit(SCL_PORT , SCL_PIN ,SET); //高电平 30 } 31 //SDA输出电平 32 void SDA_Control(u8 c){ 33 if(c==0) GPIO_WriteBit(SDA_PORT , SDA_PIN ,RESET); //低电平 34 else GPIO_WriteBit(SDA_PORT , SDA_PIN ,SET); //高电平 35 } 36 //读取SDA的电平 37 u8 SDA_Read(){ 38 if(GPIO_ReadInputDataBit(SDA_PORT , SDA_PIN)== 0) return 0; 39 return 1; 40 } 41 //设置SDA为输入模式 42 void SDA_IN_Mode(){ 43 GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_In_PU_No_IT);//输入 44 } 45 //设置SDA为输出模式 46 void SDA_OUT_Mode(){ 47 GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出 48 } 49 50 //IIC初始化 51 void IIC_Init() 52 { 53 GPIO_Init(SCL_PORT , SCL_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出 54 SDA_OUT_Mode(); 55 SDA_Control(1); 56 SCL_Control(1); 57 } 58 59 60 //IIC起始信号,SDA下降沿 61 void iic_start() 62 { 63 SDA_OUT_Mode(); //设置SDA为输出 64 SDA_Control(1); 65 SCL_Control(1); 66 delay_us(4); 67 SDA_Control(0);//下降沿 68 delay_us(4); 69 SCL_Control(0);//准备发送数据 70 } 71 72 //IIC停止信号,SDA上升沿 73 void iic_stop() 74 { 75 SDA_OUT_Mode();//设置SDA为输出 76 SCL_Control(0); 77 SDA_Control(0); 78 delay_us(4); 79 SCL_Control(1); 80 delay_us(4); 81 SDA_Control(1);//上升沿 82 } 83 84 //等待应答信号到来 85 //返回值:1,接收应答失败 86 // 0,接收应答成功 87 u8 iic_wait_ack(void) 88 { 89 u8 outTime=0; 90 SDA_OUT_Mode();//设置SDA为输出 91 SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化 92 SCL_Control(1);delay_us(1); 93 SDA_IN_Mode(); //设置SDA为输入 94 while(SDA_Read()) 95 { 96 outTime++; 97 if(outTime>250) 98 { 99 iic_stop(); 100 return 1; 101 } 102 } 103 SCL_Control(0);//时钟输出0 104 return 0; 105 } 106 //IIC发送一个字节 107 void iic_write_byte(u8 c) 108 { 109 u8 i; 110 SDA_OUT_Mode();//设置SDA为输出 111 SCL_Control(0);//拉低时钟开始数据传输 112 for(i=0;i<8;i++) 113 { 114 SDA_Control((c&0x80)>>7);//数据变化 115 c<<=1; 116 delay_us(2); 117 SCL_Control(1);//时钟线产生下降沿 118 delay_us(2); 119 SCL_Control(0); 120 delay_us(2); 121 } 122 } 123 124 //产生ACK应答 125 void iic_ack(void) 126 { 127 SCL_Control(0); 128 SDA_OUT_Mode(); 129 SDA_Control(0); 130 delay_us(2); 131 SCL_Control(1); 132 delay_us(2); 133 SCL_Control(0); 134 } 135 //不产生ACK应答 136 void iic_nack(void) 137 { 138 SCL_Control(0); 139 SDA_OUT_Mode(); 140 SDA_Control(1); 141 delay_us(2); 142 SCL_Control(1); 143 delay_us(2); 144 SCL_Control(0); 145 } 146 147 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK 148 u8 iic_read_byte(unsigned char ack) 149 { 150 unsigned char i,receive=0; 151 SCL_Control(0);//拉低时钟开始数据传输 152 SDA_OUT_Mode();//设置SDA为输出 153 SDA_Control(1); //此条后SDA电平已经无变化 154 SDA_IN_Mode(); //设置SDA为输入 155 for(i=0;i<8;i++ ) 156 { 157 SCL_Control(1);//锁定数据,让数据不变化 158 receive<<=1; 159 if(SDA_Read())receive++; //读取数据 160 delay_us(1); 161 SCL_Control(0);//释放锁定,开始下一个数据检测 162 delay_us(2); 163 } 164 165 if (!ack) iic_nack();//发送nACK 166 else iic_ack(); //发送ACK 167 168 return receive; 169 } 170 171 //根据地址写数据 172 void IIC_Write(u8 addr,u8 data) 173 { 174 iic_start(); //起始信号 175 iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写 176 iic_wait_ack(); //等待应答 177 iic_write_byte(addr); //发送数据地址 178 iic_wait_ack(); 179 iic_write_byte(data);//发送数据 180 iic_wait_ack(); 181 iic_stop();//产生一个停止条件 182 } 183 184 185 //根据地址读取数据 186 void IIC_Read(u8 addr,u8 *datax) //读寄存器或读数据 187 { 188 iic_start();//起始信号 189 iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写 190 iic_wait_ack();//等待应答 191 iic_write_byte(addr); //发送数据地址 192 iic_wait_ack(); 193 iic_start(); 194 iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读 195 iic_wait_ack(); 196 *datax =iic_read_byte(0);//读取一个字节 197 iic_stop();//产生一个停止条件 198 } 199 u8 R_Value=0; 200 void Test_Check(){ 201 IIC_Init();//IIC初始化 202 //写读同一个地址,可以判断IIC是否成功 203 IIC_Write(0x01,0xB2);//写数据到0x01 204 IIC_Read(0x01,&R_Value);//将0x01的数据读取 205 }
https://blog.csdn.net/return_oops/article/details/80965437
https://www.bilibili.com/video/av73030246