AVR单片机TWI读写读写范例讲解
ATMEGA16读写iic(TWI)(24c02)C语言程序

ATMEGA16 读写iic(24c02) C 语言程序测试通过nclude <iom16v.h>#i nclude "I2C.h"#in clude "1602.h"#in clude "delay.h"/*通过AVR往I IC写数据,并通过串口把数据读出显示出来*///===============================================================void UAR_i nit(void) //UART 初始化{ DDRD = 0x02;PORTD = 0x00;UCSRA = 0x02; /* 无倍速*/UCSRB = 0x18; /*允许接收和发送*/U(SRC= 0x06; 1*8位数据,1位停止位,无校验*/UBRRH = 0x00;UBRRL = 12; /*9600*/}//===============================================================void USART_TXD(float data) // 发送采用查询方式{while( !(UCSRA & BIT(UDRE)));UDR=data;while( !(UCSRA & BIT(TXC )));UCSRA|=BIT(TXC);}void mai n(void){un sig ned char i;// LCDinit();uart_i ni t();//TART 初始化SEI(); // 全局中断使能while(1){/*I2C_Write('n',0x00); I2C_Write('c',0x01); I2C_Write('e',0x02);I2C_Write('p',0x03); I2C_Write('u',0x04);*/ i=I2C_Read(0x00); //LCD_write_char(0,0,i); USART_TXD(i); i=I2C_Read(0x01);//LCD_write_data(i); USART_TXD(i); i=I2C_Read(0x02); //LCD_write_data(i); USART_TXD(i);i=I2C_Read(0x03); //LCD_write_data(i); USART_TXD(i); i=I2C_Read(0x04); //LCD_write_data(i); USART_TXD(i);}}/* 上面上主函数部分*/#include <macros.h>#include "delay.h"//I2C 状态定义//MT 主方式传输MR 主方式接受#define START 0x08#define RE_START 0x10#define MT_SLA_ACK 0x18 #define MT_SLA_NOACK 0x20 #define MT_DATA_ACK 0x28 #defineMT_DATA_NOACK 0x30 #define MR_SLA_ACK 0x40 #define MR_SLA_NOACK 0x48 #define MR_DATA_ACK 0x50 #define MR_DATA_NOACK 0x58#define RD_DEVICE_ADDR 0xA1 // 前4位器件固定,后三位看连线,最后1位是读写指令位#define WD_DEVICE_ADDR 0xA0//常用TWI操作(主模式写和读)#define Start() (TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN)) // 启动I2C #define Stop()(TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN)) // 停止I2C #define Wait() {while(!(TWCR&(1<<TWINT)));} // 等待中断发生#define TestAck() (TWSR&0xf8) // 观察返回状态#define SetAck (TWCR|=(1<<TWEA)) // 做出ACK应答#define SetNoAck (TWCR&=~(1<<TWEA)) // 做出Not Ack 应答#define Twi() (TWCR=(1<<TWINT)|(1<<TWEN)) // 启动I2C#define Write8Bit(x) {TWDR=(x);TWCR=(1<<TWINT)|(1<<TWEN);} // 写数据到TWDRunsigned char I2C_Write(unsigned char Wdata,unsigned char RegAddress); unsigned charI2C_Read(unsigned RegAddress);/*********************************************I2C 总线写一个字节返回0: 写成功返回1: 写失败**********************************************/unsigned char I2C_Write(unsigned char Wdata,unsigned char RegAddress){Start(); //I2C 启动Wait();if(TestAck()!=START)return 1; //ACKWrite8Bit(WD_DEVICE_ADDR); // 写I2C 从器件地址和写方式Wait(); if(TestAck()!=MT_SLA_ACK) return 1; //ACKWrite8Bit(RegAddress); // 写器件相应寄存器地址Wait();if(TestAck()!=MT_DATA_ACK)return 1; //ACKWrite8Bit(Wdata); // 写数据到器件相应寄存器Wait();if(TestAck()!=MT_DATA_ACK)return 1; //ACKStop(); //I2C 停止delay_nms(10); // 延时return 0;}/*********************************************I2C 总线读一个字节返回0: 读成功返回1: 读失败**********************************************/unsigned char I2C_Read(unsigned RegAddress){ unsigned char temp;Start();//I2C 启动Wait();if (TestAck()!=START) return 1; //ACKWrite8Bit(WD_DEVICE_ADDR); // 写I2C 从器件地址和写方式Wait(); if (TestAck()!=MT_SLA_ACK) return 1; //ACKWrite8Bit(RegAddress); // 写器件相应寄存器地址Wait();if (TestAck()!=MT_DATA_ACK) return 1;Start(); //I2C 重新启动Wait();if (TestAck()!=RE_START) return 1;Write8Bit(RD_DEVICE_ADDR); // 写I2C 从器件地址和读方式Wait(); if(TestAck()!=MR_SLA_ACK)return 1; //ACKTwi(); // 启动主I2C 读方式Wait(); if(TestAck()!=MR_DATA_NOACK) return 1; //ACKtemp=TWDR;// 读取I2C 接收数据Stop();//l2C 停止return temp;}/*以上是IlC.h头文件部分,需要对照技术文档好好研究*/延时函数编译器:I CC-AVR V6.31A 日期:2005-11-24 20:29:57 目标芯片:M16 时钟:8.0000M Hz作者:arche ng504------------------------------------------------- */#ifndef __delay_h#defi ne __delay_hvoid delay_ nus(un sig ned int n);void delay_ nms(un sig ned int n);void delay_1us(void);void delay_1ms(void);void delay_1us(void) //1us 延时函数{asm( "n op");}void delay_ nus(un sig ned int n) 〃N us 延时函数{un sig ned in t i=0;for (i=0;i <n ;i++)delay_1us();}void delay_1ms(void) //1ms 延时函数{un sig ned in t i;for (i=0;i<1140;i++);}void delay_ nms(un sig ned int n) //N ms 延时函数{un sig ned in t i=0;for (i=0;i <n ;i++)delay_1ms();}#en dif/*以上是delay.h 部分,再加上IIC中自带的iom16v.h和macros.h就可以编译通过*//*注意点:本程序在实验板ATMEGA1上测试通过,在示波器把SCL, SDA信号线有数据,移值到自己电路上可以放心使用,在ATMEGA3上一样使用,本人24C02的A2, A1, A0都是接地,若地址不一样,在程序相应位置改一下就可以,串口上调试单片机的基础,所以它一定要会用*//*本程序调试软件环境是ICC6.31*/。
09-IIC_TWI实验

第9章TWI|IIC注:HJ-2G V3.0版没有集成24C02芯片,如果需要做本实验的同学,请自己购一片24C02芯片,用杜邦线接到开发板上的IO口就可以了。
9.1概述TWI或者IIC是两线的总线。
这原本是前几章的笔记,但是一开始因为觉得IIC太复杂了所以延迟到最后在练习。
其实不是这样的,AVR的IIC硬件,从手册上看是很复杂。
但是只要慢慢的分类再分类就觉得很容易了。
当然一开始确实要下一点功夫,但是原理理解了,程式跑起来后。
你就会觉得,IIC很有趣。
这一篇笔记AT24C02A作为我们的主角。
9.2IIC总线概念IIC的总线是由SDA(串行数据)和SCL(串行时钟)组成,有从机和主机之分。
总线空闲时为高电平,所以SDA和SCL都必须使用上拉电阻将电平拉上高电平。
IIC总线又有起始信号(START),终止信号(STOP),重复起始信号(REPEAT START)。
起始信号:SCL持续高电平的时候,SDA从高电平变成低电平。
终止信号:SCL持续高电平的时候,SDA从低电平变成高电平。
重复起始信号:这个比较难明白,在访问一个设备为了改变访问方向而不写入终止信号,第二次的起始信号称为重复起始信号9.2IIC总线仲裁(寻址)当主机要寻找从机时,会发送寻址,我习惯称为硬件地址。
该地址包如下:最高七位为从机地址,而最低位为硬件地址的行为。
最低位逻辑1为读从机设备,逻辑0为写从机设备。
不同的从机设备都有不同的硬件地址,硬件地址是由硬件厂商设定定的。
如AT24C02A,ATMEL公司的串行EEPROM。
AT24C02A硬件地址的区分HJ-2G的AT24C02A的A0~2编程AT24C02A硬件地址是这样区分的:高四位(BIT4~7)为不可编程,AT24C02A是1010。
低四位(BIT0~3)中的BIT1~3,亦即A0~A2为可编程地址。
HJ-2G将A0~A2引脚连地,所以该AT24C02的硬件地址为1010000x。
ATmega8TWI读写AT24C64

#define TWI_START() (TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN))
#define TWI_RESTART() (TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN))
#define TWI_STATUS() (TWSR&0xf8)
if(TWI_SendData(addr)!=TW_MT_DATA_ACK) return 0;
if(TWI_ReStart()!=TW_REP_START)return 0;
if(TWI_SendData(TWI_SLA_RW|0X01)!=TW_MR_SLA_ACK) return 0;
uint8 TWI_WriteByte(uint16 addr ,uint8 data);
#else
uint8 TWI_WriteNByte(uint8 TWI_SLA_RW, uint16 addr ,uint8 *pdata, uint8 size);
uint8 TWI_WriteByte(uint8 TWI_SLA_RW, uint16 addr ,uint8 data);
#ifndef __TWI_H__
#define __TWI_H__
#define TWI_SLA_RW 0XA0
#define TWI_CHECK 1
#ifdef __TWI_C__
void twi_init(void);
#ifdef TWI_SLA_RW
uint8 TWI_WriteNByte(uint16 addr,uint8 *pdata, uint8 size);
利用Code Vision AVR C中断程序实现AVR单片机的TWI读写

利用Code Vision AVR C中断程序实现AVR单片机的TWI读写在AVR单片机的开发过程中,TWI(Two-Wire Interface)总线通信协议是较为常用的一种方式。
我们可以利用CodeVision AVR C中断程序来实现AVR单片机的TWI读写。
首先,我们需要了解一下TWI通信协议的基本原理。
TWI总线上有两条线路,一条为SCL时钟线,另一条为SDA数据线。
两条线路上都有上拉电阻,数据传输时由主机(如AVR单片机)控制SCL时钟线和SDA数据线的电平变化来进行数据传输。
在Code Vision AVR C中,我们可以利用TWI库函数进行TWI通信的操作。
在进行TWI读写时,我们需要先发送起始信号,然后发送设备地址和读写指令,接着进行数据传输,最后发送停止信号。
这一系列操作可以使用TWI库函数中的相应函数来完成。
在使用中断程序时,我们需要使TWI模块开启中断功能,并定义好中断服务程序。
在TWI传输过程中,当一个操作完成后,TWI模块会触发中断,执行相应的中断服务程序。
在中断服务程序中,我们可以处理TWI传输过程中出现的各种情况,如传输完成、传输错误等。
下面以TWI读取一个EEPROM芯片中的数据为例,说明如何利用Code Vision AVR C中断程序实现TWI读写。
假设EEPROM芯片的I2C地址为0xA0,我们需要读取从地址0x00开始的8个字节的数据。
首先,我们需要在程序的头文件中引用Code Vision AVR C提供的TWI库。
#include <mega328p.h>#include <delay.h>#include <twi.h>接着,定义TWI芯片地址和读写指令。
#define EEPROM_WRITE 0xA0#define EEPROM_READ 0xA1定义存储数据的缓冲区。
unsigned char EEPROM_Buffer[8];在主函数中初始化TWI模块,并开启TWI中断。
基于TWI接口实现AVR单片机主从机通信

基于TWI接口实现A VR单片机主从机通信【摘要】Atmel公司的TWI(Two-wire Serial Interface)接口具有硬件实现简单、软件设计方便、运行可靠和成本低廉的优点。
提出了一种以A VR单片机ATmega8为平台基于TWI接口的主从机通信的实现方法,给出了该系统主从机进行通信的硬件结构和软件设计。
该方法对于简化硬件电路、提高系统可靠性、缩短开发周期、降低成本是可行的。
【关键词】A VR;TWI;主机模式;从机模式Master-slave Communication of A VR Microcontroller Based on TWI Interface ZHOU Yong-gang(The 41st Institute of China Electronics Technology Group Corporation,Qingdao Shandong,266555)【Abstract】The TWI interface of ATMEL Corporation has simple hardware,software design has the advantages of convenient,reliable operation and low cost. This article presents the design of master-slave communication system for A VR microcontroller based on TWI interface on the platform of ATmega8,give the structure of the system’s hardware and software desi gn. The method is feasible to simplify the hardware circuit,improve system reliability,shorten development cycles,reduce costs.【Key words】A VR;TWI;Master mode;Slave mode0 引言A VR ATmega系列单片机片内集成两线串行接口TWI模块。
TWI接口和TWI接口器件使用

TWI接口和TWI接口器件使用A VR单片机的很多型号也具有两线制接口,即TWI接口。
实际上TWI接口时序和常I2总线是兼容的。
我们这本书结合讲的单片机Atmega16就有这种接口。
这种接口的见的C使用也十分广泛。
比如本文会结合介绍的EEPROM A T24C64;MAXIM公司的温度传感器(查出型号);有的A/D转换器;菲利普还有专门的用这种总线的I/O扩展芯片。
TWI电路接线简单,占用I/O,并且可以很多期间共享一个总线,使用比较方便,系统也很简洁。
A VR单片机用硬件实现了这种总线的时序,省去了很多编程工作。
同时支持一条总线多个主设备的通讯。
我们只需要控制相关寄存器就能实现通过TWI传输数据。
很大程度I2的基本知上减少了我们的工作量,从而使代码更简洁,开发更容易。
下面我们会介绍CI2接口的EEPROM的例识,A VR的TWI接口的功能和使用,给出一个用TWI接口读写C子,最后给出适用于A VR-GCC编译器的示例程序。
I2总线的基本知识一、CI2总线的信号线有两条,一条是时钟线SCL,另一条是数据线SDA。
总线连接起来的时候,C需要两个上拉电阻,器件内部这两个信号引脚是集电极开路(或者是漏极开路)的。
这样总线上的器件只要有一个输出低电平总线就会被拉低(实际上就是所谓线与的逻辑),这主要用于总线仲裁。
I2总线上,有几个状态表示特殊的总线信号。
1.在C开始和停止信号时序如下图所示:图上可以看出,在SCL位高电平时SDA的变化将产生总线开始和停止信号。
SDA从高电平跳变到低电平表示开始,从低电平跳变到高电平表示停止。
数据的建立和有效:上图表示在传输数据时,SCL高电平的时候,SDA上的数据不能变化,因为前面已经说明,这是数据的变化将会认为是开始或者结束的信号。
在SCL低电平时数据可以改变。
2.主器件和从器件总线上可以有很多设备但是同时只能有一个主设备进行传输,从设备都有设备地址,当总线上的地址和从设备设置的地址一致时,传输在主设备和被寻址的从设备之间进行,其他设备相当于和总线分离。
单片机-AVR-EEPROM的读写分析

1.单片机(AVR)EEPROM的读写分析由于AVR的EEPROM写周期比较长(一般为毫秒级),因此在编程使用过程中要特别注意.对于读EEPROM没什么好说的,读一个字节的数据要耗费4个时钟周期,可以忍受,写就比较麻烦了,虽然放在EEPROM的数据都不是频繁访问的;虽然可以用读-比较-写的机制降低EEPROM的写操作频度,但在写入过程中,过长的写入周期还是会造成一些问题,下面就分析一下几种方式的EEPROM写操作.1.1.循环查询式将地址和数据写入EEPROM相关的寄存器,置写标志后就循环不断查询写完成标志,直到写完成,退出循环,顺序执行其他程序.在置写入标志到写完成的这段时间,程序除了不断查询写完成标志和响应硬件中断之外什么也不干,这段时间就这么浪费了,如果是个实时性要求比较高的应用,浪费的就不仅是时间了,很可能在这段时间里I/O状态的变化不能得到及时的响应,如果一下子要进行多个字节的EEPROM的写入操作,那情况会更糟.针对这种情况,有种解决办法就是,这个写完成查询放在软件的死循环中(无操作系统的情况下,系统启动后都要进入一个死循环),每循环一次查询一次写完成标志,这样就不必一直等待写操作完成而可以干别的事情了,但是这样会带来两个问题.一是,如果死循环周期无法保证,则每一次的EEPROM写操作的完成标志查询也得不到保证,从而进行一次EEPROM写操作的周期也无法保证(最长延迟时间就是一个死循环周期的最长时间);二是,在执行一次EEPROM写入操作到写完成这段时间里死循环里其它的子程序不能进行EEPROM读写操作.第一个问题如果能保证最大循环周期在延迟允许范围内就不是问题了,否则就得采取下面讲到的方式二了;第二个问题的解决方法是每次EEPROM读写操作都要在其中加入对EEPROM写完成标志寄存器的判断,如果有数据正在写入,则等待或退出,但等待和退出又会造成等待延迟和写入操作不成功的问题,解决办法是先放入一个数据缓冲区,待上一次写入操作完成,再从缓冲区里拽出一个字节进行下一次写入周期,但这又会涉及到选择合适的缓冲区大小以及的问题,这个问题在下面中断式操作里继续讨论.1.2.定时查询式在写入EEPROM地址和数据寄存器置写标志后启动定时器,定时查询写完成标志,这种方法继承了方式1的大部分优缺点,唯一的进步就是能够确定写操作延迟时间为定时周期.1.3.中断式编写EEPROM写完成中断子程序,设置一个FIFO缓冲区,要写入的数据先放入这个FIFO,如果EEPROM操作空闲,则从FIFO中揪一字节数据出来写EEPROM,置完写完成标志就返回,然后去干别的事情,EEPROM写完成产生一个中断,在中断服务程序中再从FIFO中揪一个字节去写EEPROM,如此循环直到FIFO空,这种方式虽然能在第一时间完成一次写操作,但是还是有个和和方式1中提到一样的问题,怎么选择这个FIFO的大小.小了,待写入数据可能溢出丢失,大了,多大算大,1K够不够,10K够不够,硬件上有这么多RAM空间么,这就需要根据实际资源和系统需求来定了.如果EEPROM写操作频率很低,比一次EEPROM写操作间隔还要长得多,那么几个单位的FIFO就足够了;如果EEPROM写操作频率可能很高或一次大批量连续写入数据就要求很大的FIFO,达到能够保证数据不丢失,这就要根据批量数据大小和写频率来定了,另外还要考虑可用RAM空间的限制来找出最佳值,同时有FIFO溢出一定要制定相应的应对措施.最后注意两点:1.读写之前一定要检测是否已经有EEPROM写操作;在写完成之后一定要再读出写入的数据校验写入操作是否成功,不成功则EEPROM损坏,要做好应对措施.2.如果要保存一个16位的数据,一定要保证在这16位数据分成的两字节都成功写入了EEPROM再去读该数据,否则在刚写完一个字节就去读该数据,得到的数据显然不是你想要的,如果该数据是一重要参数,后果可想而知.。
AVR单片机TWI读写过程实例

TWI:是一种全双工的串行通讯协议,与I2C工作方式相同,由一条数据传输线SDL,一条时钟线SCL组成,对应单片机的外部引脚PC1,PC0。
由于只有两条总线,简化了系统设计。
特点:• 简单,但是强大而灵活的通讯接口,只需要两根线• 支持主机和从机操作• 器件可以工作于发送器模式或接收器模式• 7 位地址空间允许有128 个从机• 支持多主机仲裁• 高达400 kHz 的数据传输率• 斜率受控的输出驱动器• 可以抑制总线尖峰的噪声抑制器• 完全可编程的从机地址以及公共地址• 睡眠时地址匹配可以唤醒AVR主机:控制启动和停止传输的设备。
主机同时要产生SCL 时钟从机:被主机寻址的设备发送器:将数据放到总线上的设备接收器:从总线读取数据的设备TWI工作模式: 主机发送模式(MT)主机接收模式(MR)从机发送模式(ST)从机接收器模式(SR)例如,TWI 可用MT 模式给TWIEEPROM 写入数据,用MR 模式从EEPROM 读取数据。
如果系统中有其它主机存在,它们可能给TWI 发送数据,此时就可以用SR 模式。
应用程序决定采用何种模式。
模式状态缩写:S:START 状态Rs:REPEATED START 状态R:读一个比特(SDA 为高电平)W:写一个比特(SDA 为低电平)A:应答位(SDA 为低电平)A:无应答位(SDA 为高电平)Data:8 位数据P:STOP 状态SLA:从机地址一、主机模式:可以向从机发送数据,进入主机模式,首先发送一个START信号,接着的从机地址决定了进入MT还是MR模式主机发送(MT): S+SLA+W, 主机接收(MR): S+SLA+R1、波特率设置:TWBR=100;2、分频设置:TWSR|=(1<<TWPS1)|(1<<TWPS0);写过程:1)、发送启动信号:TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTA);2)、等待应答信号:while ((TWCR&(1<<TWINT))==0);3)、进入主机写模式:TWDR=0Xa0;// TWDR=SLA+W; //通过在TWDR中写入SLA+W进入主机写模式,SLA+R进入主机读模式TWCR=(1<<TWINT)|(1<<TWEN);while ((TWCR&(1<<TWINT))==0);//如果TWINT为0就原地等待4)、发送要写入的地址:TWDR=adress;TWCR=(1<<TWINT)|(1<<TWEN); //启动发送while(!(TWCR&(1<<TWINT)));5)、发送要写入的数据:TWDR=data;TWCR=(1<<TWINT)|(1<<TWEN); //启动发送while(!(TWCR&(1<<TWINT)));6)、发送停止信号:TWC(1<<TWINT)|(1<<TWEN)|(1<<TWST0); //写过程结束读过程:1)、启动信号:TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTA)while(!(TWCR&(1<<TWINT)));2)、进入主机写模式:TWDR=0Xa0;//TWDR=SLA+W;TWCR=(1<<TWINT)|(1<<TWEN);while(!(TWCR&(1<<TWINT)));3)、写入要读的地址:TWDR=address;TWCR=(1<<TWINT)|(1<<TWEN);while(!(TWCR&(1<<TWINT)));4)、发送启动信号:TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTA)while(!(TWCR&(1<<TWINT)));5)、进入主机读模式:TWDR=0Xa1 ;//TWDR=SLA+R;TWCR=(1<<TWINT)|(1<<TWEN);while(!(TWCR&(1<<TWINT)));6)、开始读数据:TWCR=(1<<TWINT)|(1<<TWEN);while(!(TWCR&(1<<TWINT)));temp=TWDR; //读取的数据送到变量temp中。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
strTWI.STATUS=TW_OK;
//启动写命令后需要10ms(最大)的编程时间才能真正的把数据记录下来
//编程期间器件不响应任何命令
}
}
else
{//写数据失败
state=ST_FAIL;
}
break;
default:
//AT24C02的读写函数(包括随机读,连续读,字节写,页写)
//根据sla的最低位决定(由中断程序中判断)
//bit0=1 TW_READ读
//bit0=0 TW_WRITE写
// sla器件地址(不能搞错)
// addr EEPROM地址(0~1023)
// *ptr读写数据缓冲区
// len读数据长度(1~1024),写数据长度(1 or 8 or 16)
***********************************************/
/*
本程序简单的示范了如何使用ATMEGA16的TWI读写AT24C02 IIC EEPROM
TWI协议
(即IIC协议,请认真参考IIC协议的内容,否则根本就不能掌握)
一主多从的应用,M16作主机
(M16做从机和多主多从的应用不多,请自行参考相关文档)
#define SLA_24CXX 0xA0 //24Cxx系列的厂商器件地址(高四位)
#define ADDR_24C02 0x00
// AT24C02的地址线A2/1/0全部接地,SLAW=0xA0+0x00<<1+0x00,SLAR=0xA0+0x00<<1+0x01
//TWI_操作状态
#define TW_BUSY 0
{//TWI忙,不能进行操作
return OP_BUSY;
}
strTWI.STATUS=TW_BUSY;
i=(addr>>8)<<1;
i&=0x06; //考虑了24C04/08的EEPROM地址高位放在SLA里面
strTWI.SLA=sla+i;
strTWI.ADDR=addr;
strTWI.pBUF=ptr;
if(status==TW_MR_SLA_ACK)
{//发送器件地址成功
if (strTWI.DATALEN--)
{//多个数据
TWCR=(1<<TWEA)|TW_ACT; //设定ACK,触发下一步动作
}
else
{//只有一个数据
TWCR=TW_ACT; //设定NAK,触发下一步动作
}
}
else
struct str_TWI //TWI数据结构
{
volatile unsigned char STATUS; //TWI_操作状态
unsigned char SLA; //从设备的器件地址
unsigned int ADDR; //从设备的数据地址
unsigned char *pBUF; //数据缓冲区指针
//bit0=0 TW_WRITE写
虽然中断服务程序很长,但每次只执行一个case,所以耗时并不长。
*/
SIGNAL(SIG_2WIRE_SERIAL)
{//IIC中断
unsigned char action,state,status;
action=strTWI.SLA&TW_READ; //取操作模式
#define ST_WADDR 3 //ADDR状态检查
//TWI读操作步骤
#define ST_RESTART 4 //RESTART状态检查
#define ST_SLAR 5 //SLAR状态检查
#define ST_RDATA 6 //读取数据状态检查,循环n字节
//TWI写操作步骤
#define ST_WDATA 7 //写数据状态检查,循环n字节
(大部分IIC接口器件都是这种类型,常见的例如AT24C01/02/04/08/16,DS1307,DS1721等)
对于有两个字节数据地址的IIC器件(例如AT24C32/64/128/256等大容量EEPROM),请稍作改动
//根据strTWI.SLA的最低位决定
//bit0=1 TW_READ读
strTWI.DATALEN--;
state=ST_WDATA-1; //下一步将跳到WDATA分支
TWCR=TW_ACT; //触发下一步动作
}
}
else
{//发送eeprom地址出错
state=ST_FAIL;
}
break;
case ST_RESTART: //RESTART状态检查,只有读操作模式才能跳到这里
#define TWBR_SET 10; //TWBR必须大于等于10
#else
#define TWBR_SET (F_CPU/fSCL-16)/2; //计算TWBR值
#endif
#define TW_ACT (1<<TWINT)|(1<<TWEN)|(1<<TWIE)
//TWCR只能IN/OUT,直接赋值比逻辑运算(|= &=)更节省空间
if(status==TW_REP_START)
{//发送restart信号成功
TWDR=strTWI.SLA; //发器件地址读SLAR
TWCR=TW_ACT; //触发下一步动作,同时清start发送标志
}
else
{//重发start信号出错
state=ST_FAIL;
}
break;
case ST_SLAR: //SLAR状态检查,只有读操作模式才能跳到这里
*/
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
//时钟定为外部晶振7.3728MHz,F_CPU=7372800
#include <compat/twi.h>
//定义了各种模式下的状态码列表(TWSR已屏蔽预分频位),本文后面附上中文描述
//错误状态
state=ST_FAIL;
break;
}
if (state==ST_FAIL)
{//错误处理
strTWI.FAILCNT++;
if (strTWI.FAILCNT<FAIL_MAX)
*strTWI.pBUF++=TWDR;
TWCR=(1<<TWSTO)|TW_ACT; //发送停止信号,不会再产生中断了
strTWI.STATUS=TW_OK;
}
else
{//读取数据出错
state=ST_FAIL;
}
break;
case ST_WDATA: //写数据状态检查,只有写操作模式才能跳到这里
//管脚定义
#define pinSCL 0 //PC0 SCL
#define pinSDA 1 //PC1 SDA
//为保险起见,最好在SCL/SDA接上1~10K的外部上拉电阻到VCC。
#define fSCL 100000 //TWI时钟为100KHz
//预分频系数=1(TWPS=0)
#if F_CPU < fSCL*36
//返回值是否能执行当前操作
unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,unsigned int len)
{
unsigned char i;
if (strTWI.STATUS==TW_BUSY)
中断模式
(因为AVR的速度很高,而IIC的速度相对较低,
采用查询模式会长时间独占CPU,令CPU的利用率明显下降。
特别是IIC速度受环境影响只能低速通讯时,对系统的实时性产生严重的影响。
查询模式可以参考其它文档和软件模拟IIC的文档)
AT24C02/04/08的操作特点
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
if(status==TW_START)
{//发送start信号成功
TWDR=strTWI.SLA&0xFE; //发送器件地址写SLAW
TWCR=TW_ACT; //触发下一步动作,同时清start发送标志
}
else
{//发送start信号出错
state=ST_FAIL;
}
break;
case ST_SLAW: //SLAW状态检查
#define TW_OK 1
#define TW_FAIL 2
//Tபைடு நூலகம்I_读写命令状态
#define OP_BUSY 0
#define OP_RUN 1
//TWI读写操作公共步骤
#define ST_FAIL 0 //出错状态
#define ST_START 1 //START状态检查
#define ST_SLAW 2 //SLAW状态检查
state--; //循环,直到写完指定长度数据
if(status==TW_MT_DATA_ACK)
{//写数据成功
if (strTWI.DATALEN)
{//还要写
TWDR=*strTWI.pBUF++;