7.1 uart
電平通信:絕對的電壓是沒有意的,電壓差是真正的意義。電平信号的傳輸線中有一個參考電平線(一般是GND)
容易受到幹擾,屆時傳輸失敗
8位二進制并行通信時:需要9根線(1參考GND+8資料線)
差分信号:也是兩條線,但是沒有1和0,通過高電平減去其對稱的低電平(詳情1.7.1 37分鐘),可能有9V-0.6V
最顯著的特征就是抗幹擾能力比較強,傳輸品質比較穩定,現代通信一般使用差分信号,電平信号很少。
而且電壓整體上移或者下移沒有影響。
8位二進制并行通信時:需要16根線(8x2資料線)
串行通信和并行通信:看起來并行通信快一些,但是串行通信才是王道,因為省信号線,速度可以由通訊提高。
經過發展,最終勝出的通信:異步、串行、差分(USB和網絡)
序列槽通信的基本概念
本節講述傳輸通信涉及到的基本概念,如:波特率、起始位、資料位、奇偶校驗位、停止位、RS232電平、TTL電平等。主要學習目的是讓大家對序列槽通信涉及到的主要概念做個了解,友善後續課程中使用。
================================================================
7.2.序列槽通信的基本概念
1.7.2.1、序列槽通信的特點:異步、電平信号、串行
(1)、異步:序列槽通信的發送方和接收方之間是沒有統一的時鐘信号的。
(2)、電平信号:序列槽通信出現的時間較早,速率較低,傳輸的距離較近,是以幹擾還不太明顯,是以當時使用了電平信号傳輸。後期出現的傳輸協定都改成差分信号傳輸了。
(3)、串行通信:序列槽通信每次同時隻能傳輸1個二進制位。
1.7.2.2、RS232電平和TTL電平
(1)電平信号是用信号線電平減去參考線電平得到電壓差,這個電壓差決定了傳輸值是1還是0.
(2)在電平信号時多少V代表1,多少V代表0不是固定的,取決于電平标準。譬如RS232電平中-3V~-15V表示1;+3~+15V表示0;TTL電平則是+5V表示1,0V表示0.
(3)不管哪種電平都是為了在傳輸線上表示1和0.差別在于适用的環境和條件不同。RS232的電平定義比較大,适合幹擾大、距離遠的情況;TTL電平電壓範圍小,适合距離近且幹擾小的情況。
(4)我們台式電腦後面的序列槽插座就是RS232接口的,在工業上用序列槽時都用這個,傳輸距離小于15米;TTL電平一般用在電路闆内部兩個晶片之間。
(5)對程式設計來說,RS232電平傳輸還是TTL電平是沒有差異的。是以電平标準對硬體工程師更有意義,而軟體工程師隻要略懂即可。(把TTL電平和RS232電平混接是不可以的)
1.7.2.3、波特率
(1)波特率(bandrate),指的是序列槽通信的速率,也就是序列槽通信時每秒鐘可以傳輸多少個二進制位。譬如每秒種可以傳輸9600個二進制位(傳輸一個二進制位需要的時間是1/9600秒,也就是104us),波特率就是9600.
(2)序列槽通信的波特率不能随意設定,而應該在一些值中去選擇。一般最常見的波特率是9600或者115200(低端單片機如51常用9600,高端單片機和嵌入式SoC一般用115200).為什麼波特率不可以随便指定?主要是因為:第一,通信雙方必須事先設定相同的波特率這樣才能成功通信,如果發送方和接收方按照不同的波特率通信則根本收不到,是以波特率最好是大家熟知的而不是随意指定的。第二,常用的波特率經過長久發展,就形成了共識,大家常用就是9600或者115200.
1.7.2.4、起始位、資料位、奇偶校驗位、停止位
(1)序列槽通信時,收發是一個周期一個周期進行的,沒周期傳輸n個二進制位。這一個周期就叫做一個通信單元,一個通信單元是由:起始位+資料位+奇偶校驗位+停止位組成的。
(2)起始位表示發送方要開始發送一個通信單元;資料位是一個通信單元中發送的有效資訊位;奇偶校驗位是用來校驗資料位,以防止資料位出錯的;停止位是發送方用來表示本通信單元結束标志的。
(3)起始位的定義是序列槽通信标準事先指定的,是由通信線上的電平變化來反映的。
(4)資料位是本次通信真正要發送的有效資料,序列槽通信一次發送多少位有效資料是可以設定的(一般可選的有6、7、8、9,99%情況下我們都是選擇8位資料位。因為我們一般通過序列槽發送的文字資訊都是ASCII碼編碼的,而ASCII碼中一個字元剛好編碼為8位。)
(5)奇偶校驗位是用來給資料位進行奇偶校驗(把待校驗的有效資料逐個位的加起來,總和為奇數奇偶校驗位就為1,總和為偶數奇偶校驗位就為0)的,可以在一定程度上防止位反轉。
(6)停止位的定義是序列槽通信标準事先指定的,是由通信線上的電平變化來反映的。常見的有1位停止位,1.5位停止位,2位停止位等。99%情況下都是用1位停止位。
總結:序列槽通信時因為是異步通信,是以通信雙方必須事先約定好通信參數,這些通信參數包括:波特率、資料位、奇偶校驗位、停止位(序列槽通信中起始位定義是唯一的,是以一般不用選擇)
異步通信: 發送方發送消息是想發就發
接收方必須是一直接收
串行性:一次隻發一個
DB9接口:
DB9是早期序列槽通信早期比較常用的一種規範化接口
串行通信在早期是計算機與外界通訊的主要手段。DB9就是那個9針大頭,我們隻引出了3個
即TX RX GND。以前9針中的其餘6根是用于流控的,現在多用于調試,禁用流控 ,是以6根就不再使用了。
================================================
7.4 UART 通用異步收發器
universal asynchronous reciver and transmitter
序列槽的時鐘來源:APB peripheral BUS
序列槽分兩部分:
發送器 和 接收器
由一個控制單元控制,但彼此獨立。
發送器:用于210發送資訊
接收器:用于從外部收資訊到210
transmit shifter 發送移位器:幫助我們自動移位8位2進制,移動1位發1位。
将來計算序列槽控制器 的源時鐘時,是以APB總線來計算的。要設定源時鐘頻率
transmitter由發送緩沖區和發送移位器構成,先将ASCII資訊轉為二進制流,然後将1幀資料寫入發送緩沖區。剩下的發送部分是硬體自動的,發送移位器自動的讀取,将其發送到Tx通信位。
我們隻需要知道寫 “要發送的資訊”到發送緩沖區就行了
reciver由接收緩沖區和接收移位器。有人發送資訊時,移位器自動将二進制儲存至緩沖區
流控線有兩端,發送方一根線拉高或拉低,接受方一根線拉低或拉高
其目的是序列槽通信非常可靠。發送方速率比接收方快的時候,流控可以保證 不會漏掉東西。
現在不用流控?是因為有了USB和Internet,有了更進階的工作方式。現在序列槽隻用于SoC輸出調試資訊。而且由于硬體水準的提高,現在的序列槽的速度基本不會出現“發送方速率比接收方快”的情況,是以流控幾乎是沒有作用了。
是以我們就不用流控了=v=
==========================================
7.5序列槽補充:
序列槽重點在上面,但現在210等SoC裝置,序列槽發展了進階功能,如下
FIFO模式
DMA模式
IrDA模式
這三個51都沒有,stm32有一部分
FIFO模式
發送緩沖區是标準8位元組,單獨一個寄存器。但這裡有一個發送效率的問題,要一直發一個位元組一個位元組不停的與CPU發送請求,CPU要切換程序,OS的效率就會很低。
解決方案:擴充序列槽的發送/接收緩沖區(如設定為64位元組,CPU一次給64位元組 待發送資料)
cpu就可以極大緩解反複切換程序的時間。
序列槽控制器要注意還是1位元組緩沖區,這個有硬體問題和相容問題是以不友善改。那麼要實作這個想法就要啟用FIFO模式。FIFO的内容依次送到transmit buffer register
FIFO,即先進先出,是一種資料結構。這個大的緩沖區也叫FIFO是因為他類似這種資料結構。我12345傳進去,那麼出的時候也是12345。
DMA模式
與FIFO要解決的是同一個問題,發送接收折騰CPU的問題,本來是一種DSP技術。
核心是:交換資料時,不需要CPU參與,子產品可以自己完成。FIFO是輕量級解決方式,DMA則更根本。
FIFO的寄存器是有限的 可以設定成64Bit 或者128
DMA可以幹脆設定個1M,根本解決序列槽去煩CPU的問題。
DMA就是專門解決大量資料的轉移的。
IrDA模式
紅外模式
紅外編碼的原理:發送方固定時間發送或者不發生,是以表示0或者1。接收方固定時間看有無紅外來判斷1或者0
1 0 0 0 0 1 1
有無無無無有有
這種原理非常類似于序列槽。
210上的某個序列槽有一個紅外模式(IR mode),開啟之後我們隻需要向序列槽方式寫資料,這些資料就以紅外光的方式發射出去,接收方接收後即可解碼得到發送資訊。
其實是通過序列槽實作紅外通信的。
(可以相容紅外晶片哦!內建哦!可以收遙控器紅外信号哦!!)
============================================================
7.6 序列槽通信與中斷
中斷:單片機講過了
發送方“一般”不需要中斷 接收方“必須”使用中斷,極端狀态也可以不使用(輪詢式..)
是以我們使用序列槽通信是需要中斷的
RXD置1 說明序列槽控制器收到了1幀資料,告訴cpu
TXD中斷 1幀資料發出去後,告訴cpu沒東西發了,趕緊給我東西
不設定中斷是,
發送方設定中斷的情景: 允許cpu幹别的
1發送方先設定一個中斷,綁定處理函數
2發送方丢一幀資料給transmitter
3transmitter發送,發送方cpu去做别的事情
4發送完成傳回一個TXD中斷
5cpu切換回來給transmitter一幀資料,切換離開;循環。。。
發送方不使用中斷: 讓cpu盯住他,像監工一樣看着他發,不發不幹别的
1發送方禁止TXD中斷
2發送方給一幀資料到transmitter
3transmitter發送資料,這段時間cpu沒有切換,一直在這等
4發送方發送完成後,cpu再給一幀資料直到發完
(狀态寄存器:裡面有一個 發送緩沖區空标志bit,cpu一直讀這個位)
cpu search 1 or 0 to get buffer empty
序列槽通信是異步的,發送方占據主導權。接收方必須時刻等待,不然後來的資料會沖掉前面的,進而丢資料。
我們一般是讓發送方輪詢,接收方使用中斷。因為發送方可以耗,接收方耗不起。
(2)序列槽通信的時鐘問題
序列槽需要時鐘,需要一個固有波特率,transmitter、receiver都需要一個時鐘信号
時鐘問題:主要是波特率發生器。時鐘從APB總線來,到序列槽控制器内部給波特率發生器(實質是個分頻器),分頻後得到一個低頻的時鐘,這個時鐘就是給序列槽transmitter和rcver的。
波特率在uart channel 波特率 Divison 寄存器中 ,設定16bit分頻系數
DIV_VAL = UBRDIVn + SLOT中“1”的個數 /16
DIV_VAL = (PCLK / (bps x 16)) - 1 bps:波特率
….
序列槽通信中時鐘的設定,主要還是看寄存器
重點:
寄存器源設定(為序列槽控制器選擇源時鐘,一般PCLK_PSYS,也可以SCLK_UART)
注意:UBRDIVn UDIVSLOTn 這兩個寄存器很重要
設定波特率 精确輔助設定
51單片機沒這個東西,但210太快了,出錯了就GG,是以有這麼個寄存器為他校準
波特率不準會導緻接受方可能出錯,波特率越大越有可能不準!
===============================================================
7.7序列槽通信程式設計
分析程式,做準備工作
1 初始化tx rx引腳,參考原理圖搜尋TX,得知分别在GPA0_1 和 GPA0_0
GPA0CON = 0x?
2 關鍵寄存器
ULCON0 0x3 //無校驗位,8資料位,1停止位
UCON0 0x5 //暫不使用DMA,PCLK作時鐘源(bit10 置0),中斷類型(開始不弄)
//回環模式:我自己發,自己收。我們不使用
///break signal 一般我們也用不到。。。
//發送接受模式都是 中斷或輪詢模式(interrupt or polling)
UFCON0 //選擇接收和發送FIFO,不用FIFO的時候選預設就行
UMCON0 //AFC,modem中斷,我們全都不用
UBRDIV0
UDIVSLOT0
//需要計算
在c語言中定義寄存器對應的宏,很重要的哦
GPA0CON E0200000
UBRDIV0
UDIVSLOT0
ULCON0
….
UTXH0 //管發送的
URXH0 //管接收的
rGPA0CON ((volatile uint )GPA0CON)
其他依法炮制
======================================
猜想
uart.c 檔案
void uart_sendchar()
{
while(tx緩沖池滿); //卡死在這裡,等待為空
rUTXH0 = //發送1
}
void uart_init()
{
rGPA0CON =
ULCON0
UCON0
UFCON0
UMCON0
//計算bps
UBRDIV0
UDIVSLOT0
}
char uart_rcvchar()
{
while(rx緩沖池滿); //卡死在這裡,等待為空
return (rURXH0 & );
}
void main()
{
while1
{
發送
delay()
}
}
==================================================================
8.1按鍵中斷
1.8.1.2、按鍵的電學原理(結合原理圖bv3 cv3分析)
(1)硬體接法: SW5:GPH0_2 SW6:GPH0_3
SW78910:GPH2_0123
(3)總結:按鍵的工作方法:其實就是按鍵的按下與彈開,分别對應GPIO的兩種電平狀态(按下則GPIO為低電平,彈開則GPIO為高電平)。此時SoC内部可以通過檢測這個GPIO的電平高低來判斷按鍵有沒有被按下,這個判斷結果即可作為SoC的輸入信号。
1)SoC處理按鍵有2種思路:輪詢方式和中斷方式。
(2)輪詢方式,就是SoC主動的每隔一段時間去讀取(按鍵所對應的)GPIO的電平高低,以此獲得按鍵資訊;缺點在于CPU要一直注意按鍵事件,會影響CPU做其他事情。
(3)中斷方式,就是SoC事先設定好GPIO觸發的中斷所對應的中斷處理程式ISR,當外部按鍵按下或彈開時會自動觸發GPIO對應的外部中斷,導緻ISR執行,進而自動處理按鍵資訊。
GPH0CON (0xE0200C00)
GPH2DAT (0xE0200C04)
GPH2CON (0xE0200C40)
GPH2DAT (0xE0200C44)
在CON寄存器中将GPIO設定為input模式,然後去讀取DAT寄存器
8.2.輪詢方式處理按鍵的程式流程
(1)第一步,先初始化GPIO模式為input;
(2)第二步,循環讀取GPIO的電平值,然後判斷有無按鍵按下
8.3.序列槽輸出和按鍵消抖
1.8.3.1、基于序列槽标準輸出的按鍵調試
(1)以之前的序列槽stdio的工程為基礎來移植添加輪詢方式按鍵處理。
(2)注意USB下載下傳方式可能有錯誤(有可能不下載下傳,也有可能下載下傳了執行不對),解決方案是用SD卡啟動來替代。【使用那個x210_fusing_tool即可】
簡易代碼邏輯:輪詢按鍵點亮led+序列槽列印資訊
【main】
include "stdio.h"
{
uart init();
key init();
key_polling();
}
【uart.c】
void uart_init(void) //序列槽初始化
void putc(char c) //序列槽輸出
char getc(void) //序列槽輸入
【key.c】
void key_init(void)
{
// 設定GPHxCON寄存器,設定為輸入模式
}
void delay20ms(void)
{...}
void key_polling(void)
{
// 依次,挨個去讀出每個GPIO的值,
//判斷其值為1還是0.如果為1則按鍵按下,為0則彈起
while()
{
if(GPH0DAT&(<<) ) /// xxxx x1xx,代表無按鍵
{
led_off();
}
else
{
delay20ms(); //消抖
if(!GPH0DAT&(<<) ) //延時後,再次檢驗
{
led1(); //按下,亮
printf("key left"); //并且列印序列槽資訊
}
}
}
}
【led.c】
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
// 亮1個led
void led1(void)
{
rGPJ0CON = ;
rGPJ0DAT = ((<<) | (<<) | (<<));
}
================================================================
8.4.S5PV210的中斷體系介紹
(1)中斷的發明是用來解決宏觀上的并行需要的。宏觀就是從整體上來看,并行就是多件事情都完成了。
(2)微觀上的并行,就是指的真正的并行,就是精确到每一秒甚至每一刻,多個事情都是在同時進行的。宏觀上面的并行并不等于圍觀的并行,有時候宏觀上是并行的,微觀上是串行的。
但是因為CPU很快,是以在宏觀看來可以并行。
(5)為什麼需要中斷?因為單核CPU實際無法并行的,但是通過中斷機制,可以實作假并行(宏觀上的并行,微觀上實際還是串行的)。
SoC對中斷的實作機制:異常向量表
(1)異常向量表是CPU中某些特定位址的特定定義。當中斷發生的時候,中斷要想辦法通知CPU去進行中斷,怎麼做到?這就要靠異常向量表。
(2)在CPU設計時,就事先定義了CPU中一些特定位址作為特定異常的入口位址(譬如定義0x00000000位址為複位異常向量位址,則發生複位異常時CPU會自動跳轉到0x00000000位址去執行指令。又譬如外部中斷對應的異常向量位址為0x30000008,則發生外部中斷後,CPU會硬體自動跳轉到0x30000008位址去執行指令。)如
(3)以上講的是CPU硬體設計時對異常向量表的支援,下來就需要軟體支援了。硬體已經決定了發生什麼異常CPU自動跳轉PC到哪個位址去執行,軟體需要做的就是把處理這個異常的代碼的首位址填入這個異常向量位址。
S5PV210的異常向量表
(1)異常向量表在1.2.14節講過,可以傳回去聽一下
(2)異常向量表中各個向量的相對位置是固定的,但是他們的起始位址是不固定的,各種SoC可以不一樣,而且複雜ARM中還可以讓使用者來軟體設定這個異常向量表的基位址。
(3)擴充到所有架構的CPU中:所有架構(譬如51單片機、PIC單片機)的CPU實作中斷都是通過異常向量表實作的,這個機制是不變的;但是不同CPU異常向量表的構造和位置是不同的。
===================================================
8.5.異常向量表的程式設計處理
像記憶體一樣去通路異常向量表
(1)S5PV210的異常向量表可以改變(在CP15協處理器中),以适應作業系統的需求。但是目前系統剛啟動時,此時DRAM尚未初始化,程式都在SRAM中運作。210在iRAM中設定了異常向量表,供暫時性使用。
(2)查210的iROM application note文檔中iRAM的位址配置設定,可知,iRAM中的異常向量表起始位址為0xD0037400。知道了異常向量表的起始位址後,各個異常對應的入口就很好知道了。
1.8.5.2、函數名的實質就是函數的首位址
拿C語言中的文法來講,函數名就是這個函數的函數指針。
總結:當我們将異常處理程式的首位址和異常向量表綁定起來後,異常處理初步階段就完成了。到目前可以保證相應異常發生後,硬體自動跳轉到對應異常向量表入口去執行時,可以執行到我們事先綁定的函數。
為什麼中斷處理要先在彙編中進行??
(1)中斷處理要注意保護現場(中斷從SVC模式來,則儲存SVC模式下的必要寄存器的值)和恢複現場(中斷處理完成後,準備傳回SVC模式前,要将儲存的SVC模式下的必要寄存器的值恢複回去,不然到了SVC模式後寄存器的值亂了,SVC模式下原來正在進行的正常任務就被你搞壞了)
(2)儲存現場包括:第一:設定IRQ棧;第二,儲存LR;第三,儲存R0~R12
(3)為什麼要儲存LR寄存器?
要考慮中斷傳回的問題。中斷ISR執行完後如何傳回SVC模式下去接着執行原來的代碼。中斷傳回其實取決于我們進入中斷時如何儲存現場。
中斷傳回時關鍵的2個寄存器就是PC和CPSR。是以我們在進入IRQ模式時,應該将SVC模式下的下一句指令的位址(中斷傳回位址)和CPSR儲存起來,将來恢複時才可以将中斷傳回位址給PC,将儲存的CPSR給CPSR。
(4)中斷傳回位址就儲存在LR中,而CPSR(自動)儲存在(IRQ模式下的)SPSR中
(1)保護現場關鍵是儲存:中斷處理程式的傳回位址,r0-r12(cpsr是自動儲存的)
(2)恢複現場主要是恢複:r0-r12,pc,cpsr
代碼編寫
【start.s】
{
設定SVC棧
...
bl main //規則性函數:IRQ普通中斷的現場保護函數
...
}
IRQ_handle:
ldr sp, =IRQ_STACK //設定IRQ下的棧
sub lr, lr, #4 //儲存LR,因為流水線的原因要-4
// |
//我們pc要傳回的是,在結束後加載,加載完
stmfd sp! , {r0-r12 , lr} //儲存 r0-r12和lr 到IRQ模式下的棧上
bl irq_handler //調用真正的isr來進行中斷
ldmfd sp! , {r0-r12 , pc}^ //這句是恢複現場,r0-r12,pc
// "^"的作用是恢複cpsr
【int.c】
r_exception_irq = (unsigned int)IRQ_handle; //位址指派!
//IRQ函數的位址通過【異常向量表】綁定。這裡運用了中斷向量表基位址和偏移量!!
void irq_handler(void) //真正的中斷處理函數在這裡,不考慮現場保護、恢複
{
printf("irq_handler")
//如果有多個IRQ中斷,那麼都會到這個函數裡來
//需要一種方法産生分支
}
注意:這些代碼看懂即可,他們是開發中的定式,是套路,大家做都一個樣子,不需要自己寫
================================================================
1.8.6.S5PV210的向量中斷控制器
1.8.6.1、異常處理的2個階段
(1)可以将異常處理分為2個階段來了解。
第一個階段是異常向量表跳轉;
第二個階段就是進入了真正的異常處理程式irq_handler之後的部分。
1.8.6.2、回顧:中斷處理的第一階段(異常向量表階段)處理。
(1)第一個階段之是以能夠進行,主要依賴于CPU設計時提供的異常向量表機制。第一個階段的主要任務是從異常發生到響應異常并且儲存/恢複現場、跳轉到真正的異常處理程式處。
(2)第二個階段的目的是識别4個中斷源中究竟哪一個發生了中斷,然後調用相應的中斷處理程式來處理這個中斷。
(1)第一階段(異常向量表階段)2440和210幾乎是完全相同的。實際上幾乎所有的CPU在第一階段都是相同的。
(2)第二階段就彼此不同了。各個SoC根據自己對實時性的要求,和支援的中斷源的多少,各自發明了各自進行中斷,找到中斷編号,進一步找到對應isr位址的方式。
8.7.S5PV210中斷處理的主要寄存器
8.7.1、
VICnINTENABLE和VICnINTENCLEAR
INTENABLE寄存器負責相應的中斷的使能,INTENCLEAR寄存器負責相應的中斷的禁止。
VICnINTSELECT
設定各個中斷的模式為irq還是fiq。一般都設定成irq
CPU如何保證fiq比irq快?有2個原因:第一,fiq模式有專用的r8~r12,是以在fiq的isr中可以直接使用r8-r12而不用儲存,這就能節省時間;第二,異常向量表中fiq是最後一個異常向量入口。是以fiq的isr不需要跳轉,可以直接寫在原地,這樣就比其他異常少跳轉一次,省了些時間。
VICnIRQSTATUS和VICnFIQSTATUS
中斷狀态寄存器,是隻讀的 靠查詢這個寄存器來得到中斷編号的
VICnVECTPRIORITY0~VICnVECTPRIORITY31
中斷優先級設定寄存器
VICnVECTADDR0~VICnVECTADDR31、VICnADDR
這三個寄存器和210中斷處理第二階段的第二階段有關。
=============================================================
8.8.S5PV210中斷處理的程式設計實踐1
整個中斷的工作分為2部分:
第一部分是我們為中斷響應而做的預備工作:
1. 初始化中斷控制器,禁止所有中斷、選擇中斷類型、清VICxADDR
void intc_init(void)
// 為什麼在中斷初始化之初要禁止所有中斷?
// 因為中斷一旦打開,因為外部或者硬體自己的原因産生中斷後一定就會尋找isr
// 而我們可能認為自己用不到這個中斷就沒有提供isr,這時它自動拿到的就是亂碼
// 則程式很可能跑飛,是以不用的中斷一定要關掉。
// 一般的做法是先全部關掉,然後再逐一打開自己感興趣的中斷。一旦打開就必須
// 給這個中斷提供相應的isr并綁定好。
2. 綁定寫好的isr到中斷控制器
// 綁定過之後我們就把isr位址交給硬體了,剩下的我們不用管了,硬體自己會處理
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
實體中斷号 isr函數指針
==========================================-
//VIC0
if(intnum<)
{
*( (volatile unsigned long *)(VIC0VECTADDR + *(intnum-)) ) =(unsigned)handler;
}
//VIC1
else if(intnum<)
{
*( (volatile unsigned long *)(VIC1VECTADDR + *(intnum-)) ) = (unsigned)handler;
}
//s5pv210的中斷支援128個中斷,使用VIC0~VIC3四個寄存器表示他們
實體中斷号号最大設計為128,但是注意中斷在寄存器中的port最大為32
3、相應中斷的所有條件使能
// 通過傳參的intnum來使能某個具體的中斷源,中斷号在int.h中定義,是實體中斷号
void intc_enable(unsigned long intnum)
第二部分是當硬體産生中斷後如何自動執行isr:
1. 第一步,經過異常向量表跳轉入IRQ/FIQ的入口
2. 第二步,做中斷現場保護(在start.S中),然後跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪個VIC中斷了,然後直接去這個VIC的ADDR
寄存器中取isr來執行即可。
4. 第四步,isr執行完,中斷現場恢複,直接傳回繼續做正常任務。
void irq_handler(void)
{
// 雖然硬體已經自動幫我們把isr放入了VICnADDR中,但是因為有4個,是以我們必須
// 先去軟體的檢查出來到底哪個VIC中斷了,也就是說isr到底在哪個VICADDR寄存器中
unsigned long vicaddr[] = {VIC0ADDR,VIC1ADDR,VIC2ADDR,VIC3ADDR};
int i=;
void (*isr)(void) = NULL;
for(i=; i<; i++)
{
// 發生一個中斷時,4個VIC中有3個是全0,1個的其中一位不是0
if(intc_getvicirqstatus(i) != )
{
isr = (void (*)(void)) vicaddr[i];
break;
}
}
(*isr)(); // 通過函數指針來調用函數
}
VIC0INTENABLE這個寄存器寫1是使能,寫0則沒有影響,想要失能需要用VIC0INTENCLEAR
VIC0INTENCLEAR某一位置1,清除某一個中斷使能
關于VIC0ADDR和VIC0VECTADDR0-31
前者是目前中斷函數的位址,硬體自動獲得
後者是VIC0中中斷号對應的isr處理函數位址,由我們填寫。
TIPS:unsigned long unsigned int unsigned
這三種寫法在32位系統中是一樣的
===============================================================
8.10.外部中斷
1.8.10.1、什麼是外部中斷?資料手冊在哪裡?
(1)SoC支援的中斷類型中有一類叫外部中斷。内部中斷就是指的中斷源來自于SoC内部(一般是内部外設),譬如序列槽、定時器等部件産生的中斷;外部中斷是SoC外部的裝置,通過外部中斷對應的GPIO引腳産生的中斷。
(2)按鍵在SoC中就使用外部中斷來實作。具體實作方法是:将按鍵電路接在外部中斷的GPIO上,然後将GPIO配置為外部中斷模式。此時人通過按按鍵改變按鍵電路的電壓高低,這個電壓高低會觸發GPIO對應的外部中斷,通過引腳傳進去給CPU處理。
(3)外部中斷相關的介紹和寄存器都在2.2.6章節(屬于GPIO部分)
)外部中斷的觸發模式主要有2種:電平觸發和邊沿觸發。
關鍵寄存器:CON、PEND、MASK
(1)外部中斷的主要配置寄存器有3個:EXT_CON、EXT_PEND、EXT_MASK
(2)EXT_CON 配置外部中斷的觸發方式。
觸發方式就是說外部電平怎麼變化就能觸發中斷
電平觸發/邊緣觸發
(3)EXT_PEND 寄存器是中斷挂起寄存器。
這個寄存器中每一位對應一個外部中斷,平時沒有中斷時值為0。 平時0,發生中斷時候置1。中斷觸發之後我們需要将其手動置0。
(4)EXT_MASK 寄存器就是各個外部中斷的使能/禁止開關。
分析X210開發闆的按鍵對應的EINT編号:
EINT2、EINT3、EINT16、EINT17、EINT18、EINT19
自己總結中斷要注意的幾個問題:
1 每次中斷完記得清挂起,清中斷标志位
這裡的中斷标志位不僅要清EINT模式的,總中斷也要清
bug現場:按鍵一次後下一次就失效了
rEXT_INT_0_PEND |= (1<<3);
intc_clearvectaddr();
2 中斷設定好(向量表、初始化中斷控制器、綁定、使能中斷、現場保護恢複)之後
若要使用key,我們還需要做:
1按鍵對應的引腳設定為 外部中斷
2main函數中包含需要的函數聲明,使用頭檔案
3編寫各個按鍵對應的isr_eint(void)
===============================================================
9 定時器、看門狗和定時器
這三個放在一起講是因為他們有内在聯系:都與時間相關
9.1定時器
定時器也是個外設,常常和計數器糾纏在一起。
計數器每隔一定時間計一個數 計數值x時間周期 = 定時器值
其實定時器和計數器就是一回事
定時器用來實作定時執行代碼的作用。他可以産生中斷提醒CPU,并執行定時器中斷isr
定時器内部有一個計數器,計數器根據時鐘(APB總線)來工作。每隔一個時鐘周期計數器+1
定時器内部有一個TCNT寄存器,存放計數值
“TCNT – IFTCNT == 0 THEN BL ISR ”
定時器變量:
1 TCNT計數值
2 時鐘周期
system timer 即systick,系統定時器,為作業系統提供時間片。系統移植時由原廠給我們。
===============================================================
9.3 pwm定時器
pwm定時器叫這個名字,是因為他本身就設計為PWM作用的,也是普通定時器。
pwm定時器:
時鐘源:PCLK_PSYS
T1/T0 共同使用一個預分頻器
T234 共同使用一個預分頻器
每個分頻器有一個專用的 獨立分頻器
形成了二級配置設定系統
定時器輸入頻率 = PCLK / (預分頻值+1)/ 分頻值
預分頻值+1是為了什麼?
為了防止一種情況,程式設計人是個傻逼把0作為了除數,那麼可能導緻短路引起cpu燒毀
cpu設計的時候考慮了這種情況,為了程式員的容錯。
很多地方都有這種思路
關鍵點: 時鐘源 分頻器 預分頻器 分頻系數分别在TCFG0 TCFG1中設定
兩個寄存器:
TCMP TCNT 程式員不能通路,與B對應
TCMPB TCNTB 有位址的寄存器
dead-zone死區
(框圖中的 三角+圓圈 的符号代表邏輯取反)
自動重載(自動裝載“auto reload”)和雙緩沖(double buffering)
分頻器 實質是mux開關,最大16分頻
預分頻器 實質是8位寄存器,最大256分頻
分頻結果串聯,即相乘。最小1分頻,最大16x256
x210所能 定時 的最長的時間是266548s,遠遠夠用,TCNTB = 2的32次方
為什麼設計兩套寄存器?
TCMP TCNT 沒有寄存器位址,程式員不能通路
TCMPB TCNTB
tips : TCMP的系列作用是生成pwm波形
我們想給定時器裡面 放 300
TCNT 負責減
TCNTB 負責讓程式員往裡面存數
在減的過程中TCNTB永遠不變。如果不這樣設計,你總不能一直減基準值吧?
全過程:
start >> init
TCNT = TCNTB = 300
start >> timer
loop(TCHT – )
這裡要講一下以後要講的重裝載。TCNT這種稱為“影子寄存器”
TCNTO 是能通路的TCNT,observeable,可觀察的
自動重載
定時器一次定時算一個工作循環,如果我們想要反複加載定時器的值:
最簡單最笨的方法:人為用代碼制造循環
現在的進階SoC:預設了内置自動裝載,發生了一次中斷,到了下一個周期會自動裝載。
======================================================
PWM
兩個重要參數:
周期 T
占空比 duty 高電平的時間 / 總周期時間
eg程式設計:拉高電平,定時到達T x duty時間,把電平拉低,T x (1 - duty),把電平拉高。
以前的51單片機是沒有專用的pwm定時器的,需要自己結合GPIO和定時器中斷程式設計。
現在我們不用中斷就可以生成pwm波形了,而是使用兩個寄存器:TCNTB TCMPB。
TCNTB 決定周期
TCMPB 決定占空比
TCNTB x 時鐘周期 可以得到pwm波形的周期
輸出電平翻轉器
TCMPB 可以制造一次電平翻轉
可以規定:當TCNT > TCMPB 時為高電平
當TCNT < TCMPB 時為低電平
基于上述,如果duty從30%變到70%,TCMPB随之要從210變化到90.
在這兩種規定下,TCMPB中的值會發生變化。于是x210為我們設計了輸出電平翻轉器。
在程式設計上反映為一個寄存器位!TCON中的output inverter就可以開啟電平翻轉
輸出電平一翻轉,30%的占空比就變成70%了
死區生成器
死區的設計時為了pwm應用,用在我們的功率電路中,給電路進行整流
交流整成直流
整流需要兩路電流,兩路分别在正電平和負電平導通工作,兩路不能同時導通,否則會短路。大功率的開關電源、逆變器廣泛使用了整流技術。“這邊高那邊低”,兩路如果同時改變的話很有可能導緻短暫的“處于同一個電平”,這個時候就短路了。
死區就是為了解決這種情況,讓另一流滞後一點,有點像“按鍵消抖”,為了安全
死區在設計産品的時候不能多也不能少。多了精度小,沒有則很危險。
我們的210提供了自帶的死區生成器,使用者直接用死區控制功能就好了=v=