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.lds。 mov 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.c中int_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存储器空间。
测试结果:
- i2c驱动--裸板程序i2c总线接AT24cxx
- I2c总线驱动
- I2C总线驱动
- Linux I2C 总线驱动
- linux i2c总线驱动
- I2C总线驱动代码
- I2C总线驱动
- kernel I2C总线驱动
- 浅析I2C总线驱动
- i2c总线测试程序
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- S3C2410 I2C 总线驱动实例
- S3C2410 I2C 总线驱动实例
- Linux驱动之I2C总线
- I2C总线的相关程序
- [U3D][Android]问题记录
- 手把手教你把Vim改装成一个IDE编程环境(图文)
- Java集合小结
- openGL对基本几何图形的定义
- 数据库分区例子
- i2c驱动--裸板程序i2c总线接AT24cxx
- UEditor上传配置&常见问题
- 微信公众号开发(三)获取access_token
- Spring 创建条件化的bean(满足一定条件才会实例化该bean)
- [U3D]问题记录
- 使用 GNU profiler 来提高代码运行速度
- 企业做网站该用什么程序呢?实用最实在
- SQL语句基础
- 2017.9.5 能量采集 思考记录