首頁 > 運維 > linux運維 > linux adc是什麼設備

linux adc是什麼設備

藏色散人
發布: 2023-04-17 09:33:00
原創
1856 人瀏覽過

linux adc是混雜設備驅動;在linux2.6.30.4中,系統已經自帶了ADC通用驅動檔“arch/arm/plat-s3c24xx/adc.c”,它是以平台驅動設備模型的架構來寫的,裡面是一些比較通用穩定的程式碼。

linux adc是什麼設備

本教學操作環境: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通用驅動檔並不完善,居然沒有讀函數。後來我去看了linux3.8版本的ADC通用檔案----arch/arm/plat-samsung/adc.c才是比較完善的。

但本節並不是分析這個文件,而是以另一個架構來寫ADC驅動,因為ADC驅動實在比較簡單,就沒有使用平台驅動設備模型為架構來編寫了,這次我們使用的是混雜(misc)裝置驅動。

問:什麼是misc裝置驅動程式?

答案:miscdevice共用一個主裝置號碼MISC_MAJOR(10),但次裝置號碼不同。所有的miscdevice設備形成一條鍊錶,對設備存取時核心會根據設備號碼來尋找對應的miscdevice設備,然後呼叫其file_operations結構體中註冊的檔案操作介面進行操作。

1

2

3

4

5

6

7

8

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入口函數分析:

#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

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,而flags為IRQF_SHARED,即共享中斷,因為觸控螢幕裡也要申請ADC中斷,最後註冊一個混雜裝置.

當應用程式open ("/dev/adc",...)時,就會呼叫到驅動裡面的open函數,那麼我們來看看open函數做了什麼?

1

2

3

4

5

6

7

8

9

10

11

12

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。

當應用程式read時,就會呼叫到驅動裡面的read函數,那麼我們來看看read函數做了什麼?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

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_LOCK訊號量,因為觸控螢幕驅動也有使用ADC資源,兩者互有競爭關係,獲得ADC資源後,啟用預分頻,選擇ADC通道,最後啟動ADC轉換,接著就呼叫wait_event_interruptible 函式進行等待,直到ev_adc>0行程才會繼續往下跑,往下跑就會adc_data資料讀出來,呼叫copy_to_user函式將ADC資料傳給應用空間,最後釋放ADC_LOCK訊號量。

問:什麼時候ev_adc>0?預設ev_adc = 0

答案:在adcdone_int_handler中斷處理函數裡,等資料讀出後,ev_adc被設定為1。

ADC中斷處理函數adcdone_int_handler

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/* 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等待隊列。
總結一下ADC的工作流程:

一、open函數裡,設定類比輸入通道,設定預分頻值

二、read函數裡,啟動AD轉換,進程休眠

三、adc_irq函数里,AD转换结束后触发ADC中断,在ADC中断处理函数将数据读出,唤醒进程

四、read函数里,进程被唤醒后,将adc转换数据传给应用程序

ADC驱动参考源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

/*************************************

 

NAME:EmbedSky_adc.c

COPYRIGHT:www.embedsky.net

 

*************************************/

 

#include <linux errno.h="">

#include <linux kernel.h="">

#include <linux module.h="">

#include <linux slab.h="">

#include <linux input.h="">

#include <linux init.h="">

#include <linux serio.h="">

#include <linux delay.h="">

#include <linux clk.h="">

#include <asm io.h="">

#include <asm irq.h="">

#include <asm uaccess.h="">

#include <mach regs-clock.h="">

#include <plat regs-timer.h="">

      

#include <plat regs-adc.h="">

#include <mach regs-gpio.h="">

#include <linux cdev.h="">

#include <linux miscdevice.h="">

 

#include "tq2440_adc.h"

 

#undef DEBUG

//#define DEBUG

#ifdef DEBUG

#define DPRINTK(x...) {printk(KERN_DEBUG "EmbedSky_adc: " x);}

#else

#define DPRINTK(x...) (void)(0)

#endif

 

#define DEVICE_NAME    "adc"       /* 设备节点: /dev/adc */

 

static void __iomem *base_addr;

 

typedef struct

{

    wait_queue_head_t wait;        /* 定义等待队列头 */

    int channel;

    int prescale;

}ADC_DEV;

 

DECLARE_MUTEX(ADC_LOCK);    /* 定义并初始化信号量,并初始化为1 */

static int ADC_enable = 0;          /* A/D转换器资是否可用标志位 */

 

static ADC_DEV adcdev;                /* 用于表示ADC设备 */

static volatile int ev_adc = 0;        /* 作为wait_event_interruptible的唤醒条件 */

static int adc_data;

 

static struct clk *adc_clock;

 

#define ADCCON     (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))  //ADC control

#define ADCTSC     (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC))  //ADC touch screen control

#define ADCDLY     (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY))  //ADC start or Interval Delay

#define ADCDAT0        (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0)) //ADC conversion data 0

#define ADCDAT1        (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1)) //ADC conversion data 1

#define ADCUPDN        (*(volatile unsigned long *)(base_addr + 0x14))            //Stylus Up/Down interrupt status

 

#define PRESCALE_DIS   (0 << 14)

#define PRESCALE_EN     (1 << 14)

#define PRSCVL(x)       ((x) << 6)

#define ADC_INPUT(x)    ((x) << 3)

#define ADC_START       (1 << 0)

#define ADC_ENDCVT      (1 << 15)

 

 

/* 使能预分频,选择ADC通道,最后启动ADC转换*/

#define START_ADC_AIN(ch, prescale) \

    do{     ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \

        ADCCON |= ADC_START; \

    }while(0)

 

 

/* 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;

}

 

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;

    }

}

 

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;

}

 

static int tq2440_adc_release(struct inode *inode, struct file *filp)

{

    DPRINTK( "ADC closed\n");

    return 0;

}

 

 

static struct file_operations dev_fops = {

    owner:  THIS_MODULE,

    open:   tq2440_adc_open,

    read:   tq2440_adc_read,   

    release:    tq2440_adc_release,

};

 

static struct miscdevice misc = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = DEVICE_NAME,

    .fops = &dev_fops,

};

 

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;

}

 

static void __exit dev_exit(void)

{

    free_irq(IRQ_ADC, &adcdev);

    iounmap(base_addr);

 

    if (adc_clock)

    {

        clk_disable(adc_clock);

        clk_put(adc_clock);

        adc_clock = NULL;

    }

 

    misc_deregister(&misc);

}

 

EXPORT_SYMBOL(ADC_LOCK);

module_init(dev_init);

module_exit(dev_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("www.embedsky.net");

MODULE_DESCRIPTION("ADC Drivers for EmbedSky SKY2440/TQ2440 Board and support touch");</linux></linux></mach></plat></plat></mach></asm></asm></asm></linux></linux></linux></linux></linux></linux></linux></linux></linux>

登入後複製
ADC应用测试参考源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

/*************************************

 

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

}

登入後複製
测试结果:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

[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视频教程

以上是linux adc是什麼設備的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板