成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從0學ARM,基于Cortex-A9 ADC裸機驅動詳解

系統 Linux
在嵌入式開發中,ADC應用比較頻繁,本文主要講解ADC的基本原理以及如何編寫基于ARM的裸機程序和基于Linux的驅動程序。

 前言

在嵌入式開發中,ADC應用比較頻繁,本文主要講解ADC的基本原理以及如何編寫基于ARM的裸機程序和基于Linux的驅動程序。

ARM架構:Cortex-A9 Linux內核:3.14

在講述ADC之前,我們需要先了解什么是模擬信號和數字信號。

模擬信號

主要是與離散的數字信號相對的連續的信號。模擬信號分布于自然界的各個角落,如每天溫度的變化,而數字信號是人為的抽象出來的在時間上不連續的信號。電學上的模擬信號是主要是指幅度和相位都連續的電信號,此信號可以被模擬電路進行各種運算,如放大,相加,相乘等。

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

如下圖所示從上到下一次是正弦波、 調幅波、 阻尼震蕩波、 指數衰減波 。


數字信號

數字信號指幅度的取值是離散的,幅值表示被限制在有限個數值之內。二進制碼就是一種數字信號。二進制碼受噪聲的影響小,易于有數字電路進行處理,所以得到了廣泛的應用。

數字信號:高清數字電視,MP3,JPG,PNG文件等等。


優點:

1. 抗干擾能力強、無噪聲積累

在模擬通信中,為了提高信噪比,需要在信號傳輸過程中及時對衰減的傳輸信號進行放大,信號在傳輸過程中不可避免地疊加上的噪聲也被同時放大。

隨著傳輸距離的增加,噪聲累積越來越多,以致使傳輸質量嚴重惡化。

對于數字通信,由于數字信號的幅值為有限個離散值(通常取兩個幅值),在傳輸過程中雖然也受到噪聲的干擾,但當信噪比惡化到一定程度時,

即在適當的距離采用判決再生的方法,再生成沒有噪聲干擾的和原發送端一樣的數字信號,所以可實現長距離高質量的傳輸。

2. 便于加密處理

信息傳輸的安全性和保密性越來越重要,數字通信的加密處理的比模擬通信容易得多,以話音信號為例,經過數字變換后的信號可用簡單的數字邏輯運算進行加密、解密處理。

3. 便于存儲、處理和交換

數字通信的信號形式和計算機所用信號一致,都是二進制代碼,因此便于與計算機聯網,也便于用計算機對數字信號進行存儲、處理和交換,

可使通信網的管理、維護實現自動化、智能化。

4. 設備便于集成化、微型

數字通信采用時分多路復用,不需要體積較大的濾波器。設備中大部分電路是數字電路,可用大規模和超大規模集成電路實現,因此體積小、功耗低。

5. 便于構成綜合數字網和綜合業務數字網

采用數字傳輸方式,可以通過程控數字交換設備進行數字交換,以實現傳輸和交換的綜合。

另外,電話業務和各種非話業務都可以實現數字化,構成綜合業務數字網。

6. 占用信道頻帶較寬

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

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


數字信號與模擬信號的轉化

模擬信號和數字信號之間可以相互轉換:模擬信號一般通過PCM脈碼調制(Pulse Code Modulation)方法量化為數字信號,

即讓模擬信號的不同幅度分別對應不同的二進制值,例如采用8位編碼可將模擬信號量化為2^8=256個量級,實用中常采取24位或30位編碼;

數字信號一般通過對載波進行移相(Phase Shift)的方法轉換為模擬信號。計算機、計算機局域網與城域網中均使用二進制數字信號,

目前在計算機廣域網中實際傳送的則既有二進制數字信號,也有由數字信號轉換而得的模擬信號。但是更具應用發展前景的是數字信號。

PCM脈沖編碼調制

脈沖編碼調制就是把一個時間連續,取值連續的模擬信號變換成時間離散,取值離散的數字信號后在信道中傳輸。

脈沖編碼調制就是對模擬信號先抽樣,再對樣值幅度量化, 編碼的過程。


抽樣:

就是對模擬信號進行周期性掃描,把時間上連續的信號變成時間上離散的信號。

該模擬信號經過抽樣后還應當包含原信號中所有信息,也就是說能無失真的恢復原模擬信號。

量化:

就是把經過抽樣得到的瞬時值將其幅度離散,即用一組規定的電平,把瞬時抽樣值用最接近的電平值來表示,通常是用二進制表示。

編碼:

就是用一組二進制碼組來表示每一個有固定電平的量化值。然而,實際上量化是在編碼過程中同時完成的,故編碼過程也稱為模/數變換,可記作A/D。

ADC

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

ADC最早用于對無線信號向數字信號轉換。如電視信號,長短播電臺發接收等。

與之相對應的DAC,Digital-to-Analog Converter,它是ADC模數轉換的逆向過程。

現在市場上的電子產品都集成了傳感器,傳感器要采集數據,他的內部結構里就一定要用到ADC,常見的傳感器如下:

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

Exynos4412 A/D轉換器

三星的Exynos4412模塊結構圖如下所示:


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

ADC控制器

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

ADC接口包括如下特性。

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

模塊圖

4412 A/D轉換器的控制器接口框圖如下:


原理我們并不需要關注,知道即可。

通道選擇


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

A/D控制器寄存器

對ADC控制器的操作主要是通過配置寄存器來實現的,查看datasheet,必須掌握寄存器的使用。以下是A/D控制器寄存器匯總。


1、A/D控制寄存器ADCCON


  1. RES     : 選擇A/D轉換精度,0:劃分成1024份  1:劃分成4096份 
  2.   ECFLG   :轉換是否結束  0:轉換中  1:轉換完畢;對于輪詢模式需要根據該位判斷數據是否轉換完畢。 
  3.   PRSCEN:A/D轉換預分頻是否使能 
  4.   PRSCVL:預分頻的值,轉換公式見下面 
  5.   STANDBY:待機模式  0:正常工作模式 1:待機模式。處于待機模式時要將PRSCEN設置為0 
  6.   READ_START: A/D轉換由讀操作觸發,設置為1后,每次讀取A/D值的操作都會觸發一次A/D轉換。 
  7.   ENABLE_START: 單次開啟A/D轉換,轉換完畢后該位自動清零,當READ_START設置為1的時候,該位無效。 

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

2、A/D轉換數據寄存器ADCDAT0


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

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


黃色部分可知,中斷例程負責清中斷,中斷結束后寫入任意值就可以清中斷。

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


每次操作都要先設置通道,因為 4個通道是共用同一套寄存器,如果有其他任務也在使用A/D,就會產生混亂。在此我們選擇通道3,置3即可。

5、ADC中斷ID

參見9.2.2GIC Interrupt Table


由此可知,ADC中斷號對應的SPI值是10,inturrupt ID 為42。對于終端查詢方式和編寫終端的驅動需要知道SPI id和inturrupt ID,后面講解基于Linux驅動還會再分析設備樹節點如何填寫。

6、Combiner中斷控制器


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

7、Combiner分組

參考章節:10.2.1Interrupt Combiner Table 10-1Interrupt Groups of Interrupt Combiner


可見ADC在INTG10,即第10組。

8、Combiner IESR2

參考章節:10.4.2.9IESR2


如果要用中斷模式設置為1即可。

9、Combiner IECR2

參考章節:10.4.2.10IECR2


此處用于關閉中斷,采用默認值即可,注意,如果設置了1,那么中斷功能就關閉了。

10、A/D轉換的轉換時間計算

例如:PCLK為100MHz,PRESCALER = 65 ;所有10位轉換時間為

100MHz/(99+1) = 1MHz

轉化時間為1/(1MHz/5 cycles) = 5us。

完成一次A/D轉換需要5個時鐘周期。A/D轉換器的最大工作時鐘為5MHz,所以最大采樣率可以達到1Mit/s.

電路連接圖


由該電路圖可知,外設是一個滑動變阻器,根據接觸點的不同,會導致輸入電壓的模擬值不同。連接的A/D控制器通道為3。該電路利用一個電位計輸出電壓到4412的AIN3管腳。輸入的電壓范圍為0~1.8V。

ADC裸機開發程序實例

ADC數據的讀取通常由2種方法:中斷模式、輪訓模式。

輪訓模式

輪詢模式讀取數據步驟如下:

1.要讀取數據首先向ADC寄存器ADCCON的bit:1寫1,發送轉換命令,采用讀-啟動模式來開啟轉換。

2.當ADC控制器轉換完畢會將ADCCON的bit:15設置為1,

3.輪詢檢測ADCCON的bit:15是否設置為1,如果設置為1,就讀走數據,否則繼續等待。

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

注:這里使用讀-啟動模式

  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.  *  裸機代碼,不同于LINUX 應用層, 一定加循環控制 
  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("開始轉換\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; 

中斷模式

中斷模式讀取數據步驟如下:

1.要讀取數據首先向ADC寄存器ADCCON的bit:0寫1,發送轉換命令;

2.當ADC控制器轉換完畢會通過中斷線向CPU發送中斷信號;

3.在中斷處理函數中,讀走數據,并清中斷.

注:中斷對應寄存器的設置,后續會更新對應的文檔。

  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);               打開的話只能讀取一次, 
  12.               //42/32 
  13.               ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (1 << 10);【清GIC中斷標志位類似于 ICDISER】 
  14.               break; 
  15.        } 
  16.        CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3ff) | irq_num; 
  17. void adc_init(void) 
  18. {    //12bit   使能分頻       分頻值                 手動 
  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);//使能相應中斷到分配器 
  26.        ICDIPTR.ICDIPTR10 = ICDIPTR.ICDIPTR10 &(~(0xff << 16)) | (0x1 << 16);//發送到相應CPU接口 
  27.        CPU0.ICCPMR = 255;//設置中斷屏蔽優先級 
  28.        CPU0.ICCICR = 1;  //全局使能開關 
  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驅動編寫

設備樹

編寫基于Linux的ADC外設驅動,首先需要編寫設備樹節點信息,在裸機程序中,我們只用到了寄存器地址,而編寫基于Linux的驅動,我們需要用到中斷功能。所以編寫設備樹節點需要知道ADC要用到的硬件資源主要包括:寄存器資源和中斷資源。

關于中斷的使用我們在后續文章中會繼續分析,現在我們只需要知道中斷信息如何填寫即可。

ADC寄存器信息填寫

在這里插入圖片描述

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

  1. reg = <0x126C0000 0x20>; 

ADC中斷信息填寫

描述中斷連接需要四個屬性:父節點提供以下信息

  1. interrupt-controller - 一個空的屬性定義該節點作為一個接收中斷信號的設備。 
  2. interrupt-cells      - 這是一個中斷控制器節點的屬性。它聲明了該中斷控制器的中斷指示符中【interrupts】 cell 的個數(類似于 #address-cells 和 #size-cells)。 

子節點描述信息

  1. interrupt-parent - 這是一個設備節點的屬性,包含一個指向該設備連接的中斷控制器的 phandle。那些沒有 interrupt-parent 的節點則從它們的父節點中繼承該屬性。 
  2. interrupts       - 一個設備節點屬性,包含一個中斷指示符的列表,對應于該設備上的每個中斷輸出信號?!驹O備的中斷信息放在該屬性中】 

父節點


首先我們必須知道ADC控制器的中斷線的父節點:

由上圖可知ADC控制器位于soc內,4個ADC通道公用一根中斷線,該中斷線連接在combiner上,所以我們需要查找到combiner這個父節點的說明:

進入設備樹文件所在目錄:arch\arm\boot\dts

  1. grep combiner *.* -n 

經過篩選得到以下信息,


因為我們使用的板子是exynos4412,而exynos系列通用的平臺設備樹文件是exynos4.dtsi,查看該文件:


上圖列舉了combiner控制器的詳細信息:

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

所以ADC控制器中斷控制器的interrupts屬性應該有兩個cell。

interrupts屬性填寫

而設備的中斷信息填寫方式由內核的以下文檔提供:

  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 

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

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

那么index應該是多少呢?

詳見datasheet的9.2.2 GIC Interrupt Table 節:


此處我們應該是填寫左側的SPI ID:10 還是填寫INTERRUPT ID:42呢?

此處我們可以參考LCD節點的interrupts填寫方法:

通過查找父節點為combiner的設備信息。

繼續grep combiner . -n


由此可見lcd這個設備的interrupts屬性index值是11,所以可知ADC控制器中斷線的index是10。中斷信息如下:

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

ADC外設設備樹信息

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

本文默認大家會使用設備樹,不知道如何使用設備樹的朋友,后續會開一篇單獨講解設備樹。

【注意】在不支持設備樹內核中,以Cortex-A8為例,中斷信息填寫在以下文件中

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

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

寄存器信息填寫在以下位置:

  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. }; 

結構體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 */ 

由代碼可知,平臺驅動對應的platform_device具體內容由宏CONFIG_PLAT_S3C24XX、CONFIG_SAMSUNG_DEV_ADC來控制。驅動編寫架構和流程如下

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

驅動需要首先捕獲中斷信號后再去寄存器讀取相應的數據,在ADC控制器沒有準備好數據之前,應用層需要阻塞讀取數據,所以在讀取數據的函數中,需要借助等待隊列來實現驅動對應用進程的阻塞。驅動程序

驅動程序對寄存器的操作參考裸機程序,只是基地址需要通過ioremap()做映射,對寄存器的讀寫操作需要用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); 

測試程序

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); 

 

 

責任編輯:姜華 來源: 一口Linux
相關推薦

2021-01-08 12:06:59

WDT定時裝置

2020-12-22 11:54:42

C語言Cortex-A9LED匯編

2021-01-26 06:15:42

Cortex-A9 R嵌入式系統啟動代碼

2021-01-19 19:32:01

Cortex-A9 R嵌入式系統i2c 外設

2020-12-30 15:17:25

Cortex-A9UARTprintf函數

2021-01-06 05:42:42

Cortex-A9 R嵌入式系統 RTC

2020-12-11 09:05:04

ARMMDKGNU

2021-01-13 11:51:25

ARM位置無關碼

2020-12-10 08:13:15

ARM架構 嵌入式

2021-05-25 11:50:32

ARMuboot網絡協議棧

2022-10-31 07:33:05

Javafor循環

2022-10-30 10:14:43

Java循環語句

2022-09-30 07:32:48

循環while循環體

2022-11-26 00:34:57

數組Java程序

2022-09-22 07:31:14

Java變量計算

2015-02-04 19:13:48

ARMCortex-A 72

2022-09-30 07:32:39

架構

2022-09-16 07:32:15

編程計算機命令

2022-10-28 07:38:06

Javawhile循環

2023-03-20 16:21:26

ADC數字轉換器
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲最大av | 99视频入口 | 日韩欧美国产精品一区二区 | 视频一二区| 国产精品美女久久久久久久久久久 | 亚洲午夜精品视频 | 日韩欧美国产一区二区三区 | 人人鲁人人莫人人爱精品 | a视频在线观看 | 激情五月综合网 | 99视频 | 国产高清久久 | 又黑又粗又长的欧美一区 | 日本在线综合 | 国产精品免费在线 | 日本欧美黄色片 | 国产精产国品一二三产区视频 | 精品一二三 | 国产日韩精品一区 | 国产精品久久九九 | 久久激情视频 | 日本一区二区高清不卡 | 91九色婷婷 | 真人女人一级毛片免费播放 | аⅴ资源新版在线天堂 | 久久在看 | 国产精品久久久久久久久久免费看 | 亚洲综合色网站 | 国产欧美一区二区三区久久 | 欧美亚洲国产日韩 | 免费视频一区二区 | 国产乱码精品一区二区三区中文 | 国产一区在线免费观看视频 | 亚洲中午字幕 | www成年人视频| 久久久久久艹 | 在线亚洲欧美 | 成人一级片在线观看 | 亚洲精精品 | 色一级 | 国产人久久人人人人爽 |