ARM——I2C
S5PC100的RISC微处理器支持多主机I2C总线串行接口。一个专用的串行数据线(SDA)和一个串行时钟线(SCL)在总线主机和连接到I2C总线的外围设备之间传递信息。SDA和SCL线是双向的。
在多主机i2c总线模式中,多个微处理器发送到从机设备或从从机设备接收。主机S5PC100启动和终止一个在I2C总线上的数据传输。S5PC100上的I2C总线使用标准总线仲裁程序。
为了控制多主机I2C总线操作,值必须写到一下寄存器:
多主机I2C控制寄存器 I2CCON
多主机I2C控制/状态寄存器 I2CSTAT
多主机I2C发送/接收转换寄存器 I2CDS
多主机I2C地址寄存器 I2CADD
如果I2C总线空闲,SDA和SCL线应该均为高电平。当SCL线稳定在高电平时,SDA线电平由高到低转换将发起开始条件,SDA线电平由低到高转换将发起停止条件。
主机设备总是产生开始条件和停止条件。当开始条件发起后,被放到总线上第一数据字节的7位地址值能够决定总线主机设备选择的从机设备。第8位决定传输方向(读/写)。
每一个被放到SDA线上的数据字节应该是总共八位。总线传输期间发送或接收的数据字节数没有限制。数据总是首先从最高有效位MSB发送,每一字节后紧跟应答位ACK。
S5PC100 I2C总线接口有四种操作模式:
主机发送模式;主机接收模式;从机发送模式;从机接收模式
这些操作模式功能关系:
开始和停止条件
如果I2C总线接口未启动,通常是从机模式。换句话说,接口在检测到SDA线上开始条件(当SCL线时钟信号高电平时SDA线电平由高到低转换发起开始条件)之前应该是从机模式。如果接口状态转换到主机模式,SDA线上数据传输可以被发起,SCL信号产生。
通过SDA线开始条件传输一字节串行数据,停止条件终止数据传输。当SCL线时钟信号高电平时SDA线电平由低到高发起停止条件。主机产生开始和停止条件。如果开始条件产生,I2C总线将变为忙状态。停止条件释放I2C总线。如果主机发起开始条件,应该发送一个从机地址来通知从机设备,一个字节的地址域包含一个7位地址值和一个1位传输方向指示值(写或读)。如果第8位是0,则表示写操作(发送操作);如果第8位是1,则表示读数据请求(接收操作)。
主机发送停止条件来完成传输操作,如果主机箱继续总线数据的发送,应该产生另一个开始条件和从机地址。这样,可以进行各种格式的读写操作。
数据传输格式
SDA线上的每一字节应该长度为8位,每次传输发送的字节数没有限制。跟随开始条件的第一字节应该包含地址域。如果I2C总线操作在主机模式下,主机发送地址域。每一字节后应该紧跟一个应答位ACK。串行数据和地址的最高有效位MSB应该首先被发送。
应答信号发送
为了完成一字节传输操作,接收方发送应答位ACK到发送方。应答脉冲在SCL线的第9时钟周期产生。一字节数据传输使用了八个时钟。主机发送应答位ACK要产生时钟脉冲。
发送方如果接收到应答位ACK则通过使SDA线置高来释放SDA线。接收方在应答位ACK时钟脉冲期间驱使SDA线置低以使在SCL线第9脉冲周期间保持SDA线置低。
软件(I2CSTAT)使能或禁用应答位ACK发送功能。然而,在SCL线第9脉冲周期应答位ACK脉冲被请求来完成一字节数据传输操作。
读/写操作
在发送模式下,如果数据被传输,I2C总线接口等待直到I2C总线数据转换寄存器(I2CDS)接收到一个新的数据。在新数据写到寄存器之前,SCL线保持低电平,然后写完之后释放。S5PC100获得中断来识别当前数据发送是否完成。CPU接收中断请求之后,再写一个新数据到I2CDS寄存器。
在接收模式下,如果数据被接收,I2C总线接口等待直到I2C总线数据转换寄存器(I2CDS)被读到。在新数据被读出之前,SCL线保持低电平,然后读完释放。S5PC100获得中断来识别当前数据接收是否完成。CPU接收中断请求之后,再从I2CDS寄存器读数据。
总线仲裁程序
仲裁发生在SDA线上来阻止总线上两主机冲突,如果一个SDA线高电平的主机检测到另一个SDA线低电平的主机,它将不发起数据传输,因为总线上的当前电平不能对应到他自己,仲裁程序延迟直到SDA线电平变高。
如果主机同时降低SDA线电平,没一个主机评估是否主控权在它自身。为了评估每一个主机检测地址位。当每一主机产生从机地址时,检测SDA线上的地址位,因为SDA线很可能获得低电平而不是保持高电平。假设一个主机产生低电平作为地址第一位,而另一个主机维持在高电平,这种情况,两主机检测到总线上低电平,因为低电平状态优于高电平状态。如果发生,产生低电平(作为地址第一位)的主机获得主控权而产生高电平(作为地址第一位)的主机撤出主控权。如果量主机产生低电平作为地址第一位,再对第二地址位仲裁。这种仲裁持续到地址位结束。
中止条件
如果从机接收方不能应答从机地址的确认,将保持SDA线高电平。这种情况,主机产生停止条件然后中止传输。
如果主机接收方忙于中止的传输,则通过在从机接收完最后的数据字节后取消产生应答位ACK来发信号中止从机发送。从机发送方释放SDA线来允许主机产生停止条件。
配置I2C总线
为了控制串行总线时钟SCL的频率,4位预分频值被写进I2CCON寄存器。I2C总线接口地址保存在I2C总线地址(I2CADD)寄存器(默认情况下I2C总线接口地址有一个未知值)。
每个模式下的操作流程图
下面的步骤在每一个I2C发送/接收操作中都被执行
1如果需要,自己的从机地址到I2CADD寄存器
2设置I2CCON寄存器
a使能中断
b定义SCL周期
3设置I2CSTAT为使能串口输出
#include "s5pc100.h"
void putc(const char data)
{
while(!(UART0.UTRSTAT0 & 0X2));
UART0.UTXH0 = data;
if(data == '\n')
putc('\r');
}
void puts(const char *pstr)
{
while(*pstr != '\0')
putc(*pstr++);
}
void uart0_init(void)
{
GPA0.GPA0CON = (GPA0.GPA0CON & ~(0xff)) | 0X22;//enable GPA0.0 GPA0.1 pin function mode // RXD0 TXD0
UART0.UFCON0 = 0X00; //disable fifo
UART0.UMCON0 = 0X00; //disable AFC
UART0.ULCON0 = 0X03; //data length 8 bit
UART0.UCON0 = 0X305; //
UART0.UBRDIV0 = 0X23; // Baud rate divisior register 115200 UBRDIV0 = (PCLK / (bps x 16 ) )
UART0.UDIVSLOT0 = 0xDDD5; //set SLOT register generate more accurate baud rate
printf("open uart device ok !\n");
}
#include "s5pc100.h"
#include "uart.h"
/****
* functioal : Reading the value of the internal lm75 register
* param: address register of the internal lm75 register
* return: value of the internal lm75 register
*/
int set_pointer_and_read_temperature(void)
{
int delay;
int low, high;
I2C0.I2CDS0 = 0x90; /*LM75 SLAVE ADDRESS */
I2C0.I2CCON0 = 0xe0; /*ENABLE ACK BIT, PRESCALER:512 ,RX/TX INTERRUPT ENABLE ,*/
I2C0.I2CSTAT0 =0xf0; /*Master Trans mode ,START ,ENABLE RX/TX ,*/
while(!(I2C0.I2CCON0&(1<<4))); /*The end of the waiting to be sent */
I2C0.I2CDS0 = 0x0; /*pointer register Temperature(read only) send address of SFR in the lm75*/
I2C0.I2CCON0 &= ~(1<<4); /* Clear pending condition & Resume the operation */
while(!(I2C0.I2CCON0&(1<<4))); /*The end of the waiting to be sent */
I2C0.I2CDS0 = 0x90; /*Again to send LM75 salve address*/
I2C0.I2CSTAT0 =0xb0; /*Master receive mode ,START ,ENABLE RX/TX ,*/
I2C0.I2CCON0 &= ~(1<<4); /* Clear pending condition & Resume the operation */
while(!(I2C0.I2CCON0&(1<<4))); /*The end of the waiting to be sent */
I2C0.I2CCON0 &= ~(1<<4); /* Clear pending condition & Resume the operation */
for(delay=0; delay<0xffff; delay++);
high = I2C0.I2CDS0; /*read temperature of high 8 bit */
I2C0.I2CCON0 &= ~(1<<4); /* Clear pending condition & Resume the operation */
for(delay=0; delay<0xffff; delay++);
low = I2C0.I2CDS0; /*read temperature of low 1 bit */
I2C0.I2CSTAT0 &= ~(1<<5); /*STOP signal generation,free bus */
I2C0.I2CCON0 &= ~(1<<4); /*clean interrup pending bit */
return ((high << 8) | low);
}
int main()
{
int temp;
int low, high;
uart0_init();
GPD.GPDCON = (GPD.GPDCON & ~(0X0F << 12)) | (0X2 << 12); /*SDA set gpd[3] functional mode*/
GPD.GPDCON = (GPD.GPDCON & ~(0X0F << 16)) | (0X2 << 16); /*SCL set gpd[4] functional mode*/
while (1){
temp = set_pointer_and_read_temperature();
high = temp >> 8;
low = temp & 0xff;
printf("TEMP is : %d.%d\n", high, (((low>>7)==0) ? 0 : 5));
}
return 0;
}