linux adc はハイブリッド デバイス ドライバーです。linux2.6.30.4 では、システムにはすでに ADC ユニバーサル ドライバー ファイル「arch/arm/plat-s3c24xx/adc.c」が付属しています。プラットフォーム ドライバー デバイス モデルのアーキテクチャに従って記述されており、比較的一般的で安定したコードが含まれています。
このチュートリアルの動作環境: linux2.6.30.4 システム、Dell G3 コンピューター。
Linux ADC とはどのようなデバイスですか?
linux 混合デバイス ドライバー adc ドライバー
linux2.6.30.4 では、システムは次のようになりました。自動的に ADC ユニバーサル ドライバー ファイル ---arch/arm/plat-s3c24xx/adc.c が付属しており、これはプラットフォーム ドライバー デバイス モデルのアーキテクチャに基づいて記述されており、比較的一般的で安定したコードがいくつか含まれていますが、linux2 です。 6.30.4 ADC ユニバーサル ドライバー ファイルのバージョンが不完全で、読み取り機能がありません。その後、比較的完成した Linux 3.8 バージョンの ADC 一般ファイル、arch/arm/plat-samsung/adc.c を調べました。
ただし、このセクションはこのファイルを分析することではなく、別のアーキテクチャで ADC ドライバーを作成することを目的としています。ADC ドライバーは比較的単純であるため、使用しません。プラットフォーム ドライバーのデバイス モデルはアーキテクチャ用に記述されており、今回はその他のデバイス ドライバーを使用します。
#Q: その他のデバイス ドライバーとは何ですか?
回答:miscdevice はメジャー デバイス番号 MISC_MAJOR (10) を共有しますが、マイナー デバイス番号は異なります。すべてのmiscdeviceデバイスはリンクリストを形成しており、カーネルはデバイスにアクセスする際、デバイス番号を基に対応するmiscdeviceデバイスを検索し、file_operations構造体に登録されているファイル操作インタフェースを呼び出して動作します。
struct miscdevice { int minor; //次设备号,如果设置为MISC_DYNAMIC_MINOR则系统自动分配 const char *name; //设备名 const struct file_operations *fops; //操作函数 struct list_head list; struct device *parent; struct device *this_device; };dev_init エントリ関数の分析:
static int __init dev_init(void) { int ret; base_addr=ioremap(S3C2410_PA_ADC,0x20); if (base_addr == NULL) { printk(KERN_ERR "failed to remap register block\n"); return -ENOMEM; } adc_clock = clk_get(NULL, "adc"); if (!adc_clock) { printk(KERN_ERR "failed to get adc clock source\n"); return -ENOENT; } clk_enable(adc_clock); ADCTSC = 0; ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev); if (ret) { iounmap(base_addr); return ret; } ret = misc_register(&misc); printk (DEVICE_NAME" initialized\n"); return ret; }最初は、ADC レジスタ アドレスをマップし、それを次のように変換します。次に、ADC クロックを取得して ADC クロックを有効にし、ADC 割り込みを適用します。割り込みハンドラー関数は
adcdone_int_handler、フラグは IRQF_SHARED (共有割り込み) です。画面でもADC割り込みを申請し、最後にハイブリッド機器を登録する必要があります。
アプリケーションが開くと ("/dev/adc",...)、ドライバー内の open 関数が呼び出されますので、見てみましょう。 open 関数が何をするかわかりますか?
static int tq2440_adc_open(struct inode *inode, struct file *filp) { /* 初始化等待队列头 */ init_waitqueue_head(&(adcdev.wait)); /* 开发板上ADC的通道2连接着一个电位器 */ adcdev.channel=2; //设置ADC的通道 adcdev.prescale=0xff; DPRINTK( "ADC opened\n"); return 0; }非常に簡単です。最初に待機キューのヘッドを初期化します。エントリ関数には ADC 割り込みのアプリケーションがあるため、待機キューを使用してから ADC チャネルを設定する必要があります。TQ2440 の ADC 入力チャネルはデフォルトで 2 であるため、プリスケーラ値を 0xff に設定します。
アプリケーションが読み取りを行うと、ドライバーの読み取り関数が呼び出されます。それでは、読み取り関数が何を行うかを見てみましょう。
static ssize_t tq2440_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { char str[20]; int value; size_t len; /* 尝试获得ADC_LOCK信号量,如果能够立刻获得,它就获得信号量并返回0 * 否则,返回非零,它不会导致调用者睡眠,可以在中断上下文使用 */ if (down_trylock(&ADC_LOCK) == 0) { /* 表示A/D转换器资源可用 */ ADC_enable = 1; /* 使能预分频,选择ADC通道,最后启动ADC转换*/ START_ADC_AIN(adcdev.channel, adcdev.prescale); /* 等待事件,当ev_adc = 0时,进程被阻塞,直到ev_adc>0 */ wait_event_interruptible(adcdev.wait, ev_adc); ev_adc = 0; DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ((ADCCON & 0x80) ? 1:0)); /* 将在ADC中断处理函数读取的ADC转换结果赋值给value */ value = adc_data; sprintf(str,"%5d", adc_data); copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data)); ADC_enable = 0; up(&ADC_LOCK); } else { /* 如果A/D转换器资源不可用,将value赋值为-1 */ value = -1; } /* 将ADC转换结果输出到str数组里,以便传给应用空间 */ len = sprintf(str, "%d\n", value); if (count >= len) { /* 从str数组里拷贝len字节的数据到buffer,即将ADC转换数据传给应用空间 */ int r = copy_to_user(buffer, str, len); return r ? r : len; } else { return -EINVAL; } }tq2440_adc_read 関数は、タッチ スクリーン ドライバーも ADC リソースを使用し、この 2 つが競合するため、最初に ADC_LOCK セマフォを取得しようとします。 ADC リソースを有効にした後、プリスケーラーを有効にし、ADC チャネルを選択し、最後に ADC 変換を開始してから、wait_event_interruptible 関数を呼び出して、ev_adc>0 プロセスが実行を継続するまで待機します。 adc_data データが読み出され、copy_to_user 関数が呼び出されて ADC データがアプリケーション空間に転送され、最後に ADC_LOCK セマフォが解放されます。 Q: ev_adc>0 はいつですか?デフォルト ev_adc = 0
回答: adcdone_int_handler 割り込み処理関数では、データ読み出し後、ev_adc が 1 に設定されます。
#ADC 割り込みハンドラ関数 acdone_int_handler
##
/* ADC中断处理函数 */ static irqreturn_t adcdone_int_handler(int irq, void *dev_id) { /* A/D转换器资源可用 */ if (ADC_enable) { /* 读ADC转换结果数据 */ adc_data = ADCDAT0 & 0x3ff; /* 唤醒标志位,作为wait_event_interruptible的唤醒条件 */ ev_adc = 1; wake_up_interruptible(&adcdev.wait); } return IRQ_HANDLED; }
AD 変換が完了すると、ADC 割り込みが発生し、adcdone_int_handler に入り、AD 変換データを adc_data に読み込み、ウェイクアップ フラグ ev_adc を 1 に設定し、最後に wake_up_interruptible 関数を呼び出します。 adcdev.wait と wait.queue を起動します。 ADC のワークフローを要約します:
1. open 関数で、アナログ入力チャネルを設定し、プリスケーラー値を設定します三、adc_irq函数里,AD转换结束后触发ADC中断,在ADC中断处理函数将数据读出,唤醒进程 四、read函数里,进程被唤醒后,将adc转换数据传给应用程序 ADC驱动参考源码:
相关推荐:《Linux视频教程》/*************************************
NAME:EmbedSky_adc.c
COPYRIGHT:www.embedsky.net
*************************************/
#include
ADC应用测试参考源码:/*************************************
NAME:EmbedSky_adc.c
COPYRIGHT:www.embedsky.net
*************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>
int main(void)
{
int fd ;
char temp = 1;
fd = open("/dev/adc", 0);
if (fd < 0)
{
perror("open ADC device !");
exit(1);
}
for( ; ; )
{
char buffer[30];
int len ;
len = read(fd, buffer, sizeof buffer -1);
if (len > 0)
{
buffer[len] = '\0';
int value;
sscanf(buffer, "%d", &value);
printf("ADC Value: %d\n", value);
}
else
{
perror("read ADC device !");
exit(1);
}
sleep(1);
}
adcstop:
close(fd);
}
测试结果:[WJ2440]# ./adc_test
ADC Value: 693
ADC Value: 695
ADC Value: 694
ADC Value: 695
ADC Value: 702
ADC Value: 740
ADC Value: 768
ADC Value: 775
ADC Value: 820
ADC Value: 844
ADC Value: 887
ADC Value: 937
ADC Value: 978
ADC Value: 1000
ADC Value: 1023
ADC Value: 1023
ADC Value: 1023
以上がLinux ADCとはどのようなデバイスですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。