嵌入式微机原理及应用课程大作业.docx
《嵌入式微机原理及应用课程大作业.docx》由会员分享,可在线阅读,更多相关《嵌入式微机原理及应用课程大作业.docx(14页珍藏版)》请在冰豆网上搜索。
嵌入式微机原理及应用课程大作业
基于ARM的I2C总线实验
马邦德
xx理工大学xxx学院测控技术与仪器系,xx,xxxx
摘要:
I2C总线是一种用于IC器件之间连接的二线制总线。
它通过SDA(串行数据线)及SCL(串行时钟线)两根线在连到总线上的器件之间传送数据,并根据地址识别每个器件:
不管是单片机、存储器、LCD驱动器还是键盘接口。
I2C能用于替代标准的并行总线,能连接各种集成电路和功能模块。
支持I2C的设备有微控制器、ADC、DAC、存储器、LCD控制器、LED驱动器以及实时时钟等。
关键词:
ARM,中断,I2C接口
1引言
随着ARM核心芯片在日常应用中的逐渐普及,越来越多的人投入到ARM开发大军中。
Samsung公司的S3C44B0X以其功能强大、价格低廉逐渐成为低端入门的首选。
JX44B0教学实验系统是一套硬件、软件集成的,基于ARM的完整的教学实验系统,系统包含丰富的硬件资源、完善而优越的调试手段和详尽的教学实验教程。
采用三星的S3C44B0X处理器,开发平台采用武汉创维特信息技术有限公司的ADT集成开发环境(以下简称ADTIDE),嵌入式开发的所有过程都可以在该环境下完成。
2功能综述
通过I2C总线读写EEPROM器件24C08,实现从同一地址写入再读出数据,并进行比较。
3硬件设计
1)I2C总线的基本结构
采用I2C总线标准的单片机或IC器件,其内部不仅有I2C接口电路,而且将内部各单元电路按功能划分为若干相对独立的模块,通过软件寻址实现片选,减少了器件片选线的连接。
CPU不仅能通过指令将某个功能单元挂靠或摘离总线,还可对该单元的工作状况进行检测,从而实现对硬件系统简单而灵活的扩展与控制。
I2C总线接口电路结构如下图所示。
图5-34I2C总线接口电路结构
2)双向传输的接口特性
传统的单片机串行接口的发送和接收一般都各用一条线,如MCS51系列的TXD和RXD,而I2C总线则根据器件的功能通过软件程序使其可工作于发送或接收方式。
当某个器件向总线上发送信息时,它就是发送器(也叫主器件),而当其从总线上接收信息时,又成为接收器(也叫从器件)。
主器件用于启动总线上传送数据并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。
I2C总线的控制完全由挂接在总线上的主器件送出的地址和数据决定。
在总线上,既没有中心机,也没有优先机。
总线上主和从(即发送和接收)的关系不是一成不变的,而是取决于此时数据传送的方向。
SDA和SCL均为双向I/O线,通过上拉电阻接正电源。
当总线空闲时,两根线都是高电平。
连接总线的器件的输出级必须是集电极或漏极开路,以具有线“与”功能。
I2C总线的数据传送速率在标准工作方式下为100kbit/s,快速方式下最高传送速率达400kbit/s。
3)I2C总线上的时钟信号
在I2C总线上传送信息时的时钟同步信号是由挂接在SCL时钟线上的所有器件的逻辑与完成的。
SCL线上由高电平到低电平的跳变将影响到这些器件,一旦某个器件的时钟信号下跳为低电平,将使SCL线一直保持低电平,使SCL线上的所有器件开始低电平期。
此时,低电平周期短的器件的时钟由低至高的跳变并不能影响SCL线的状态,于是这些器件将进入高电平等待的状态。
当所有器件的时钟信号都上跳为高电平时,低电平期结束,SCL线被释放返回高电平,即所有的器件都同时开始它们的高电平期。
其后,第一个结束高电平期的器件又将SCL线拉成低电平。
这样就在SCL线上产生一个同步时钟。
可见,时钟低电平时间由时钟低电平期最长的器件确定,而时钟高电平时间由时钟高电平期最短的器件确定。
4)数据的传送
在数据传送过程中,必须确认数据传送的开始和结束。
在I2C总线技术规范中,开始和结束信号(也称启动和停止信号)的定义如下图所示。
当时钟线SCL为高电平时,数据线SDA由高电平跳变为低电平定义为“开始”信号;当SCL线为高电平时,SDA线发生低电平到高电平的跳变为“结束”信号。
开始和结束信号都是由主器件产生。
在开始信号以后,总线即被认为处于忙状态;在结束信号以后的一段时间内,总线被认为是空闲的。
图5-35I2C总线启动和停止信号的定义
I2C总线的数据传送格式是:
在I2C总线开始信号后,送出的第一个字节数据是用来选择从器件地址的,其中前7位为地址码,第8位为方向位(R/W)。
方向位为“0”表示发送,即主器件把信息写到所选择的从器件;方向位为“1”表示主器件将从从器件读信息。
开始信号后,系统中的各个器件将自己的地址和主器件送到总线上的地址进行比较,如果与主器件发送到总线上的地址一致,则该器件即为被主器件寻址的器件,其接收信息还是发送信息则由第8位(R/W)确定。
在I2C总线上每次传送的数据字节数不限,但每一个字节必须为8位,而且每个传送的字节后面必须跟一个认可位(第9位),也叫应答位(ACK)。
数据的传送过程如下图所示。
每次都是先传最高位,通常从器件在接收到每个字节后都会作出响应,即释放SCL线返回高电平,准备接收下一个数据字节,主器件可继续传送。
如果从器件正在处理一个实时事件而不能接收数据时,(例如正在处理一个内部中断,在这个中断处理完之前就不能接收I2C总线上的数据字节)可以使时钟SCL线保持低电平,从器件必须使SDA保持高电平,此时主器件产生1个结束信号,使传送异常结束,迫使主器件处于等待状态。
当从器件处理完毕时将释放SCL线,主器件继续传送。
图5-36I2C总线数据传送过程
当主器件发送完一个字节的数据后,接着发出对应于SCL线上的一个时钟(ACK)认可位,在此时钟内主器件释放SDA线,一个字节传送结束,而从器件的响应信号将SDA线拉成低电平,使SDA在该时钟的高电平期间为稳定的低电平。
从器件的响应信号结束后,SDA线返回高电平,进入下一个传送周期。
I2C总线还具有广播呼叫地址用于寻址总线上所有器件的功能。
若一个器件不需要广播呼叫寻址中所提供的任何数据,则可以忽略该地址不作响应。
如果该器件需要广播呼叫寻址中提供的数据,则应对地址作出响应,其表现为一个接收器。
5)总线竞争的仲裁
总线上可能挂接有多个器件,有时会发生两个或多个主器件同时想占用总线的情况。
例如,多单片机系统中,可能在某一时刻有两个单片机要同时向总线发送数据,这种情况叫做总线竞争。
I2C总线具有多主控能力,可以对发生在SDA线上的总线竞争进行仲裁,其仲裁原则是这样的:
当多个主器件同时想占用总线时,如果某个主器件发送高电平,而另一个主器件发送低电平,则发送电平与此时SDA总线电平不符的那个器件将自动关闭其输出级。
总线竞争的仲裁是在两个层次上进行的。
首先是地址位的比较,如果主器件寻址同一个从器件,则进入数据位的比较,从而确保了竞争仲裁的可靠性。
由于是利用I2C总线上的信息进行仲裁,因此不会造成信息的丢失。
6)I2C总线的一次典型工作流程
1、开始:
信号表明传输开始。
2、地址:
主设备发送地址信息,包含7位的从设备地址和1位的指示位(表明读或者写,即数据流的方向)。
3、数据:
根据指示位,数据在主设备和从设备之间传输。
数据一般以8位传输,最重要的位放在前面;具体能传输多少量的数据并没有限制。
接收器上用一位的ACK(回答信号)表明每一个字节都收到了。
传输可以被终止和重新开始。
4、停止:
信号结束传输。
S3C44B0X的I2C控制
S3C44B0X处理器提供了一个I2C串行总线,其模块框图如下图所示,包括一个专门的串行数据线和串行时钟线。
它的操作模式有四种:
1、主设备发送模式;2、主设备接收模式;
3、从设备发送模式;4、从设备接收模式。
图5-37S3C44B0X处理器I2C串行总线框图
1)I2C总线控制寄存器
表5-34I2C总线控制寄存器
寄存器名称
地址
读写状态
描述
复位值
IICCON
0x01D60000
R/W
IIC总线控制寄存器
0000XXXX
IICCON
位
描述
初始状态
ACK使能
[7]
0:
禁止产生ACK信号
1:
允许产生ACK信号
0
Tx时钟源选择
[6]
0:
IICCLK=fMCLK/16
1:
IICCLK=fMCLK/512
0
Tx/Rx中断使能
[5]
0:
禁止Tx/Rx中断1:
使能Tx/Rx中断
0
中断清除标记
[4]
写0:
清除中断标志并重新启动写操作
读1:
中断标志置位
0
发送时钟值
[3:
0]
发送加载初始数据,决定发送频率
未定义
2)状态寄存器
表5-35状态寄存器
寄存器名称
地址
读写状态
描述
复位值
IICSTAT
0x01D60004
R/W
IIC总线控制/状态寄存器
00000000
IICSTAT
位
描述
初始状态
模式选择
[7:
6]
IIC总线主/从Tx/Rx模式选择位
00:
从接收模式01:
主接收模式
10:
从发送模式11:
主发送模式
0
忙信号状态/起停条件
[5]
读0:
IIC总线不忙写0:
产生IIC总线停止信号
读1:
IIC总线忙写1:
产生IIC总线启动信号
0
串行输出使能
[4]
0:
禁止Tx/Rx信号传输1:
使能Tx/Rx信号传输
0
仲裁状态标记
[3]
0:
总线仲裁成功
1:
总线仲裁不成功
0
从设备状态标记
[2]
作为从设备时:
0:
检测到启动或停止信号1:
接收到地址
0
零地址状态标记
[1]
作为从设备时:
0:
检测到启动或停止信号1:
总线地址为0
0
接收到的最低数据位状态标记
[0]
0:
接收到ACK应答信号1:
没有接收到ACK应答信号
0
3)地址寄存器
表5-36地址寄存器
寄存器名称
地址
读写状态
描述
复位值
IICADD
0x01D60008
R/W
IIC总线地址寄存器
XXXXXXXX
IICADD
位
描述
初始状态
从地址
[7:
0]
从设备的设备地址和页面地址
位0为读写控制(0为写,1为读)
任何时候都可对该值进行读操作,当IICSTAT的串行输出使能位为0时,可对该值进行写操作
XXXXXXXX
4)移位数据寄存器
表5-37移位数据寄存器
寄存器名称
地址
读写状态
描述
复位值
IICDS
0x01D6000C
R/W
IIC总线发送/接收数据漂移寄存器
XXXXXXXX
IICDS
位
描述
初始状态
数据漂移
[7:
0]
IIC总线要移位发送/接收的数据。
任何时候都可对该值进行读操作,当IICSTAT的串行输出使能位为0时,可对该值进行写操作。
XXXXXXXX
4软件设计
1)I2C接口初始化
I2C接口初始化操作通过函数voidiic_init()完成,首先必须进行I2C端口初始化
rPCONF=(rPCONF&0xFFFFFF0)|0xa;/*PF0:
IICSCL,PF1:
IICSDA*/
rPUPF|=0x3;/*pull-updisable*/
然后配置I2C控制寄存器
rIICCON=(1<<7)|(0<<6)|(1<<5)|(0xf);/*Enableinterrupt,IICCLK=MCLK/16*/
rIICADD=0x10;/*S3C44B0Xaddress*/
rIICSTAT=0x10;
2)I2C写操作
I2C写操作通过函数voidiic_write_24C08(U32slvAddr,U32addr,U8data)完成,其中slvAddr为从设备地址,在本系统中为0xa0,addr为待写入数据到芯片的地址,data为待写入的数据。
I2C写操作代码如下所示。
a.填写I2C命令,I2C缓冲区数据及大小。
iic_command=WRDATA;
iic_data_tx_index=0;
iic_buffer[0]=(U8)addr;
iic_buffer[1]=data;
iic_data_tx_size=2;
b.然后设置从设备地址并启动I2C操作,并将引发中断。
在I2C中断处理函数中进行读写操作,操作完毕iic_data_tx_size将被置为-1。
rIICDS=slvAddr;
rIICSTAT=0xf0;/*MasTx,Start*/
c.等待写操作完成。
while(iic_data_tx_size!
=-1);
d.等待从设备应答。
rIICSTAT=0xd0;/*topMasTxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopcondtionisineffect.*/
iic_command=POLLACK;
while
(1)
{
rIICDS=slvAddr;
iic_status=0x100;
rIICSTAT=0xf0;/*MasTx,Start*/
rIICCON=0xaf;/*resumesIICoperation.*/
while(iic_status==0x100);
if(!
(iic_status&0x1))
break;/*whenACKisreceived*/
}
rIICSTAT=0xd0;/*stopMasTxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopconditionisineffect.*/
3)I2C读操作
I2C读操作通过函数voidiic_read_24C08(U32slvAddr,U32addr,U8*data)完成,其中slvAddr为从设备地址,在本系统中为0xa0,addr为待读入数据的地址,data为待读入数据的缓冲区指针。
I2C读操作代码如下所示。
a.填写I2C命令。
iic_command=SETRDADDR;
iic_data_tx_index=0;
iic_buffer[0]=(U8)addr;
iic_data_tx_size=1;
b.等待写操作完成。
rIICDS=slvAddr;
rIICSTAT=0xf0;/*MasTx,Start*/
while(iic_data_tx_size!
=-1);
c.启动I2C操作,并将引发中断。
在I2C中断处理函数中进行读写操作,操作完毕读取的数据被送入iic_buffer中。
iic_command=RDDATA;
iic_data_rx_index=0;
iic_data_rx_size=1;
rIICDS=slvAddr;
rIICSTAT=0xb0;/*MasRx,Start*/
rIICCON=0xaf;/*resumesIICoperation.*/
while(iic_data_rx_size!
=-1);
rIICSTAT=0x90;/*stopMasRxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopconditionisineffect.*/
*data=iic_buffer[1];
4)I2C中断处理
I2C中断中接收I2C命令并进行相应处理,包括对POLLACK、RDDATA、WRDATA命令的处理,代码如下所示。
rI_ISPC=BIT_IIC;
iicSt=rIICSTAT;
switch(iic_command)
{
casePOLLACK:
iic_status=iicSt;
break;
caseRDDATA:
iic_data_rx_size--;
iic_buffer[iic_data_rx_index++]=rIICDS;
if(iic_data_rx_size==-1)break;
/*Thelastdatahastobereadwithnoack.*/
if((iic_data_rx_size)==0)
{
rIICCON=0x2f;/*resumesIICoperationwithNOACK.*/
}
else
{
rIICCON=0xaf;/*resumesIICoperationwithACK*/
}
break;
caseWRDATA:
caseSETRDADDR:
iic_data_tx_size--;
if(iic_data_tx_size==-1)break;
rIICDS=iic_buffer[iic_data_tx_index++];/*iic_buffer[0]hasdummy*/
for(i=0;i<10;i++);/*untilrisingedgeofIICSCL*/
rIICCON=0xaf;/*resumesIICoperation.*/
break;
default:
break;
}
5实验~计算结果
程序向24C08中写入256个字节数据,地址从0到255,然后读出256个字节数据,地址从0到255。
查看结果,发现写入和读出的结果相同,说明实验成功。
达到了本实验通过I2C总线向该芯片进行读写操作达到掌握I2C总线编程的目的。
6总结
通过此次实验我了解ADTIDE集成开发环境的基本功能,知道了I2C接口的用法同时也了解了I2C总线结构。
JX44B0教学实验系统上的EEPROM芯片24C08使用I2C总线与S3C44B0XI2C总线控制器进行连接,容量为1K字节。
当程序向24C08中写入256个字节数据时,地址从0到255,然后读出256个字节数据,地址从0到255,可以从结果看到前后两者相同。
此次实验让我明白了所学知识应用在实践方面的重要性,嵌入式的知识在生活中的应用也很广泛,这也更加增加了我对这么课程的兴趣。
参考文献
<1>田泽ARM7嵌入开发实验与实践北京:
北京航空航天大学出版社,2006
<2>张勇 ARM原理与C程序设计西安:
西安电子科技大学出版社,2009
附录:
实验程序及注释(自编部分)
rPCONF=(rPCONF&0xFFFFFF0)|0xa;/*PF0:
IICSCL,PF1:
IICSDA*/
rPUPF|=0x3;/*pull-updisable*/
rIICCON=(1<<7)|(0<<6)|(1<<5)|(0xf);/*Enableinterrupt,IICCLK=MCLK/16*/
rIICADD=0x10;/*S3C44B0Xaddress*/
rIICSTAT=0x10;
iic_command=WRDATA;
iic_data_tx_index=0;
iic_buffer[0]=(U8)addr;
iic_buffer[1]=data;
iic_data_tx_size=2;
rIICDS=slvAddr;
rIICSTAT=0xf0;/*MasTx,Start*/
while(iic_data_tx_size!
=-1);
rIICSTAT=0xd0;/*topMasTxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopcondtionisineffect.*/
iic_command=POLLACK;
while
(1)
{
rIICDS=slvAddr;
iic_status=0x100;
rIICSTAT=0xf0;/*MasTx,Start*/
rIICCON=0xaf;/*resumesIICoperation.*/
while(iic_status==0x100);
if(!
(iic_status&0x1))
break;/*whenACKisreceived*/
}
rIICSTAT=0xd0;/*stopMasTxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopconditionisineffect.*/
iic_command=SETRDADDR;
iic_data_tx_index=0;
iic_buffer[0]=(U8)addr;
iic_data_tx_size=1;
rIICDS=slvAddr;
rIICSTAT=0xf0;/*MasTx,Start*/
while(iic_data_tx_size!
=-1);
iic_command=RDDATA;
iic_data_rx_index=0;
iic_data_rx_size=1;
rIICDS=slvAddr;
rIICSTAT=0xb0;/*MasRx,Start*/
rIICCON=0xaf;/*resumesIICoperation.*/
while(iic_data_rx_size!
=-1);
rIICSTAT=0x90;/*stopMasRxcondition*/
rIICCON=0xaf;/*resumesIICoperation.*/
Delay
(1);/*untilstopconditionisineffect.*/
*data=iic_buffer[1];
rI_ISPC=BIT_IIC;
iicSt=rIICSTAT;
switch(iic_command)
{
casePOLLACK:
iic_status=iicSt;
break;
caseRDDATA:
iic_data_rx_size--;
iic_buffer[iic_data_rx_index++]=rIICDS;
if(iic_data_rx_size==-1)break;
/*Thelastdatahastobereadwithnoack.*/
if((iic_