调了两天终于把2440上面的IIC调通了,代码记录如下:
head.s
@this is a test program of uart using com0, the properties of com0 is the same as usual (115200,8, 1, n, n)
.text
.global _start
_start:
b Reset
b . @undefined instruction
b . @swi
b . @pregetch instruction
b . @data abort
b . @Reserved
b HandleIRQ @irq
b . @fiq
Reset:
ldr sp, =4096 @set the stack pointer of sys mode to 4096
bl disable_watchdog
bl init_led
bl init_sdram
bl copy_sdram
ldr pc, =on_sdram
on_sdram:
msr cpsr_c, #0xd2 @set cpu to irq mode
ldr sp, =3072 @set the stack pointer of irq mode to 3072
msr cpsr_c, #0xdf @reset cpu to sys mode
bl init_clock
mov r0, #1
mov r1, #10
bl blink
bl init_iic
mov r0, #2
mov r1, #10
bl blink
bl init_com0
mov r0, #3
mov r1, #10
bl blink
bl init_irq
msr cpsr_c, #0x5f @enable irq
bl main
b .
.ltorg
.ltorg
.ltorg
HandleIRQ:
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
ldr lr, =next
ldr pc, =interrupt_func
next:
ldmia sp!, {r0-r12, pc}^ @return to main
.ltorg
.ltorg
init.c
#define GPBCON (*((volatile unsigned long*)(0x56000010)))
#define GPBDAT (*((volatile unsigned long*)(0x56000014)))
#define WTCON (*((volatile unsigned long*)(0x53000000)))
#define LOCKTIME (*((volatile unsigned long*)(0x4C000000)))
#define CLKDIVN (*((volatile unsigned long*)(0x4C000014)))
#define MPLLCON (*((volatile unsigned long*)(0x4C000004)))
#define TCFG0 (*((volatile unsigned long*)(0x51000000)))
#define TCFG1 (*((volatile unsigned long*)(0x51000004)))
#define TCNTB0 (*((volatile unsigned long*)(0x5100000C)))
#define TCON (*((volatile unsigned long*)(0x51000008)))
#define INTMSK (*((volatile unsigned long*)(0X4A000008)))
#define INTSUBMASK (*((volatile unsigned long*)(0X4A00001C)))
#define GPHCON (*((volatile unsigned long*)(0x56000070)))
#define GPHUP (*((volatile unsigned long*)(0x56000078)))
//control register of com0
#define ULCON0 (*((volatile unsigned long*)(0x50000000)))
#define UCON0 (*((volatile unsigned long*)(0x50000004)))
#define UFCON0 (*((volatile unsigned long*)(0x50000008)))
#define UMCON0 (*((volatile unsigned long*)(0x5000000C)))
#define UBRDIV0 (*((volatile unsigned long*)(0x50000028)))
//the control register of sckl, sdat (IIC)
#define GPECON (*((volatile unsigned long*)(0x56000040)))
#define GPEUP (*((volatile unsigned long*)(0x56000048)))
#define IICCON (*((volatile unsigned long*)(0x54000000)))
#define IICSTAT (*((volatile unsigned long*)(0x54000004)))
//initilize GPB to output mode
void init_led()
{
GPBCON &= (~(0xff<<10));
GPBCON |= (0X55<<10);
}
void delay(int times)
{
int i, j;
for(i=0; i<times; i++)
{
for(j=0; j<1000; j++);
}
}
//blink function is used to debug
void blink(int number, int delay_num)
{
GPBDAT |= (0X0F<<5);
GPBDAT &= (~(number<<5));
delay(delay_num);
GPBDAT |= (0X0F<<5);
delay(delay_num);
}
void disable_watchdog()
{
WTCON = 0;
}
void init_clock()
{
LOCKTIME = 0xffffffff;
CLKDIVN = 0X03; //fclk:hclk:pclk=1:2:4
__asm__
(
"mrc p15, 0, r1, c1, c0, 0\n"
"orr r1, r1, #0xc0000000\n"
"mcr p15, 0, r1, c1, c0, 0\n"
);
MPLLCON = (92<<12)|(1<<4)|(2<<0); //fclk=200M, hclk=100M, pclk=50M
}
void init_sdram()
{
volatile unsigned long *p = (volatile unsigned long *)(0x48000000);
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
p[9] = 0x008C04F4; //REFRESH value is different than before, because the clock is different
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_sdram()
{
unsigned long* src = (unsigned long *)(0);
unsigned long* des = (unsigned long *)(0x30000000);
int i;
for(i=0; i<1024; i++)
{
des[i] = src[i];
}
}
void init_irq()
{
//enable interrupt of rxd0 txd0
INTSUBMASK &= (~(0b11));
//enable interrupt of uart0
INTMSK &= (~(1<<28));
//enable IIC interrupt
INTMSK &= (~(1<<27));
}
//initialize the IIC bus
void init_iic()
{
//set gpe[14:15] used for IIC
GPECON &= (~(0b1111<<28));
GPECON |= (0b1010<<28);
//enable the pulling up function
GPEUP &= (~(0b11<<14));
/*
0b00100111
[7] disable ack signal
[6] IICCLK=PCLK/16
[5] enable IIC rx interrupt and tx interrupt
[4] clear IIC interrupt
[3:0] tx clock = IICCLK/8 = 50000000/16/16 = 195.3125KHz
*/
IICCON = 0b00101111;
//enable IIC R/T ability
IICSTAT = 0x10;
}
void init_com0()
{
//set gph[2:3] used as txd and rxd for com0
GPHCON &= (~(0xff<<4));
GPHCON |= (0x0a<<4);
//disable pull up functon for gph[2:3]
GPHUP = 0x0c;
//set properties of com0
ULCON0 = 0b00000011;
//enable the rxd and txd, and interrupt will happen when con0 received data or the send buffer is empty in non-fifo mode
UCON0 = 0b00000101;
//disable fifo
UFCON0 = 0x00;
//disable fifo
UMCON0 = 0x00;
//set baud-rate to 115200
UBRDIV0 = (int)(50000000/(115200*16)) -1;
}
com0.c
#define UTRSTAT0 (*((volatile unsigned long*)(0x50000010)))
#define UTXH0 (*((volatile unsigned long*)(0x50000020)))
#define URXH0 (*((volatile unsigned long*)(0x50000024)))
#define SUBSRCPND (*((volatile unsigned long*)(0X4A000018)))
#define SRCPND (*((volatile unsigned long*)(0X4A000000)))
#define INTPND (*((volatile unsigned long*)(0X4A000010)))
#define IICSTAT (*((volatile unsigned long*)(0x54000004)))
#define IICDS (*((volatile unsigned long*)(0x5400000C)))
extern void blink(int, int);
extern unsigned char write_ack;
extern unsigned char iic_mode;
extern unsigned char read_over;
extern unsigned char read_data;
void put_char(char ch)
{
while (!(UTRSTAT0 & (1<<2)));
UTXH0 = ch;
}
unsigned char get_char()
{
while (!(UTRSTAT0 & (1<<0)));
return URXH0;
}
void put_string(char *string)
{
int i = 0;
while(string[i] != '\0')
put_char(string[i++]);
}
void com0_read(char *ch)
{
*ch = URXH0;
}
void com0_write(char ch)
{
UTXH0 = ch;
}
//the function to serve the uart0 interrupt
void interrupt_func()
{
//uart0 interrupt
if(INTPND & (1<<28))
{
char ch;
//rxd sub interrupt
if(SUBSRCPND & (1<<0))
{
com0_read(&ch);
if(ch == '\r')
{
com0_write('\r');
com0_write('\n');
}
else
{
com0_write(ch);
}
}
else if(SUBSRCPND & (1<<1)) //txd interrupt
{
}
//clear the interrupt of uart0
SUBSRCPND = SUBSRCPND;
SRCPND |= (1<<28);
INTPND = INTPND;
}
else if(INTPND & (1<<27)) //IIC interrupt
{
put_string("IIC interrupt!\r\n");
if(iic_mode) //write mode
{
if(!(IICSTAT&(1<<0)))
{
write_ack = 1;
put_string("get ack!\r\n");
}
else
{
write_ack = 0;
put_string("no ack!\r\n");
}
}
else //read mode
{
read_over = 1;
put_string("get data\r\n");
}
//clear the interrupt
SRCPND |= (1<<27);
INTPND = INTPND;
}
}
IIC.c
#define IICDS (*((volatile unsigned long*)(0x5400000C)))
#define IICSTAT (*((volatile unsigned long*)(0x54000004)))
#define IICCON (*((volatile unsigned long*)(0x54000000)))
extern void delay(int);
extern void put_string(char *);
//the ack signal form slave device
unsigned char write_ack = 0;
//the data read from IIC slave device
unsigned char read_data = 0;
//the mode of iic, 0----read mode , 1----write mode
unsigned char iic_mode = 0;
//the flag of whether reading is over
unsigned char read_over = 0;
/*
write one byte to slave device
salve_addr ---- the address of slave address
data_addr ---- the address of the byte writen by host
data ----- the writen data
*/
void iic_write(unsigned char slave_addr, int data_addr, unsigned char data)
{
slave_addr &= 0b11111110;
iic_mode = 1; //write mode
//send S signal and the slave address
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0xf0;
//wait the interrupt to change write_ack
while(!write_ack);
//send data_addr
write_ack = 0;
IICDS = data_addr;
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
//send data
write_ack = 0;
IICDS = data;
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
//send P signal
IICSTAT = 0xd0;
IICCON = 0b00101111; //recover the transmission
delay(3);
}
//read a byte from slave device
unsigned char iic_read(unsigned char slave_addr, int data_addr)
{
slave_addr &= 0b11111110;
iic_mode = 1; //write mode
//send S signal and the slave address
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0xf0;
//wait the interrupt to change write_ack
while(!write_ack);
//send data_addr
write_ack = 0;
IICDS = data_addr;
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
//send S signal and the slave address
slave_addr |= 0b00000001;
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0b10110000;
IICCON = 0b00101111; //recover the transmission
//wait the interrupt to change write_ack
while(!write_ack);
/*
here is the most important part
we must clear IICCON[4] to inter IIC read interrupt
*/
iic_mode = 0; //read mode
read_over = 0;
IICCON = 0b00101111; //recover the transmission
delay(5);
/*
we only need to read once, but two interrupt will happen
the first interrupt happens because of receiving data from slave device
the second interrupt happens because of getting data from IICDS
*/
read_data = IICDS;
while(!read_over)
//send P signal
IICSTAT = 0xd0;
IICCON = 0b00101111; //recover the transmission
delay(3);
return read_data;
}
//write 8 bytes to eeprom
void iic_write_page(unsigned char slave_addr, int data_addr, unsigned char *pdata)
{
int i;
slave_addr &= 0b11111110;
iic_mode = 1; //write mode
//send S signal and the slave address
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0xf0;
//wait the interrupt to change write_ack
while(!write_ack);
//send data_addr
write_ack = 0;
IICDS = data_addr;
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
for(i=0; i<8; i++)
{
//send data
write_ack = 0;
IICDS = pdata[i];
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
}
//send P signal
IICSTAT = 0xd0;
IICCON = 0b00101111; //recover the transmission
delay(3);
}
//read bytes continuioiusly
void iic_serial_read(unsigned char slave_addr, int data_addr, unsigned char *buffer, int length)
{
int i;
slave_addr &= 0b11111110;
iic_mode = 1; //write mode
//send S signal and the slave address
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0xf0;
//wait the interrupt to change write_ack
while(!write_ack);
//send data_addr
write_ack = 0;
IICDS = data_addr;
IICCON = 0b00101111; //recover the transmission
while(!write_ack);
//send S signal and the slave address
slave_addr |= 0b00000001;
write_ack = 0;
IICDS = slave_addr;
IICSTAT = 0b10110000;
IICCON = 0b00101111; //recover the transmission
//wait the interrupt to change write_ack
while(!write_ack);
/*
here is the most important part
we must clear IICCON[4] to inter IIC read interrupt
*/
for(i=0; i<length; i++)
{
iic_mode = 0; //read mode
read_over = 0;
if(i<length-1)
IICCON = 0b10101111; //recover the transmission with ack
else
IICCON = 0b00101111; //recover the transmission without ack
delay(5);
/*
we only need to read once, but two interrupt will happen
the first interrupt happens because of receiving data from slave device
the second interrupt happens because of getting data from IICDS
*/
buffer[i] = IICDS;
while(!read_over);
}
//send P signal
IICSTAT = 0xd0;
IICCON = 0b00101111; //recover the transmission
delay(3);
}
main.c
extern void put_char(char);
extern unsigned char get_char();
extern void put_string(char *);
extern void blink(int, int);
extern void put_string(char*);
extern void delay(int);
extern void iic_write(unsigned char,int,unsigned char);
extern unsigned char iic_read(unsigned char, int);
extern void iic_write_page(unsigned char,int,unsigned char*);
extern void iic_serial_read(unsigned char, int, unsigned char*, int);
int main(void)
{
unsigned char i;
unsigned char ch = 48;
unsigned char ch_array[8];
unsigned char read_buffer[8];
for(i=0; i<8; i++)
{
ch_array[i] = 'a'+i;
}
put_string("\r\nthis is a test of IIC\r\n");
//iic_write(0b10100000, 0, 65);
//iic_write(0b10100000, 1024, 66);
iic_write_page(0b10100000, 0, ch_array);
iic_serial_read(0b10100000, 0, read_buffer, 8);
//ch = iic_read(0b10100000, 7);
for(i=0; i<8; i++)
{
put_char(read_buffer[i]);
}
//put_string("get char:\r\n");
//put_char(ch);
while(1);
return 0;
}
最后粘上IIC写1页数据和读取数据的截图,下面是串口最后的输出结果: