20.IIC裸板

目录

I2C 总线介绍

电路图

1.设备地址:寻址过程 与 写数据

I2C 裸机程序

一,开发板上电初始化工作:

二,主函数: main.c

1,初始化串口和 I2C 设置:

i2c.c

at24cxx.c

i2c.h

main.c

serial.c

serial.h


 

I2C 总线介绍

电路图

I2C 是个主从结构,即所有的传输都是从主机发起。从机不可能主动引起数据的传输。

对于 I2C 协议来说,它只能规定到发出的第一个数据是“地址”,后面发出的内容是什

么,每个 I2C 可能不同。

下面是一种情况:

1,平时 SDA 和 SCL 都是高电平。

2,开始信号:当 SCL 为高电平的同时, SDA 有个脉冲。

3,数据在 SCL 低电平时变化 ,在 SCL 脉冲为高电平间稳定。

4,发出地址,每个 I2C 芯片里面都有一个地址,是固化在芯片里面。

1.设备地址:寻址过程 与 写数据

1,设备地址总共是 7bit,“1010”固定,后面“A2,A1,A0”可以从硬件上敲定。

看开发板实际的接线。

开发板上, A0,A1,A2 都是接地,即此三位都是 0.

2,则此 AT24C08 的地址:

发出的前面 7 位就是设备地址。此时后面接的 I2C 设备,接收到一个 start 信号时,

就知道下面紧接着的就是7bit 的设备地址。若发现此地址与固化在设备内的地址相同时,就知道是访问自已。

3,访问时,是“读”还是“写”:由 7bit 后的第 8 位决定。

启动一个传输时,主机先发出 S 信号,然后发出 8 位 数据。这 8 位数据的前 7bit 为从机的地址,第 8bit 表示传输的方向(0 表示写操作, 1 表示读操作)。

4, ACK 回应信号:

在第 9 个 CLK 里, I2C 主机释放 SDA,由从机驱动SDA.若从机发现该“设备地址”是自已的,是把 SDA 拉

为低电平。这时主机就能知道此“设备地址”的设备是存在的,后面就能再发数据了。

5.后面接着再 8 个 CLK 时钟,是具体的数据,是与设备有关的。后面还有一个第 9bit,是从机把 SDA 拉低确认 ACK。

若为主机到从机的读:

寻址后的 8 个时钟是由 从机响应,从机会把设备驱动到 SDA 中,最后的第 9 个时钟也是 ACK,但是由主机响应。就是说主机已经接收到数据了,就到 ACK 拉为低电平。

结束传输:

SCL 时钟信号在高电平期间, SDA 由低电平变成高电平时结束传输。

I2C 裸机程序

看一个裸板程序从“入口函数”开始看: head.S

一,开发板上电初始化工作:

假设 NAND 启动,一上电,程序的代码前 4K 就会复制到 2440 片内内存。

1,第一步:跳转到 reset 处:

2, reset 处代码工作:

①,设置栈、关看门狗、设置时钟、设置内存控制器、初始化 NANDFLASH。

这些函数现在是在 0 地址(片内内存)处运行。

②,把代码复制到 0x30000000 地址处。

复制到 0x30000000 处,是因为“链接地址”规定

之前是把程序分成了两段,实际上可以分成一段。修改如下:

a,链接地址: 0x3000000

b,代码段:

c,只读数据段:所有文件的只读数据段。

d,所有文件的数据段:

e, bss 段:

所有文件的的 bss 和 COMMON 段。

3,链接地址:

ldr sp, =4096

bl disable_watch_dog

bl clock_initbl memsetup

bl nand_init

这些函数现在是在 0 地址(片内内存)处运行。它们的链接地址是“0x30000000”。链

接地址并不等于它们当前所在的位置。所以它们就需要以“位置无关码”来写。就是说写

这些函数时,要用“位置无关码”来写,不能用全局变量等。

①,关看门狗:

,初始化时钟:

③,内存设置:

④,初始化 NAND:

位置无关码不能用全局变量,这里的 "S3C2410_NAND”是局部变量。所以这个

“nand.c”中的代码都要修改。看修改过的“nand.c”把全局变量全换成局部变量,即

把全局变量放到局部中(函数里)。

4, head.S 重定位:

把 NANDFLASH 0 地址从 0 地址拷贝。拷贝的长度是"__bss_start = ."减去

“.=0x30000000” .这样便有足够长了。

以前为了简单代码,直接把长度写了 16K:

修改为如下:就是不包括 bss 段的长度。

bss 段是放那些初始值为 0 的全局变量的。假设有 1W 个 0 时,没有必要把所有的 0 全放

在最后的二进制文件里面,要是带上这 1W 个 0 就会很浪费空间。那么二进制文件里面就

不去含有 bss 段。 bss 段部分需要我们自已来清零。

4, head.S 接着调用: CopyCode2SDRAM 函数,将代码拷贝到内存中去。

用"nand_read"函数来拷贝。

5, head.S 中将代码拷贝到内存中后,接着清 bss 段:

就是将“ __bss_start”到“ __bss_end;”间全部清为 0.

这里在链接时会分配链接地址。就把这段清为 0.那么以前访问这些全局变量时就都是 0.

6, head.S 中其他就是中断相关的设置,最后是 main 函数。

二,主函数: main.c

1,初始化串口和 I2C 设置:

a,初始化串口:

b,初始化 I2C:

3,两个读写函数:

Random Read 随机读的时序:

前 7bit 发出设备地址找到从机,第 8bit 从机回应。再接着发出存储地址写给从机。

接着就开始一个“START”信号,再发出设备地址,这时候表示读。最后读到此数据(WORD ADDRESS n 处)。

AT24108,是 8K(1024*8)即要 10 位地址才能表示 1024。“WORD ADDRESSn”是 8位根本无法寻址 1K 的空间。 2 的 8 次方是最多访问到 256 个地址。但这里 E2PROM 的容量有 1K、 2K 时,显然 8 位的地址是不够的。

Device Addressing 设备寻址:

里面的“128”、“256”可以用 8 位来寻址到。

而 512, 1024 则有 2 页或 4 页数据。

2K是256B字节时,它所有的存储地址就可以用 8位来寻址。

4K即512字节时, P0等于0时表示访问第一页。 P1等于1时表示访问第1页。

8K即1024字节时, P1,P0总共就可以表示 4 页。

16K即2048字节时, P2,P1,P0共表示8页。

对于小容量的 AT24C02 为 256 字节,就可以用“WORD ADDRESS” 8 位地址来寻址。但

对于上面 AT24C08 这个 1024 字节的大容量 E2PROM 设备,则"WORD ADDRESS"只能寻

址它的“PAGE0”空间。对于其他页的空间,以前面发出“START”信号后“设备地址”:

A2 是硬件引脚, P1,P0 可以变。只要发出“1010+A2”就能访问到设备(A2 硬件上接低

电平时,则 A2 为 0;硬件上接的是高电平,则 A2 为 1), P1,P0 就是用来表示访问哪

一页。 AT20C08 有 4 页(每页 256 字节)。

最后编译测试:

得到了 bin 有 10K。

链接脚本要保证“head.o” "init.o"“nand.o”三个文件位于前面 4K,因为这个三个

文件在重定位之前,都得位于片内内存里面。可以看反汇编文件: 4K=4096=0x1000,即

是 0x30001000 处:

链接脚本中最后一个 nand.o 的最后一个函数是:

在反汇编文件中搜索这个“nand_read”的链接地址:

链接地址是“3e4”没有超过“4K”。

i2c.c

/*
 * 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;         // 主机发送,启动
    
    /* 等待直至数据传输完毕 */    
    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;    
            }

            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--);
}

at24cxx.c


#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.h

//====================================================================
// File Name : 2410IIC.h
// Function  : S3C2410 IIC Test Program Head file
// Program   : Shin, On Pil (SOP)
// Date      : March 20, 2002
// Version   : 0.0
// History
//   0.0 : Programming start (March 11, 2002) -> SOP
//====================================================================

#ifndef __2410IIC_H__
#define __2410IIC_H__

void i2c_init(void);
void i2c_write(unsigned int slvAddr, unsigned char *buf, int len);
void i2c_read(unsigned int slvAddr, unsigned char *buf, int len);
void I2CIntHandle(void);

#endif    //__2410IIC_H__

 

 

main.c

#include <stdio.h>
#include "serial.h"
#include "i2c.h"

unsigned char at24cxx_read(unsigned char address);
void at24cxx_write(unsigned char address, unsigned char data);


int main()
{
    char c;
    char str[200];
    int i;
	int address;
	int data;
    
    uart0_init();   // 波特率115200,8N1(8个数据位,无校验位,1个停止位)
    
    i2c_init();
    
    while (1)
    {
        printf("\r\n##### AT24CXX Menu #####\r\n");
        printf("[R] Read AT24CXX\n\r");
        printf("[W] Write AT24CXX\n\r");
        printf("Enter your selection: ");

        c = getc();
        printf("%c\n\r", c);
        switch (c)
        {
            case 'r':
            case 'R':
            {
                printf("Enter address: ");
                i = 0;
                do
                {
                    c = getc();
                    str[i++] = c;
                    putc(c);
                } while(c != '\n' && c != '\r');
                str[i] = '\0';

                while(--i >= 0)
                {
                    if (str[i] < '0' || str[i] > '9')
                        str[i] = ' ';
                }

                sscanf(str, "%d", &address);
				printf("\r\nread address = %d\r\n", address);
				data = at24cxx_read(address);
				printf("data = %d\r\n", data);
                    
                break;
            }
            
            case 'w':
            case 'W':
            {
                printf("Enter address: ");
                i = 0;
                do
                {
                    c = getc();
                    str[i++] = c;
                    putc(c);
                } while(c != '\n' && c != '\r');
                str[i] = '\0';
				printf("\r\n");

                while(--i >= 0)
                {
                    if (str[i] < '0' || str[i] > '9')
                        str[i] = ' ';
                }

                sscanf(str, "%d", &address);
				//printf("get str %s\r\n", str);

                printf("Enter data: ");
                i = 0;
                do
                {
                    c = getc();
                    str[i++] = c;
                    putc(c);
                } while(c != '\n' && c != '\r');
                str[i] = '\0';
				printf("\r\n");
				//printf("get str %s\r\n", str);

                while(--i >= 0)
                {
                    if (str[i] < '0' || str[i] > '9')
                        str[i] = ' ';
                }

                sscanf(str, "%d", &data);
				//address = 12;
				//data = 13;
				printf("write address %d with data %d\r\n", address, data);
				
				at24cxx_write(address, data);

                break;
            }
        }
        
    }
    
    return 0;
}

serial.c

#include "s3c24xx.h"
#include "serial.h"

#define TXD0READY   (1<<2)
#define RXD0READY   (1)

#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}

/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
    
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

/*
 * 接收字符
 */
unsigned char getc(void)
{
    /* 等待,直到接收缓冲区中的有数据 */
    while (!(UTRSTAT0 & RXD0READY));
    
    /* 直接读取URXH0寄存器,即可获得接收到的数据 */
    return URXH0;
}

/*
 * 判断一个字符是否数字
 */
int isDigit(unsigned char c)
{
    if (c >= '0' && c <= '9')
        return 1;
    else
        return 0;       
}

/*
 * 判断一个字符是否英文字母
 */
int isLetter(unsigned char c)
{
    if (c >= 'a' && c <= 'z')
        return 1;
    else if (c >= 'A' && c <= 'Z')
        return 1;       
    else
        return 0;
}

serial.h

void uart0_init(void);
void putc(unsigned char c);
unsigned char getc(void);
int isDigit(unsigned char c);
int isLetter(unsigned char c);

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值