九.mini2440實現I2C協議裸機程序(完成)

**

I2C協議的mini2440裸機程序實現

**

  1. 上篇博客說道了I2C的理論知識,但是實際應用的時候,遇到很多的問題,加上程序的龐大,一直在調試。這幾天重新看了一下,最終解決問題。
  2. 首先的問題是程序超過4K之後一直卡住不運行,我之前的猜想是程序既然大於4K不能運行,那麼爲了能調試,先把其他的實驗的代碼進行裁剪,能不能滿足要求,但是發現光是I2C的代碼已經超過4K,所以一定是不行的;接着爲什麼是4K,跟CPU裏面的standstone大小剛好一致,是巧合嗎?不是的,這肯定是.start.S出了問題,才導致拷貝到內存的時候出了問題,因爲nand flash 拷貝到standstone是自動設置的,我們不需要做什麼,然後程序肯定會超過4K,那就需要拷貝到內存進行運行,可以肯定的說問題一定是出現在拷貝的過程中。
  3. 在參考了國嵌的.start.S代碼後,發現問題,直接使用國嵌的代碼,來進行初始化,發現果然可以調試了,說明代碼已經複製到內存了,這一步成功後,接下來的就是進行調試了;這裏就是我的第二個問題。由於直接使用的代碼,並沒有及時的發現中斷處理這個地方,國嵌是空着沒處理的。這就導致了I2C中斷產生後一直卡住,進不了處理函數;進過一番檢查後,將中斷的現場保存和處理函數以及恢復現場進行重新修改,最終能進入中斷函數了;可以進行調試了;
  4. 第四個問題,在第一個中斷出現的時候是發送設備地址結束後,從設備將回復一個ACK,然後進入處理函數,進行判別,完成後發送第一個數據到從設備;然後退出處理函數,等待第二個中斷,這裏又卡住了;經過縝密的思考,想到中斷處理完成後應該清除中斷標誌,免得下一次的中斷觸發不了,這就是一直卡住的問題,解決了這個問題之後,終於可以發送數據的時候多次產生中斷了,因爲I2C發送數據是8bits發的,所以數據超過8bits的時候肯定會多次產生中斷;
  5. 解決完這些問題之後進行調試,如下所示。
    在這裏插入圖片描述
    第二次測試:
    在這裏插入圖片描述
    可以看到我們的程序可以實現寫進AT24C08 I2C設備,也可以從AT24C08 I2C設備讀出來,並且是正確的。
  6. 但是我們可以看到另外一個問題是讀出來的數據前面有很多的點和P在這裏插入圖片描述
  7. 這個問題還亟待解決。

直接貼上代碼,有問題的留言回覆。

  1. 下面貼出代碼:

第一個程序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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章