哎,以前總想裸機加系統一齊學,學著學著發現好混亂,D驅動~~然后又重新齋跑2440的裸機。以下系少少心得,記錄下學習過程。
里面有三個地方還沒弄好:1、 硬盤的文件系統 FAT部分(后面有空補補)
2、 NORFLASH中的讀寫有點問題
3、 USB主機中對U盤的UFI命令
有什么問題歡迎一起交流。以下是部分心得:
LED
GPXCON中每兩位控制一個引腳:00輸入,01輸出,10特殊功能
初始化 rGPBCON , rGPBUP , rGPBDAT 三個寄存器
關于ADS配置的解釋:
在Target Settings中的Post-linker中選擇ARM fromELF,因為我們需要把ELF文件格式轉化為下載到flash中所必須的二進制文件。
在Language Settings中的所有語言的Target下的Architecture or Processor,選擇ARM920T,因為s3c2440是ARM920T內核。
在ARM Linker中的Output中的RO Base填寫0x30000000,因為在開發板上SDRAM是從0x30000000地址開始。在Layout中的Object/Symbol中填寫2440init.o,它是啟動文件的目標文件,在Section中填寫Init,它是在啟動文件中我們所定義的入口代碼段的名稱。
外部中斷
我們要打開某一中斷的屏蔽,這樣才能響應該中斷,相對應的寄存器為INTMSK;
還要設置外部中斷的觸發方式,如低電平、高電平、上升沿、下降沿等,相對應的寄存器為EXTINTn。
另外由于EINT4到EINT7共用一個中斷向量,EINT8到EINT23也共用一個中斷向量,而INTMSK只負責總的中斷向量的屏蔽,要具體打開某一具體的中斷屏蔽,還需要設置EINTMASK。
有一些中斷是共用一個中斷向量的,而一個中斷向量只能有一個中斷執行函數,因此具體是哪個外部中斷,還需要EINTPEND或rINTOFFSET來判斷
使用__irq這個關鍵詞來定義中斷處理函數,這樣系統會為我們自動保存一些必要的變量,并能夠在中斷處理函數執行完后正確地返回
這種形式:void __irq key(); pISR_EINT0=(int)key;
定時器使用
主要配置 :外部時鐘源→通過寄存器MPLLCON得到FCLK→再通過寄存器CLKDIVN得到HCLK和PCLK->再得到定時器的工作頻率
PWM定時器使用:
蜂鳴器=GPB0=TOUT0=定時器0
蜂鳴器 高電平響,低電平不響
關鍵是設置寄存器TCNTBn和TCMPBn,前者可以確定一個計數周期的時間長度,而后者可以確定方波的占空比
定時器中斷使用:
不需要配置TCMPBn,只需TCNTBn,TCNTBn*1/定時器的工作頻率=定時的時間
但要記住中斷要:
void __irq timer0_sever();
pISR_TIMER0=(int)timer0_sever;
rINTMSK =~BIT_TIMER0;
Uart
主要配置以下寄存器:
UBRDIVn ,UCONn ,UTRSTATn
收發寄存器: UTXHn ,URXHn
注意幾點:
1. 對于s3c2440來說,接收數據是被動的,發送數據是主動的,因此一般來說,接收數據用中斷方式,發送數據用查詢方式較好;
2. 在中斷方式下,當接收到數據時,盡管可能該數據無用,但也一定要讀取它,否則下次再接收數據時,不會再引起中斷,因為接收數據緩存器被上次接收到的數據所霸占,只要沒有讀取它,它就永遠在那里;
3. 由于UART中斷涉及到SUBSRCPND寄存器,因此在中斷處理程序中不僅要清SRCPND寄存器,還要清SUBSRCPND寄存器,它們的順序一定是先清SUBSRCPND寄存器,再清SRCPND寄存器,否則就會引起一個中斷兩次響應的問題。因為是否中斷由SRCPND寄存器決定,而SRCPND寄存器的相關狀態位由SUBSRCPND寄存器決定
簡單的串口控制臺:1. 鍵盤輸入數據后在控制臺上進行回顯
recv_data = uart_getc();
return recv_data;
2. 讓接收的字符組成命令 data_buf[i] = uart0_scanf();
if(data_buf[i] == '\0')
3回車——命令判斷、退格——刪除字符、判斷命令后進入相應分支進行處理
else if(data_buf[i] == '\b')
if(!(no_system_strcmp("read iic",cmd_buf)))
LCD (4.3寸,480*272)
在2440中時鐘設置是 FCLK:HCLK:PCLK=400:100:50
注意兩點:
1時序,參數的設置
VCLK是像素時鐘信號。VCLK=HCLK÷[(CLKVAL+1)×2]
例如,HCLK的頻率為100MHz,要想驅動像素時鐘信號為6.4MHz的LCD屏,則通過上式計算CLKVAL值
在s3c2440中,所有的這些信號(VSPW、VFPD、VBPD、LINEVAL、HBPD、HFPD、HSPW和HOZVAL)都是實際值減1的結果。這些值是通過寄存器LCDCON2、LCDCON3和LCDCON4來配置
行頻(HSF)和場頻(VSF):
HSF=VCLK÷[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]
=7.1÷408=17.5kHz
VSF=HSF÷[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]
=17.5÷270=64.8Hz
2、顯示緩存區
一般我們是通過定義一個與屏幕尺寸大小相同的二維數組來開辟該空間的,這樣控制屏幕內容會方便一些,如當屏幕的尺寸為480×272時,可以定義該緩存區為LCD_BUFFER[272][480]。
在s3c2440中,寄存器LCDSADDR1和LCDSADDR2用于設置顯示緩存區,即把我們定義的那個二維數組告訴s3c2440。其中LCDBANK的9位數據指定LCD的BANK,即顯示緩存區的第30位到第22位地址;LCDBASEU的21位數據指定了LCD的基址,即顯示緩存區開始地址的第21位到第1位;LCDBASEL的21位數據指定了LCD的尾址,即顯示緩存區結束地址的第21位到第1位。
拓展:LCD的字符顯示
漢字庫
中文字符和ASCII碼混合在一樣,如何區分它們呢?其實也很簡單,ASCII碼的最高位是0,而中文的最高位是1,因此當讀取到的一個字節的最高位是0,則該字節為ASCII碼,它的下一個字節與這個字節無關;當取得到的字節的最高位是1,則表示的是中文字符,并且該字節與它的下一個字節組合在一起表示完整的一個漢字。
把字庫變換成一個超大的數組,那么我們就可以像操作數組一樣讀取字庫了
#include "font_libs.h" //內有兩個數組__HZK[ ]和__ASCII[ ]
if(String[k]&0x80) //中文字符
qh=String[k]-0xa0; //區號
wh=String[k+1]-0xa0; //位號
mould = & __HZK[ ( ( qh - 1 )*94 + wh- 1 )*32 ];
RTC實時時鐘
內部的寄存器BCDSEC,BCDMIN,BCDHOUR,BCDDAY,BCDDATE,BCDMON和BCDYEAR分別存儲了當前的秒,分,小時,星期,日,月和年,表示時間的數值都是BCD碼。這些寄存器的內容可讀可寫,并且只有在寄存器RTCCON的第0位為1時才能進行讀寫操作。
“一秒誤差”,因此當讀取到的秒為0時,需要重新再讀取一遍這些寄存器的內容,才能保證時間的正確。
以下兩個中斷實用,注意要按照一般中斷初始化的格式:開屏蔽,清屏蔽
時間節拍中斷 pISR_TICK時間節拍中斷的周期公式為:(n+1)÷128,單位是秒
其中n的值為1~127,它存儲在寄存器TICNT的低6位中,當寄存器TICNT的第7位被置1時,表示開啟時間節拍中斷
報警中斷 pISR_RTC LMYEAR(年)、ALMMON(月)、ALMDATE(日)、ALMHOUR(小時)、ALMMIN(分)和ALMSEC(秒)。而如何報警,是由報警控制寄存器RTCALM控制的
要學習一下Fzb版本的控制臺設計,其中提到一個專用函數
// 將一個十位數轉化為字符串輸出(專用函數) BCD碼轉字符串
static void digit_to_char(char *buf, int number)
{
buf[0] = ((char)(number >> 4) + 48); //轉換高4位
buf[1] = ((char)(number & 0x0f) + 48); //轉換低4位
}
要多學習用結構體,數組,和指針
IIC總線(按中斷法寫的,和51時按時序法不同)
SDA->GPE15 , SCL->GPE14
尋址AT24C02A
S3C2440作為主機,所以就關注主機模式下的收/發
配置了IIC管腳所在的寄存器,然后配置了IIC工作的信號以及時鐘源的配置。配置rIICCON,rIICSTAT
設立變量FLAG,來看是否進入了中斷,出來中斷后要清除
while(FLAG == 1)
for(i = 0;i < 100;i++);
FLAG = 1;
讀函數的流程: 首先是發出從設備尋找地址并指定為寫入操作→寫入要讀取數據的首地址→發出從設備尋找地址并指定為讀取操作→這時會先返回一個數據(這個是不需要的要拋棄,上面紅色部分)→按照給定的數據長度讀取數據(長度超過8位會折返讀)→結束操作。
寫函數的流程:發出從設備尋址地址并指定寫入模式→向從設備寫入要寫入數據區的首地址→寫入數據到設備中(最多8個字節)→結束。
幾句經典的解析:
rIICCON = 0xaf; //清中斷標記,開啟中斷
rIICSTAT = 0xf0; rIICSTAT = 0xb0; //使能接收,發送功能,S信號開始
rIICSTAT = 0xd0; rIICSTAT = 0x90; //發出P信號,停止
參考fzb的讀寫8位對齊法,很美觀牛B
ADC和觸摸屏
AD(輪詢或中斷 自己選擇)
A/D轉換比較簡單,主要應用的是ADC控制寄存器ADCCON和ADC轉換數據寄存器ADCDAT0
這個想法不錯:在AIN2引腳上接了一個溫度傳感器,被檢測的溫度范圍為0度~99度,它對應于A/D轉換數據的0~0x3FF。檢測到的溫度被實時地顯示在LCD上,這里我們只顯示溫度的整數部分。
temperature = (int)readADC()*99/0x3ff; //讀取溫度?可以直接這樣?不用寫時序讓溫度傳感器工作先?這種不是DS18B20,是那種直接純傳感
注意的地方:
根據S3C2440的中斷體系,從INTOFFSET獲取中斷發生的種類標號然后就像這樣處理各個中斷。ADC的中斷體系分為兩個層次。首先是主要中斷,由于ADC和觸摸屏使用同一個主要中斷標號,所以在這個中斷處理中還需要判斷次要中斷以最終確定到底是ADC還是觸摸屏發生了中斷。
觸摸屏
工作的兩種典型模式:
自動(序列)X/Y坐標轉換模式
自動(序列)X/Y坐標轉換模式作為如下方式被操作。觸摸屏控制器依次轉換被觸摸點的X坐標和Y坐標。在觸摸控制器寫X測量數據到ADCDAT0和Y測量數據到ADCDAT1過后,觸摸屏接口產生中斷到中斷控制器在自動坐標轉換模式中。
等待中斷模式
觸摸屏控制器產生中斷信號(INT_TC)當觸摸筆按下的時候。等待中斷模式設置值是ADCTSC = 0xd3(X_PU、XP_Dis、XM_Dis、YP_Dis、YM_En)。
一般來說,觸摸屏的響應過程是:等待中斷(觸摸筆按下)模式→中斷發生后進入自動(連續)X/Y軸轉換→等待中斷(觸摸筆抬起)模式。
這三種模式的初始化分別為:
1等待中斷(觸摸筆按下):
rADCTSC=0;
rADCTSC=0xd3;
2(連續)X/Y軸轉換:
rADCTSC=0;
rADCTSC=0x04;
3等待中斷(觸摸筆抬起):
rADCTSC=0;
rADCTSC=0xd3;
rADCTSC |=(1<<8);
暈,看回以前的寫字板程序,都是錯,沒有區分清楚兩個不同的中斷
只有一個中斷觸發函數pISR_ADC 但是對應著不同的兩個中斷,
要通過在中斷中判斷if(SUBSRCPND & (1 << 9)) 和else if(SUBSRCPND & (1 << 10))
等待檢測 AD處理中斷
注意: 在等待檢測中,又分為 按下中斷 和 抬起中斷
用 if(ADCDAT0 & 0x8000) 和 else 來區分
抬起 按下
拖曳功能的實現:
Y
按下——》xy自動模式轉換——》收集數據,畫點——》檢測是否松手
N
X通過 標志 flagTC 來判斷是否松手,若松手,flagTC在松手中斷中置1
上面那種標志的方法會與 void TC_interrupt()//等待模式中斷,判斷是按下還是松手
有矛盾
故還是直接采取在拖曳函數中test_down_still()直接判斷
wait_up_init();
if(!(rADCDAT0&0x8000))
每次畫點要延時一段時間,即控制好采樣時間
校正:
利用三個坐標點 通過公式計算出待定系數A,B,C,D,E,F,K ,表示出觸摸屏和LCD關系
公式:LCD上每個點PD的坐標為[XD,YD],觸摸屏上每個點PT的坐標為[XT,YT]。
XD=A×XT+B×YT+C
YD=D×XT+E×YT+F
K=(XT0-XT2)×(YT1-YT2)-(XT1-XT2)×(YT0-YT2)
A=[(XD0-XD2)×(YT1-YT2)-(XD1-XD2)×(YT0-YT2)] / K
B=[(XT0-XT2)×(XD1-XD2)-(XD0-XD2)×(XT1-XT2)] / K
C=[YT0×(XT2×XD1-XT1×XD2)+YT1×(XT0×XD2-XT2×XD0)+YT2×(XT1×XD0-XT0×XD1)] / K
D=[(YD0-YD2)×(YT1-YT2)-(YD1-YD2)×(YT0-YT2)] / K
E=[(XT0-XT2)×(YD1-YD2)-(YD0-YD2)×(XT1-XT2)] / K
F=[YT0×(XT2×YD1-XT1×YD2)+YT1×(XT0×YD2-XT2×YD0)+YT2×(XT1×YD0-XT0×YD1)] / K
利用EEPROM來保存這幾個參數,即A,B,C,D,E,F,K分別保存在以0x20,0x30,0x40,0x50,0x60,0x70,0x80為首地址內存的連續4個字節空間內,另外內存地址0x1F保存一個標識信息,當為0x6A時,表示這幾個參數已計算并保存好了,只需從上述內存地址中讀取參數就行,而當為其他值時,就需要進行校正。
難點是如何將其寫存入 E2PROM,不用每次都重新校正:
注意兩個函數:因為E2PROM的每個地址單位是以1字節(8位的)
//把一個32位整型轉換為4個8位字節型,并寫入EEPROM中
iic_buffer[0]=(unsigned char)((coef&0xFF000000)>>24);
iic_buffer[1]=(unsigned char)((coef&0x00FF0000)>>16);
iic_buffer[2]=(unsigned char)((coef&0x0000FF00)>>8);
iic_buffer[3]=(unsigned char)(coef&0x000000FF);
//讀取EEPROM中的4個8位字節,并把它們組合成一個32位的整型。
temp=(iic_buffer[0]<<24)|(iic_buffer[1]<<16)|(iic_buffer[2]<<8)|(iic_buffer[3]);
感覺都不知道否IIC讀寫函數有問題?怎么下面一個讀函數讀不出值?
rdat24c02a(0x1F,iic_buffer,1); 讀0x1f的位置的值
if(iic_buffer[0]!=0x6A) 判斷是否值等,即校正過的
9、IIS音頻
一些基本概念:
擴展了IIS的應用,要外擴CODEC才行。因為接口的數據傳出和接收的只是數字采樣信號而已,要通過CODEC進行DA轉換或者AD轉換才行。
一個wav文件作為例子(采樣頻率44.1KHz、聲道數2、數據位數16)。該文件的聲音要得到還原,那么數據必須以44.1KHz的速率傳輸
IISSCLK=采樣頻率×采樣位數×通道數,采樣頻率記為fs。于是IISSCLK為32fs
輪詢FIFO法的使用:
寄存器配置好預分頻器和音頻數據的數據位、主時鐘、數據時鐘以及FIFO后便可以讀或者寫FIFO來實現上述功能
UDA1341TS的配置,只有配置了它(CODEC)模擬信號才能正確的轉換為IIS接口的數據或者將IIS接口的數據轉換為模擬信號輸出。這部分的配置是通過一個叫做“L3”總線接口來實現
UDA1341TS的L3微控制器部分說明(當AD和DA用)
L3寫的一個實現,通過這個我們可以向CODEC發出各種命令進行配置,由于TQ2440將這三個管腳和GPIO相連,所以我們按照嚴格的地址模式和數據傳輸模式時序來編寫總線操作函數
注意小技巧,宏定義:#define L3M (1<<2) //GPB2 L3MODE
#define L3D (1<<3) //GPB3 L3DATA
#define L3C (1<<4) //GPB4 L3CLOCK
地址模式 UDA1341TS的地址是000101。
數據位0到1指向隨后傳送數據的類型
數據傳輸模式 各總線數據的賦值,就像賦給寄存器一樣
輪詢FIFO法具體使用步驟:
1、 初始化底層硬件 :配置rGPBCON,rGPECON,設波特和主頻:rMPLLCON和rUBRDIV0
2、配置UDA1341 :STATUS,DATA0和擴展地址
3、配置s3c2440的IIS寄存器 :rIISPSR, rIISCON,rIISMOD, rIISFCON
4、輸入或輸出數據 : 注意FIFO中的數據為16位,深度為32
if((rIISCON & (1<<6))==0) //檢查輸入FIFO是否為滿
temp=(*IISFIFO); //錄音讀取
(*IISFIFO)=music; //放音寫入
5、結束恢復 :rGPBCON,rGPECON,設波特和主頻:rMPLLCON和rUBRDIV0
DMA通道法的使用:
DMA的一些基本概念:
DMA的優勢是它在傳輸數據的時候不需要CPU的干預。
DMA一大堆寄存器要配置:
思路:描述源的地址和控制方式;描述目地的地址和控制方式;描述DMA控制方式;打開DMA操作。
難度最大的基本也就是DMA的控制方式配置。DMA控制方式的設置主要是這么幾個方面:選擇DMA傳輸控制方式;選擇DMA是否開啟中斷;選擇DMA服務的對象;DMA服務方式。
DMA通道法的使用步驟:(基本和輪詢法差不多,再補一些不同的)
1、初始化底層硬件 +中斷部分初始化
2、配置UDA1341
3、配置s3c2440的IIS寄存器 將FIFO改為DMA模式
4、配置DMA中寄存器 (輸出要用DMA通道2,輸入用通道1)
5、中斷處理 利用商和余數,判斷每次傳0xfffff*2后還剩多少數據量
6、結束還原,關DMA,關中斷
幾個要注意的小地方:
每次中斷的最大傳輸量:字節大小為:DSZ×TSZ×TC= 半字 * 單元傳輸 * 寄存器DCONn的低20位存放的數據=(0xfffff<<1)= 2 * 1 * DCONn中的值
MP3模式處理函數(靜音,音量增減,停止,暫停、開始)
用FZB的串口控制臺命令來編寫
//輸入以下東西實現功能:
//1、"mute" 靜音 在DATA0中MT位置置一
//2、"++""--" 音量增減 通過對volume變量賦值,然后通過L3寫進(0x14,1)中
//3、"stop" 停止 關DMA2,即關rDMASKTRIG中的stop位
//4、"pause" 暫停/開始 關開 rIISCON
DMA的播放/錄音實現和輪詢實現的區別
在于,輪詢需要自己編寫數據輸出/讀取代碼,但是DMA方式下不用,只需要指明源地址和目標地址以及數據大小就可以由硬件自動傳輸數據了,只是要注意的是S3C2440下的DMA使用時候數據大小是有限制的,一次最多可以傳輸的字節大小為:DSZ×TSZ×TC
由于是用DMA的方式,因此在播放的過程中不占用系統資源,我們可以很容易的實現聲音的各種操作而絲毫不影響播放的效果,如音量的提高和降低、靜音、暫停等