這節課我們來寫一個定時器的中斷服務程序
使用定時器來實現點燈計數
查考資料就是第10章PWM TIMER
我們先把這個結構圖展示出來
這個圖的結構很好
這里面肯定有一個clk(時鐘),
1 、每來一個clk(時鐘)這個TCNTn減去1
2、 當TCNTn == TCMPn時,可以產生中斷,也可以讓對應的SPWM引腳反轉,(比如說原來是高電平,發生之后電平轉換成低電平)
3、 TCNTn繼續減1,當TCNTn == 0時,可以產生中斷,pwm引腳再次反轉 TCMPn 和 TCNTn的初始值來自TCMPBn,TCNTBn
4 、TCNTn == 0時,可自動加載初始
怎么使用定時器?
1、 設置時鐘
2 、設置初值
3、 加載初始,啟動Timer
4、 設置為自動加載
5 、中斷相關
由于2440沒有引出pwm引腳,所以pwm功能無法使用,也就無法做pwm相關實驗,所謂pwm是指可調制脈沖
T1高脈沖和T2低脈沖它的時間T1, T2可調整,可以輸出不同頻率不同占控比的波形,在控制電機時特別有用
我們這個程序只做一個實驗,當TCNTn這個計數器到0的時候,就產生中斷,在這個中斷服務程序里我們點燈
寫代碼
打開我們的main函數
int main(void)
{
led_init();
interrupt_init(); /* 初始化中斷控制器 */
//我們初始化了中斷源,同樣的,我們初始化timer
key_eint_init(); /* 初始化按鍵, 設為中斷源 */
//初始化定時器
timer_init();
我們需要實現定時器初始化函數
新建一個timer.c ,我們肯定需要操作一堆寄存器,添加頭文件
#include "s3c2440_soc.h"
void timer_init(void)
設置TIMER0的時鐘
設置TIMER0的初值
加載初值, 啟動timer0
設置為自動加載并啟動(值到0以后會自動加載)
設置中斷,顯然我們需要提供一個中斷處理函數void timer_irq(void)在這里面我們需要點燈
打開芯片手冊,我們想設置timer0的話
首先設置8-Bit Prescaler
設置5.1 MUX(選擇一個時鐘分頻)
設置TCMPB0和TCNTB0
設置TCONn寄存器
看手冊上寫如何初始化timer
把初始值寫到TCNTBn 和TCMPBn寄存器
設置手動更新位
設置啟動位
往下看到時鐘配置寄存器
有個計算公式
Timer clk = PCLK / {(預分頻數)prescaler value+1} / {divider value(5.1MUX值)}
PCLK是50M
= 50000000/(99+1)/16
= 31250
也就是說我們得TCON是31250的話,從這個值一直減到0
Prescaler0等于99
TCFG0 = 99; /* Prescaler 0 = 99, 用于timer0,1 */
TCFG1 MUX多路復用器的意思,他有多路輸入,我們可以通過MUX選擇其中一路作為輸出
根據上面mux的值,我們要把MUX0 設置成0011
只需要設置這4位即可,先清零
再或上 0011 就是3
TCFG1 &= ~0xf;
TCFG1 |= 3; /* MUX0 : 1/16 */
再來看看初始值控制寄存器
一秒鐘點燈太慢了 ,就讓0.5秒
TCNTB0 = 15625; /* 0.5s中斷一次 */
這個寄存器是用來觀察里面的計數值的,不需要設置
現在可以設置TCON來設置這個寄存器
現在需要設置Timer0
開始需要手工更新
TCON |= (1<<1); /* Update from TCNTB0 & TCMPB0 */
把這兩個值放到TCNTB0 和 TCMPB0中
注意:這一位必須清楚才能寫下一位
設置為自動加載并啟動,先清掉手動更新位,再或上bit0 bit3
TCON &=~ (1<<1);
TCON |= (1<<0) | (1<<3); /* bit0: start, bit3: auto reload */
設置中斷,顯然我們需要提供一個中斷處理函數void timer_irq(void)
在Timer里沒有看到中斷相關的控制器,我們需要回到中斷章節去看看中斷控制器,看看有沒有定時器相關的中斷
我們沒有看到更加細致的Timer0寄存器
當TCNTn=TCMPn時,他不會產生中斷,會發生脈沖的反轉,只有當TCNTn等于0的時候才可以產生中斷,我們之前以為這個定時器可以產生兩種中斷,那么肯定有寄存器中斷或者禁止兩種寄存器其中之一,那現在只有一種中斷的話,就相對簡單些
設置中斷的話,我們只需要設置中斷控制器
設置interrupu.c中斷控制器
*初始化中斷控制器 void interrupt_init(void)
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
*把定時器相應的位清零就可以了,哪一位呢?
INTPND的哪一位?
INT_TIMER0第10位即可
INTMSK &= ~(1<<10); /* enable timer0 int */
當定時器減到0的時候就會產生中斷,就會進到start.s這里一路執行do_irq
do_irq:
/* 執行到這里之前:
* 1. lr_irq保存有被中斷模式中的下一條即將執行的指令的地址
* 2. SPSR_irq保存有被中斷模式的CPSR
* 3. CPSR中的M4-M0被設置為10010, 進入到irq模式
* 4. 跳到0x18的地方執行程序
*/
/* sp_irq未設置, 先設置它 */
ldr sp, =0x33d00000
/* 保存現場 */
/* 在irq異常處理函數中有可能會修改r0-r12, 所以先保存 */
/* lr-4是異常處理完后的返回地址, 也要保存 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
/* 處理irq異常 */
bl handle_irq_c
/* 恢復現場 */
ldmia sp!, {r0-r12, pc}^ /* ^會把spsr_irq的值恢復到cpsr里 */
讓后進入irq處理函數中處理,處理這個irq
void handle_irq_c(void)
{
/* 分辨中斷源 */
int bit = INTOFFSET;
/* 調用對應的處理函數 */
if(bit ==0 || bit == 2 || bit == 5)/*eint0,2,rint8_23*/
{
key_eint_irq(bit);/*處理中斷,清中斷源EINTPEND*/
}else if(bit == 10)//如果等于10的話說明發生的是定時器中斷,這時候就調用我們得timer_irq
{
timer_irq();
}
/* 清中斷 : 從源頭開始清 */
SRCPND = (1< 回到timer.c文件中,在這個定時器處理函數中我們需要點燈 void timer_irq(void) { /* 點燈計數 循環點燈*/ static int cnt = 0; int tmp; cnt++; tmp = ~cnt; tmp &= 7; GPFDAT &= ~(7<<4); GPFDAT |= (tmp<<4); } 代碼寫完我們實驗一下,上傳代碼,在Makefile中添加timer.o,進行編譯 編譯后進行燒寫 發現燈已經開始閃 對程序進行改進 進入main函數中執行 timer_init(); 還需要修改interrupt.c 初始化函數 void interrupt_init(void) 還需要調用中斷處理函數 void handle_irq_c(void) 每次添加一個中斷我都需要修改handle_irq這個函數,這樣太麻煩,我能不能保證這個interrupt文件不變,只需要在timer.c中引用即可,這里我們使用指針數組 在interrupt.c中定義函數指針數組 typedef void(*irq_func)(int); 定義一個數組,我們來卡看下這里有多少項,一共32位,我們想把每一個中斷的處理函數都放在這個數組里面來,當發生中斷時,我們可以得到這個中斷號,讓后我從數組里面調用對應的中斷號就可以了 irq_func irq_array[32]; 那么我們得提供一個注冊函數 void register_irq (int irq, irq_func fp) { irq_array[irq] = fp; INTMASK &= ~(1 << irq) } 以后我直接調用對應的處理函數 void handle_irq_c(void) { /* 分辨中斷源 */ int bit = INTOFFSET; /* 調用對應的處理函數 */ irq_array[bit](bit); /* 清中斷 : 從源頭開始清 */ SRCPND = (1< //按鍵中斷初始化函數需要注冊 /* 初始化按鍵, 設為中斷源 */ void key_eint_init(void) { /* 配置GPIO為中斷引腳 */ GPFCON &= ~((3<<0) | (3<<4)); GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置為中斷引腳 */ GPGCON &= ~((3<<6) | (3<<22)); GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置為中斷引腳 */ /* 設置中斷觸發方式: 雙邊沿觸發 */ EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */ EXTINT1 |= (7<<12); /* S4 */ EXTINT2 |= (7<<12); /* S5 */ /* 設置EINTMASK使能eint11,19 */ EINTMASK &= ~((1<<11) | (1<<19)); register_irq(0, key_eint_irq); register_irq(2, key_eint_irq); register_irq(5, key_eint_irq); } 在timer.c中也需要設置中斷 void timer_init(void) { /* 設置TIMER0的時鐘 */ /* Timer clk = PCLK / {prescaler value+1} / {divider value} = 50000000/(99+1)/16 = 31250 */ TCFG0 = 99; /* Prescaler 0 = 99, 用于timer0,1 */ TCFG1 &= ~0xf; TCFG1 |= 3; /* MUX0 : 1/16 */ /* 設置TIMER0的初值 */ TCNTB0 = 15625; /* 0.5s中斷一次 */ /* 加載初值, 啟動timer0 */ TCON |= (1<<1); /* Update from TCNTB0 & TCMPB0 */ /* 設置為自動加載并啟動 */ TCON &= ~(1<<1); TCON |= (1<<0) | (1<<3); /* bit0: start, bit3: auto reload */ /* 設置中斷 */ register_irq(10, timer_irq); } 把interrupt.c中按鍵的初始化放在最后面 我們來看看我們做了什么事情, <1>我們定義了一個指針數組 typedef void(*irq_func)(int); 注:這里看不懂請參考typedef函數指針用法 這個指針數組里面放有各個指針的處理函數 irq_func irq_array[32]; 當我們去初始化按鍵中斷時,我們給這按鍵注冊中斷函數 register_irq(0, key_eint_irq); register_irq(2, key_eint_irq); register_irq(5, key_eint_irq); 這個注冊函數會做什么事情,他會把這個數組放在注冊函數里面,同時使能中斷 void register_irq(int irq, irq_func fp) { irq_array[irq] = fp; INTMSK &= ~(1< //我們的timer.c中 timer_init(); //也會注冊這個函數 /* 設置中斷 */ register_irq(10, timer_irq); 把這個中斷irq放在第10項里同時使能中斷,以后我們只需要添加中斷號,和處理函數即可,再也不需要修改函數 燒寫執行 我們從start.s開始看, 一上電從 b reset運行做一列初始化 .text .global _start _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ ldr pc, swi_addr /* vector 8 : swi */ b halt /* vector 0x0c : prefetch aboot */ b halt /* vector 0x10 : data abort */ b halt /* vector 0x14 : reserved */ 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] /* 恢復原來的值 */ bl sdram_init //bl sdram_init2 /* 用到有初始值的數組, 不是位置無關碼 */ /* 重定位text, rodata, data段整個程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss /* 復位之后, cpu處于svc模式 * 現在, 切換到usr模式 */ 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 ldr pc, =sdram sdram: bl uart0_init
上一篇:S3c2440ARM異常與中斷體系詳解2---CPU模式(Mode)狀態(State)
下一篇:S3c2440ARM異常與中斷體系詳解3---Thumb指令集程序示例
推薦閱讀
史海拾趣
隨著電子行業的快速發展和市場競爭的加劇,eMemory始終保持著對新技術和新市場的敏銳洞察力。公司不斷推出新的硅智財產品和解決方案,以滿足客戶不斷變化的需求。同時,eMemory還積極拓展國際市場,與全球多家知名芯片設計廠商建立了長期穩定的合作關系。這些努力使得eMemory在電子行業中保持著領先地位,并為其未來的發展奠定了堅實的基礎。
2014年5月19日,ConnectBlue公司被全球領先的定位與無線通信技術公司u-blox收購。這一收購為ConnectBlue公司帶來了新的發展機遇和更廣闊的市場空間。在u-blox的支持下,ConnectBlue公司得以進一步加大研發投入,拓展產品線,提升產品品質和服務水平。同時,公司還借助u-blox的全球銷售網絡和品牌影響力,進一步開拓國際市場,實現了新的跨越式發展。
隨著市場競爭的加劇,Chyao Shiunn意識到品質管理的重要性。公司開始加強產品質量的控制,從原材料的采購到生產流程的每一個環節都嚴格把關。同時,公司還加大了品牌宣傳力度,通過參加行業展會、舉辦技術研討會等方式提升品牌知名度。這些舉措不僅提高了公司的市場份額,也增強了客戶對公司的信任。
在產品質量和技術水平得到市場認可后,Auctor Corp公司開始積極拓展市場。公司通過與國內外知名企業建立合作關系,成功將產品打入多個國家和地區的市場。同時,公司還注重品牌塑造,通過參加國際電子展會、舉辦產品發布會等方式,提升品牌知名度和影響力。這些舉措使得Auctor Corp在電子行業中的地位逐漸提升。
背景:GarrettCom始創于1989年,專注于工業網絡通信產品的研發與制造。在那個工業自動化初具規模的年代,GarrettCom憑借其卓越的產品質量和創新的解決方案,迅速在市場中嶄露頭角。公司推出的工業以太網交換機和路由器等產品,以其高穩定性和可靠性,贏得了眾多工業客戶的信賴。
發展:隨著工業自動化程度的不斷提升,GarrettCom不斷加大研發投入,推出了一系列適應惡劣工業環境的產品。這些產品不僅滿足了工業自動化對通信穩定性的嚴苛要求,還推動了工業通信技術的進步。通過不斷的技術創新和市場拓展,GarrettCom逐步確立了其在工業通信領域的領先地位。
隨著公司業務的不斷拓展和市場份額的逐步擴大,海芯科技開始注重品牌建設和市場推廣。公司積極參加各類行業展會和交流活動,與業內同行和客戶進行深入交流和合作。同時,公司還加大了對品牌形象的宣傳力度,通過廣告、宣傳冊等多種方式提升品牌知名度和美譽度。這些舉措不僅提升了公司的市場影響力,也為公司的長期發展奠定了堅實的基礎。
51匯編語言指令集很多書上有,但是有很多書沒有放在一起,每章一部分。還有些朋友有書沒帶在身邊,或是使用過,望了買本書又劃不來。把這個傳上來方便各位。[ip]… 查看全部問答∨ |
最近打算設計新產品,在Friendly Arm買了一套Matrix5系統研究一下。發現用ARM-Linux開發似乎比較明智些! 但用Windows習慣了,還不太熟悉Linux,而且重裝RH9恐怕很費事。還是先用Win2000吧,Linux熟悉熟悉再說! 看見版上有個Cygwin,可以在Windo ...… 查看全部問答∨ |
在手機上開發了一個錄音機程序,但在正在錄音過程中來電時,錄音自動停止,可是寫文件出現了錯誤(WriteFile函數中參數出錯)。請問怎么怎樣才能解決?謝謝。… 查看全部問答∨ |
是這樣的,我想在windows ce上的運行SKYPE程序?哪位兄弟有沒有移植好的EXE文件,或者源碼呀,能不能給我一份呀?小弟不勝感謝!我的郵箱是sunboyljp@163.com… 查看全部問答∨ |