一. 總體
要驅動按鍵中斷控制LED亮滅,程序要進行如下幾部分操作:
在start.S中對CPSR寄存器中清除I位,使能IRQ,這是大前提
根據原理圖找出按鍵對應的外部中斷,對外部中斷對應引腳做相應配置,使能相應的外部中斷:EINTMASK
開啟中斷使能:INTMSK要設置
編寫C中斷處理函數,通過INTOFFSET、EINTPEND確定哪個中斷觸發,并做相應處理,還要清除中斷標志位
編寫start.S中的IRQ異常處理函數
二. CPSR設置
CPSR的IRQ中斷使能位不使能,都行不通,我就找bug找了一天。。。
在代碼重定位之后就對CPSR的I位清零,并且分配棧指針,如下:
mrs r0, cpsr /* 讀出cpsr */
bic r0, r0, #0xf /* 修改M4-M0為0b10000, 進入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中斷 */
msr cpsr, r0
/* 設置 sp_usr */
ldr sp, =0x33f00000
CPU再運行程序的時候,會自動使用棧,棧指針sp在調用C程序之前就要設定。 CPU的內存RAM空間存放規律一般是分段的,從地址向高地址,依次為:程序段(.text),BSS段,然后上面還可能會有堆空間,然后最上面才是堆棧段,這樣安排堆棧,是因為堆棧的特點決定的,所以堆棧的指針SP初始化一般在堆棧段的高地址,也就是內存的高地址,然后讓堆棧指針向下增長(其實就是遞減)。這樣做的好處就是堆棧空間遠離了其他段,不會跟其他段重疊,造成修改其他段數據,而引起不可預料的后果,還有設置堆棧大小的原則,要保證棧不會下溢出到數據空間或者程序空間。所謂堆棧溢出,是指堆棧指針SP向下增長到其他段空間,如果棧指針向下增長到其他段空間,稱為堆棧溢出。堆棧溢出會修改其他空間的值,嚴重情況下可造成死機.
三. 中斷源設置
中斷源的設置,主要是三點:
配置按鍵引腳為外部中斷模式
配置外部中斷的觸發方式:上升沿、下降沿都觸發
使能相應的外部中斷線(EINT0~3默認開啟使能)
通過查看板子的原理圖獲取按鍵對應的引腳,以及對應的外部中斷線,中斷源配置的代碼如下:
/* 中斷源(按鍵引腳)初始化
* EINT0 EINT2 EINT11 EINT19
* GPF0 GPF2 GPG3 GPG11
* S2 S3 S4 S5
* 說明:配置按鍵對應的引腳、配置中斷觸發方式
*/
void Exti_SoucreKeyInit( void )
{
/* GPF0、2配置為外部中斷引腳模式 */
GPFCON &= ~( (3<<0)|(3<<4) );
GPFCON |= ( (2<<0)|(2<<4) );
/* GPG3、11配置為外部中斷引腳模式 */
GPGCON &= ~( (3<<6)|(3<<22) );
GPGCON |= ( (2<<6)|(2<<22) );
/* EXTINTx 外部中斷控制寄存器設置中斷觸發模式:上升沿、下降沿都觸發 配置相應位為11x */
EXTINT0 |= ( (7<<0)|(7<<8) ); //EXINT0、2
EXTINT1 |= (7<<12); //EXINT11
EXTINT2 |= (7<<12); //EXINT19
/* EINTMASK 外部中斷使能寄存器,開啟對應的外部中斷,清零開啟 */
EINTMASK &= ~( (1<<11)|(1<<19) ); //EINT0~3默認開啟使能
}
四. 中斷控制器設置
根據中斷的流程圖來配置,先分析一下各個寄存器:
MODE用來配置中斷的模式,可以選擇中斷模式為FIQ,這里我們只使用IRQ,所以MODE寄存器不需要設置,如圖:默認是IRQ
MASK相當于使能位(默認是屏蔽的),所以要清零INTMASK寄存器使能相應的中斷。設置INTMASK來使能:
讀取INTPND可以得到當前正在處理的中斷是哪一個,因為可能存在多個中斷請求在排隊,優先級問題。配合OFFSET寄存器使用效果更加。
而且不需要手動清除INTPND,在清除SRCPND的時候,INTPND會自動清除
SRCPND寄存器用來產看發生請求的中斷控制線(中斷源):
外部中斷是without sub-register的,所以中斷的請求直達SRCPND,所以可以通過SRCPND寄存器來顯示是否有中斷請求,相當于中斷標志位,所以在處理中斷之后需要清除SRCPND。
所以在初始化中斷控制器的時候,只要打開使能位即可,就是配置一下INTMASK就可以。SRCPND和INTPND是在中斷處理函數里使用的,判斷是哪一個中斷請求的
中斷控制器有關的初始化代碼如下:
/* 中斷控制器初始化
* 說明:開啟中斷使能,在EINTMASK、INTMSK中,只有EINT0~3是默認不屏蔽的
*/
void Exti_InterruptControlInit( void )
{
/* 開啟中斷使能 */
INTMSK &= ~( (1<<0)|(1<<2)|(1<<5) );
}
五. C中斷處理函數
在C中斷處理函數中,要分辨中斷請求,根據相應的中斷請求做出回應,我使用按鍵來控制三個LED的亮滅狀態,具體代碼如下:
#define LED1_ON GPFDAT &= ~(1<<4)
#define LED2_ON GPFDAT &= ~(1<<5)
#define LED3_ON GPFDAT &= ~(1<<6)
#define LED1_OFF GPFDAT |= (1<<4)
#define LED2_OFF GPFDAT |= (1<<5)
#define LED3_OFF GPFDAT |= (1<<6)
/* 中斷處理函數
* 說明:觸發外部中斷后,進入中斷處理函數,通過SRCPND來判斷觸發了哪一個中斷,從而進行相應的操作。退出時要清理中斷
*/
void Exti_ProcessingInterrupt( void )
{
unsigned int temp = INTOFFSET;
unsigned int reset = 0;//用來清零up,
puts("nrIRQ!nr");
/* 可以通過INTOFFSET寄存器的值來直接判斷哪個中斷觸發了 */
if( temp==0 ) //EINT0
{
if(GPFDAT&(1<<0)) //引腳高電平,按鍵按下,點亮LED
{
LED1_ON;
}
else
{
LED1_OFF;
}
reset |= (1<<0);
}
else if( temp==2 ) //EINT2
{
if(GPFDAT&(1<<2)) //GPF2
{
LED2_ON;
}
else
{
LED2_OFF;
}
reset |= (1<<2);
}
else if( temp==5 ) //EINT8_23
{
if( EINTPEND&(1<<11) )
{
if(GPGDAT&(1<<3)) //GPG3
{
LED3_ON;
}
else
{
LED3_OFF;
}
}
else if( EINTPEND&(1<<19) )
{
if(GPGDAT&(1<<11)) //GPG11
{
LED1_ON;
LED2_ON;
LED3_ON;
}
else
{
LED1_OFF;
LED2_OFF;
LED3_OFF;
}
}
reset |= (1<<5);
}
/* 清除SRCPND、INTPND標志位 */
EINTPEND |= ( (1<<11)|(1<19) );//寫1清除
SRCPND |= ( (1<<0)|(1<2)|(1<<5) );
INTPND |= ( (1<<0)|(1<2)|(1<<5) );
}
最后還要清除EINTPEND、SRCPND、INTPND中的標志位,不然程序會一直處于中斷中。
六. 匯編IRQ異常處理程序
當觸發外部中斷時,CPU會去中斷向量表中IRQ的指令位置,跳轉到IRQ的處理程序中,所以首先得在_start之后的0X18處寫一個跳轉指令,跳轉至IRQ處理程序,如下:
_start:
/* 異常向量表 */
bl reset /* 0X0 Reset 上電復位,從0地址開始執行程序,依次:關閉看門狗、配置時鐘系統、初始化sdram、拷貝代碼到sdram(重定位)、清除.bcc段、進入mian函數 */
bl halt /* 0X4 Undefined instruction */
bl halt /* 0X8 Software Interrupt */
bl halt /* 0XC Abort(prefetch) */
bl halt /* 0X10 Abort(data) */
bl halt /* 0X14 Reserved */
ldr pc, =irq_addr /* 0X18 IRQ */
bl halt /* 0X1C FIQ */
.align 4
irq_addr:
.word do_irq
在do_irq程序中,首先指定棧指針,然后保存現場(注意返回的值是lr-4),然后調用C處理函數,然后恢復現場,如下:
.align 4
do_irq:
ldr sp, =0x33d00000
sub lr, lr, #4 /* 發生中斷時,返回值是lr-4 */
stmdb sp!, {r0-r12,lr}
bl Exti_ProcessingInterrupt
ldmia sp!, {r0-r12,pc}^
七. 源碼
start.S
.text
.global _start
_start:
/* 異常向量表 */
bl reset /* 0X0 Reset 上電復位,從0地址開始執行程序,依次:關閉看門狗、配置時鐘系統、初始化sdram、拷貝代碼到sdram(重定位)、清除.bcc段、進入mian函數 */
bl halt /* 0X4 Undefined instruction */
bl halt /* 0X8 Software Interrupt */
bl halt /* 0XC Abort(prefetch) */
bl halt /* 0X10 Abort(data) */
bl halt /* 0X14 Reserved */
ldr pc, =irq_addr /* 0X18 IRQ */
bl halt /* 0X1C FIQ */
.align 4
irq_addr:
.word do_irq
.align 4
do_irq:
ldr sp, =0x33d00000
sub lr, lr, #4 /* 發生中斷時,返回值是lr-4 */
stmdb sp!, {r0-r12,lr}
bl Exti_ProcessingInterrupt
ldmia sp!, {r0-r12,pc}^
.align 4
reset:
/* 關閉看門狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 設置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 設置CPU工作于異步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 設置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦設置PLL, 就會鎖定lock time直到PLL輸出穩定
* 然后CPU工作于新的頻率FCLK
*/
/* 設置內存: sp 棧 */
/* 分辨是nor/nand啟動
* 寫0到0地址, 再讀出來
* 如果得到0, 表示0地址上的內容被修改了, 它對應ram, 這就是nand啟動
* 否則就是nor啟動
*/
mov r1, #0
ldr r0, [r1] /* 讀出原來的值備份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND啟動 */
ldr sp, =0x40000000+4096 /* 先假設是nor啟動 */
moveq sp, #4096 /* nand啟動 */
streq r0, [r1] /* 恢復原來的值 */
/* 代碼重定位的時候,首先初始化SDRAM,然后拷貝代碼,然后清除.bss段(防止內存訪問出錯),最后執行main函數,main就在重定位的SDRAM中去執行 */
bl sdram_init
/* 重定位text, rodata, data段整個程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
mrs r0, cpsr /* 讀出cpsr */
bic r0, r0, #0xf /* 修改M4-M0為0b10000, 進入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中斷 */
msr cpsr, r0
/* 設置 sp_usr */
ldr sp, =0x33f00000
//bl main /* 使用BL命令相對跳轉, 程序仍然在NOR/sram執行 */
ldr pc, =main /* 絕對跳轉, 跳到SDRAM */
.align 4
halt:
b halt
exti.c
#include "s3c2440_soc.h"
#include "uart.h"
/* LED引腳低電平點亮 */
#define LED1_ON GPFDAT &= ~(1<<4)
#define LED2_ON GPFDAT &= ~(1<<5)
#define LED3_ON GPFDAT &= ~(1<<6)
#define LED1_OFF GPFDAT |= (1<<4)
#define LED2_OFF GPFDAT |= (1<<5)
#define LED3_OFF GPFDAT |= (1<<6)
/* 中斷控制器初始化
* 說明:開啟中斷使能,在EINTMASK中,只有EINT0~3是默認不屏蔽的
*/
void Exti_InterruptControlInit( void )
{
/* 開啟中斷使能 */
INTMSK &= ~( (1<<0)|(1<<2)|(1<<5) );
}
/* 中斷源(按鍵引腳)初始化
* EINT0 EINT2 EINT11 EINT19
* GPF0 GPF2 GPG3 GPG11
上一篇:LPC2000系列學習筆記4--存儲器映射控制
下一篇:S3C2440—11.und異常
推薦閱讀
史海拾趣
隨著公司實力的不斷增強,AB Connectors Ltd開始將目光投向國際市場。公司積極參加國際電子展會和商務洽談活動,與海外客戶建立了廣泛的合作關系。同時,公司還通過設立海外辦事處和建立分銷網絡等方式,進一步拓展國際市場。這些舉措不僅提高了公司的知名度和影響力,還為公司的快速發展提供了有力支持。
為了進一步提升國際競爭力,Goldentech制定了明確的國際化戰略。公司通過設立海外研發中心、銷售網絡和服務中心,加強與國際市場的聯系和溝通。同時,Goldentech還積極參與國際標準的制定和推廣工作,提升其在全球半導體行業的話語權和影響力。經過多年的努力,Goldentech已經成功在多個國家和地區建立了完善的業務體系和服務網絡,為全球客戶提供更加便捷、高效的服務和支持。
進入21世紀后,隨著全球電子市場的競爭加劇,Goldentech意識到單靠技術創新已不足以維持其市場地位。因此,公司開始積極尋求與行業領先企業的戰略合作。通過與一家知名芯片設計公司的深度合作,Goldentech成功將其高性能離散半導體器件集成到對方的芯片設計中,從而實現了產品的廣泛應用。這一合作不僅擴大了Goldentech的市場份額,還提升了其品牌知名度和行業影響力。
隨著國內市場的逐漸飽和,eLED.com Corp開始將目光投向國際市場。公司積極參與各類國際展會,向全球客戶展示其優質的產品和技術。同時,eLED.com Corp還與國際知名企業開展合作,共同研發新技術,推動LED行業的發展。通過與國際巨頭的合作,eLED.com Corp不僅提升了自身的技術水平,還拓展了國際市場,實現了品牌的全球化布局。
作為一家致力于LED行業的公司,eLED.com Corp深知LED產品對于節能減排的重要性。因此,公司始終將綠色環保理念貫穿于產品的研發、生產和銷售過程中。通過采用環保材料和節能技術,eLED.com Corp的產品不僅具有出色的性能,還具備較低的能耗和較長的使用壽命。此外,公司還積極參與各類環保公益活動,推動LED行業向更加綠色、環保的方向發展。
AUREL公司早期在電子行業中并不顯眼,直到它成功研發出一款具有革命性的半導體芯片。這款芯片在功耗、性能和穩定性上都達到了前所未有的水平,立刻引起了市場的廣泛關注。公司通過積極的市場推廣和技術合作,逐漸打開了國內外市場,奠定了在電子行業中的技術領先地位。
不知大家有沒有弄過I2C的 原來沒有弄過, 想弄下, 前幾天看啦1114的例程大概了解,這幾天 尋思再研究下, 不知有沒有弄過的 , 分享下, 單片機的I2C 倒是弄啦下, 就是AT24C02 的EEPROM , 但是弄到1114上, 應該是有點不得 ...… 查看全部問答∨ |
|
主要是INF文檔怎么寫的啊,是不是裝好WINDDK后,用INF去安裝winusb.sys文件啊,弄了好久都沒弄出來不知道有沒有人在成功過的,幫幫忙啊。… 查看全部問答∨ |
|
現有一個運行在IBM大型主機上的DB2數據庫,數據量比較大,LS讓我找一個攜帶方便的小開發板(價錢不要超過一臺普通PC,可以掛在墻上,能手持的最好),我選了MINI2440,現在需要從數據庫里取一部分數據,在板子上用形象一點的圖顯示出來,同時也能往 ...… 查看全部問答∨ |
學硬件和嵌入式系統方面的一般有什么證要考呢? 其考試時間一般是什么時候? 在哪里可以找到相關的考試資料和信息? 這些證的含金量怎樣?我是學硬件的,將來也想從事嵌入式系統設計方面工作,考什么證好些呢?… 查看全部問答∨ |
2009年應是金融危機體現最大一年,但是我們公司逆流而上,借此機遇擴充、收編開發隊伍。只要符合招聘要求的,愿意到珠三角工作,薪金待遇絕對豐厚。 高級wince開發工程師 職責描述: 1、負責WinCE嵌入式產品底層驅動、軟件的開發,主要包括CE內 ...… 查看全部問答∨ |
CEPC選擇"Enable Kernel Debugger"后出現錯誤 1.我用CEPC創建了一個工程,但是發現使用KITL和"Enable Full Kernel Mode"項后,是可以啟動的 2.增加"Enable Kernel Debugger"項后,CEPC無法起來 彈出"The Debugger KITL packet receive has timed out. Do you wish to retr ...… 查看全部問答∨ |
用vs2005mfc編寫wince程序,如何遍歷系統下的所有目錄及子目錄? 用vs2005mfc編寫wince程序,如何遍歷系統下的所有目錄及子目錄? 試過了很多方法,都不行,mfc中的CFileFind類在人wince下不支持,郁悶哦! 請高手指教!!… 查看全部問答∨ |
SOSO姐,TIANKAI大哥。首先謝謝你們的板子。雖然到現在還用不上,但是通過這幾天自己的擺弄,對51有了初步的認識。 51MUC內部結構還不是很懂,但是對咱壇子里的板子原理,我是了解的很深了。現在,我發現了一個問題。不知道對不對。 大家 ...… 查看全部問答∨ |