**
I2C協議的mini2440裸機程序實現
**
- 上篇博客說道了I2C的理論知識,但是實際應用的時候,遇到很多的問題,加上程序的龐大,一直在調試。這幾天重新看了一下,最終解決問題。
- 首先的問題是程序超過4K之後一直卡住不運行,我之前的猜想是程序既然大於4K不能運行,那麼爲了能調試,先把其他的實驗的代碼進行裁剪,能不能滿足要求,但是發現光是I2C的代碼已經超過4K,所以一定是不行的;接着爲什麼是4K,跟CPU裏面的standstone大小剛好一致,是巧合嗎?不是的,這肯定是.start.S出了問題,才導致拷貝到內存的時候出了問題,因爲nand flash 拷貝到standstone是自動設置的,我們不需要做什麼,然後程序肯定會超過4K,那就需要拷貝到內存進行運行,可以肯定的說問題一定是出現在拷貝的過程中。
- 在參考了國嵌的.start.S代碼後,發現問題,直接使用國嵌的代碼,來進行初始化,發現果然可以調試了,說明代碼已經複製到內存了,這一步成功後,接下來的就是進行調試了;這裏就是我的第二個問題。由於直接使用的代碼,並沒有及時的發現中斷處理這個地方,國嵌是空着沒處理的。這就導致了I2C中斷產生後一直卡住,進不了處理函數;進過一番檢查後,將中斷的現場保存和處理函數以及恢復現場進行重新修改,最終能進入中斷函數了;可以進行調試了;
- 第四個問題,在第一個中斷出現的時候是發送設備地址結束後,從設備將回復一個ACK,然後進入處理函數,進行判別,完成後發送第一個數據到從設備;然後退出處理函數,等待第二個中斷,這裏又卡住了;經過縝密的思考,想到中斷處理完成後應該清除中斷標誌,免得下一次的中斷觸發不了,這就是一直卡住的問題,解決了這個問題之後,終於可以發送數據的時候多次產生中斷了,因爲I2C發送數據是8bits發的,所以數據超過8bits的時候肯定會多次產生中斷;
- 解決完這些問題之後進行調試,如下所示。
第二次測試:
可以看到我們的程序可以實現寫進AT24C08 I2C設備,也可以從AT24C08 I2C設備讀出來,並且是正確的。 - 但是我們可以看到另外一個問題是讀出來的數據前面有很多的點和P
- 這個問題還亟待解決。
直接貼上代碼,有問題的留言回覆。
- 下面貼出代碼:
第一個程序I2C_test.c
void do_write_at24cxx(void)
{
unsigned int addr;
unsigned char str[100];
int err;
/* 獲得地址 */
puts("Enter the address of sector to write: ");
scanf("%d",&addr);
if (addr > 256)
{
puts("address > 256, error!\n\r");
return;
}
puts("Enter the string to write: ");
gets(str);
puts("writing ...\n\r");
err = at24cxx_write(addr, str, strlen(str)+1);
printf("at24cxx_write ret = %d",err);
}
void do_read_at24cxx(void)
{
unsigned int addr;
int i, j;
unsigned char c;
unsigned char data[100];
unsigned char str[16];
int len;
int err;
int cnt = 0;
/* 獲得地址 */
puts("Enter the address to read: ");
scanf("%d",&addr);
if (addr > 256)
{
puts("address > 256, error!\n\r");
return;
}
/* 獲得長度 */
puts("Enter the length to read: ");
scanf("%d",&len);
err = at24cxx_read(addr, data, len);
printf("at24cxx_read ret = %d \n",err);
puts("Data : \n\r");
/* 長度固定爲64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16個數據 */
for (j = 0; j < 16; j++)
{
/* 先打印數值 */
c = data[cnt++];
str[j] = c;
printf("%02x ", c);
}
puts(" ; ");
for (j = 0; j < 16; j++)
{
/* 後打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可視字符 */
put_char('.');
else
put_char(str[j]);
}
puts("\n\r");
}
}
void i2c_test(void)
{
char c;
/* 初始化 */
i2c_init();
while (1)
{
/* 打印菜單, 供我們選擇測試內容 */
puts("[w] Write at24cxx\n\r");
puts("[r] Read at24cxx\n\r");
puts("[q] quit\n\r");
puts("Enter selection: ");
c = getc();
put_char(c);
/* 測試內容:
* 3. 編寫某個地址
* 4. 讀某個地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 'w':
case 'W':
do_write_at24cxx();
break;
case 'r':
case 'R':
do_read_at24cxx();
break;
default:
break;
}
}
}
第二個程序:at24cxx.c
#include "i2c_controller.h"
#define AT24CXX_ADDR 0x50
int at24cxx_write(unsigned int addr, unsigned char *data, int len)
{
i2c_msg msg;
int i;
int err;
unsigned char buf[2];
//printf("len = %d\n",len);
for (i = 0; i < len; i++)
{
buf[0] = addr++;
buf[1] = data[i];
/* 構造i2c_msg */
msg.addr = AT24CXX_ADDR;
msg.flags = 0; /* write */
msg.len = 2;
msg.buf = buf;
msg.err = 0;
msg.cnt_transferred = -1;
/* 調用i2c_transfer */
err = i2c_transfer(&msg, 1);
if (err)
return err;
}
return 0;
}
int at24cxx_read(unsigned int addr, unsigned char *data, int len)
{
i2c_msg msg[2];
int err;
//puts("at24xx_read func!\n");
/* 構造i2c_msg */
msg[0].addr = AT24CXX_ADDR;
msg[0].flags = 0; /* write */
msg[0].len = 1;
msg[0].buf = &addr;
msg[0].err = 0;
msg[0].cnt_transferred = -1;
msg[1].addr = AT24CXX_ADDR;
msg[1].flags = 1; /* read */
msg[1].len = len;
msg[1].buf = data;
msg[1].err = 0;
msg[1].cnt_transferred = -1;
/* 調用i2c_transfer */
err = i2c_transfer(&msg, 2);
if (err)
return err;
return 0;
}
第三個程序: i2c_controller.c
#include "i2c_controller.h"
#define I2C_CONTROLLER_NUM 10
/* 有一個i2c_controller數組用來存放各種不同芯片的操作結構體 */
static p_i2c_controller p_i2c_controllers[I2C_CONTROLLER_NUM];
static p_i2c_controller p_i2c_con_selected;
void register_i2c_controller(p_i2c_controller *p)
{
int i;
for (i = 0; i < I2C_CONTROLLER_NUM; i++)
{
if (!p_i2c_controllers[i])
{
p_i2c_controllers[i] = p;
return 0;
}
}
}
/* 根據名字來選擇某款I2C控制器 */
int select_i2c_controller(char *name)
{
int i;
for (i = 0; i < I2C_CONTROLLER_NUM; i++)
{
if (p_i2c_controllers[i] && !strcmp(name, p_i2c_controllers[i]->name))
{
p_i2c_con_selected = p_i2c_controllers[i];
return 0;
}
}
return -1;
}
/* 實現 i2c_transfer 接口函數 */
int i2c_transfer(p_i2c_msg msgs, int num)
{
//puts("i2c_transfer func!\n");
return p_i2c_con_selected->master_xfer(msgs, num);
}
void i2c_init(void)
{
/* 註冊下面的I2C控制器 */
s3c2440_i2c_con_add();
/* 選擇某款I2C控制器 */
select_i2c_controller("s3c2440");
/* 調用它的init函數 */
p_i2c_con_selected->init();
}
第四個也是最重要的一個:s3c2440_i2c_controller.c
#include"i2c_controller.h"
#include"interrupt_init.h"
#include"led_on.h"
#include"uart.h"
#define IICCON (*(volatile unsigned char *)0x54000000) //IIC控制寄存器
#define IICSTAT (*(volatile unsigned char *)0x54000004)//IIC狀態寄存器
#define IICDS (*(volatile unsigned char *)0x5400000C) //IIC讀寫寄存器
#define GPECON (*(volatile unsigned long *)0x56000040)//GPIOE控制寄存器
#define GPEUP (*(volatile unsigned long *)0x56000048) //GPIOE上拉電阻配置寄存器
#define INTPND (*(volatile unsigned long *)0X4A000010)
#define SRCPND (*(volatile unsigned long *)0X4A000000)
#define INTMSK (*(volatile unsigned long *)0X4A000008)
#define INTOFFSET (*(volatile unsigned long *)0X4A000014)
static p_i2c_msg p_cur_msg;
int isLastData(void)
{
if (p_cur_msg->cnt_transferred == p_cur_msg->len - 1)
return 1; /* 正要開始傳輸最後一個數據 */
else
return 0;
}
void resume_iic_with_ack(void)
{
unsigned int iiccon = IICCON;
iiccon |= (1<<7); /* 迴應ACK */
iiccon &= ~(1<<4); /* 恢復IIC操作 */
IICCON = iiccon;
}
void resume_iic_without_ack(void)
{
unsigned int iiccon = IICCON;
iiccon &= ~((1<<7) | (1<<4)); /* 不迴應ACK, 恢復IIC操作 */
IICCON = iiccon;
}
int i2c_interrupt_func(int irq)
{
int index;
unsigned int iicstat = IICSTAT;
unsigned int iiccon;
//puts("i2c_interrupt_func! ");
p_cur_msg->cnt_transferred++;
/* 每傳輸完一個數據將產生一箇中斷 */
/* 對於每次傳輸, 第1箇中斷是"已經發出了設備地址" */
if (p_cur_msg->flags == 0) /* write */
{
/* 對於第1箇中斷, 它是發送出設備地址後產生的
* 需要判斷是否有ACK
* 有ACK : 設備存在
* 無ACK : 無設備, 出錯, 直接結束傳輸
*/
if (p_cur_msg->cnt_transferred == 0) /* 第1次中斷 */
{
if (iicstat & (1<<0))
{ /* no ack */
/* 停止傳輸 */
IICSTAT = 0xd0;
/*清除中斷*/
IICCON &= ~(1<<4);
/*返回錯誤*/
p_cur_msg->err = -1;
puts("tx err, no ack\n\r");
delay(1000);
return;
}
//puts(" first write addr.");
}
if (p_cur_msg->cnt_transferred < p_cur_msg->len)
{
/* 對於其他中斷, 要繼續發送下一個數據
*/
//puts("write continue. ");
//printf(" Arealy writen data size %d",p_cur_msg->cnt_transferred+1);
IICDS = p_cur_msg->buf[p_cur_msg->cnt_transferred];
//IICCON &= ~(1<<4);
}
else
{
//puts("transmitting end!");
/* 停止傳輸 */
IICSTAT = 0xd0;
/*清除中斷*/
IICCON &= ~(1<<4);
delay(1000);
}
}
else /* read */
{
//puts("this is read. ");
/* 對於第1箇中斷, 它是發送出設備地址後產生的
* 需要判斷是否有ACK
* 有ACK : 設備存在, 恢復I2C傳輸, 這樣在下一個中斷纔可以得到第1個數據
* 無ACK : 無設備, 出錯, 直接結束傳輸
*/
if (p_cur_msg->cnt_transferred == 0) /* 第1次中斷 */
{
if (iicstat & (1<<0))
{ /* no ack */
/* 停止傳輸 */
IICSTAT = 0x90;
IICCON &= ~(1<<4);
p_cur_msg->err = -1;
puts(" rx err, no ack\n\r");
delay(1000);
return;
}
else /* have ack */
{
/* 如果是最後一個數據, 啓動傳輸時要設置爲不迴應ACK */
/* 恢復I2C傳輸 */
if (isLastData())
{
resume_iic_without_ack();
}
else
{
resume_iic_with_ack();
}
return;
}
}
/* 非第1箇中斷, 表示得到了一個新數據
* 從IICDS讀出、保存
*/
if (p_cur_msg->cnt_transferred < p_cur_msg->len)
{
index = p_cur_msg->cnt_transferred - 1;
p_cur_msg->buf[index] = IICDS;
/* 如果是最後一個數據, 啓動傳輸時要設置爲不迴應ACK */
/* 恢復I2C傳輸 */
if (isLastData())
{
resume_iic_without_ack();
}
else
{
resume_iic_with_ack();
}
}
else
{
/* 發出停止信號 */
IICSTAT = 0x90;
IICCON &= ~(1<<4);
delay(1000);
}
}
//puts("interrput end!!\n");
}
void s3c2440_i2c_con_init(void)
{
//puts("init func!\n");
INTPND |= (1 << 27);
SRCPND |= (1 << 27);
INTMSK &= ~(1 << 27);
IICCON |= (1 << 5);
GPECON |= ((0x2 << 28) | (0x2 << 30));
GPEUP |= (0x3 << 14);
IICSTAT |= (1 << 4);
IICCON |= (1 << 7);
IICCON &= ~(1 << 6);
IICCON &= ~(0xf << 0);
IICCON |= (0x5 << 0);
/* 設置時鐘 */
/* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode
* [6] : 時鐘源, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512
* [5] : 1-enable interrupt
* [4] : 讀出爲1時表示中斷髮生了, 寫入0來清除並恢復I2C操作
* [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1).
* Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1)
*/
/* 註冊中斷處理函數 */
register_irq(27, i2c_interrupt_func);
}
int do_master_tx(p_i2c_msg msg)
{
//puts("do_master_tx!\n");
p_cur_msg = msg;
msg->cnt_transferred = -1;
msg->err = 0;
/* 設置寄存器啓動傳輸 */
/*設置處理器爲主發送模式*/
IICSTAT |= (1 << 7);
IICSTAT |= (3 << 6);
/* 2. 把從設備地址寫入IICDS */
IICDS = msg->addr<<1;
/*清除中斷*/
IICCON &= ~(1 << 4);
/* 3. IICSTAT = 0xf0 , 數據即被髮送出去, 將導致中斷產生 */
IICSTAT = 0xf0;
/* 後續的傳輸由中斷驅動 */
/* 循環等待中斷處理完畢 */
while (!msg->err && msg->cnt_transferred != msg->len)
{
IICCON &= ~(1<<4);
}
//puts("transmitting complishment!!! \n");
if (msg->err)
return -1;
else
return 0;
}
int do_master_rx(p_i2c_msg msg)
{
//puts("do_master_rx!\n");
p_cur_msg = msg;
msg->cnt_transferred = -1;
msg->err = 0;
/* 設置寄存器啓動傳輸 */
/* 1. 配置爲 Master Rx mode */
/* RX mode, 在ACK週期迴應ACK */
IICSTAT |= (1 << 7);
IICSTAT |= (3 << 6);
/* 2. 把從設備地址寫入IICDS */
IICDS = (msg->addr<<1)|(1<<0);
IICCON &= ~(1 << 4);
IICSTAT = 0xf0;
/* 3. IICSTAT = 0xb0 , 從設備地址即被髮送出去, 將導致中斷產生 */
/* 後續的傳輸由中斷驅動 */
/* 循環等待中斷處理完畢 */
while (!msg->err && msg->cnt_transferred != msg->len)
{
IICCON &= ~(1<<4);
}
//puts("Reading complishment!!! \n");
if (msg->err)
return -1;
else
return 0;
}
int s3c2440_master_xfer(p_i2c_msg msgs, int num)
{
int i;
int err;
//puts("s3c2440_master_xfer!\n");
for (i = 0; i < num; i++)
{
if (msgs[i].flags == 0)/* write */
err = do_master_tx(&msgs[i]);
else
err = do_master_rx(&msgs[i]);
if (err)
return err;
}
return 0;
}
/* 實現i2c_controller
.init
.master_xfer
.name
*/
static i2c_controller s3c2440_i2c_con = {
.name = "s3c2440",
.init = s3c2440_i2c_con_init,
.master_xfer = s3c2440_master_xfer,
};
void s3c2440_i2c_con_add(void)
{
register_i2c_controller(&s3c2440_i2c_con);
}
第五個:start.S
.text
.global _start
_start:
b reset
ldr pc, _undefine_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
/*棣栧厛瀹氫箟寮傚父鍚戦噺琛紝鍐嶈瀹氳煩杞湴鍧€*/
_undefine_instruction: .word undefine_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
undefine_instruction:
nop
software_interrupt:
nop
prefetch_abort:
nop
data_abort:
nop
not_used:
nop
irq:
/*淇濆瓨鐜*/
sub lr, lr, #4
/*鎵歸噺淇濆瓨*/
stmdb sp!,{r0-r12,lr} /*db,SP鎸囬拡鍏堝噺鍚庡姞*/
bl handle_irq_c
/*鎭㈠鐜板満*/
ldmia sp!,{r0-r12,pc}^ /*ia 鍏堣鍚庡姞,鎶妉r閲岄潰瀛樼殑鎭㈠鍒癙C
*^浼氭妸spsr鐨勫€兼仮澶嶅埌cpsr閲岄潰*/
fiq:
nop
reset:
bl set_svc
bl disable_watchdog
bl disable_interrupt
bl disable_cache_mmu
bl clock_init
bl sdram_init
bl stack_init
bl nandflash_init
bl copy_to_ram
bl bss_init
bl uart_init
ldr pc, =main
set_svc:
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
mov pc, lr
#define pWTCON 0x53000000
disable_watchdog:
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov pc, lr
disable_interrupt:
mvn r1, #0x0
ldr r0, =0x4a000008
str r1, [r0]
mov pc, lr
disable_cache_mmu:
mcr p15,0,r0,c7,c7,0
mrc p15,0,r0,c1,c0,0
bic r0, r0, #0x00000007
mcr p15,0,r0,c1,c0,0
mov pc, lr
#define CLKDIVN 0x4c000014
#define MPLLCON 0x4c000004
#define MPLL_405MHz ((127<<12) | (2<<4) | (1<<0))
clock_init:
ldr r0, =CLKDIVN
mov r1, #0x5
str r1, [r0]
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
mcr p15,0,r0,c1,c0,0
ldr r0, =MPLLCON
ldr r1, =MPLL_405MHz
str r1, [r0]
mov pc, lr
#define BWSCON 0x48000000
sdram_init:
ldr r0, =BWSCON
add r3, r0, #4*13
adrl r1, mem_data
0:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r0, r3
bne 0b
mov pc, lr
mem_data:
.long 0x22000000
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00018001
.long 0x00018001
.long 0x008c04f5
.long 0x000000b1
.long 0x00000030
.long 0x00000030
copy_to_ram:
mov r0, #0x00
ldr r1, =_start
ldr r2, =bss_end
sub r2, r2, r1
mov ip, lr //淇濆瓨lr
bl nand_to_ram//nand璺寵嚦鍐呭瓨
mov lr, ip //鎭㈠lr
mov pc, lr
stack_init:
msr cpsr_c, #0xd2 //璁劇疆涓轟腑鏂ā寮?
ldr sp, =0x33000000 //鍒濆鍖杛13_irq瀵勫瓨鍣ㄧ殑sp鎸囬拡
msr cpsr_c, #0xd3 //璁劇疆涓轟腑鏂ā寮?
ldr sp, =0x34000000 //鍒濆鍖杛13_svc瀵勫瓨鍣ㄧ殑sp鎸囬拡
mov pc, lr
bss_init:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1
moveq pc, lr
clean_loop:
mov r2, #0
str r2, [r0], #4
cmp r0, r1
bne clean_loop
mov pc, lr