由 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信号
图1 图2
图3
图4
图5
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