自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從0學(xué)ARM,基于Cortex-A9 ADC裸機(jī)驅(qū)動(dòng)詳解

系統(tǒng) Linux
在嵌入式開(kāi)發(fā)中,ADC應(yīng)用比較頻繁,本文主要講解ADC的基本原理以及如何編寫(xiě)基于ARM的裸機(jī)程序和基于Linux的驅(qū)動(dòng)程序。

 前言

在嵌入式開(kāi)發(fā)中,ADC應(yīng)用比較頻繁,本文主要講解ADC的基本原理以及如何編寫(xiě)基于ARM的裸機(jī)程序和基于Linux的驅(qū)動(dòng)程序。

ARM架構(gòu):Cortex-A9 Linux內(nèi)核:3.14

在講述ADC之前,我們需要先了解什么是模擬信號(hào)和數(shù)字信號(hào)。

模擬信號(hào)

主要是與離散的數(shù)字信號(hào)相對(duì)的連續(xù)的信號(hào)。模擬信號(hào)分布于自然界的各個(gè)角落,如每天溫度的變化,而數(shù)字信號(hào)是人為的抽象出來(lái)的在時(shí)間上不連續(xù)的信號(hào)。電學(xué)上的模擬信號(hào)是主要是指幅度和相位都連續(xù)的電信號(hào),此信號(hào)可以被模擬電路進(jìn)行各種運(yùn)算,如放大,相加,相乘等。

模擬信號(hào)是指用連續(xù)變化的物理量表示的信息,其信號(hào)的幅度,或頻率,或相位隨時(shí)間作連續(xù)變化,如目前廣播的聲音信號(hào),或圖像信號(hào)等。

如下圖所示從上到下一次是正弦波、 調(diào)幅波、 阻尼震蕩波、 指數(shù)衰減波 。


數(shù)字信號(hào)

數(shù)字信號(hào)指幅度的取值是離散的,幅值表示被限制在有限個(gè)數(shù)值之內(nèi)。二進(jìn)制碼就是一種數(shù)字信號(hào)。二進(jìn)制碼受噪聲的影響小,易于有數(shù)字電路進(jìn)行處理,所以得到了廣泛的應(yīng)用。

數(shù)字信號(hào):高清數(shù)字電視,MP3,JPG,PNG文件等等。


優(yōu)點(diǎn):

1. 抗干擾能力強(qiáng)、無(wú)噪聲積累

在模擬通信中,為了提高信噪比,需要在信號(hào)傳輸過(guò)程中及時(shí)對(duì)衰減的傳輸信號(hào)進(jìn)行放大,信號(hào)在傳輸過(guò)程中不可避免地疊加上的噪聲也被同時(shí)放大。

隨著傳輸距離的增加,噪聲累積越來(lái)越多,以致使傳輸質(zhì)量嚴(yán)重惡化。

對(duì)于數(shù)字通信,由于數(shù)字信號(hào)的幅值為有限個(gè)離散值(通常取兩個(gè)幅值),在傳輸過(guò)程中雖然也受到噪聲的干擾,但當(dāng)信噪比惡化到一定程度時(shí),

即在適當(dāng)?shù)木嚯x采用判決再生的方法,再生成沒(méi)有噪聲干擾的和原發(fā)送端一樣的數(shù)字信號(hào),所以可實(shí)現(xiàn)長(zhǎng)距離高質(zhì)量的傳輸。

2. 便于加密處理

信息傳輸?shù)陌踩院捅C苄栽絹?lái)越重要,數(shù)字通信的加密處理的比模擬通信容易得多,以話(huà)音信號(hào)為例,經(jīng)過(guò)數(shù)字變換后的信號(hào)可用簡(jiǎn)單的數(shù)字邏輯運(yùn)算進(jìn)行加密、解密處理。

3. 便于存儲(chǔ)、處理和交換

數(shù)字通信的信號(hào)形式和計(jì)算機(jī)所用信號(hào)一致,都是二進(jìn)制代碼,因此便于與計(jì)算機(jī)聯(lián)網(wǎng),也便于用計(jì)算機(jī)對(duì)數(shù)字信號(hào)進(jìn)行存儲(chǔ)、處理和交換,

可使通信網(wǎng)的管理、維護(hù)實(shí)現(xiàn)自動(dòng)化、智能化。

4. 設(shè)備便于集成化、微型

數(shù)字通信采用時(shí)分多路復(fù)用,不需要體積較大的濾波器。設(shè)備中大部分電路是數(shù)字電路,可用大規(guī)模和超大規(guī)模集成電路實(shí)現(xiàn),因此體積小、功耗低。

5. 便于構(gòu)成綜合數(shù)字網(wǎng)和綜合業(yè)務(wù)數(shù)字網(wǎng)

采用數(shù)字傳輸方式,可以通過(guò)程控?cái)?shù)字交換設(shè)備進(jìn)行數(shù)字交換,以實(shí)現(xiàn)傳輸和交換的綜合。

另外,電話(huà)業(yè)務(wù)和各種非話(huà)業(yè)務(wù)都可以實(shí)現(xiàn)數(shù)字化,構(gòu)成綜合業(yè)務(wù)數(shù)字網(wǎng)。

6. 占用信道頻帶較寬

一路模擬電話(huà)的頻帶為4kHz帶寬,一路數(shù)字電話(huà)約占64kHz,這是模擬通信目前仍有生命力的主要原因。隨著寬頻帶信道(光纜、數(shù)字微波)的大量利用(一對(duì)光纜可開(kāi)通幾千路電話(huà))以及數(shù)字信號(hào)處理技術(shù)的發(fā)展(可將一路數(shù)字電話(huà)的數(shù)碼率由64kb/s壓縮到32kb/s甚至更低的數(shù)碼率),數(shù)字電話(huà)的帶寬問(wèn)題已不是主要問(wèn)題了。

常用的數(shù)字信號(hào)編碼有不歸零(NRZ)編碼、 曼徹斯特(Manchester)編碼和差分曼徹斯特(Differential Manchester)編碼。


數(shù)字信號(hào)與模擬信號(hào)的轉(zhuǎn)化

模擬信號(hào)和數(shù)字信號(hào)之間可以相互轉(zhuǎn)換:模擬信號(hào)一般通過(guò)PCM脈碼調(diào)制(Pulse Code Modulation)方法量化為數(shù)字信號(hào),

即讓模擬信號(hào)的不同幅度分別對(duì)應(yīng)不同的二進(jìn)制值,例如采用8位編碼可將模擬信號(hào)量化為2^8=256個(gè)量級(jí),實(shí)用中常采取24位或30位編碼;

數(shù)字信號(hào)一般通過(guò)對(duì)載波進(jìn)行移相(Phase Shift)的方法轉(zhuǎn)換為模擬信號(hào)。計(jì)算機(jī)、計(jì)算機(jī)局域網(wǎng)與城域網(wǎng)中均使用二進(jìn)制數(shù)字信號(hào),

目前在計(jì)算機(jī)廣域網(wǎng)中實(shí)際傳送的則既有二進(jìn)制數(shù)字信號(hào),也有由數(shù)字信號(hào)轉(zhuǎn)換而得的模擬信號(hào)。但是更具應(yīng)用發(fā)展前景的是數(shù)字信號(hào)。

PCM脈沖編碼調(diào)制

脈沖編碼調(diào)制就是把一個(gè)時(shí)間連續(xù),取值連續(xù)的模擬信號(hào)變換成時(shí)間離散,取值離散的數(shù)字信號(hào)后在信道中傳輸。

脈沖編碼調(diào)制就是對(duì)模擬信號(hào)先抽樣,再對(duì)樣值幅度量化, 編碼的過(guò)程。


抽樣:

就是對(duì)模擬信號(hào)進(jìn)行周期性?huà)呙?,把時(shí)間上連續(xù)的信號(hào)變成時(shí)間上離散的信號(hào)。

該模擬信號(hào)經(jīng)過(guò)抽樣后還應(yīng)當(dāng)包含原信號(hào)中所有信息,也就是說(shuō)能無(wú)失真的恢復(fù)原模擬信號(hào)。

量化:

就是把經(jīng)過(guò)抽樣得到的瞬時(shí)值將其幅度離散,即用一組規(guī)定的電平,把瞬時(shí)抽樣值用最接近的電平值來(lái)表示,通常是用二進(jìn)制表示。

編碼:

就是用一組二進(jìn)制碼組來(lái)表示每一個(gè)有固定電平的量化值。然而,實(shí)際上量化是在編碼過(guò)程中同時(shí)完成的,故編碼過(guò)程也稱(chēng)為模/數(shù)變換,可記作A/D。

ADC

ADC,Analog-to-Digital Converter的縮寫(xiě),指模/數(shù)轉(zhuǎn)換器或者模數(shù)轉(zhuǎn)換器。是指將連續(xù)變化的模擬信號(hào)轉(zhuǎn)換為離散的數(shù)字信號(hào)的器件。真實(shí)世界的模擬信號(hào),例如溫度、壓力、聲音或者圖像等,需要轉(zhuǎn)換成更容易儲(chǔ)存、處理和發(fā)射的數(shù)字形式。模/數(shù)轉(zhuǎn)換器可以實(shí)現(xiàn)這個(gè)功能,在各種不同的產(chǎn)品中都可以找到它的身影。

ADC最早用于對(duì)無(wú)線(xiàn)信號(hào)向數(shù)字信號(hào)轉(zhuǎn)換。如電視信號(hào),長(zhǎng)短播電臺(tái)發(fā)接收等。

與之相對(duì)應(yīng)的DAC,Digital-to-Analog Converter,它是ADC模數(shù)轉(zhuǎn)換的逆向過(guò)程。

現(xiàn)在市場(chǎng)上的電子產(chǎn)品都集成了傳感器,傳感器要采集數(shù)據(jù),他的內(nèi)部結(jié)構(gòu)里就一定要用到ADC,常見(jiàn)的傳感器如下:

溫濕度:溫度傳感器,DHT11聲音:音頻芯片進(jìn)行錄音,WM8906圖像:索尼IMX386/IMX283傳感器

Exynos4412 A/D轉(zhuǎn)換器

三星的Exynos4412模塊結(jié)構(gòu)圖如下所示:


Adc控制器集成在exynos4412 soc中,控制器內(nèi)部有一根中斷線(xiàn)連接到中斷控制器combiner,然后路由到GIC(Generic Interrupt Controller),滑動(dòng)變阻器連接到adc控制器的通道3。

ADC控制器

參考《Exynos 4412 SCP》 的datasheet。ADC控制器是10位或12位CMOS再循環(huán)式模擬數(shù)字轉(zhuǎn)換器,它具有10個(gè)通道輸入,并可將模擬量轉(zhuǎn)換至10位或12位二進(jìn)制數(shù)。5Mhz A/D 轉(zhuǎn)換時(shí)鐘,最大1Msps的轉(zhuǎn)換速度。A/D轉(zhuǎn)換具備片上采樣保持功能,同時(shí)也支持待機(jī)工作模式。

ADC接口包括如下特性。

  • 10bit/12bit輸出位可選。
  • 微分誤差 1.0LSB。
  • 積分誤差 2.0LSB。
  • 最大轉(zhuǎn)換速率5Msps.
  • 功耗少,電壓輸入1.8V。
  • 電壓輸入范圍 0~1.8V。
  • 支持偏上樣本保持功能。
  • 通用轉(zhuǎn)換模式。

模塊圖

4412 A/D轉(zhuǎn)換器的控制器接口框圖如下:


原理我們并不需要關(guān)注,知道即可。

通道選擇


由上圖可知,A/D控制器一共有4個(gè)通道,通用寄存器地址為0x126c0000。

A/D控制器寄存器

對(duì)ADC控制器的操作主要是通過(guò)配置寄存器來(lái)實(shí)現(xiàn)的,查看datasheet,必須掌握寄存器的使用。以下是A/D控制器寄存器匯總。


1、A/D控制寄存器ADCCON


  1. RES     : 選擇A/D轉(zhuǎn)換精度,0:劃分成1024份  1:劃分成4096份 
  2.   ECFLG   :轉(zhuǎn)換是否結(jié)束  0:轉(zhuǎn)換中  1:轉(zhuǎn)換完畢;對(duì)于輪詢(xún)模式需要根據(jù)該位判斷數(shù)據(jù)是否轉(zhuǎn)換完畢。 
  3.   PRSCEN:A/D轉(zhuǎn)換預(yù)分頻是否使能 
  4.   PRSCVL:預(yù)分頻的值,轉(zhuǎn)換公式見(jiàn)下面 
  5.   STANDBY:待機(jī)模式  0:正常工作模式 1:待機(jī)模式。處于待機(jī)模式時(shí)要將PRSCEN設(shè)置為0 
  6.   READ_START: A/D轉(zhuǎn)換由讀操作觸發(fā),設(shè)置為1后,每次讀取A/D值的操作都會(huì)觸發(fā)一次A/D轉(zhuǎn)換。 
  7.   ENABLE_START: 單次開(kāi)啟A/D轉(zhuǎn)換,轉(zhuǎn)換完畢后該位自動(dòng)清零,當(dāng)READ_START設(shè)置為1的時(shí)候,該位無(wú)效。 

通常設(shè)置值為(1 << 16 | 1 << 14 | 99 <<6 | 1 << 1)。

2、A/D轉(zhuǎn)換數(shù)據(jù)寄存器ADCDAT0


注意該寄存器的值只有低12位有效。

3、A/D清中斷寄存器CLRINTADC


黃色部分可知,中斷例程負(fù)責(zé)清中斷,中斷結(jié)束后寫(xiě)入任意值就可以清中斷。

4、A/D通道選擇寄存器ADCMUX


每次操作都要先設(shè)置通道,因?yàn)?4個(gè)通道是共用同一套寄存器,如果有其他任務(wù)也在使用A/D,就會(huì)產(chǎn)生混亂。在此我們選擇通道3,置3即可。

5、ADC中斷ID

參見(jiàn)9.2.2GIC Interrupt Table


由此可知,ADC中斷號(hào)對(duì)應(yīng)的SPI值是10,inturrupt ID 為42。對(duì)于終端查詢(xún)方式和編寫(xiě)終端的驅(qū)動(dòng)需要知道SPI id和inturrupt ID,后面講解基于Linux驅(qū)動(dòng)還會(huì)再分析設(shè)備樹(shù)節(jié)點(diǎn)如何填寫(xiě)。

6、Combiner中斷控制器


combiner的配置寄存器:IMSRn、IECRn、ISERn、ISTRn,類(lèi)似于GPIO 對(duì)中斷源分組。只有中斷模式才需要考慮combiner中斷控制器的操作。

7、Combiner分組

參考章節(jié):10.2.1Interrupt Combiner Table 10-1Interrupt Groups of Interrupt Combiner


可見(jiàn)ADC在INTG10,即第10組。

8、Combiner IESR2

參考章節(jié):10.4.2.9IESR2


如果要用中斷模式設(shè)置為1即可。

9、Combiner IECR2

參考章節(jié):10.4.2.10IECR2


此處用于關(guān)閉中斷,采用默認(rèn)值即可,注意,如果設(shè)置了1,那么中斷功能就關(guān)閉了。

10、A/D轉(zhuǎn)換的轉(zhuǎn)換時(shí)間計(jì)算

例如:PCLK為100MHz,PRESCALER = 65 ;所有10位轉(zhuǎn)換時(shí)間為

100MHz/(99+1) = 1MHz

轉(zhuǎn)化時(shí)間為1/(1MHz/5 cycles) = 5us。

完成一次A/D轉(zhuǎn)換需要5個(gè)時(shí)鐘周期。A/D轉(zhuǎn)換器的最大工作時(shí)鐘為5MHz,所以最大采樣率可以達(dá)到1Mit/s.

電路連接圖


由該電路圖可知,外設(shè)是一個(gè)滑動(dòng)變阻器,根據(jù)接觸點(diǎn)的不同,會(huì)導(dǎo)致輸入電壓的模擬值不同。連接的A/D控制器通道為3。該電路利用一個(gè)電位計(jì)輸出電壓到4412的AIN3管腳。輸入的電壓范圍為0~1.8V。

ADC裸機(jī)開(kāi)發(fā)程序?qū)嵗?/span>

ADC數(shù)據(jù)的讀取通常由2種方法:中斷模式、輪訓(xùn)模式。

輪訓(xùn)模式

輪詢(xún)模式讀取數(shù)據(jù)步驟如下:

1.要讀取數(shù)據(jù)首先向ADC寄存器ADCCON的bit:1寫(xiě)1,發(fā)送轉(zhuǎn)換命令,采用讀-啟動(dòng)模式來(lái)開(kāi)啟轉(zhuǎn)換。

2.當(dāng)ADC控制器轉(zhuǎn)換完畢會(huì)將ADCCON的bit:15設(shè)置為1,

3.輪詢(xún)檢測(cè)ADCCON的bit:15是否設(shè)置為1,如果設(shè)置為1,就讀走數(shù)據(jù),否則繼續(xù)等待。

這種方式比較占用CPU資源。

注:這里使用讀-啟動(dòng)模式

  1. /***********************ADC ******************/ 
  2. #define   ADC_CFG  __REG(0x10010118) 
  3. #define  ADCCON  __REG(0x126C0000) 
  4. #define  ADCDLY  __REG(0x126C0008) 
  5. #define  ADCDAT  __REG(0x126C000C) 
  6. #define  CLRINTADC __REG(0x126C0018) 
  7. #define  ADCMUX  __REG(0x126C001C) 
  8.  
  9. #include "exynos_4412.h" 
  10. #include "pwm.h" 
  11. #include "uart.h" 
  12.  
  13. unsigned char table[10] = {'0','1','2','3','4','5','6','7','8','9'}; 
  14.  
  15. void mydelay_ms(int time
  16.   int i, j; 
  17.  
  18.   while(time--) 
  19.   { 
  20.     for (i = 0; i < 5; i++) 
  21.     for (j = 0; j < 514; j++); 
  22.   } 
  23.  
  24. adc_init(int temp
  25.   ADCCON = (1 << 16 | 1 << 14 | 99 <<6 | 1 << 1); 
  26.   ADCMUX = 3; 
  27.   temp = ADCDAT & 0xfff; 
  28.  
  29. /* 
  30.  *  裸機(jī)代碼,不同于LINUX 應(yīng)用層, 一定加循環(huán)控制 
  31.  */ 
  32.  
  33. int main (void) 
  34.   unsigned char bit4,bit3,bit2,bit1; 
  35.   unsigned int temp = 0; 
  36.    
  37.   uart_init(); 
  38.   adc_init(temp); 
  39.   puts("開(kāi)始轉(zhuǎn)換\n"); 
  40.  
  41.   while(1) 
  42.   { 
  43.     while(!(ADCCON & 0x8000)); 
  44.     temp = ADCDAT & 0xfff; 
  45.     printf("U = %d\n",temp); 
  46.     temp = 1.8 * 1000 * temp/0xfff; 
  47.     bit4 = temp /1000; 
  48.     putc(table[bit4]); 
  49.     bit3 = (temp % 1000)/100?; 
  50.     putc(table[bit3]); 
  51.     bit2 = ((temp % 1000)%100)/10; 
  52.     putc(table[bit2]); 
  53.     bit1 = ((temp % 1000)%100)%10; 
  54.     putc(table[bit1]); 
  55.     puts("mV"); 
  56.     putc('\n'); 
  57.     mydelay_ms(1000); 
  58.   } 
  59.   return 0; 

中斷模式

中斷模式讀取數(shù)據(jù)步驟如下:

1.要讀取數(shù)據(jù)首先向ADC寄存器ADCCON的bit:0寫(xiě)1,發(fā)送轉(zhuǎn)換命令;

2.當(dāng)ADC控制器轉(zhuǎn)換完畢會(huì)通過(guò)中斷線(xiàn)向CPU發(fā)送中斷信號(hào);

3.在中斷處理函數(shù)中,讀走數(shù)據(jù),并清中斷.

注:中斷對(duì)應(yīng)寄存器的設(shè)置,后續(xù)會(huì)更新對(duì)應(yīng)的文檔。

  1. void do_irq(void) 
  2.        int irq_num; 
  3.  
  4.        irq_num = CPU0.ICCIAR &0x3ff; 
  5.        switch(irq_num) 
  6.        { 
  7.          case 42: 
  8.               adc_num = ADCDAT&0xfff; 
  9.               printf("adc = %d\n",adc_num); 
  10.               CLRINTADC = 0; 
  11.        //    IECR2 = IECR2 | (1 << 19);               打開(kāi)的話(huà)只能讀取一次, 
  12.               //42/32 
  13.               ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (1 << 10);【清GIC中斷標(biāo)志位類(lèi)似于 ICDISER】 
  14.               break; 
  15.        } 
  16.        CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3ff) | irq_num; 
  17. void adc_init(void) 
  18. {    //12bit   使能分頻       分頻值                 手動(dòng) 
  19.        ADCCON = (1 << 16) | (1 << 14) | (0xff << 6) | (1 << 0); 
  20.        ADCMUX = 3; 
  21. void adcint_init(void) 
  22.        IESR2 = IESR2 | (1 << 19); 
  23.        ICDDCR = 1;    //使能分配器 
  24.        //42/32 
  25.        ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 10);//使能相應(yīng)中斷到分配器 
  26.        ICDIPTR.ICDIPTR10 = ICDIPTR.ICDIPTR10 &(~(0xff << 16)) | (0x1 << 16);//發(fā)送到相應(yīng)CPU接口 
  27.        CPU0.ICCPMR = 255;//設(shè)置中斷屏蔽優(yōu)先級(jí) 
  28.        CPU0.ICCICR = 1;  //全局使能開(kāi)關(guān) 
  29. int main (void) 
  30. {  
  31.   adc_init(); 
  32.        adcint_init(); 
  33.        while(1) 
  34.        { 
  35.               ADCCON = ADCCON | 1; 
  36.               delay_ms(1000); 
  37.        } 
  38.    return 0; 

基于Linux驅(qū)動(dòng)編寫(xiě)

設(shè)備樹(shù)

編寫(xiě)基于Linux的ADC外設(shè)驅(qū)動(dòng),首先需要編寫(xiě)設(shè)備樹(shù)節(jié)點(diǎn)信息,在裸機(jī)程序中,我們只用到了寄存器地址,而編寫(xiě)基于Linux的驅(qū)動(dòng),我們需要用到中斷功能。所以編寫(xiě)設(shè)備樹(shù)節(jié)點(diǎn)需要知道ADC要用到的硬件資源主要包括:寄存器資源和中斷資源。

關(guān)于中斷的使用我們?cè)诤罄m(xù)文章中會(huì)繼續(xù)分析,現(xiàn)在我們只需要知道中斷信息如何填寫(xiě)即可。

ADC寄存器信息填寫(xiě)

在這里插入圖片描述

由上可知,寄存器基地址為0x126c0000,其他寄存器只需要根據(jù)基地址做偏移即可獲取,所以設(shè)備樹(shù)的reg屬性信息如下:

  1. reg = <0x126C0000 0x20>; 

ADC中斷信息填寫(xiě)

描述中斷連接需要四個(gè)屬性:父節(jié)點(diǎn)提供以下信息

  1. interrupt-controller - 一個(gè)空的屬性定義該節(jié)點(diǎn)作為一個(gè)接收中斷信號(hào)的設(shè)備。 
  2. interrupt-cells      - 這是一個(gè)中斷控制器節(jié)點(diǎn)的屬性。它聲明了該中斷控制器的中斷指示符中【interrupts】 cell 的個(gè)數(shù)(類(lèi)似于 #address-cells 和 #size-cells)。 

子節(jié)點(diǎn)描述信息

  1. interrupt-parent - 這是一個(gè)設(shè)備節(jié)點(diǎn)的屬性,包含一個(gè)指向該設(shè)備連接的中斷控制器的 phandle。那些沒(méi)有 interrupt-parent 的節(jié)點(diǎn)則從它們的父節(jié)點(diǎn)中繼承該屬性。 
  2. interrupts       - 一個(gè)設(shè)備節(jié)點(diǎn)屬性,包含一個(gè)中斷指示符的列表,對(duì)應(yīng)于該設(shè)備上的每個(gè)中斷輸出信號(hào)?!驹O(shè)備的中斷信息放在該屬性中】 

父節(jié)點(diǎn)


首先我們必須知道ADC控制器的中斷線(xiàn)的父節(jié)點(diǎn):

由上圖可知ADC控制器位于soc內(nèi),4個(gè)ADC通道公用一根中斷線(xiàn),該中斷線(xiàn)連接在combiner上,所以我們需要查找到combiner這個(gè)父節(jié)點(diǎn)的說(shuō)明:

進(jìn)入設(shè)備樹(shù)文件所在目錄:arch\arm\boot\dts

  1. grep combiner *.* -n 

經(jīng)過(guò)篩選得到以下信息,


因?yàn)槲覀兪褂玫陌遄邮莈xynos4412,而exynos系列通用的平臺(tái)設(shè)備樹(shù)文件是exynos4.dtsi,查看該文件:


上圖列舉了combiner控制器的詳細(xì)信息:

  1. interrupt-cells ; 
  2. interrupt-cells =<2>; 

所以ADC控制器中斷控制器的interrupts屬性應(yīng)該有兩個(gè)cell。

interrupts屬性填寫(xiě)

而設(shè)備的中斷信息填寫(xiě)方式由內(nèi)核的以下文檔提供:

  1. Documentation\devicetree\bindings\interrupt-controller\interrupts.txt 

  1. 69. b) two cells 
  2.  70.  ------------ 
  3.  71.  The #interrupt-cells property is set to 2 and the first cell 72. defines the 
  4.  73.  index of the interrupt within the controller, while the second cell is used 
  5.  74.  to specify any of the following flags: 
  6.  75.    - bits[3:0] trigger type and level flags 
  7.  76.        1 = low-to-high edge triggered 
  8.  77.        2 = high-to-low edge triggered 
  9.  78.        4 = active high level-sensitive 
  10.  79.        8 = active low level-sensitive 

由以上信息可知,中斷的第一個(gè)cell是該中斷源所在中斷控制器的index,第二個(gè)cell表示中斷的觸發(fā)方式

*. 1:上升沿觸發(fā) *. 2:下降沿觸發(fā) *. 3:高電平觸發(fā) *. 4:低電平觸發(fā)

那么index應(yīng)該是多少呢?

詳見(jiàn)datasheet的9.2.2 GIC Interrupt Table 節(jié):


此處我們應(yīng)該是填寫(xiě)左側(cè)的SPI ID:10 還是填寫(xiě)INTERRUPT ID:42呢?

此處我們可以參考LCD節(jié)點(diǎn)的interrupts填寫(xiě)方法:

通過(guò)查找父節(jié)點(diǎn)為combiner的設(shè)備信息。

繼續(xù)grep combiner . -n


由此可見(jiàn)lcd這個(gè)設(shè)備的interrupts屬性index值是11,所以可知ADC控制器中斷線(xiàn)的index是10。中斷信息如下:

  1. interrupt-parent = <&combiner>; 
  2. interrupts = <10 3>; 

ADC外設(shè)設(shè)備樹(shù)信息

  1. fs4412-adc{ 
  2.     compatible = "fs4412,adc"
  3.     reg = <0x126C0000 0x20>; 
  4.     interrupt-parent = <&combiner>; 
  5.     interrupts = <10 3>; 
  6. }; 

本文默認(rèn)大家會(huì)使用設(shè)備樹(shù),不知道如何使用設(shè)備樹(shù)的朋友,后續(xù)會(huì)開(kāi)一篇單獨(dú)講解設(shè)備樹(shù)。

【注意】在不支持設(shè)備樹(shù)內(nèi)核中,以Cortex-A8為例,中斷信息填寫(xiě)在以下文件中

  1. 內(nèi)部中斷,Irqs.h (arch\arm\mach-s5pc100\include\mach) 
  2. 外部中斷在Irqs.h (arch\arm\plat-s5p\include\plat) 

ADC屬于內(nèi)部中斷,位于arch\arm\mach-s5pc100\include\mach\Irqs.h中。

寄存器信息填寫(xiě)在以下位置:

  1. arch\arm\mach-s5pc100\Mach-smdkc100.c 

  1. static struct platform_device *smdkc100_devices[] __initdata = { 
  2.   &s3c_device_adc, 
  3.   &s3c_device_cfcon, 
  4.   &s3c_device_i2c0, 
  5.   &s3c_device_i2c1, 
  6.   &s3c_device_fb, 
  7.   &s3c_device_hsmmc0, 
  8.   &s3c_device_hsmmc1, 
  9.   &s3c_device_hsmmc2, 
  10.   &samsung_device_pwm, 
  11.   &s3c_device_ts, 
  12.   &s3c_device_wdt, 
  13.   &smdkc100_lcd_powerdev, 
  14.   &s5pc100_device_iis0, 
  15.   &samsung_device_keypad, 
  16.   &s5pc100_device_ac97, 
  17.   &s3c_device_rtc, 
  18.   &s5p_device_fimc0, 
  19.   &s5p_device_fimc1, 
  20.   &s5p_device_fimc2, 
  21.   &s5pc100_device_spdif, 
  22. }; 

結(jié)構(gòu)體s3c_device_adc定義在以下文件:

  1. \arch\arm\plat-samsung\Devs.c 

  1. #ifdef CONFIG_PLAT_S3C24XX 
  2. static struct resource s3c_adc_resource[] = { 
  3.   [0] = DEFINE_RES_MEM(S3C24XX_PA_ADC, S3C24XX_SZ_ADC), 
  4.   [1] = DEFINE_RES_IRQ(IRQ_TC), 
  5.   [2] = DEFINE_RES_IRQ(IRQ_ADC), 
  6. }; 
  7.  
  8. struct platform_device s3c_device_adc = { 
  9.   .name    = "s3c24xx-adc"
  10.   .id    = -1, 
  11.   .num_resources  = ARRAY_SIZE(s3c_adc_resource), 
  12.   .resource  = s3c_adc_resource, 
  13. }; 
  14. #endif /* CONFIG_PLAT_S3C24XX */ 
  15.  
  16. #if defined(CONFIG_SAMSUNG_DEV_ADC) 
  17. static struct resource s3c_adc_resource[] = { 
  18.   [0] = DEFINE_RES_MEM(SAMSUNG_PA_ADC, SZ_256), 
  19.   [1] = DEFINE_RES_IRQ(IRQ_TC), 
  20.   [2] = DEFINE_RES_IRQ(IRQ_ADC), 
  21. }; 
  22.  
  23. struct platform_device s3c_device_adc = { 
  24.   .name    = "samsung-adc"
  25.   .id    = -1, 
  26.   .num_resources  = ARRAY_SIZE(s3c_adc_resource), 
  27.   .resource  = s3c_adc_resource, 
  28. }; 
  29. #endif /* CONFIG_SAMSUNG_DEV_ADC */ 

由代碼可知,平臺(tái)驅(qū)動(dòng)對(duì)應(yīng)的platform_device具體內(nèi)容由宏CONFIG_PLAT_S3C24XX、CONFIG_SAMSUNG_DEV_ADC來(lái)控制。驅(qū)動(dòng)編寫(xiě)架構(gòu)和流程如下

  1. read() 
  2.        1、向adc設(shè)備發(fā)送要讀取的命令 
  3.           ADCCON    1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6 
  4.        2、讀取不到數(shù)據(jù)就休眠 
  5.             wait_event_interruptible(); 
  6.        3、等待被喚醒讀數(shù)據(jù) 
  7.           havedata = 0; 
  8. adc_handler() 
  9.        1、清中斷 ADC使用中斷來(lái)通知轉(zhuǎn)換數(shù)據(jù)完畢的 
  10.        2、狀態(tài)位置位; 
  11.             havedata=1; 
  12.        3、喚醒阻塞進(jìn)程 
  13.             wake_up() 
  14. probe() 
  15.       1、讀取中斷號(hào),注冊(cè)中斷處理函數(shù) 
  16.       2、讀取寄存器的地址,ioremap 
  17.       3、字符設(shè)備的操作 

驅(qū)動(dòng)需要首先捕獲中斷信號(hào)后再去寄存器讀取相應(yīng)的數(shù)據(jù),在ADC控制器沒(méi)有準(zhǔn)備好數(shù)據(jù)之前,應(yīng)用層需要阻塞讀取數(shù)據(jù),所以在讀取數(shù)據(jù)的函數(shù)中,需要借助等待隊(duì)列來(lái)實(shí)現(xiàn)驅(qū)動(dòng)對(duì)應(yīng)用進(jìn)程的阻塞。驅(qū)動(dòng)程序

驅(qū)動(dòng)程序?qū)拇嫫鞯牟僮鲄⒖悸銠C(jī)程序,只是基地址需要通過(guò)ioremap()做映射,對(duì)寄存器的讀寫(xiě)操作需要用readl、writel。

driver.c

  1. #include <linux/module.h> 
  2. #include <linux/device.h> 
  3. #include <linux/platform_device.h> 
  4. #include <linux/interrupt.h> 
  5. #include <linux/fs.h> 
  6. #include <linux/wait.h> 
  7. #include <linux/sched.h> 
  8. #include <asm/uaccess.h> 
  9. #include <asm/io.h> 
  10. static int major = 250; 
  11.   
  12.   
  13. static wait_queue_head_t wq; 
  14. static int have_data = 0; 
  15. static int adc; 
  16. static struct resource *res1; 
  17. static struct resource *res2; 
  18. static void *adc_base; 
  19.   
  20. #define ADCCON 0x0000 
  21. #define ADCDLY 0x0008 
  22. #define ADCDAT 0x000C 
  23. #define CLRINTADC 0x0018 
  24. #define ADCMUX 0x001C 
  25.   
  26.   
  27. static  irqreturn_t adc_handler(int irqno, void *dev) 
  28.   have_data = 1; 
  29.   
  30.   printk("11111\n"); 
  31.   /*清中斷*/ 
  32.   writel(0x12,adc_base + CLRINTADC); 
  33.   wake_up_interruptible(&wq); 
  34.   return IRQ_HANDLED; 
  35. static int adc_open (struct inode *inod, struct file *filep) 
  36.   
  37.   return 0; 
  38. static ssize_t adc_read(struct file *filep, char __user *buf, size_t len, loff_t *pos) 
  39.     writel(0x3,adc_base + ADCMUX); 
  40.   writel(1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6 ,adc_base +ADCCON ); 
  41.   
  42.   wait_event_interruptible(wq, have_data==1); 
  43.   
  44.   /*read data*/ 
  45.   adc = readl(adc_base+ADCDAT)&0xfff; 
  46.    
  47.   if(copy_to_user(buf,&adc,sizeof(int))) 
  48.   { 
  49.     return -EFAULT; 
  50.   } 
  51.   have_data = 0; 
  52.   return len; 
  53. static  int adc_release(struct inode *inode, struct file *filep) 
  54.   return 0; 
  55. static struct file_operations  adc_ops = 
  56.   .open = adc_open, 
  57.   .release = adc_release, 
  58.   .read = adc_read, 
  59. }; 
  60.   
  61.   
  62. static int hello_probe(struct platform_device *pdev) 
  63.   int ret; 
  64.   printk("match 0k \n"); 
  65.   
  66.   res1 = platform_get_resource(pdev,IORESOURCE_IRQ, 0); 
  67.     res2 = platform_get_resource(pdev,IORESOURCE_MEM, 0); 
  68.       
  69.   ret = request_irq(res1->start,adc_handler,IRQF_DISABLED,"adc1",NULL); 
  70.       adc_base = ioremap(res2->start,res2->end-res2->start); 
  71.   
  72.   register_chrdev( major, "adc", &adc_ops); 
  73.   init_waitqueue_head(&wq); 
  74.    
  75.   return 0; 
  76. static int hello_remove(struct platform_device *pdev) 
  77.   free_irq(res1->start,NULL); 
  78.   free_irq(res2->start,NULL);   
  79.   unregister_chrdev( major, "adc"); 
  80.   return 0; 
  81.   
  82. static struct of_device_id adc_id[]= 
  83.   {.compatible = "fs4412,adc" }, 
  84. }; 
  85.   
  86. static struct platform_driver hello_driver= 
  87.    
  88.   .probe = hello_probe, 
  89.   .remove = hello_remove, 
  90.   .driver ={ 
  91.     .name = "bigbang"
  92.     .of_match_table = adc_id, 
  93.   }, 
  94. }; 
  95.   
  96. static int hello_init(void) 
  97.   printk("hello_init"); 
  98.   return platform_driver_register(&hello_driver); 
  99. static void hello_exit(void) 
  100.   platform_driver_unregister(&hello_driver); 
  101.   printk("hello_exit \n"); 
  102.   return
  103. MODULE_LICENSE("GPL"); 
  104. module_init(hello_init); 
  105. module_exit(hello_exit); 

測(cè)試程序

test.c

  1. #include <sys/types.h> 
  2. #include <sys/stat.h> 
  3. #include <fcntl.h> 
  4. #include <stdio.h> 
  5.   
  6. main() 
  7.   int fd,len; 
  8.   int adc; 
  9.   fd = open("/dev/hello",O_RDWR); 
  10.   if(fd<0) 
  11.   { 
  12.     perror("open fail \n"); 
  13.     return ; 
  14.   } 
  15.   
  16.   while(1) 
  17.   { 
  18.     read(fd,&adc,4); 
  19.     printf("adc%0.2f V \n",(1.8*adc)/4096); 
  20.   } 
  21.   
  22.   close(fd); 

 

 

責(zé)任編輯:姜華 來(lái)源: 一口Linux
相關(guān)推薦

2021-01-08 12:06:59

WDT定時(shí)裝置

2020-12-22 11:54:42

C語(yǔ)言Cortex-A9LED匯編

2021-01-19 19:32:01

Cortex-A9 R嵌入式系統(tǒng)i2c 外設(shè)

2021-01-26 06:15:42

Cortex-A9 R嵌入式系統(tǒng)啟動(dòng)代碼

2020-12-30 15:17:25

Cortex-A9UARTprintf函數(shù)

2021-01-06 05:42:42

Cortex-A9 R嵌入式系統(tǒng) RTC

2020-12-11 09:05:04

ARMMDKGNU

2021-01-13 11:51:25

ARM位置無(wú)關(guān)碼

2020-12-10 08:13:15

ARM架構(gòu) 嵌入式

2021-05-25 11:50:32

ARMuboot網(wǎng)絡(luò)協(xié)議棧

2022-10-31 07:33:05

Javafor循環(huán)

2022-10-30 10:14:43

Java循環(huán)語(yǔ)句

2022-09-30 07:32:48

循環(huán)while循環(huán)體

2022-11-26 00:34:57

數(shù)組Java程序

2022-09-22 07:31:14

Java變量計(jì)算

2015-02-04 19:13:48

ARMCortex-A 72

2022-09-30 07:32:39

架構(gòu)

2022-09-16 07:32:15

編程計(jì)算機(jī)命令

2022-10-28 07:38:06

Javawhile循環(huán)

2009-05-22 08:22:09

iPhone蘋(píng)果移動(dòng)OS
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)