i2c驱动--裸板程序i2c总线接AT24cxx

来源:互联网 发布:environ linux 编辑:程序博客网 时间:2024/06/05 04:57

AT24cxx在mini2440这块开发板上接有,i2c的裸板程序及驱动程序测试均使用这块开发板。

head.s文件分析
功能: 设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行

_start: b   Reset ... Reset:                      ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启    bl  clock_init          @ 设置MPLL,改变FCLK、HCLK、PCLK    bl  memsetup            @ 设置存储控制器以使用SDRAM    bl  nand_init           @ 初始化NAND Flash                            @ 复制代码到SDRAM中    ldr r0, =0x30000000     @ 1. 目标地址 = 0x30000000,这是SDRAM的起始地址。可以查看,i2c.ldsmov r1, #0              @ 2. 源地址   = 0,从nandflash的0地址开始拷贝    ldr r2, =__bss_start    @ 3000003c: e59f2044    ldr r2, [pc, #68]   ; 30000088 <.text+0x88>                            @68 =0x44, 3000003c+44+8=    30000088                             @30000088:  30002bc8    andcc   r2, r0, r0, asr #23 (编译后的反汇编文件)                            @0x2bc8=11208 ,和i2c.bin文件的大小一致     sub r2, r2, r0          @ 3. 复制长度    bl  CopyCode2SDRAM      @ 调用C函数CopyCode2SDRAM    bl  clean_bss           @ 清除bss段,未初始化或初值为0的全局/静态变量保存在bss段    msr cpsr_c, #0xd2       @ 进入中断模式    ldr sp, =0x31000000     @ 设置中断模式栈指针    msr cpsr_c, #0xdf       @ 进入系统模式    ldr sp, =0x34000000     @ 设置系统模式栈指针    ldr lr, =ret_initirq    @ 设置返回地址        ldr pc, =init_irq       @ 调用中断初始化函数ret_initirq:    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断    ldr lr, =halt_loop      @ 设置返回地址    ldr pc, =main           @ 调用main函数halt_loop:    b   halt_loopHandleIRQ:    sub lr, lr, #4                  @ 计算返回地址    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器                                    @ 注意,此时的sp是中断模式的sp                                    @ 初始值是上面设置的4096    ldr lr, =int_return             @ 设置调用IRQ_Handle函数后的返回地址      ldr pc, =IRQ_Handle             @ 调用中断分发函数,在interrupt.cint_return:    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

说明:

1、 ldr pc, =label . 即把 label后的数据或者指令的运行地址赋值为pc。运行地址 = 链接地址 + 这些数据或指令相对于程序起始地址的偏移。
2、 ldr pc, label, 即把label后的数据或者指令内容赋值给pc。
3、__bss_start :表示全局未初始化变量的开始地址。
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。

关于:
bl nand_init @ 初始化NAND Flash

在head.s中执行 nand_init–jump to definition
在上一行看到

  static unsigned char read_data(void){    return nand_chip.read_data();}

nand_chip是一个全局变量,不在链接地址上,在原nand_init下做如下修改
jump to definition—-S3C2410_NAND

 static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

修改为

 S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

在原nand_init下做如下修改

 void nand_init(void){S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;#define TACLS   0#define TWRPH0  3#define TWRPH1  0     /* 判断是S3C2410还是S3C2440 */    if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))    {        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */        s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);        /* 复位NAND Flash */        s3c2410_nand_reset();    }    else    {        /* 设置时序 */        s3c2410nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */        s3c2410nand->NFCONT = (1<<4)|(1<<1)|(1<<0);        /* 复位NAND Flash */        s3c2410_nand_reset();    }}

再逐个修改一下函数:

/* S3C2410的NAND Flash处理函数 */static void s3c2410_wait_idle(void);static void s3c2410_nand_select_chip(void);static void s3c2410_nand_deselect_chip(void);static void s3c2410_write_cmd(int cmd);static void s3c2410_write_addr(unsigned int addr);static unsigned char s3c2410_read_data();/* S3C2440的NAND Flash处理函数 */static void s3c2440_wait_idle(void);static void s3c2440_nand_select_chip(void);static void s3c2440_nand_deselect_chip(void);static void s3c2440_write_cmd(int cmd);static void s3c2440_write_addr(unsigned int addr);static unsigned char s3c2440_read_data(void);

添加

{  ...    S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;    S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;    ...}

修改

static void nand_reset(void)static void wait_idle(void)static void nand_select_chip(void)static void nand_deselect_chip(void)static void write_cmd(int cmd)static void write_addr(unsigned int addr)static unsigned char read_data(void)

添加

...if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))    {        s3c2410_xxx_xxx();    }    else    {        s3c2440_xxx_xxx();    }... 

关于
ldr pc, =main @ 调用main函数

       int main(){    char c;    char str[200];    int i;    int address;    int data;    uart0_init();   // 波特率115200,8N1(8个数据位,无校验位,1个停止位)    i2c_init();  //i2c 初始化    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("Enter data: ");                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", &data);                printf("write address %d with data %d\r\n", address, data);                at24cxx_write(address, data);                break;            }        }    }    return 0;}

解释:

  • sscanf(str, “%d”, &address);

定义函数

int sscanf (const char *str,const char * format,........);   

sscanf()会将参数str的字符串根据参数format字符串来转换并格式化数据。格式转换形式请参考scanf()。转换后的结果存于对应的参数内。

这里就是将str中的数据以整形的形式存入address。

  • data = at24cxx_read(address);

at24cxx_read 的定义:

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(0xA1, (unsigned char *)&val, 1);    printf("at24cxx_read get data ok\r\n");    return val;}

at24cxx_read 才是真正的读取,前面的函数实现地址的写入,再寻址,读取。这里的打印是为了验证写入地址,读出地址的每一步是否正确。

  • at24cxx_write(address, data);
    at24cxx_write的定义
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_write、i2c_read的定义:

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);}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);//不等于0,也就是数据没有传完,继续等...DataCount--}

IICSTAT = 0xf0; // 主机发送,启动
这行代码将程序转到中断处理函数:

/* * 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            // 将数据写入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;          }}

读:设备地址–写–写地址给从机–start–设备地址–读–数据
这里写图片描述

写:设备地址–写–地址–数据
这里写图片描述

  • i2c_write(0xA0, &address, 1);

    这里设置为0xA0,查看AT24C0手册的Device Address可以知道。
    1k/2k所有的存储地址用八位表示
    存储地址为4k时p0=0表示访问第一页p0=1表示访问第二页,
    存储地址为8k时p0p1表示能访问四页,存储地址为16k时p0p1p2表示能访问八页。
    这就解释了8位寻址1k–16k存储器空间。
    这里写图片描述
    测试结果:
    这里写图片描述

原创粉丝点击