s3c2440 IIC控制器裸板程序

        I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。


要点:
1、总线空闲:I2C总线空闲的时候,两条线SDA和SCL都是高电平。
2、开始信号 S 信号:SCL 为高电平时,SDA由高电平向低电平跳变,开始传送数据。
3、结束信号 P 信号:SCL 为高电平时,SDA由低电平向高电平跳变,结束传送数据。
4、响应信号 ACK:接收器在接收到8位数据后,在第9个时钟周期,拉低 SDA 电平。
(注意:在第9个时钟周期,发送器保持SDA为高,如果有ACK,那么第9个时钟周期SDA为    低电平,如果没有为高电平,发送器根据电平高低分辨是否有ACK信号。)
5、正常数据传输时:SDA 在 SCL 为低电平时改变,在 SCL 为高电平时保持稳定。
        如果使能了IIC中断,发送完8bit数据后,主机自动进入中断处理函数,此时SCL被发送器拉低,让接收器被迫等待。恢复传输只需要清除中断挂起。
        接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。  

        发送模式中当发送了数据时,在 IIC 总线数据移位(IICDS)寄存器收到新数据之前 IIC 总线接口将会一直等待。在新数据写入到寄存器之前,SCL 线将会保持为低,然后在其写入后释放。S3C2440A 应该等待中断来确定当前数据发送的完成。在 CPU 收到中断请求后,需要再次写一个新数据到 IICDS 寄存器中。

        接收模式中当收到了数据时,在读取 IICDS 寄存器前 IIC 接口将会一直等待。在新数据读出前,SCL 线将会保持为低,然后在其读取后释放。S3C2440A 应该等待中断来确定当前数据接收的完成。在 CPU 收到中断请求后,需要从 IICDS 寄存器中读取数据。

特别注意:
IIC 总线中断发生在:
1、当完成了 1 字节发送或接收操作;
2、当广播呼叫或从地址匹配发生时;
3、如果总线仲裁失败。

s3c2440的IIC总线控制器:
s3c2440提供4个寄存器来完成所有IIC的操作:
IICDS:
IICADD:
IICCON:ACK信号使能,发送模式时钟源选择,发送、接收中断使能,中断标记,发送模式时钟分频系数。
IICSTAT:工作模式,忙状态,发出S信号,P信号等,串行输出使能(IICDS输出使能),最后一位的状态(是否接收到ACK)。当发出S信号后,IICDS寄存器中的数据被自动发送。

S3C2440A 的 IIC 总线接口有 4 种工作模式:
–  主机发送模式
–  主机接收模式
–  从机发送模式
–  从机接收模式 

这里只要介绍主机发送模式和主机接收模式流程:

主机发送模式流程图:


主机接收模式流程图:


结合at24cxx的读写来学习吧:

at24cxx.c:  

实现两个函数at24cxx_read()和at24cxx_write(),这两个函数直接提供给用户操作at24cxx。 

#include <string.h>
#include "i2c.h"

unsigned char at24cxx_read(unsigned char address)
{
	unsigned char val;
	printf("at24cxx_read address = %d\r\n", address);
    i2c_write(0xA0, &address, 1);
	printf("at24cxx_read send address ok\r\n");
    i2c_read(0xA0, (unsigned char *)&val, 1);
	printf("at24cxx_read get data ok\r\n");
	return val;
}

void at24cxx_write(unsigned char address, unsigned char data)
{
	unsigned char val[2];
	val[0] = address;
	val[1] = data;
    i2c_write(0xA0, val, 2);
}

i2c.c:

注意:控制器读/写一字节后,会自动更新下一个读/写的地址,所以发送一次读/写地址,可连续读写多个数据。
同时注意,EEPROM 接口, Rx 模式中为了产生停止条件在读取最后数据之前会禁止产生应答。
在次强调一下:IIC 总线中断发生的时机:1、当完成了 1 字节发送或接收操作;2、当广播呼叫或从地址匹配发生时;3、如果总线仲裁失败。

/*
 * FILE: i2c.c
 * 用于主机发送/接收
 */
#include <stdio.h>
#include "s3c24xx.h"
#include "i2c.h"

void Delay(int time);

#define WRDATA      (1)
#define RDDATA      (2)

typedef struct tI2C {
    unsigned char *pData;   /* 数据缓冲区 */
    volatile int DataCount; /* 等待传输的数据长度 */
    volatile int Status;    /* 状态 */
    volatile int Mode;      /* 模式:读/写 */
    volatile int Pt;        /* pData中待传输数据的位置 */
}tS3C24xx_I2C, *ptS3C24xx_I2C;

static tS3C24xx_I2C g_tS3C24xx_I2C;

/*
 * I2C初始化
 */
void i2c_init(void)
{
    GPEUP  |= 0xc000;       // 禁止内部上拉
    GPECON |= 0xa0000000;   // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL

    INTMSK &= ~(BIT_IIC);

    /* bit[7] = 1, 使能ACK
     * bit[6] = 0, IICCLK = PCLK/16
     * bit[5] = 1, 使能中断
     * bit[3:0] = 0xf, Tx clock = IICCLK/16
     * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
     */
    IICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);  // 0xaf

    IICADD  = 0x10;     // S3C24xx slave address = [7:1]
    IICSTAT = 0x10;     // I2C串行输出使能(Rx/Tx)
}

/*
 * 主机发送
 * slvAddr : 从机地址,buf : 数据存放的缓冲区,len : 数据长度 
 */
void i2c_write(unsigned int slvAddr, unsigned char *buf, int len)
{
    g_tS3C24xx_I2C.Mode = WRDATA;   // 写操作
    g_tS3C24xx_I2C.Pt   = 0;        // 索引值初始为0
    g_tS3C24xx_I2C.pData = buf;     // 保存缓冲区地址
    g_tS3C24xx_I2C.DataCount = len; // 传输长度
    
    IICDS   = slvAddr;
    IICSTAT = 0xf0;         // 主机发送模式,发送从机地址,使能串行传输,发出S信号启动
    
    /* 等待直至数据传输完毕 */    
    while (g_tS3C24xx_I2C.DataCount != -1);
}
        
/*
 * 主机接收
 * slvAddr : 从机地址,buf : 数据存放的缓冲区,len : 数据长度 
 */
void i2c_read(unsigned int slvAddr, unsigned char *buf, int len)
{
    g_tS3C24xx_I2C.Mode = RDDATA;   // 读操作
    g_tS3C24xx_I2C.Pt   = -1;       // 索引值初始化为-1,表示第1个中断时不接收数据(地址中断)
    g_tS3C24xx_I2C.pData = buf;     // 保存缓冲区地址
    g_tS3C24xx_I2C.DataCount = len; // 传输长度
    
    IICDS        = slvAddr;
    IICSTAT      = 0xb0;    // 主机接收,启动
    
    /* 等待直至数据传输完毕 */    
    while (g_tS3C24xx_I2C.DataCount != 0);
}

/*
 * I2C中断服务程序
 * 根据剩余的数据长度选择继续传输或者结束
 */
void I2CIntHandle(void)
{
    unsigned int iicSt,i;

    // 清中断
    SRCPND = BIT_IIC;
    INTPND = BIT_IIC;
    
    iicSt  = IICSTAT; 

    if(iicSt & 0x8){ printf("Bus arbitration failed\n\r"); }

    switch (g_tS3C24xx_I2C.Mode)
    {    
        case WRDATA:
        {
            if((g_tS3C24xx_I2C.DataCount--) == 0)
            {
                // 下面两行用来恢复I2C操作,发出P信号
                IICSTAT = 0xd0;
                IICCON  = 0xaf;
                Delay(10000);  // 等待一段时间以便P信号已经发出
                break;    
            }
            //数据的格式是,地址 数据1 数据2 数据3...,对于各个数据的写地址,控制器会自动+1更新
            //想要改为读模式,需要在发出P信号之后,发出读模式地址。	
            IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++];
            
            // 将数据写入IICDS后,需要一段时间才能出现在SDA线上
            for (i = 0; i < 10; i++);   

            IICCON = 0xaf;      // 恢复I2C传输
            break;
        }

        case RDDATA:
        {
            if (g_tS3C24xx_I2C.Pt == -1)
            {
                // 这次中断是发送I2C设备地址后发生的,没有数据
                // 只接收一个数据时,不要发出ACK信号
                g_tS3C24xx_I2C.Pt = 0;
                if(g_tS3C24xx_I2C.DataCount == 1)
                   IICCON = 0x2f;   // 恢复I2C传输,开始接收数据,接收到数据时不发出ACK
                else 
                   IICCON = 0xaf;   // 恢复I2C传输,开始接收数据
                break;
            }

			g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS;
			g_tS3C24xx_I2C.DataCount--;
            
            if (g_tS3C24xx_I2C.DataCount == 0)
            {

                // 下面两行恢复I2C操作,发出P信号
                IICSTAT = 0x90;
                IICCON  = 0xaf;
                Delay(10000);  // 等待一段时间以便P信号已经发出
                break;    
            }      
			else
			{           
	           // 接收最后一个数据时,不要发出ACK信号
	           if(g_tS3C24xx_I2C.DataCount == 1)
	               IICCON = 0x2f;   // 恢复I2C传输,接收到下一数据时无ACK
	           else 
	               IICCON = 0xaf;   // 恢复I2C传输,接收到下一数据时发出ACK
			}
           break;
        }
       
        default:
            break;      
    }
}

/*
 * 延时函数
 */
void Delay(int time)
{
    for (; time > 0; time--);
}


        本文到此为止,谢谢阅读。


猜你喜欢

转载自blog.csdn.net/qq_22863733/article/details/80672889