Tiny6410裸机IIC读写EEPROM

一、S3C6410  IIC资源

      由 IICCON、 IICSTAT、 IICDS 和IICADD 4个寄存器完成所有的操作。本实验中6410作为主机,因此只用到了前三个寄存器。IICADD只有在作为从机的时候才被用来存放从机的地址。

     IICCON:bit[7]用于接收时的ACK使能, 在发送模式下该位不起作用

                     bit[6]用于IICCLK的选择

                     bit[5]用于IIC中断的使能

                     bit[4]用于中断标记位,在连续传输时需要清除中断标志位

     IICSTAT: bit[7:6] 用于工作模式的选择:主发,主接,从发,从接

                    bit[5]总线状态位和S、P信号发送位

                    bit[1]表示是否接收到S/P 信号

                    bit[0] 表示是否接收到ACK信号

 

主机发送和主机接收流程图:

Tiny6410裸机IIC读写EEPROM_第1张图片Tiny6410裸机IIC读写EEPROM_第2张图片

                                   图1                                                                           图2

二、AT24C08

   AT24C08地址:

Tiny6410裸机IIC读写EEPROM_第3张图片

                                                                    图3

   AT24C08按页写操作顺序:

Tiny6410裸机IIC读写EEPROM_第4张图片

                                                                             图4

  AT24C08按页读操作顺序:

Tiny6410裸机IIC读写EEPROM_第5张图片

                                                                         图5

三、IIC程序

IIC信息结构体:

typedef struct i2c_frame
{
	int mode;                //IIC 模式
	int pt;                  //索引初始值
	unsigned char* buf;      //存放数据
	int datacount;           //数据长度
	char data_addr;          //读写位置(相对于AT24C08)
	int w_addr;              //写读写位置的标志
}i2c_frame;
i2c_frame i2c;                   //定义全局变量

初始化程序:

void I2CInit(void)
{
	// 引脚配置为IIC引脚模式   cl-->GPB5  sda-->GPB6
	GPBCON &= 0xf00fffff;
	GPBCON |= 0x02200000;
	// IIC 配置:IIC使能、IICCLK选择、中断使能、发送时钟选择
	IICCON0 = (1<<7)|(0<<6)|(1<<5)|(0xf);
	// 串行使能
	IICSTAT0 = 0x10;
	// IIC 中断清除
	IICCON0 &= ~(1<<4);
	// 使能中断
	VIC1INTENABLE |= 0x00040000;
	// 注册中断服务函数
	VIC1VECTADDR18 = (unsigned long)I2CHandler;
}

写函数:

void I2CWrite(char addr,unsigned char* buf, int len)
{
	i2c.mode = WRITE;
	i2c.pt   = 0;
	i2c.buf  = buf;
	i2c.datacount = len;
	i2c.w_addr = 0;
	i2c.data_addr = addr; 
	IICSTAT0 = 0xd0;
        // 写入器件的地址
	IICDS0 = 0xa0;
        // 发送S信号
	IICSTAT0 = 0xf0;
	/* 等待ACK应答 */
	while(IICSTAT0 & 0x20);
}

读函数:

void I2CRead(char addr, unsigned char* buf, int len)
{
	i2c.mode = READ;
	i2c.pt   = -1;
	i2c.buf  = buf;
	i2c.datacount = len-1 ;
	i2c.data_addr = addr;
	i2c.w_addr = 0;
	IICSTAT0 = 0xd0;
        // 写入器件的地址(写操作)
	IICDS0 = 0xa0;
        // S信号
	IICSTAT0 = 0xf0;
	while(IICSTAT0 & 0x20);
        // 写入器件的地址(读操作)
	IICDS0 = 0xa1;
        // s信号
	IICSTAT0 = 0xb0;
	while (IICSTAT0 & 0x20);
}

中断函数:

void I2CHandler(void)
{
	//入栈操作
    __asm__(			    
			"sub lr, lr, #4\n"  
			"stmfd sp!, {r0-r12, lr}\n"       
			);
	int i = 0;
	switch(i2c.mode)
	{
		case WRITE:
			{
				if (i2c.w_addr == 0)          
				{
					IICDS0 = i2c.data_addr;  // 1.先发送要写数据的位置 
					delay_(1000);   
					IICCON0 = 0xaf;
					i2c.w_addr++;
					break;
				}
				if ((i2c.datacount--) == 0)     //  3.当数据全发送完毕后
				{                               //    发送P信号停止传输
					IICSTAT0 = 0xd0;      
					IICCON0  = 0xaf;
					delay_(1000);
					break;
				}
				IICDS0 = i2c.buf[i2c.pt++];      //2. 然后发送数据
				delay_(1000);
				IICCON0 = 0xaf;
				break;
			}
  		case READ:
			{
				if (i2c.w_addr == 0)           // 1.先发送要读取数据的位置  
				{
					IICDS0 = i2c.data_addr;
					delay_(1000);
					IICCON0 = 0xaf;
					i2c.w_addr++;
					break;
				}
				if (i2c.w_addr == 1)           // 2.当位置发送完毕之后
				{                              //   发送P信号停止传输
					IICSTAT0 = 0xd0;
					IICCON0  = 0xaf;
					delay_(1000);
					i2c.w_addr++;
					break;
				}
				if (i2c.pt == -1)             //  3.写入0xa1之后会进入中断
				{                             //    在这个中断中不接收数据
					i2c.pt = 0;
					if (i2c.datacount == 0)
						IICCON0 = 0x2f;
					else
					  IICCON0 = 0xaf;
					break;
				}
				if ((i2c.datacount--) == 0)
				{
					i2c.buf[i2c.pt++] = IICDS0; // 5.接收最后一个数据
					IICSTAT0 = 0x90;            // 发送P信号
					IICCON0 = 0xaf;
					delay_(1000);
					break;
				}
				i2c.buf[i2c.pt++] = IICDS0;  // 4.接收数据
				if (i2c.datacount == 0)
				  IICCON0 = 0x2f;
				else
				  IICCON0 = 0xaf;
				break;
			}
	}
        // 清除中断
	VIC1ADDRESS = 0;	
	VIC0ADDRESS = 0;	
        // 出栈操作
	__asm__( "ldmfd sp!, {r0-r12, pc}^ \n");
}

对于IIC中断函数的理解可以参照图1、图2、图4、图5。3C6410的IIC0中断只用到了VIC1中断控制器,但是在清除中断时需要对VIC0ADDR和VIC1ADDR都清零,否则无法连续进入中断。

四、其他函数

主函数:

#include "stdio.h"
#include "uart.h"
#include "led.h"
#include "iic.h"

unsigned char dat[256] = {0};    //发送数据域
unsigned char rev[256] = {0};    //接收数据域
int main()
{

	int a = 0;          
	init_uart();             //串口初始化
	LedInit();               //led初始化
	I2CInit();               //IIC初始化
	for (a=0; a<256; a++)
	{
		dat[a] = a;     // dat 0~255
	}
	for (a=0; a<1; a++)
	{
		I2CWrite(a*16, dat+a*16, 16);  // 从第0页到第15页发送数据
		delay();
	}
	printf("--------------------------\r\n");
	for (a=0; a<16; a++)
	{
		I2CRead(a*16, rev+a*16, 16);  // 从第0页到第15页读数据
	}
	delay();
	for (a=0; a<256; a++)
	{
		printf("%d\r\n", rev[a]);     // 打印数据
	}
	while(1)
	{
		;
	}
	return 0;
}

启动函数:

.global _start
_start:

reset:
	// 把外设基地址告诉CPU
	ldr r0, =0x70000000   // 6410内存地址0~0x60000000 外设地址0x70000000 ~ 0x7fffffff
	orr r0,r0,#0x13       // 外设地址大小256M
	mcr p15,0,r0,c15,c2,4 // 把r0的值(外设基地址和外设大小)写给CPU
						  // mcr:ARM寄存器到协处理寄存器的数据传送指令
						  // mrc:协处理寄存器到ARM寄存器的数据传送指令
	// 关闭看门狗
	ldr r0, =0x7E004000
	mov r1, #0
	str r1, [r0]

	// 设置栈
	ldr sp, =0x0c002000


	//设置时钟
	bl clock_init
	bl irq_init
	// 开中断
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#(1<<24)   @ SET VE=0
	mcr p15,0,r0,c1,c0,0
	mov r0, #0x53			
	msr CPSR_cxsf, r0
	//main函数
	ldr pc, =main

halt:
	b halt

makefile:

CC      = arm-linux-gcc
LD      = arm-linux-ld
AR      = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump

INCLUDEDIR 	:= $(shell pwd)/include
CFLAGS 		:= -Wall -Os -fno-builtin
CPPFLAGS   	:= -nostdinc -I$(INCLUDEDIR)

export 	CC AR LD OBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS 

objs := start.o main.o uart.o clock.o led.o irq.o  iic.o lib/libc.a

stdio.bin: $(objs)
	${LD} -Ttext 0x50000000 -o stdio.elf $^
	${OBJCOPY} -O binary -S stdio.elf $@
	${OBJDUMP} -D stdio.elf > stdio.dis

.PHONY : lib/libc.a
lib/libc.a:
	cd lib; make; cd ..
	
%.o:%.c
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o:%.S
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
	make  clean -C lib
	rm -f *.bin *.elf *.dis *.o

 

你可能感兴趣的:(Tiny6410裸机IIC读写EEPROM)