IIC详解,包括原理、过程,最后一步步教你实现IIC
-
- #define I2C_SCL GPIO_Pin_6
- #define I2C_SDA GPIO_Pin_7
- #define GPIO_I2C GPIOB
- #define I2C_SCL_H GPIO_SetBits(GPIO_I2C, I2C_SCL) //把PB6置高
- #define I2C_SCL_L GPIO_ResetBits(GPIO_I2C, I2C_SCL) //把PB6置低
- #define I2C_SDA_H GPIO_ResetBits(GPIO_I2C, I2C_SDA) //把PB7置高
- #define I2C_SCL_L GPIO_ResetBits(GPIO_I2C, I2C_SDA) //把PB7置低
转自:IIC时序详解——http://blog.csdn.net/drivermonkey/article/details/7695547
AT24C02是由ATMEL公司提供的,IIC总线串行EEPROM(electronic eraser programmer read only memory),其容量为2kbit(256B),工作电压在2.7v"5.5v之间,生产工艺是CMOS。
一般数字芯片都在左下角和右上角为GND,VCC。容量的计算方法:AT24Cxx :01"1024
容量 = xx * 1kbit。
写入过程:
AT24C系列EEPROM芯片的固定部分为1010,A2,A1,A0引脚接高低电平后得到确定的3位编码,形成7位编码即为该器件的地址码。
单片机进行写操作时,首先发送该器件的7位地址码和写方向位”0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。传送数据时,单片机首先发送一个字节的被写入存储器的首地址,收到存储器器件的应答后,单片机就逐个发送数据字节,但每发送一个字节后都要等待应答。AT24C系列片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
字节写:
页写:
读入过程:
单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
当前地址读:
随机读:
有序读:
IIC总线模拟时序图:
IIC总线应答时序图:
设备地址:
写周期:
两次写之间要有一个10ms的twR间隔
转自:http://bbs.21ic.com/icview-236765-1-1.html
终于弄出来了,用IO口模拟的,但是最后一位不知道为什么总是为0呢
|
转自:AVR模拟IIC总线-https://download.csdn.net/download/iamfengpeng/1913080
#include <iom128.h> #include <stdio.h> #include "iic.h" #define uchar unsigned char #define uint unsigned int /******************************************************************** 此程序是I2C操作平台(主方式的软件平台)的底层的C子程序,如发送数据 及接收数据,应答位发送,并提供了几个直接面对器件的操作函数,它很方便的 与用户程序连接并扩展..... 注意:函数是采用软件延时的方法产生SCL脉冲,固对高晶振频率要作 一定的修改....(本例是1us机器周期,即晶振频率要小于12MHZ) ********************************************************************/ #define uchar unsigned char /*宏定义*/ #define uint unsigned int #define _Nop() asm("nop") /*定义空指令*/ /* 常,变量定义区 */ /*端口位定义*/ #define SDA PORTA_Bit1 /*模拟I2C数据传送位*/ #define SCL PORTA_Bit0 /*模拟I2C时钟控制位*/ #define SDA_pin DDRA_Bit1 /*SDA输入输出*/ #define SCL_pin DDRA_Bit0 #define SDA_in PINA_Bit1 #define iic_delay() delay_us(1) // 根据系统时钟进行调整 uchar TAB_T[]={ 0x31,0x06,0x10,//秒,分,时 0x13,0x01,0x07,0x08};//日,星期,月,年. void delay_us( uchar us ) { uchar dly; while ( us -- ) { for(dly=0;dly<8;dly++); } } /*状态标志*/ uchar ack; /*应答标志位*/ /******************************************************************* 起动总线函数 函数原型: void Start_I2c(); 功能: 启动I2C总线,即发送I2C起始条件. ********************************************************************/ void Start() { SCL_pin=0; SDA_pin=0; SDA=1; /*发送起始条件的数据信号*/ _Nop(); SCL=1; iic_delay();/*起始条件建立时间大于4.7us,延时*/ SDA_pin=1; SDA=0; /*发送起始信号*/ iic_delay();/* 起始条件锁定时间大于4μs*/ SCL=0; /*钳住I2C总线,准备发送或接收数据 */ SCL_pin=1; iic_delay(); } /******************************************************************* 结束总线函数 函数原型: void Stop_I2c(); 功能: 结束I2C总线,即发送I2C结束条件. ********************************************************************/ void Stop() { SDA=0; /*发送结束条件的数据信号*/ SDA_pin=1; iic_delay(); /*发送结束条件的时钟信号*/ SCL_pin=0; SCL=1; iic_delay(); /*结束条件建立时间大于4μs*/ SDA_pin=0; SDA=1; /*发送I2C总线结束信号*/ iic_delay(); } /******************************************************************* 字节数据传送函数 函数原型: void SendByte(uchar c); 功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对 此状态位进行操作.(不应答或非应答都使ack=0 假) 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。 ********************************************************************/ void WriteByte(uchar c) { uchar BitCnt; for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/ { if((c<<BitCnt)&0x80) { SDA_pin=0; SDA=1; /*判断发送位*/ } else { SDA=0; SDA_pin=1; } _Nop(); SCL_pin=0; SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/ iic_delay(); /*保证时钟高电平周期大于4μs*/ SCL=0; SCL_pin=1; } iic_delay() ; SDA_pin=0; SDA=1; /*8位发送完后释放数据线,准备接收应答位*/ iic_delay() ; SCL_pin=0; SCL=1; iic_delay(); if(SDA_in==1) ack=0; else ack=1; /*判断是否接收到应答信号*/ SCL=0; SCL_pin=1; iic_delay() ; } /******************************************************************* 字节数据传送函数 函数原型: uchar RcvByte(); 功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号), 发完后请用应答函数。 ********************************************************************/ uchar ReadByte() { uchar retc; uchar BitCnt; retc=0; SDA_pin=0; SDA=1; /*置数据线为输入方式*/ for(BitCnt=0;BitCnt<8;BitCnt++) { SCL=0; /*置时钟线为低,准备接收数据位*/ SCL_pin=1; iic_delay(); /*时钟低电平周期大于4.7μs*/ SCL_pin=0; SCL=1; /*置时钟线为高使数据线上数据有效*/ iic_delay(); retc=retc<<1; if(SDA_in==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */ iic_delay(); } SCL=0; SCL_pin=1; iic_delay(); return(retc); } /******************************************************************** 应答子函数 原型: void Ack_I2c(bit a); 功能:主控器进行应答信号,(可以是应答或非应答信号) ********************************************************************/ void Ack_I2c(uchar a) { if(a==0) { SDA_pin=1; SDA=0; /*在此发出应答或非应答信号 */ } else { SDA_pin=0; SDA=1; } iic_delay(); SCL_pin=0; SCL=1; iic_delay(); /*时钟低电平周期大于4μs*/ iic_delay(); SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/ SCL_pin=1; iic_delay(); } void Write8563(uchar ucAddr,uchar ucData) { Start(); WriteByte(0xa2); WriteByte(ucAddr); WriteByte(ucData); Stop(); } uchar Read8563(uchar ucAddr) { uchar ucData; Start(); WriteByte(0xa2); //写器件地址 WriteByte(ucAddr); //写字节地址 Start(); WriteByte(0xa3); //写器件地址,最低为1表示读 ucData=ReadByte(); //写字节地址 Stop(); return ucData; //读数据 } void Init8563(void) { uchar i,ucAddr=0x02; Write8563(0x0d,0x80); i = Read8563(0x0D); Write8563(0x00,0x00); Write8563(0x01,0x11); i = Read8563(0x00); i = Read8563(0x01); for(i=0;i<7;i++) { Write8563(ucAddr,TAB_T[i]); ucAddr++; } } void GetTime(void) { uchar i,ucData1,ucData2,ucAddr=0x02; uchar *pTime=TAB_T; for(i=0;i<7;i++) { pTime[i]=Read8563(ucAddr); ucAddr++; } pTime[0]&=0x7f; //屏蔽无效位 pTime[1]&=0x7f; pTime[2]&=0x3f; pTime[3]&=0x3f; pTime[4]&=0x07; pTime[5]&=0x1f; // for(i=0;i<7;i++) // // { // // ucData1=pTime[i]/16; //BCD码转十六进制 // // ucData2=pTime[i]%16; // // pTime[i]=ucData1*10+ucData2; // // } } /* 完毕 */
转自:http://bbs.21ic.com/icview-236765-1-1.html
终于弄出来了,用IO口模拟的,但是最后一位不知道为什么总是为0呢 #include<avr/io.h> #define uint unsigned int #define uchar unsigned char #define BIT(x) (1<<x) //PORTA0=SCL //PORTA1=SDA void delay1(uint n) { uint i,j; for(i=0;i<n;i++) for(j=0;j<367;j++); } void delay() { asm("NOP"); asm("NOP");asm("NOP"); } void start() //开始信号 { PORTA|=BIT(1); delay(); PORTA|=BIT(0); delay(); asm("NOP");asm("NOP"); PORTA&=~BIT(1); delay(); asm("NOP");asm("NOP"); PORTA&=~BIT(0); delay(); } void stop() //停止 { PORTA&=~BIT(1); delay(); PORTA|=BIT(0); delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP"); PORTA|=BIT(1); delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP"); } void respons() //应答 { //uchar i; PORTA&=~BIT(1); delay(); PORTA|=BIT(0); delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP"); PORTA&=~BIT(0); delay(); } /* void respons_send() //应答 { uchar i; PORTA|=BIT(0); delay(); while(((PORA&BIT(1))==1)&&(i<250))i++; PORTA&=~BIT(0); delay(); } */ void init() { PORTA|=BIT(1); delay(); PORTA|=BIT(0); delay(); } void write_byte(uchar date) { uchar i,temp; for(i=0;i<8;i++) //要传送的数据长度为8位 { if((date<<i)&0x80) PORTA|=BIT(1); //判断发送位 else PORTA&=~BIT(1); delay(); PORTA|=BIT(0); //置时钟线为高,通知被控器开始接收数据位 delay(); delay(); //在此期间取走数据 PORTA&=~BIT(0); delay(); delay(); } PORTA&=~BIT(0); delay(); PORTA|=BIT(1); delay(); } uchar read_byte() { uchar i,k,temp=0; PORTA&=~BIT(0); delay(); PORTA|=BIT(1); //释放数据线SDA delay(); for(i=0;i<8;i++) { PORTA|=BIT(0); delay(); DDRA&=~BIT(1); PORTA|=BIT(1); delay(); temp=(PINA&0x02); k=((k<<1)|temp); // DDRA|=BIT(1); PORTA&=~BIT(0); delay(); } DDRA|=BIT(1); delay(); return k; } void write_add(uchar address,uchar date) { start(); write_byte(0x56); respons(); write_byte(address); respons(); write_byte(date); respons(); stop(); } uchar read_add(uchar address) { uchar date; start(); write_byte(0x57); respons(); write_byte(address); respons(); date=read_byte(); stop(); return date; } main() { DDRA=0xff; DDRB=0xFF; init(); write_add(0x03,0x3f); delay(); write_add(0x00,0x41); delay(1); write_add(0x08,0xab); delay1(10); PORTB=read_add(0x08); delay(); // PORTB=0XFF; delay(); while(1); } |