文章目录

  • 前言
  • 一、根据硬件修改设备树
    • 输入事件定义源码
  • 二、Linux Kernel中的LRADC驱动源码
    • 1、Kernel内置的LRADC驱动源码
    • 2、驱动测试
      • 驱动测试软件使用信号量的方法
        • 测试失败--键值错误
      • 驱动测试软件使用轮询方式
        • 测试失败,键值显示错误
    • 3、另一个测试方式
    • 4、测试软件的编译方法
  • 三、改进
  • 四、重新开始一个
    • 新编一个驱动.ko
    • 测试程序

前言

LRADC全称“低分辨率模拟数字转换器”,分辨率很低,如下原理图就只有6位分辨率(0~64)通过一个ADC引脚识别多个按键,可以极大的节省引脚资源。

  • KEY_ADC0:112–LRADC0
  • 当按键按下时对应电压:
按下的按键 S1按键按下 S2按键按下 S3按键按下 S4按键按下
ADC检测到的电压 0.2V 0.4V 0.6V 0.8V

参考:https://blog.csdn.net/lengyuefeng212/article/details/120033179
参考:https://dandelioncloud.cn/article/details/1419660822644854785/
参考:https://blog.csdn.net/u012577474/article/details/103166505

一、根据硬件修改设备树

1、添加pinctrl节点

&lradc {vref-supply = <&reg_vcc3v0>;status = "okay";button@200 {label = "Volume Up";linux,code = <KEY_VOLUMEUP>;channel = <0>;voltage = <200000>;};button@400 {label = "Volume Down";linux,code = <KEY_VOLUMEDOWN>;channel = <0>;voltage = <400000>;};button@600 {label = "Select";linux,code = <KEY_SELECT>;channel = <0>;voltage = <600000>;};button@800 {label = "Start";linux,code = <KEY_OK>;channel = <0>;voltage = <800000>;};
};

linux,cod对应键值:

物理按键 16进制键值 10进制键值
S1按键 KEY_VOLUMEUP 0x73 115
S2按键 KEY_VOLUMEDOWN 0x72 114
S3按键 KEY_SELECT 0x161 353
S4按键 KEY_OK 0x160 352

输入事件定义源码

在上面有一个属性:linux,code = <KEY_OK>这个KEY_OK是在哪里定义的呢?

  • linux-zero-4.10.y/include/dt-bindings/input/linux-event-codes.h
  • linux-zero-4.10.y/arch/arm/boot/dts/include/dt-bindings/input/linux-event-codes.h
/** Input event codes**    *** IMPORTANT **** This file is not only included from C-code but also from devicetree source* files. As such this file MUST only contain comments and defines.** Copyright (c) 1999-2002 Vojtech Pavlik* Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation.*/
#ifndef _UAPI_INPUT_EVENT_CODES_H
#define _UAPI_INPUT_EVENT_CODES_H/** Device properties and quirks*/#define INPUT_PROP_POINTER       0x00    /* needs a pointer */
#define INPUT_PROP_DIRECT       0x01    /* direct input devices */
#define INPUT_PROP_BUTTONPAD        0x02    /* has button(s) under pad */
#define INPUT_PROP_SEMI_MT      0x03    /* touch rectangle only */
#define INPUT_PROP_TOPBUTTONPAD     0x04    /* softbuttons at top of pad */
#define INPUT_PROP_POINTING_STICK   0x05    /* is a pointing stick */
#define INPUT_PROP_ACCELEROMETER    0x06    /* has accelerometer */#define INPUT_PROP_MAX           0x1f
#define INPUT_PROP_CNT          (INPUT_PROP_MAX + 1)/** Event types*/#define EV_SYN            0x00
#define EV_KEY          0x01
#define EV_REL          0x02
#define EV_ABS          0x03
#define EV_MSC          0x04
#define EV_SW           0x05
#define EV_LED          0x11
#define EV_SND          0x12
#define EV_REP          0x14
#define EV_FF           0x15
#define EV_PWR          0x16
#define EV_FF_STATUS        0x17
#define EV_MAX          0x1f
#define EV_CNT          (EV_MAX+1)/** Synchronization events.*/#define SYN_REPORT      0
#define SYN_CONFIG      1
#define SYN_MT_REPORT       2
#define SYN_DROPPED     3
#define SYN_MAX         0xf
#define SYN_CNT         (SYN_MAX+1)/** Keys and buttons** Most of the keys/buttons are modeled after USB HUT 1.12* (see http://www.usb.org/developers/hidpage).* Abbreviations in the comments:* AC - Application Control* AL - Application Launch Button* SC - System Control*/#define KEY_RESERVED       0
#define KEY_ESC         1
#define KEY_1           2
#define KEY_2           3
#define KEY_3           4
#define KEY_4           5
#define KEY_5           6
#define KEY_6           7
#define KEY_7           8
#define KEY_8           9
#define KEY_9           10
#define KEY_0           11
#define KEY_MINUS       12
#define KEY_EQUAL       13
#define KEY_BACKSPACE       14
#define KEY_TAB         15
#define KEY_Q           16
#define KEY_W           17
#define KEY_E           18
#define KEY_R           19
#define KEY_T           20
#define KEY_Y           21
#define KEY_U           22
#define KEY_I           23
#define KEY_O           24
#define KEY_P           25
#define KEY_LEFTBRACE       26
#define KEY_RIGHTBRACE      27
#define KEY_ENTER       28
#define KEY_LEFTCTRL        29
#define KEY_A           30
#define KEY_S           31
#define KEY_D           32
#define KEY_F           33
#define KEY_G           34
#define KEY_H           35
#define KEY_J           36
#define KEY_K           37
#define KEY_L           38
#define KEY_SEMICOLON       39
#define KEY_APOSTROPHE      40
#define KEY_GRAVE       41
#define KEY_LEFTSHIFT       42
#define KEY_BACKSLASH       43
#define KEY_Z           44
#define KEY_X           45
#define KEY_C           46
#define KEY_V           47
#define KEY_B           48
#define KEY_N           49
#define KEY_M           50
#define KEY_COMMA       51
#define KEY_DOT         52
#define KEY_SLASH       53
#define KEY_RIGHTSHIFT      54
#define KEY_KPASTERISK      55
#define KEY_LEFTALT     56
#define KEY_SPACE       57
#define KEY_CAPSLOCK        58
#define KEY_F1          59
#define KEY_F2          60
#define KEY_F3          61
#define KEY_F4          62
#define KEY_F5          63
#define KEY_F6          64
#define KEY_F7          65
#define KEY_F8          66
#define KEY_F9          67
#define KEY_F10         68
#define KEY_NUMLOCK     69
#define KEY_SCROLLLOCK      70
#define KEY_KP7         71
#define KEY_KP8         72
#define KEY_KP9         73
#define KEY_KPMINUS     74
#define KEY_KP4         75
#define KEY_KP5         76
#define KEY_KP6         77
#define KEY_KPPLUS      78
#define KEY_KP1         79
#define KEY_KP2         80
#define KEY_KP3         81
#define KEY_KP0         82
#define KEY_KPDOT       83#define KEY_ZENKAKUHANKAKU    85
#define KEY_102ND       86
#define KEY_F11         87
#define KEY_F12         88
#define KEY_RO          89
#define KEY_KATAKANA        90
#define KEY_HIRAGANA        91
#define KEY_HENKAN      92
#define KEY_KATAKANAHIRAGANA    93
#define KEY_MUHENKAN        94
#define KEY_KPJPCOMMA       95
#define KEY_KPENTER     96
#define KEY_RIGHTCTRL       97
#define KEY_KPSLASH     98
#define KEY_SYSRQ       99
#define KEY_RIGHTALT        100
#define KEY_LINEFEED        101
#define KEY_HOME        102
#define KEY_UP          103
#define KEY_PAGEUP      104
#define KEY_LEFT        105
#define KEY_RIGHT       106
#define KEY_END         107
#define KEY_DOWN        108
#define KEY_PAGEDOWN        109
#define KEY_INSERT      110
#define KEY_DELETE      111
#define KEY_MACRO       112
#define KEY_MUTE        113
#define KEY_VOLUMEDOWN      114
#define KEY_VOLUMEUP        115
#define KEY_POWER       116 /* SC System Power Down */
#define KEY_KPEQUAL     117
#define KEY_KPPLUSMINUS     118
#define KEY_PAUSE       119
#define KEY_SCALE       120 /* AL Compiz Scale (Expose) */#define KEY_KPCOMMA       121
#define KEY_HANGEUL     122
#define KEY_HANGUEL     KEY_HANGEUL
#define KEY_HANJA       123
#define KEY_YEN         124
#define KEY_LEFTMETA        125
#define KEY_RIGHTMETA       126
#define KEY_COMPOSE     127#define KEY_STOP     128 /* AC Stop */
#define KEY_AGAIN       129
#define KEY_PROPS       130 /* AC Properties */
#define KEY_UNDO        131 /* AC Undo */
#define KEY_FRONT       132
#define KEY_COPY        133 /* AC Copy */
#define KEY_OPEN        134 /* AC Open */
#define KEY_PASTE       135 /* AC Paste */
#define KEY_FIND        136 /* AC Search */
#define KEY_CUT         137 /* AC Cut */
#define KEY_HELP        138 /* AL Integrated Help Center */
#define KEY_MENU        139 /* Menu (show menu) */
#define KEY_CALC        140 /* AL Calculator */
#define KEY_SETUP       141
#define KEY_SLEEP       142 /* SC System Sleep */
#define KEY_WAKEUP      143 /* System Wake Up */
#define KEY_FILE        144 /* AL Local Machine Browser */
#define KEY_SENDFILE        145
#define KEY_DELETEFILE      146
#define KEY_XFER        147
#define KEY_PROG1       148
#define KEY_PROG2       149
#define KEY_WWW         150 /* AL Internet Browser */
#define KEY_MSDOS       151
#define KEY_COFFEE      152 /* AL Terminal Lock/Screensaver */
#define KEY_SCREENLOCK      KEY_COFFEE
#define KEY_ROTATE_DISPLAY  153 /* Display orientation for e.g. tablets */
#define KEY_DIRECTION       KEY_ROTATE_DISPLAY
#define KEY_CYCLEWINDOWS    154
#define KEY_MAIL        155
#define KEY_BOOKMARKS       156 /* AC Bookmarks */
#define KEY_COMPUTER        157
#define KEY_BACK        158 /* AC Back */
#define KEY_FORWARD     159 /* AC Forward */
#define KEY_CLOSECD     160
#define KEY_EJECTCD     161
#define KEY_EJECTCLOSECD    162
#define KEY_NEXTSONG        163
#define KEY_PLAYPAUSE       164
#define KEY_PREVIOUSSONG    165
#define KEY_STOPCD      166
#define KEY_RECORD      167
#define KEY_REWIND      168
#define KEY_PHONE       169 /* Media Select Telephone */
#define KEY_ISO         170
#define KEY_CONFIG      171 /* AL Consumer Control Configuration */
#define KEY_HOMEPAGE        172 /* AC Home */
#define KEY_REFRESH     173 /* AC Refresh */
#define KEY_EXIT        174 /* AC Exit */
#define KEY_MOVE        175
#define KEY_EDIT        176
#define KEY_SCROLLUP        177
#define KEY_SCROLLDOWN      178
#define KEY_KPLEFTPAREN     179
#define KEY_KPRIGHTPAREN    180
#define KEY_NEW         181 /* AC New */
#define KEY_REDO        182 /* AC Redo/Repeat */#define KEY_F13         183
#define KEY_F14         184
#define KEY_F15         185
#define KEY_F16         186
#define KEY_F17         187
#define KEY_F18         188
#define KEY_F19         189
#define KEY_F20         190
#define KEY_F21         191
#define KEY_F22         192
#define KEY_F23         193
#define KEY_F24         194#define KEY_PLAYCD       200
#define KEY_PAUSECD     201
#define KEY_PROG3       202
#define KEY_PROG4       203
#define KEY_DASHBOARD       204 /* AL Dashboard */
#define KEY_SUSPEND     205
#define KEY_CLOSE       206 /* AC Close */
#define KEY_PLAY        207
#define KEY_FASTFORWARD     208
#define KEY_BASSBOOST       209
#define KEY_PRINT       210 /* AC Print */
#define KEY_HP          211
#define KEY_CAMERA      212
#define KEY_SOUND       213
#define KEY_QUESTION        214
#define KEY_EMAIL       215
#define KEY_CHAT        216
#define KEY_SEARCH      217
#define KEY_CONNECT     218
#define KEY_FINANCE     219 /* AL Checkbook/Finance */
#define KEY_SPORT       220
#define KEY_SHOP        221
#define KEY_ALTERASE        222
#define KEY_CANCEL      223 /* AC Cancel */
#define KEY_BRIGHTNESSDOWN  224
#define KEY_BRIGHTNESSUP    225
#define KEY_MEDIA       226#define KEY_SWITCHVIDEOMODE  227 /* Cycle between available videooutputs (Monitor/LCD/TV-out/etc) */
#define KEY_KBDILLUMTOGGLE  228
#define KEY_KBDILLUMDOWN    229
#define KEY_KBDILLUMUP      230#define KEY_SEND     231 /* AC Send */
#define KEY_REPLY       232 /* AC Reply */
#define KEY_FORWARDMAIL     233 /* AC Forward Msg */
#define KEY_SAVE        234 /* AC Save */
#define KEY_DOCUMENTS       235#define KEY_BATTERY      236#define KEY_BLUETOOTH        237
#define KEY_WLAN        238
#define KEY_UWB         239#define KEY_UNKNOWN      240#define KEY_VIDEO_NEXT       241 /* drive next video source */
#define KEY_VIDEO_PREV      242 /* drive previous video source */
#define KEY_BRIGHTNESS_CYCLE    243 /* brightness up, after max is min */
#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manualbrightness control is off,rely on ambient */
#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
#define KEY_DISPLAY_OFF     245 /* display device to off state */#define KEY_WWAN       246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
#define KEY_WIMAX       KEY_WWAN
#define KEY_RFKILL      247 /* Key that controls all radios */#define KEY_MICMUTE       248 /* Mute / unmute the microphone *//* Code 255 is reserved for special needs of AT keyboard driver */#define BTN_MISC        0x100
#define BTN_0           0x100
#define BTN_1           0x101
#define BTN_2           0x102
#define BTN_3           0x103
#define BTN_4           0x104
#define BTN_5           0x105
#define BTN_6           0x106
#define BTN_7           0x107
#define BTN_8           0x108
#define BTN_9           0x109#define BTN_MOUSE      0x110
#define BTN_LEFT        0x110
#define BTN_RIGHT       0x111
#define BTN_MIDDLE      0x112
#define BTN_SIDE        0x113
#define BTN_EXTRA       0x114
#define BTN_FORWARD     0x115
#define BTN_BACK        0x116
#define BTN_TASK        0x117#define BTN_JOYSTICK       0x120
#define BTN_TRIGGER     0x120
#define BTN_THUMB       0x121
#define BTN_THUMB2      0x122
#define BTN_TOP         0x123
#define BTN_TOP2        0x124
#define BTN_PINKIE      0x125
#define BTN_BASE        0x126
#define BTN_BASE2       0x127
#define BTN_BASE3       0x128
#define BTN_BASE4       0x129
#define BTN_BASE5       0x12a
#define BTN_BASE6       0x12b
#define BTN_DEAD        0x12f#define BTN_GAMEPAD        0x130
#define BTN_SOUTH       0x130
#define BTN_A           BTN_SOUTH
#define BTN_EAST        0x131
#define BTN_B           BTN_EAST
#define BTN_C           0x132
#define BTN_NORTH       0x133
#define BTN_X           BTN_NORTH
#define BTN_WEST        0x134
#define BTN_Y           BTN_WEST
#define BTN_Z           0x135
#define BTN_TL          0x136
#define BTN_TR          0x137
#define BTN_TL2         0x138
#define BTN_TR2         0x139
#define BTN_SELECT      0x13a
#define BTN_START       0x13b
#define BTN_MODE        0x13c
#define BTN_THUMBL      0x13d
#define BTN_THUMBR      0x13e#define BTN_DIGI       0x140
#define BTN_TOOL_PEN        0x140
#define BTN_TOOL_RUBBER     0x141
#define BTN_TOOL_BRUSH      0x142
#define BTN_TOOL_PENCIL     0x143
#define BTN_TOOL_AIRBRUSH   0x144
#define BTN_TOOL_FINGER     0x145
#define BTN_TOOL_MOUSE      0x146
#define BTN_TOOL_LENS       0x147
#define BTN_TOOL_QUINTTAP   0x148   /* Five fingers on trackpad */
#define BTN_TOUCH       0x14a
#define BTN_STYLUS      0x14b
#define BTN_STYLUS2     0x14c
#define BTN_TOOL_DOUBLETAP  0x14d
#define BTN_TOOL_TRIPLETAP  0x14e
#define BTN_TOOL_QUADTAP    0x14f   /* Four fingers on trackpad */#define BTN_WHEEL     0x150
#define BTN_GEAR_DOWN       0x150
#define BTN_GEAR_UP     0x151#define KEY_OK         0x160
#define KEY_SELECT      0x161
#define KEY_GOTO        0x162
#define KEY_CLEAR       0x163
#define KEY_POWER2      0x164
#define KEY_OPTION      0x165
#define KEY_INFO        0x166   /* AL OEM Features/Tips/Tutorial */
#define KEY_TIME        0x167
#define KEY_VENDOR      0x168
#define KEY_ARCHIVE     0x169
#define KEY_PROGRAM     0x16a   /* Media Select Program Guide */
#define KEY_CHANNEL     0x16b
#define KEY_FAVORITES       0x16c
#define KEY_EPG         0x16d
#define KEY_PVR         0x16e   /* Media Select Home */
#define KEY_MHP         0x16f
#define KEY_LANGUAGE        0x170
#define KEY_TITLE       0x171
#define KEY_SUBTITLE        0x172
#define KEY_ANGLE       0x173
#define KEY_ZOOM        0x174
#define KEY_MODE        0x175
#define KEY_KEYBOARD        0x176
#define KEY_SCREEN      0x177
#define KEY_PC          0x178   /* Media Select Computer */
#define KEY_TV          0x179   /* Media Select TV */
#define KEY_TV2         0x17a   /* Media Select Cable */
#define KEY_VCR         0x17b   /* Media Select VCR */
#define KEY_VCR2        0x17c   /* VCR Plus */
#define KEY_SAT         0x17d   /* Media Select Satellite */
#define KEY_SAT2        0x17e
#define KEY_CD          0x17f   /* Media Select CD */
#define KEY_TAPE        0x180   /* Media Select Tape */
#define KEY_RADIO       0x181
#define KEY_TUNER       0x182   /* Media Select Tuner */
#define KEY_PLAYER      0x183
#define KEY_TEXT        0x184
#define KEY_DVD         0x185   /* Media Select DVD */
#define KEY_AUX         0x186
#define KEY_MP3         0x187
#define KEY_AUDIO       0x188   /* AL Audio Browser */
#define KEY_VIDEO       0x189   /* AL Movie Browser */
#define KEY_DIRECTORY       0x18a
#define KEY_LIST        0x18b
#define KEY_MEMO        0x18c   /* Media Select Messages */
#define KEY_CALENDAR        0x18d
#define KEY_RED         0x18e
#define KEY_GREEN       0x18f
#define KEY_YELLOW      0x190
#define KEY_BLUE        0x191
#define KEY_CHANNELUP       0x192   /* Channel Increment */
#define KEY_CHANNELDOWN     0x193   /* Channel Decrement */
#define KEY_FIRST       0x194
#define KEY_LAST        0x195   /* Recall Last */
#define KEY_AB          0x196
#define KEY_NEXT        0x197
#define KEY_RESTART     0x198
#define KEY_SLOW        0x199
#define KEY_SHUFFLE     0x19a
#define KEY_BREAK       0x19b
#define KEY_PREVIOUS        0x19c
#define KEY_DIGITS      0x19d
#define KEY_TEEN        0x19e
#define KEY_TWEN        0x19f
#define KEY_VIDEOPHONE      0x1a0   /* Media Select Video Phone */
#define KEY_GAMES       0x1a1   /* Media Select Games */
#define KEY_ZOOMIN      0x1a2   /* AC Zoom In */
#define KEY_ZOOMOUT     0x1a3   /* AC Zoom Out */
#define KEY_ZOOMRESET       0x1a4   /* AC Zoom */
#define KEY_WORDPROCESSOR   0x1a5   /* AL Word Processor */
#define KEY_EDITOR      0x1a6   /* AL Text Editor */
#define KEY_SPREADSHEET     0x1a7   /* AL Spreadsheet */
#define KEY_GRAPHICSEDITOR  0x1a8   /* AL Graphics Editor */
#define KEY_PRESENTATION    0x1a9   /* AL Presentation App */
#define KEY_DATABASE        0x1aa   /* AL Database App */
#define KEY_NEWS        0x1ab   /* AL Newsreader */
#define KEY_VOICEMAIL       0x1ac   /* AL Voicemail */
#define KEY_ADDRESSBOOK     0x1ad   /* AL Contacts/Address Book */
#define KEY_MESSENGER       0x1ae   /* AL Instant Messaging */
#define KEY_DISPLAYTOGGLE   0x1af   /* Turn display (LCD) on and off */
#define KEY_BRIGHTNESS_TOGGLE   KEY_DISPLAYTOGGLE
#define KEY_SPELLCHECK      0x1b0   /* AL Spell Check */
#define KEY_LOGOFF      0x1b1   /* AL Logoff */#define KEY_DOLLAR       0x1b2
#define KEY_EURO        0x1b3#define KEY_FRAMEBACK      0x1b4   /* Consumer - transport controls */
#define KEY_FRAMEFORWARD    0x1b5
#define KEY_CONTEXT_MENU    0x1b6   /* GenDesc - system context menu */
#define KEY_MEDIA_REPEAT    0x1b7   /* Consumer - transport control */
#define KEY_10CHANNELSUP    0x1b8   /* 10 channels up (10+) */
#define KEY_10CHANNELSDOWN  0x1b9   /* 10 channels down (10-) */
#define KEY_IMAGES      0x1ba   /* AL Image Browser */#define KEY_DEL_EOL       0x1c0
#define KEY_DEL_EOS     0x1c1
#define KEY_INS_LINE        0x1c2
#define KEY_DEL_LINE        0x1c3#define KEY_FN         0x1d0
#define KEY_FN_ESC      0x1d1
#define KEY_FN_F1       0x1d2
#define KEY_FN_F2       0x1d3
#define KEY_FN_F3       0x1d4
#define KEY_FN_F4       0x1d5
#define KEY_FN_F5       0x1d6
#define KEY_FN_F6       0x1d7
#define KEY_FN_F7       0x1d8
#define KEY_FN_F8       0x1d9
#define KEY_FN_F9       0x1da
#define KEY_FN_F10      0x1db
#define KEY_FN_F11      0x1dc
#define KEY_FN_F12      0x1dd
#define KEY_FN_1        0x1de
#define KEY_FN_2        0x1df
#define KEY_FN_D        0x1e0
#define KEY_FN_E        0x1e1
#define KEY_FN_F        0x1e2
#define KEY_FN_S        0x1e3
#define KEY_FN_B        0x1e4#define KEY_BRL_DOT1       0x1f1
#define KEY_BRL_DOT2        0x1f2
#define KEY_BRL_DOT3        0x1f3
#define KEY_BRL_DOT4        0x1f4
#define KEY_BRL_DOT5        0x1f5
#define KEY_BRL_DOT6        0x1f6
#define KEY_BRL_DOT7        0x1f7
#define KEY_BRL_DOT8        0x1f8
#define KEY_BRL_DOT9        0x1f9
#define KEY_BRL_DOT10       0x1fa#define KEY_NUMERIC_0      0x200   /* used by phones, remote controls, */
#define KEY_NUMERIC_1       0x201   /* and other keypads */
#define KEY_NUMERIC_2       0x202
#define KEY_NUMERIC_3       0x203
#define KEY_NUMERIC_4       0x204
#define KEY_NUMERIC_5       0x205
#define KEY_NUMERIC_6       0x206
#define KEY_NUMERIC_7       0x207
#define KEY_NUMERIC_8       0x208
#define KEY_NUMERIC_9       0x209
#define KEY_NUMERIC_STAR    0x20a
#define KEY_NUMERIC_POUND   0x20b
#define KEY_NUMERIC_A       0x20c   /* Phone key A - HUT Telephony 0xb9 */
#define KEY_NUMERIC_B       0x20d
#define KEY_NUMERIC_C       0x20e
#define KEY_NUMERIC_D       0x20f#define KEY_CAMERA_FOCUS   0x210
#define KEY_WPS_BUTTON      0x211   /* WiFi Protected Setup key */#define KEY_TOUCHPAD_TOGGLE   0x212   /* Request switch touchpad on or off */
#define KEY_TOUCHPAD_ON     0x213
#define KEY_TOUCHPAD_OFF    0x214#define KEY_CAMERA_ZOOMIN  0x215
#define KEY_CAMERA_ZOOMOUT  0x216
#define KEY_CAMERA_UP       0x217
#define KEY_CAMERA_DOWN     0x218
#define KEY_CAMERA_LEFT     0x219
#define KEY_CAMERA_RIGHT    0x21a#define KEY_ATTENDANT_ON   0x21b
#define KEY_ATTENDANT_OFF   0x21c
#define KEY_ATTENDANT_TOGGLE    0x21d   /* Attendant call on or off */
#define KEY_LIGHTS_TOGGLE   0x21e   /* Reading light on or off */#define BTN_DPAD_UP        0x220
#define BTN_DPAD_DOWN       0x221
#define BTN_DPAD_LEFT       0x222
#define BTN_DPAD_RIGHT      0x223#define KEY_ALS_TOGGLE     0x230   /* Ambient light sensor */#define KEY_BUTTONCONFIG      0x240   /* AL Button Configuration */
#define KEY_TASKMANAGER     0x241   /* AL Task/Project Manager */
#define KEY_JOURNAL     0x242   /* AL Log/Journal/Timecard */
#define KEY_CONTROLPANEL        0x243   /* AL Control Panel */
#define KEY_APPSELECT       0x244   /* AL Select Task/Application */
#define KEY_SCREENSAVER     0x245   /* AL Screen Saver */
#define KEY_VOICECOMMAND        0x246   /* Listening Voice Command */#define KEY_BRIGHTNESS_MIN     0x250   /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX      0x251   /* Set Brightness to Maximum */#define KEY_KBDINPUTASSIST_PREV      0x260
#define KEY_KBDINPUTASSIST_NEXT     0x261
#define KEY_KBDINPUTASSIST_PREVGROUP        0x262
#define KEY_KBDINPUTASSIST_NEXTGROUP        0x263
#define KEY_KBDINPUTASSIST_ACCEPT       0x264
#define KEY_KBDINPUTASSIST_CANCEL       0x265/* Diagonal movement keys */
#define KEY_RIGHT_UP            0x266
#define KEY_RIGHT_DOWN          0x267
#define KEY_LEFT_UP         0x268
#define KEY_LEFT_DOWN           0x269#define KEY_ROOT_MENU          0x26a /* Show Device's Root Menu */
/* Show Top Menu of the Media (e.g. DVD) */
#define KEY_MEDIA_TOP_MENU      0x26b
#define KEY_NUMERIC_11          0x26c
#define KEY_NUMERIC_12          0x26d
/** Toggle Audio Description: refers to an audio service that helps blind and* visually impaired consumers understand the action in a program. Note: in* some countries this is referred to as "Video Description".*/
#define KEY_AUDIO_DESC          0x26e
#define KEY_3D_MODE         0x26f
#define KEY_NEXT_FAVORITE       0x270
#define KEY_STOP_RECORD         0x271
#define KEY_PAUSE_RECORD        0x272
#define KEY_VOD             0x273 /* Video on Demand */
#define KEY_UNMUTE          0x274
#define KEY_FASTREVERSE         0x275
#define KEY_SLOWREVERSE         0x276
/** Control a data application associated with the currently viewed channel,* e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)*/
#define KEY_DATA            0x277#define BTN_TRIGGER_HAPPY      0x2c0
#define BTN_TRIGGER_HAPPY1      0x2c0
#define BTN_TRIGGER_HAPPY2      0x2c1
#define BTN_TRIGGER_HAPPY3      0x2c2
#define BTN_TRIGGER_HAPPY4      0x2c3
#define BTN_TRIGGER_HAPPY5      0x2c4
#define BTN_TRIGGER_HAPPY6      0x2c5
#define BTN_TRIGGER_HAPPY7      0x2c6
#define BTN_TRIGGER_HAPPY8      0x2c7
#define BTN_TRIGGER_HAPPY9      0x2c8
#define BTN_TRIGGER_HAPPY10     0x2c9
#define BTN_TRIGGER_HAPPY11     0x2ca
#define BTN_TRIGGER_HAPPY12     0x2cb
#define BTN_TRIGGER_HAPPY13     0x2cc
#define BTN_TRIGGER_HAPPY14     0x2cd
#define BTN_TRIGGER_HAPPY15     0x2ce
#define BTN_TRIGGER_HAPPY16     0x2cf
#define BTN_TRIGGER_HAPPY17     0x2d0
#define BTN_TRIGGER_HAPPY18     0x2d1
#define BTN_TRIGGER_HAPPY19     0x2d2
#define BTN_TRIGGER_HAPPY20     0x2d3
#define BTN_TRIGGER_HAPPY21     0x2d4
#define BTN_TRIGGER_HAPPY22     0x2d5
#define BTN_TRIGGER_HAPPY23     0x2d6
#define BTN_TRIGGER_HAPPY24     0x2d7
#define BTN_TRIGGER_HAPPY25     0x2d8
#define BTN_TRIGGER_HAPPY26     0x2d9
#define BTN_TRIGGER_HAPPY27     0x2da
#define BTN_TRIGGER_HAPPY28     0x2db
#define BTN_TRIGGER_HAPPY29     0x2dc
#define BTN_TRIGGER_HAPPY30     0x2dd
#define BTN_TRIGGER_HAPPY31     0x2de
#define BTN_TRIGGER_HAPPY32     0x2df
#define BTN_TRIGGER_HAPPY33     0x2e0
#define BTN_TRIGGER_HAPPY34     0x2e1
#define BTN_TRIGGER_HAPPY35     0x2e2
#define BTN_TRIGGER_HAPPY36     0x2e3
#define BTN_TRIGGER_HAPPY37     0x2e4
#define BTN_TRIGGER_HAPPY38     0x2e5
#define BTN_TRIGGER_HAPPY39     0x2e6
#define BTN_TRIGGER_HAPPY40     0x2e7/* We avoid low common keys in module aliases so they don't get huge. */
#define KEY_MIN_INTERESTING KEY_MUTE
#define KEY_MAX         0x2ff
#define KEY_CNT         (KEY_MAX+1)/** Relative axes*/#define REL_X            0x00
#define REL_Y           0x01
#define REL_Z           0x02
#define REL_RX          0x03
#define REL_RY          0x04
#define REL_RZ          0x05
#define REL_HWHEEL      0x06
#define REL_DIAL        0x07
#define REL_WHEEL       0x08
#define REL_MISC        0x09
#define REL_MAX         0x0f
#define REL_CNT         (REL_MAX+1)/** Absolute axes*/#define ABS_X            0x00
#define ABS_Y           0x01
#define ABS_Z           0x02
#define ABS_RX          0x03
#define ABS_RY          0x04
#define ABS_RZ          0x05
#define ABS_THROTTLE        0x06
#define ABS_RUDDER      0x07
#define ABS_WHEEL       0x08
#define ABS_GAS         0x09
#define ABS_BRAKE       0x0a
#define ABS_HAT0X       0x10
#define ABS_HAT0Y       0x11
#define ABS_HAT1X       0x12
#define ABS_HAT1Y       0x13
#define ABS_HAT2X       0x14
#define ABS_HAT2Y       0x15
#define ABS_HAT3X       0x16
#define ABS_HAT3Y       0x17
#define ABS_PRESSURE        0x18
#define ABS_DISTANCE        0x19
#define ABS_TILT_X      0x1a
#define ABS_TILT_Y      0x1b
#define ABS_TOOL_WIDTH      0x1c#define ABS_VOLUME      0x20#define ABS_MISC        0x28#define ABS_MT_SLOT     0x2f    /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR  0x30    /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR  0x31    /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR  0x32    /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR  0x33    /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION  0x34    /* Ellipse orientation */
#define ABS_MT_POSITION_X   0x35    /* Center X touch position */
#define ABS_MT_POSITION_Y   0x36    /* Center Y touch position */
#define ABS_MT_TOOL_TYPE    0x37    /* Type of touching device */
#define ABS_MT_BLOB_ID      0x38    /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID  0x39    /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE     0x3a    /* Pressure on contact area */
#define ABS_MT_DISTANCE     0x3b    /* Contact hover distance */
#define ABS_MT_TOOL_X       0x3c    /* Center X tool position */
#define ABS_MT_TOOL_Y       0x3d    /* Center Y tool position */#define ABS_MAX         0x3f
#define ABS_CNT         (ABS_MAX+1)/** Switch events*/#define SW_LID           0x00  /* set = lid shut */
#define SW_TABLET_MODE      0x01  /* set = tablet mode */
#define SW_HEADPHONE_INSERT 0x02  /* set = inserted */
#define SW_RFKILL_ALL       0x03  /* rfkill master switch, type "any"set = radio enabled */
#define SW_RADIO        SW_RFKILL_ALL   /* deprecated */
#define SW_MICROPHONE_INSERT    0x04  /* set = inserted */
#define SW_DOCK         0x05  /* set = plugged into dock */
#define SW_LINEOUT_INSERT   0x06  /* set = inserted */
#define SW_JACK_PHYSICAL_INSERT 0x07  /* set = mechanical switch set */
#define SW_VIDEOOUT_INSERT  0x08  /* set = inserted */
#define SW_CAMERA_LENS_COVER    0x09  /* set = lens covered */
#define SW_KEYPAD_SLIDE     0x0a  /* set = keypad slide out */
#define SW_FRONT_PROXIMITY  0x0b  /* set = front proximity sensor active */
#define SW_ROTATE_LOCK      0x0c  /* set = rotate locked/disabled */
#define SW_LINEIN_INSERT    0x0d  /* set = inserted */
#define SW_MUTE_DEVICE      0x0e  /* set = device disabled */
#define SW_PEN_INSERTED     0x0f  /* set = pen inserted */
#define SW_MAX          0x0f
#define SW_CNT          (SW_MAX+1)/** Misc events*/#define MSC_SERIAL      0x00
#define MSC_PULSELED        0x01
#define MSC_GESTURE     0x02
#define MSC_RAW         0x03
#define MSC_SCAN        0x04
#define MSC_TIMESTAMP       0x05
#define MSC_MAX         0x07
#define MSC_CNT         (MSC_MAX+1)/** LEDs*/#define LED_NUML      0x00
#define LED_CAPSL       0x01
#define LED_SCROLLL     0x02
#define LED_COMPOSE     0x03
#define LED_KANA        0x04
#define LED_SLEEP       0x05
#define LED_SUSPEND     0x06
#define LED_MUTE        0x07
#define LED_MISC        0x08
#define LED_MAIL        0x09
#define LED_CHARGING        0x0a
#define LED_MAX         0x0f
#define LED_CNT         (LED_MAX+1)/** Autorepeat values*/#define REP_DELAY        0x00
#define REP_PERIOD      0x01
#define REP_MAX         0x01
#define REP_CNT         (REP_MAX+1)/** Sounds*/#define SND_CLICK       0x00
#define SND_BELL        0x01
#define SND_TONE        0x02
#define SND_MAX         0x07
#define SND_CNT         (SND_MAX+1)#endif

二、Linux Kernel中的LRADC驱动源码

1、Kernel内置的LRADC驱动源码

源码地址:https://github.com/Lichee-Pi/linux/blob/zero-4.10.y/drivers/input/keyboard/sun4i-lradc-keys.c

#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>#define LRADC_CTRL     0x00
#define LRADC_INTC      0x04
#define LRADC_INTS      0x08
#define LRADC_DATA0     0x0c
#define LRADC_DATA1     0x10/* LRADC_CTRL bits */
#define FIRST_CONVERT_DLY(x)    ((x) << 24) /* 8 bits */
#define CHAN_SELECT(x)          ((x) << 22) /* 2 bits */
#define CONTINUE_TIME_SEL(x)    ((x) << 16) /* 4 bits */
#define KEY_MODE_SEL(x)         ((x) << 12) /* 2 bits */
#define LEVELA_B_CNT(x)         ((x) << 8)  /* 4 bits */
#define HOLD_KEY_EN(x)          ((x) << 7)
#define HOLD_EN(x)              ((x) << 6)
#define LEVELB_VOL(x)           ((x) << 4)  /* 2 bits */
#define SAMPLE_RATE(x)          ((x) << 2)  /* 2 bits */
#define ENABLE(x)               ((x) << 0)/* LRADC_INTC and LRADC_INTS bits */
#define CHAN1_KEYUP_IRQ         BIT(12)
#define CHAN1_ALRDY_HOLD_IRQ    BIT(11)
#define CHAN1_HOLD_IRQ          BIT(10)
#define CHAN1_KEYDOWN_IRQ       BIT(9)
#define CHAN1_DATA_IRQ          BIT(8)
#define CHAN0_KEYUP_IRQ         BIT(4)
#define CHAN0_ALRDY_HOLD_IRQ    BIT(3)
#define CHAN0_HOLD_IRQ          BIT(2)
#define CHAN0_KEYDOWN_IRQ       BIT(1)
#define CHAN0_DATA_IRQ          BIT(0)/* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant* @divisor_numerator:        The numerator of lradc Vref internally divisor* @divisor_denominator:  The denominator of lradc Vref internally divisor*/
struct lradc_variant {u8 divisor_numerator;u8 divisor_denominator;
};static const struct lradc_variant lradc_variant_a10 = {.divisor_numerator = 2,.divisor_denominator = 3
};static const struct lradc_variant r_lradc_variant_a83t = {.divisor_numerator = 3,.divisor_denominator = 4
};struct sun4i_lradc_keymap {u32 voltage;u32 keycode;
};
/*sun4i_lradc_data结构体*/
struct sun4i_lradc_data {struct device *dev;struct input_dev *input;void __iomem *base;struct regulator *vref_supply;struct sun4i_lradc_keymap *chan0_map;const struct lradc_variant *variant;u32 chan0_map_count;u32 chan0_keycode;u32 vref;
};
/*lradc中断*/
static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
{struct sun4i_lradc_data *lradc = dev_id;u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;ints  = readl(lradc->base + LRADC_INTS);  //读取中断类型/** lradc supports only one keypress at a time, release does not give* any info as to which key was released, so we cache the keycode.*///中断类型为按键释放if (ints & CHAN0_KEYUP_IRQ) {input_report_key(lradc->input, lradc->chan0_keycode, 0); /*向输入子系统报告产生按键事件*/lradc->chan0_keycode = 0;  //按键编码置零}//中断类型为按键按下,并且当前按键编码为0if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {val = readl(lradc->base + LRADC_DATA0) & 0x3f;  //读取adc值voltage = val * lradc->vref / 63;  //计算电压值(vref/63 是电压分辨率,采样位数6bit)//根据电压,匹配按键值for (i = 0; i < lradc->chan0_map_count; i++) {diff = abs(lradc->chan0_map[i].voltage - voltage);//匹配电压差值最小的键值if (diff < closest) {closest = diff;keycode = lradc->chan0_map[i].keycode;  //确定当前按下的键值}}lradc->chan0_keycode = keycode;input_report_key(lradc->input, lradc->chan0_keycode, 1);  /*向输入子系统报告产生按键事件*/}input_sync(lradc->input);   /*通知接收者,一个报告发送完毕*/writel(ints, lradc->base + LRADC_INTS); //写回寄存器(还未查阅芯片手册,应该是清除中断吧)return IRQ_HANDLED;
}static int sun4i_lradc_open(struct input_dev *dev)
{struct sun4i_lradc_data *lradc = input_get_drvdata(dev); /* 从设备的私有数据区获取设备描述结构体,可通过nput_set_drvdata 保存数据*/int error;/*regulator 是驱动中电源管理的基础设施。要先注册到内核中,然后使用这些电压输出的模块get其regulator,在驱动中的init里,在适当时间中进行电压电流的设置.与 gpio 差不多? 一样是基础设施?一种称为校准器(regulator)的动态电压和电流控制的方法,很有参考意义和实际使用价值。*/error = regulator_enable(lradc->vref_supply);  //打开校准器if (error)return error;//通过此接口获取当前电源电压。lradc->vref = regulator_get_voltage(lradc->vref_supply) *lradc->variant->divisor_numerator /lradc->variant->divisor_denominator;/**配置LRADC_INTC寄存器,设定采样时间为4ms / 250hz。等待2 * 4 ms的key to*稳定按下,等待(1 + 1)* 4毫秒后释放按键*/writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);return 0;
}static void sun4i_lradc_close(struct input_dev *dev)
{struct sun4i_lradc_data *lradc = input_get_drvdata(dev);/* Disable lradc, leave other settings unchanged */writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |SAMPLE_RATE(2), lradc->base + LRADC_CTRL);writel(0, lradc->base + LRADC_INTC);regulator_disable(lradc->vref_supply);
}
//加载设备树资源
static int sun4i_lradc_load_dt_keymap(struct device *dev,struct sun4i_lradc_data *lradc)
{struct device_node *np, *pp;int i;int error;np = dev->of_node;if (!np)return -EINVAL;lradc->chan0_map_count = of_get_child_count(np);if (lradc->chan0_map_count == 0) {dev_err(dev, "keymap is missing in device tree\n");return -EINVAL;}lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,sizeof(struct sun4i_lradc_keymap),GFP_KERNEL);if (!lradc->chan0_map)return -ENOMEM;i = 0;for_each_child_of_node(np, pp) {struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];u32 channel;error = of_property_read_u32(pp, "channel", &channel);if (error || channel != 0) {dev_err(dev, "%pOFn: Inval channel prop\n", pp);return -EINVAL;}error = of_property_read_u32(pp, "voltage", &map->voltage);if (error) {dev_err(dev, "%pOFn: Inval voltage prop\n", pp);return -EINVAL;}error = of_property_read_u32(pp, "linux,code", &map->keycode);if (error) {dev_err(dev, "%pOFn: Inval linux,code prop\n", pp);return -EINVAL;}i++;}return 0;
}static int sun4i_lradc_probe(struct platform_device *pdev)
{struct sun4i_lradc_data *lradc;struct device *dev = &pdev->dev;int i;int error;lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);if (!lradc)return -ENOMEM;error = sun4i_lradc_load_dt_keymap(dev, lradc);if (error)return error;lradc->variant = of_device_get_match_data(&pdev->dev);if (!lradc->variant) {dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n");return -EINVAL;}lradc->vref_supply = devm_regulator_get(dev, "vref");if (IS_ERR(lradc->vref_supply))return PTR_ERR(lradc->vref_supply);lradc->dev = dev;lradc->input = devm_input_allocate_device(dev);if (!lradc->input)return -ENOMEM;lradc->input->name = pdev->name;lradc->input->phys = "sun4i_lradc/input0";lradc->input->open = sun4i_lradc_open;lradc->input->close = sun4i_lradc_close;lradc->input->id.bustype = BUS_HOST;lradc->input->id.vendor = 0x0001;lradc->input->id.product = 0x0001;lradc->input->id.version = 0x0100;__set_bit(EV_KEY, lradc->input->evbit);for (i = 0; i < lradc->chan0_map_count; i++)__set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);input_set_drvdata(lradc->input, lradc);lradc->base = devm_ioremap_resource(dev,platform_get_resource(pdev, IORESOURCE_MEM, 0));if (IS_ERR(lradc->base))return PTR_ERR(lradc->base);error = devm_request_irq(dev, platform_get_irq(pdev, 0),sun4i_lradc_irq, 0,"sun4i-a10-lradc-keys", lradc);if (error)return error;error = input_register_device(lradc->input);if (error)return error;return 0;
}static const struct of_device_id sun4i_lradc_of_match[] = {{ .compatible = "allwinner,sun4i-a10-lradc-keys",.data = &lradc_variant_a10 },{ .compatible = "allwinner,sun8i-a83t-r-lradc",.data = &r_lradc_variant_a83t },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);static struct platform_driver sun4i_lradc_driver = {.driver = {.name    = "sun4i-a10-lradc-keys",.of_match_table = of_match_ptr(sun4i_lradc_of_match),},.probe  = sun4i_lradc_probe,
};module_platform_driver(sun4i_lradc_driver);MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");

2、驱动测试

驱动测试软件使用信号量的方法

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>#include <linux/input.h>int fd;void my_signal_fun(int signum)
{struct input_event buttons_event, leds_event;/* [cgw]: 异步通知产生时返回的数据 */read(fd, &buttons_event, sizeof(struct input_event));/* [cgw]: 打印事件类型,事件码,事件值 */printf("type: 0x%x code: 0x%x value: 0x%x\n",buttons_event.type,buttons_event.code,  buttons_event.value);// /* [cgw]: 返回的是KEY_L或KEY_S值 */// if (buttons_event.code == KEY_L || buttons_event.code == KEY_S) {//     /* [cgw]: 按键弹起 */ //     if (buttons_event.value == 0) {//         /* [cgw]: 构造一个EV_LED事件 *///         //leds_event.type = EV_SND;//         leds_event.type = EV_LED;//         //leds_event.code = SND_BELL;//         leds_event.code = LED_MUTE;//         /* [cgw]: KEY_L和KEY_S控制LED的亮灭 *///         if (buttons_event.code == KEY_L) {//             leds_event.value = 0xAA;//         } else if (buttons_event.code == KEY_S) {//             leds_event.value = 0xEE;    //         }//         /* [cgw]: 发送LED控制事件 *///         write(fd, &leds_event, sizeof(struct input_event));//         printf("led write!\n");//     }// }
}int main(int argc, char **argv)
{int Oflags;/* [cgw]: 设置需要处理的信号SIGIO,即输入文件会请求一个SIGIO* 信号,当有新数据到来这个信号会发给filp->f_owner进程*/signal(SIGIO, my_signal_fun);fd = open("/dev/input/event0", O_RDWR | O_NONBLOCK);//printf("fd = 0x%x\n", fd);if (fd < 0){printf("can't open!\n");}/* [cgw]: 根据文件标识符fd,设置能够获得这个文件的进程(owner) * getpid()获得当前进程ID*/fcntl(fd, F_SETOWN, getpid());/* [cgw]: 获得file->f_flags标志 */Oflags = fcntl(fd, F_GETFL);/* [cgw]: 置位FASYNC使能异步通知 */fcntl(fd, F_SETFL, Oflags | FASYNC);while (1){/* [cgw]: 休眠 */sleep(1000);}return 0;
}

测试失败–键值错误

依次按上述原理图中的按键:S1-S2-S3-S4,如下显示,好像并不能通过键值识别出来。

  • S1按键:KEY_VOLUMEUP:0x73 = 115
  • S2按键:KEY_VOLUMEDOWN:0x72 = 114
  • S3按键:KEY_SELECT:0x161 = 353
  • S4按键:KEY_OK:0x160 = 352

# ./signalkeyinputApp
type: 0x1 code: 0x72 value: 0x1    # S1按键
type: 0x0 code: 0x0 value: 0x0
type: 0x1 code: 0x72 value: 0x0    # S2按键
type: 0x0 code: 0x0 value: 0x0
type: 0x1 code: 0x160 value: 0x1   # S3按键
type: 0x0 code: 0x0 value: 0x0
type: 0x1 code: 0x160 value: 0x0   # S4按键
type: 0x0 code: 0x0 value: 0x0

可以看出,只有S2按键和S4按键的键值是对的,其他两个是错误的键值。

驱动测试软件使用轮询方式

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>#include <linux/input.h>/*** file name:keyinputApp* date: 2022-3-5 11:08:53* version:1.0* author:liefyuan* describe:input系统测试APP* 执行命令:./keyinputApp /dev/input/event0*//* 定义一个input_event变量 存放输入事件信息 */
static struct input_event inputevent;/* 主程序 */
int main(int argc, char *argv[])
{char *filename;  // 可执行文件名int fd,ret =0 ;  //  fd: 文件句柄 ret:函数操作返回值/* 先判断输入的参数 */if(argc !=  2){  // 本身文件名带1个 执行文件1个  printf("parameter error!\r\n");return -1;}/* 分析参数 ,提取有用的信息 */filename = argv[1];  // 可执行文件名 /* 打开key文件 */fd = open(filename, O_RDWR);  // 可读可写 if(fd < 0){printf("can't open file:%s\r\n",filename);return -1;}/* 循环读取按键值 */while(1){ret = read(fd, &inputevent, sizeof(inputevent));if(ret  >  0 ){   /* 数据有效   */switch (inputevent.type){  // 判断事件的类型 case EV_KEY:if(inputevent.code < BTN_MISC){  // 是键盘键值// 输入按键代码 并且判断按键是按下还是松开printf("key %d %s \r\n", \inputevent.code, \inputevent.value ? "press" : "release");  }else{// 输入按键代码 并且判断按键是按下还是松开printf("button %d %s \r\n", \inputevent.code, \inputevent.value ? "press" : "release");  }break;/* 其他类型事件 根据需要自行处理 */case EV_REL:break;case EV_ABS:break;case EV_MSC:break;case EV_SW:break;}}else{  // 数据无效 printf("read data error\r\n");}}/* 关闭文件 */ret = close(fd);if(ret < 0){printf("can't close file %s \r\n", filename);return -1;}return 0;
}

测试失败,键值显示错误

依次按上述原理图中的按键:S1-S2-S3-S4,如下显示,只能显示两个按键值。

# ./keyinputApp /dev/input/event0
key 114 press      # S1按键
key 114 release
key 352 press      # S2按键
key 352 release
key 352 press      # S3按键
key 352 release
key 352 press      # S4按键
key 352 release

可能原因:
设备树里面的电压值和电阻网络是不是匹配,

3、另一个测试方式

源码:https://elinux.org/images/9/93/Evtest.c

# ./evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "1c22800.lradc"
Supported events:Event type 0 (Sync)Event type 1 (Key)Event code 114 (VolumeDown)Event code 115 (VolumeUp)Event code 352 (Ok)Event code 353 (Select)
Testing ... (interrupt to exit)
Event: time 357.242358, type 1 (Key), code 114 (VolumeDown), value 1
Event: time 357.242358, -------------- Report Sync ------------
Event: time 357.484543, type 1 (Key), code 114 (VolumeDown), value 0
Event: time 357.484543, -------------- Report Sync ------------
Event: time 360.066660, type 1 (Key), code 352 (Ok), value 1
Event: time 360.066660, -------------- Report Sync ------------
Event: time 360.304947, type 1 (Key), code 352 (Ok), value 0
Event: time 360.304947, -------------- Report Sync ------------

键值与按键的关系是不对的!

查看中断列表:

# cat /proc/interruptsCPU019:        576     GIC-0  27 Level     arch_timer21:          0     GIC-0  50 Level     sun4i_timer022:          0     GIC-0  82 Level     1c02000.dma-controller23:       5039     GIC-0  92 Level     sunxi-mmc24:        159     GIC-0  93 Level     sunxi-mmc25:          1     GIC-0 103 Level     musb-hdrc.1.auto26:          0     GIC-0  72 Level     1c20400.rtc32:          2     GIC-0  62 Level     sun4i-a10-lradc-keys34:        226     GIC-0  32 Level     serial35:         63     GIC-0  38 Level     mv64xxx_i2c
IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:          0  Rescheduling interrupts
IPI3:          0  Function call interrupts
IPI4:          0  CPU stop interrupts
IPI5:          0  IRQ work interrupts
IPI6:          0  completion interrupts
Err:          0

32: 2 GIC-0 62 Level sun4i-a10-lradc-keys LRADC KEY是存在的。

可能还是硬件问题!

4、测试软件的编译方法

编译测试应用:

$ arm-linux-gnueabihf-gcc keyinputApp.c -o keyinputApp

拷贝编译出来的文件到开发板上去:

  • /dev/input/event0
# ./keyinputApp /dev/input/event0
key 114 press
key 114 release
key 114 press
key 114 release
key 114 press

只能识别一个按键?

三、改进

参考一个linux3.4的源码里面可以正常识别如下:

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
*
* Copyright (c) 2011
*
* ChangeLog
*
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/keyboard.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <mach/sys_config.h>
#undef CONFIG_HAS_EARLYSUSPEND
#ifdef CONFIG_ARCH_SUN9IW1P1
#include <linux/clk/clk-sun9iw1.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM)
#include <linux/pm.h>
#endif
#include "sun8i-keyboard.h"
#include <linux/power/scenelock.h>#ifdef MODE_0V2
#ifndef CONFIG_ARCH_SUN9IW1P1
static unsigned char keypad_mapindex[64] = {0,0,0,0,0,0,0,0,                /* key 1, 8���� 0-7 */1,1,1,1,1,1,1,                  /* key 2, 7���� 8-14 */2,2,2,2,2,2,2,                  /* key 3, 7���� 15-21 */3,3,3,3,3,3,                    /* key 4, 6���� 22-27 */4,4,4,4,4,4,                    /* key 5, 6���� 28-33 */5,5,5,5,5,5,                    /* key 6, 6���� 34-39 */6,6,6,6,6,6,6,6,6,6,            /* key 7, 10����40-49 */7,7,7,7,7,7,7,7,7,7,7,7,7,7     /* key 8, 17����50-63 */
};
#else
static unsigned char keypad_mapindex[64] = {0,0,0,0,0,0,0,0,0,              /* key 1, 0-8 */1,1,1,1,1,                      /* key 2, 9-13 */2,2,2,2,2,2,                    /* key 3, 14-19 */3,3,3,3,3,3,                    /* key 4, 20-25 */4,4,4,4,4,4,4,4,4,4,4,          /* key 5, 26-36 */5,5,5,5,5,5,5,5,5,5,5,          /* key 6, 37-39 */6,6,6,6,6,6,6,6,6,              /* key 7, 40-49 */7,7,7,7,7,7,7                   /* key 8, 50-63 */
};
#endif
#endif#ifdef MODE_0V15
/* 0.15V mode */
static unsigned char keypad_mapindex[64] = {0,0,0,                          /* key1 */1,1,1,1,1,                      /* key2 */2,2,2,2,2,3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,12,12,12,12,12,12,12   /*key13 */
};
#endif#ifdef CONFIG_HAS_EARLYSUSPEND
struct sunxi_keyboard_data {struct early_suspend early_suspend;
};
#else
#ifdef CONFIG_PM
static struct dev_pm_domain keyboard_pm_domain;
#endif
#endif#if defined(CONFIG_ARCH_SUN8IW6P1)||defined(CONFIG_ARCH_SUN9IW1P1)
#define ADC_MEASURE     (1350)
#define ADC_RESOL       (21)
#else
#define ADC_MEASURE     (2000)
#define ADC_RESOL       (31)
#endif
#define VOL_NUM 16static int key_vol[VOL_NUM];
static int key_num = 0;
static volatile u32 key_val;
static struct input_dev *sunxikbd_dev;
static unsigned char scancode;static unsigned char key_cnt = 0;
static unsigned char compare_buffer[REPORT_START_NUM] = {0};
static unsigned char transfer_code = INITIAL_VALUE;
#ifdef CONFIG_ARCH_SUN9IW1P1
static struct clk *key_clk;
static struct clk *key_clk_source;
#endif
enum {DEBUG_INIT = 1U << 0,DEBUG_INT = 1U << 1,DEBUG_DATA_INFO = 1U << 2,DEBUG_SUSPEND = 1U << 3,
};
static u32 debug_mask = 0;
#define dprintk(level_mask, fmt, arg...)        if (unlikely(debug_mask & level_mask)) \printk(fmt , ## arg)module_param_named(debug_mask, debug_mask, int, 0644);#ifdef CONFIG_HAS_EARLYSUSPEND
static struct sunxi_keyboard_data *keyboard_data;
#endif#ifdef CONFIG_ARCH_SUN9IW1P1
static void sunxikbd_clk_cfg(void)
{unsigned long rate = 0; /* 3Mhz */key_clk_source = clk_get(NULL, APB0_CLK);if (!key_clk_source || IS_ERR(key_clk_source)) {pr_err("try to get key_clk_source failed!\n");return;}rate = clk_get_rate(key_clk_source);dprintk(DEBUG_INIT, "%s: get key_clk_source rate %dHZ\n", __func__, (__u32)rate);key_clk = clk_get(NULL, LRADC_CLK);if (!key_clk || IS_ERR(key_clk)) {pr_err("try to get key clock failed!\n");return;}if(clk_set_parent(key_clk, key_clk_source))pr_err("%s: set key_clk parent to key_clk_source failed!\n", __func__);if (clk_prepare_enable(key_clk)) {pr_err("try to enable key_clk failed!\n");}return;
}static void sunxikbd_clk_uncfg(void)
{if(NULL == key_clk || IS_ERR(key_clk)) {pr_err("key_clk handle is invalid, just return!\n");return;} else {clk_disable_unprepare(key_clk);clk_put(key_clk);key_clk = NULL;}if(NULL == key_clk_source || IS_ERR(key_clk_source)) {pr_err("key_clk_source handle is invalid, just return!\n");return;} else {clk_put(key_clk_source);key_clk_source = NULL;}return;
}
#endifstatic void sunxi_keyboard_ctrl_set(enum key_mode key_mode, u32 para)
{u32 ctrl_reg = 0;if (0 != para)ctrl_reg = readl((const volatile void __iomem *)(KEY_BASSADDRESS + LRADC_CTRL));if (CONCERT_DLY_SET & key_mode)ctrl_reg |= (FIRST_CONCERT_DLY & para);if (ADC_CHAN_SET & key_mode)ctrl_reg |= (ADC_CHAN_SELECT & para);if (KEY_MODE_SET & key_mode)ctrl_reg |= (KEY_MODE_SELECT & para);if (LRADC_HOLD_SET & key_mode)ctrl_reg |= (LRADC_HOLD_EN & para);if (LEVELB_VOL_SET & key_mode)ctrl_reg |= (LEVELB_VOL & para);if (LRADC_SAMPLE_SET & key_mode)ctrl_reg |= (LRADC_SAMPLE_250HZ & para);if (LRADC_EN_SET & key_mode)ctrl_reg |= (LRADC_EN & para);writel(ctrl_reg, (volatile void __iomem *)(KEY_BASSADDRESS + LRADC_CTRL));
}static void sunxi_keyboard_int_set(enum int_mode int_mode, u32 para)
{u32 ctrl_reg = 0;if (0 != para)ctrl_reg = readl((const volatile void __iomem *)(KEY_BASSADDRESS + LRADC_INTC));if (ADC0_DOWN_INT_SET & int_mode)ctrl_reg |= (LRADC_ADC0_DOWN_EN & para);if (ADC0_UP_INT_SET & int_mode)ctrl_reg |= (LRADC_ADC0_UP_EN & para);if (ADC0_DATA_INT_SET & int_mode)ctrl_reg |= (LRADC_ADC0_DATA_EN & para);writel(ctrl_reg, (volatile void __iomem *)(KEY_BASSADDRESS + LRADC_INTC));
}static u32 sunxi_keyboard_read_ints(void)
{u32 reg_val;reg_val  = readl((const volatile void __iomem *)(KEY_BASSADDRESS + LRADC_INT_STA));return reg_val;
}static void sunxi_keyboard_clr_ints(u32 reg_val)
{writel(reg_val, (volatile void __iomem *)(KEY_BASSADDRESS + LRADC_INT_STA));
}static u32 sunxi_keyboard_read_data(unsigned long addr)
{u32 reg_val;reg_val = readl((const volatile void __iomem *)(addr));return reg_val;
}#ifdef CONFIG_HAS_EARLYSUSPEND
/* ͣ���豸 */
static void sunxi_keyboard_early_suspend(struct early_suspend *h)
{//int ret;//struct sunxi_keyboard_data *ts = container_of(h, struct sunxi_keyboard_data, early_suspend);dprintk(DEBUG_SUSPEND, "[%s] enter standby state: %d. \n", __FUNCTION__, (int)standby_type);disable_irq_nosync(SW_INT_IRQNO_LRADC);if (NORMAL_STANDBY == standby_type) {sunxi_keyboard_ctrl_set(0, 0);
#ifdef CONFIG_ARCH_SUN9IW1P1clk_disable_unprepare(key_clk);
#endif/* process for super standby */ } else if (SUPER_STANDBY == standby_type) {if (check_scene_locked(SCENE_TALKING_STANDBY) == 0) {printk("lradc-key: talking standby, enable wakeup source lradc!!\n");enable_wakeup_src(CPUS_LRADC_SRC, 0);} else {sunxi_keyboard_ctrl_set(0, 0);
#ifdef CONFIG_ARCH_SUN9IW1P1clk_disable_unprepare(key_clk);
#endif}}return ;
}/* ���»��� */
static void sunxi_keyboard_late_resume(struct early_suspend *h)
{unsigned long mode, para;//int ret;//struct sunxi_keyboard_data *ts = container_of(h, struct sunxi_keyboard_data, early_suspend);dprintk(DEBUG_SUSPEND, "[%s] return from standby state: %d. \n", __FUNCTION__, (int)standby_type);#ifdef CONFIG_ARCH_SUN9IW1P1clk_prepare_enable(key_clk);
#endif/* process for normal standby */if (NORMAL_STANDBY == standby_type) {mode = CONCERT_DLY_SET | ADC_CHAN_SET | KEY_MODE_SET | LRADC_HOLD_SET | LEVELB_VOL_SET \| LRADC_SAMPLE_SET | LRADC_EN_SET;para = FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT \|LRADC_SAMPLE_250HZ|LRADC_EN;sunxi_keyboard_ctrl_set(mode, para);/* process for super standby */ } else if (SUPER_STANDBY == standby_type) {if (check_scene_locked(SCENE_TALKING_STANDBY) != 0) {#ifdef ONE_CHANNELmode = ADC0_DOWN_INT_SET | ADC0_UP_INT_SET | ADC0_DATA_INT_SET;para = LRADC_ADC0_DOWN_EN | LRADC_ADC0_UP_EN | LRADC_ADC0_DATA_EN;sunxi_keyboard_int_set(mode, para);mode = CONCERT_DLY_SET | ADC_CHAN_SET | KEY_MODE_SET | LRADC_HOLD_SET | LEVELB_VOL_SET \| LRADC_SAMPLE_SET | LRADC_EN_SET;para = FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT \|LRADC_SAMPLE_250HZ|LRADC_EN;sunxi_keyboard_ctrl_set(mode, para);
#else
#endif} else {disable_wakeup_src(CPUS_LRADC_SRC, 0);printk("lradc-key: resume from talking standby!!\n");}}enable_irq(SW_INT_IRQNO_LRADC);return ;
}
#else
#ifdef CONFIG_PM
static int sunxi_keyboard_suspend(struct device *dev)
{//int ret;//struct sunxi_keyboard_data *ts = container_of(h, struct sunxi_keyboard_data, early_suspend);dprintk(DEBUG_SUSPEND, "[%s] enter standby state: %d. \n", __FUNCTION__, (int)standby_type);disable_irq_nosync(SW_INT_IRQNO_LRADC);if (NORMAL_STANDBY == standby_type) {sunxi_keyboard_ctrl_set(0, 0);
#ifdef CONFIG_ARCH_SUN9IW1P1clk_disable_unprepare(key_clk);
#endif/* process for super standby */ } else if (SUPER_STANDBY == standby_type) {if (check_scene_locked(SCENE_TALKING_STANDBY) == 0) {printk("lradc-key: talking standby, enable wakeup source lradc!!\n");enable_wakeup_src(CPUS_LRADC_SRC, 0);} else {sunxi_keyboard_ctrl_set(0, 0);
#ifdef CONFIG_ARCH_SUN9IW1P1clk_disable_unprepare(key_clk);
#endif}}return 0;
}/* ���»��� */
static int sunxi_keyboard_resume(struct device *dev)
{unsigned long mode, para;//int ret;//struct sunxi_keyboard_data *ts = container_of(h, struct sunxi_keyboard_data, early_suspend);dprintk(DEBUG_SUSPEND, "[%s] return from standby state: %d. \n", __FUNCTION__, (int)standby_type);#ifdef CONFIG_ARCH_SUN9IW1P1clk_prepare_enable(key_clk);
#endif/* process for normal standby */if (NORMAL_STANDBY == standby_type) {mode = CONCERT_DLY_SET | ADC_CHAN_SET | KEY_MODE_SET | LRADC_HOLD_SET | LEVELB_VOL_SET \| LRADC_SAMPLE_SET | LRADC_EN_SET;para = FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT \|LRADC_SAMPLE_250HZ|LRADC_EN;sunxi_keyboard_ctrl_set(mode, para);/* process for super standby */} else if (SUPER_STANDBY == standby_type) {if (check_scene_locked(SCENE_TALKING_STANDBY) != 0) {#ifdef ONE_CHANNELmode = ADC0_DOWN_INT_SET | ADC0_UP_INT_SET | ADC0_DATA_INT_SET;para = LRADC_ADC0_DOWN_EN | LRADC_ADC0_UP_EN | LRADC_ADC0_DATA_EN;sunxi_keyboard_int_set(mode, para);mode = CONCERT_DLY_SET | ADC_CHAN_SET | KEY_MODE_SET | LRADC_HOLD_SET | LEVELB_VOL_SET \| LRADC_SAMPLE_SET | LRADC_EN_SET;para = FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT \|LRADC_SAMPLE_250HZ|LRADC_EN;sunxi_keyboard_ctrl_set(mode, para);
#else
#endif} else {disable_wakeup_src(CPUS_LRADC_SRC, 0);printk("lradc-key: resume from talking standby!!\n");}}enable_irq(SW_INT_IRQNO_LRADC);return 0;
}
#endif
#endifstatic irqreturn_t sunxi_isr_key(int irq, void *dummy)
{u32  reg_val = 0;int judge_flag = 0;dprintk(DEBUG_INT, "Key Interrupt\n");reg_val = sunxi_keyboard_read_ints();//writel(reg_val,KEY_BASSADDRESS + LRADC_INT_STA);if (reg_val & LRADC_ADC0_DOWNPEND) {dprintk(DEBUG_INT, "key down\n");}if (reg_val & LRADC_ADC0_DATAPEND) {key_val = sunxi_keyboard_read_data(KEY_BASSADDRESS+LRADC_DATA0);if (key_val < 0x3f) {compare_buffer[key_cnt] = key_val&0x3f;}if ((key_cnt + 1) < REPORT_START_NUM) {key_cnt++;/* do not report key message */} else {if(compare_buffer[0] == compare_buffer[1]){key_val = compare_buffer[1];scancode = keypad_mapindex[key_val&0x3f];judge_flag = 1;key_cnt = 0;} else {key_cnt = 0;judge_flag = 0;}if (1 == judge_flag) {dprintk(DEBUG_INT, "report data: key_val :%8d transfer_code: %8d , scancode: %8d\n", \key_val, transfer_code, scancode);if (transfer_code == scancode) {/* report repeat key value */
#ifdef REPORT_REPEAT_KEY_FROM_HWinput_report_key(sunxikbd_dev, sunxi_scankeycodes[scancode], 0);input_sync(sunxikbd_dev);input_report_key(sunxikbd_dev, sunxi_scankeycodes[scancode], 1);input_sync(sunxikbd_dev);
#else/* do not report key value */
#endif} else if (INITIAL_VALUE != transfer_code) {     /* report previous key value up signal + report current key value down */input_report_key(sunxikbd_dev, sunxi_scankeycodes[transfer_code], 0);input_sync(sunxikbd_dev);input_report_key(sunxikbd_dev, sunxi_scankeycodes[scancode], 1);input_sync(sunxikbd_dev);transfer_code = scancode;} else {/* INITIAL_VALUE == transfer_code, first time to report key event */input_report_key(sunxikbd_dev, sunxi_scankeycodes[scancode], 1);input_sync(sunxikbd_dev);transfer_code = scancode;}}}}if (reg_val & LRADC_ADC0_UPPEND) {if(INITIAL_VALUE != transfer_code) {dprintk(DEBUG_INT, "report data: key_val :%8d transfer_code: %8d \n",key_val, transfer_code);input_report_key(sunxikbd_dev, sunxi_scankeycodes[transfer_code], 0);input_sync(sunxikbd_dev);}dprintk(DEBUG_INT, "key up \n");key_cnt = 0;judge_flag = 0;transfer_code = INITIAL_VALUE;}sunxi_keyboard_clr_ints(reg_val);return IRQ_HANDLED;
}#ifdef CONFIG_ARCH_SUN8IW8P1
static int use_adc_check_bat = 0;
static int bat_max_vol = 0;
static int bat_min_vol = 0;static void battery_adc_init(void)
{script_item_u script_val;script_item_value_type_e type;type = script_get_item("bat_adc_para", "used", &script_val);if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {return ;}use_adc_check_bat = script_val.val;type = script_get_item("bat_adc_para", "bat_max_vol", &script_val);if (SCIRPT_ITEM_VALUE_TYPE_INT == type) {bat_max_vol = script_val.val;}type = script_get_item("bat_adc_para", "bat_min_vol", &script_val);if (SCIRPT_ITEM_VALUE_TYPE_INT == type) {bat_min_vol = script_val.val;}
}static ssize_t adc_get_data(struct device *dev, struct device_attribute *attr, char *buf)
{int key_val = 100;int modify_vol = 0;if (use_adc_check_bat && bat_max_vol && bat_min_vol){key_val = sunxi_keyboard_read_data(KEY_BASSADDRESS+LRADC_DATA0);printk("adc_val=%d\n", key_val);modify_vol = 1000 * key_val / 64 * 2;  // mvif (modify_vol > bat_max_vol)key_val = 100;else if (modify_vol < bat_min_vol)key_val = 0;elsekey_val = 100 * (modify_vol - bat_min_vol) /  (bat_max_vol - bat_min_vol);}printk("modify_vol=%d\n", modify_vol);return sprintf(buf, "%d\n", key_val);
}static struct device_attribute keyboard_attributes[] = {__ATTR(data, 0444, adc_get_data, NULL),
};
#endifstatic void sunxikbd_map_init(void)
{int i = 0;unsigned char j = 0;for(i = 0; i < 64; i++){ if(i * ADC_RESOL > key_vol[j])j++;keypad_mapindex[i] = j;}
}static int sunxikbd_script_init(void)
{int i;char key_name[16];script_item_u   val;script_item_value_type_e  type;type = script_get_item("key_para", "key_used", &val);if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {pr_err("%s: key para not found, used default para. \n", __func__);return 0;}if(1 == val.val){type = script_get_item("key_para", "key_cnt", &val);if(SCIRPT_ITEM_VALUE_TYPE_INT != type){pr_err("%s: get key cnt err! \n", __func__);return -1;}key_num = val.val;dprintk(DEBUG_INT,"%s key number = %d.\n", __func__, key_num);if(key_num < 1 || key_num > VOL_NUM){pr_err("incorrect key number.\n");return -1;}for(i = 0; i<VOL_NUM; i++)key_vol[i] = ADC_MEASURE;for(i = 1; i <= key_num; i++){sprintf(key_name, "key%d_vol", i);type = script_get_item("key_para", key_name, &val);if(SCIRPT_ITEM_VALUE_TYPE_INT != type){pr_err("%s: get %s err! \n", __func__, key_name);return -1;}key_vol[i-1] = val.val;dprintk(DEBUG_INT,"%s: key%d vol = %d.\n", __func__, i, val.val);}sunxikbd_map_init();}else{dprintk(DEBUG_INT,"sunxi key board no used.\n");return -1;}return 0;
}static int __init sunxikbd_init(void)
{int i;int err =0;unsigned long mode, para;dprintk(DEBUG_INIT, "sunxikbd_init \n");if(sunxikbd_script_init()){err = -EFAULT;goto fail1;}#ifdef CONFIG_ARCH_SUN8IW8P1battery_adc_init();
#endifsunxikbd_dev = input_allocate_device();if (!sunxikbd_dev) {pr_err("sunxikbd: not enough memory for input device\n");err = -ENOMEM;goto fail1;}sunxikbd_dev->name = INPUT_DEV_NAME;sunxikbd_dev->phys = "sunxikbd/input0";sunxikbd_dev->id.bustype = BUS_HOST;sunxikbd_dev->id.vendor = 0x0001;sunxikbd_dev->id.product = 0x0001;sunxikbd_dev->id.version = 0x0100;#ifdef REPORT_REPEAT_KEY_BY_INPUT_COREsunxikbd_dev->evbit[0] = BIT_MASK(EV_KEY)|BIT_MASK(EV_REP);pr_info("REPORT_REPEAT_KEY_BY_INPUT_CORE is defined, support report repeat key value. \n");
#elsesunxikbd_dev->evbit[0] = BIT_MASK(EV_KEY);
#endiffor (i = 0; i < KEY_MAX_CNT; i++)set_bit(sunxi_scankeycodes[i], sunxikbd_dev->keybit);#ifdef CONFIG_ARCH_SUN9IW1P1sunxikbd_clk_cfg();
#endif#ifdef ONE_CHANNEL
#ifdef CONFIG_ARCH_SUN8IW8P1if (!use_adc_check_bat)
#endif{mode = ADC0_DOWN_INT_SET | ADC0_UP_INT_SET | ADC0_DATA_INT_SET;para = LRADC_ADC0_DOWN_EN | LRADC_ADC0_UP_EN | LRADC_ADC0_DATA_EN;sunxi_keyboard_int_set(mode, para);}mode = CONCERT_DLY_SET | ADC_CHAN_SET | KEY_MODE_SET | LRADC_HOLD_SET | LEVELB_VOL_SET \| LRADC_SAMPLE_SET | LRADC_EN_SET;para = FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT \|LRADC_SAMPLE_250HZ|LRADC_EN;sunxi_keyboard_ctrl_set(mode, para);
#else
#endifif (request_irq(SW_INT_IRQNO_LRADC, sunxi_isr_key, 0, "sunxikbd", NULL)) {err = -EBUSY;pr_err("request irq failure. \n");goto fail2;}#ifdef CONFIG_HAS_EARLYSUSPEND
#else
#ifdef CONFIG_PMkeyboard_pm_domain.ops.suspend = sunxi_keyboard_suspend;keyboard_pm_domain.ops.resume = sunxi_keyboard_resume;sunxikbd_dev->dev.pm_domain = &keyboard_pm_domain;
#endif
#endiferr = input_register_device(sunxikbd_dev);if (err)goto fail3;#ifdef CONFIG_HAS_EARLYSUSPEND dprintk(DEBUG_INIT, "==register_early_suspend =\n");keyboard_data = kzalloc(sizeof(*keyboard_data), GFP_KERNEL);if (keyboard_data == NULL) {err = -ENOMEM;goto err_alloc_data_failed;}keyboard_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 3;keyboard_data->early_suspend.suspend = sunxi_keyboard_early_suspend;keyboard_data->early_suspend.resume     = sunxi_keyboard_late_resume;register_early_suspend(&keyboard_data->early_suspend);
#endif#ifdef CONFIG_ARCH_SUN8IW8P1if (use_adc_check_bat)device_create_file(&sunxikbd_dev->dev, &keyboard_attributes[0]);
#endifdprintk(DEBUG_INIT, "sunxikbd_init end\n");return 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
err_alloc_data_failed:
#endif
fail3:free_irq(SW_INT_IRQNO_LRADC, NULL);
fail2:input_free_device(sunxikbd_dev);
fail1:pr_err("sunxikbd_init failed. \n");return err;
}static void __exit sunxikbd_exit(void)
{#ifdef CONFIG_HAS_EARLYSUSPEND  unregister_early_suspend(&keyboard_data->early_suspend);
#endiffree_irq(SW_INT_IRQNO_LRADC, NULL);
#ifdef CONFIG_ARCH_SUN9IW1P1sunxikbd_clk_uncfg();
#endif
#ifdef CONFIG_ARCH_SUN8IW8P1if (use_adc_check_bat)device_remove_file(&sunxikbd_dev->dev, &keyboard_attributes[0]);
#endifinput_unregister_device(sunxikbd_dev);
}module_init(sunxikbd_init);
module_exit(sunxikbd_exit);MODULE_AUTHOR(" <@>");
MODULE_DESCRIPTION("sunxi-keyboard driver");
MODULE_LICENSE("GPL");

四、重新开始一个

新编一个驱动.ko

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>          //含有ioremap函数iounmap函数
#include <asm/uaccess.h>     //含有copy_from_user函数和含有copy_to_user函数
#include <linux/device.h>    //含有类相关的设备函数
#include <linux/cdev.h>
#include <linux/platform_device.h> //包含platform函数
#include <linux/of.h>        //包含设备树相关函数
#include <linux/irq.h>       //含有IRQ_HANDLED和IRQ_TYPE_EDGE_RISING
#include <linux/interrupt.h> //含有request_irq、free_irq函数
#include <linux/input.h>
#include <linux/irq.h>struct input_dev *mykeypad_dev;           //定义一个input_dev结构体  ssize_t  volatile *keyadc_ctrl;
ssize_t  volatile *keyadc_intc;
ssize_t  volatile *keyadc_ints;
ssize_t  volatile *keyadc_data;static unsigned int key_value = 0; //定义一个变量保存按键值static irqreturn_t  mykeypad_irq (int irq, void *dev_id)       //中断服务函数
{/*上报事件*/if(((*keyadc_ints)&(1<<1)) != 0)  //按键按下{ key_value = ((*keyadc_data)&(0x3f));   //获取按键值if(key_value == 24){input_event(mykeypad_dev, EV_KEY, KEY_A , 1); //上报EV_KEY类型,1(按下)    }else if(key_value == 17){input_event(mykeypad_dev, EV_KEY, KEY_B, 1);  //上报EV_KEY类型, 1(按下)    }else if(key_value == 11){input_event(mykeypad_dev, EV_KEY, KEY_C, 1);  //上报EV_KEY类型, 1(按下)    }else{input_event(mykeypad_dev, EV_KEY, KEY_D, 1);  //上报EV_KEY类型, 1(按下)    }}else if(((*keyadc_ints)&(1<<4)) != 0) //按键释放{if(key_value == 24){input_event(mykeypad_dev, EV_KEY, KEY_A , 0);  //上报EV_KEY类型, 0(释放)    }else if(key_value == 17){input_event(mykeypad_dev, EV_KEY, KEY_B, 0);  //上报EV_KEY类型, 0(释放)   }else if(key_value == 11){input_event(mykeypad_dev, EV_KEY, KEY_C, 0);  //上报EV_KEY类型, 0(释放)    }else{input_event(mykeypad_dev, EV_KEY, KEY_D, 0);  //上报EV_KEY类型, 0(释放)    }}input_sync(mykeypad_dev);       // 上传同步事件,告诉系统有事件出现*keyadc_ints |= ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4));return IRQ_HANDLED;
}static int mykeypad_probe(struct platform_device *pdev)
{int ret;      struct resource *res;mykeypad_dev=input_allocate_device();      //向内核申请input_dev结构体set_bit(EV_KEY,mykeypad_dev->evbit);       //支持键盘事件set_bit(EV_REP,mykeypad_dev->evbit);       //支持键盘重复按事件set_bit(KEY_A,mykeypad_dev->keybit);       //支持按键 Aset_bit(KEY_B,mykeypad_dev->keybit);       //支持按键 Bset_bit(KEY_C,mykeypad_dev->keybit);       //支持按键 Cset_bit(KEY_D,mykeypad_dev->keybit);       //支持按键 Dmykeypad_dev->name = pdev->name;           //设备名称mykeypad_dev->phys = "mykeypad/input0";    //设备文件路径mykeypad_dev->open = NULL;                 //设备打开操作函数mykeypad_dev->close = NULL;                //设备关闭操作函数mykeypad_dev->id.bustype = BUS_HOST;       //设备总线类型mykeypad_dev->id.vendor = 0x0001;          //设备厂家编号mykeypad_dev->id.product = 0x0001;         //设备产品编号mykeypad_dev->id.version = 0x0100;         //设备版本ret = input_register_device(mykeypad_dev);       //注册input_devif(ret){input_free_device(mykeypad_dev);printk(KERN_ERR "regoster input device failed!\n");return ret;}mykeypad_dev->name="mykeypad";res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取device中的LRADC_CTRLkeyadc_ctrl = ioremap(res->start,(res->end - res->start)+1);res = platform_get_resource(pdev, IORESOURCE_MEM, 1);//获取device中的LRADC_INTCkeyadc_intc = ioremap(res->start,(res->end - res->start)+1);res = platform_get_resource(pdev, IORESOURCE_MEM, 2);//获取device中的LRADC_INTSkeyadc_ints = ioremap(res->start,(res->end - res->start)+1);res = platform_get_resource(pdev, IORESOURCE_MEM, 3);//获取device中的LRADC_DATAkeyadc_data = ioremap(res->start,(res->end - res->start)+1);ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0), mykeypad_irq, 0, "mykey", (void*)&key_value);*keyadc_ctrl&=0;*keyadc_ctrl |= (1<<12);  //单次模式,其他默认*keyadc_intc |= (1<<4) | (1<<1);*keyadc_ctrl |= (1<<0);if(ret){return ret;}return 0;
}static int mykeypad_remove(struct platform_device *pdev)
{iounmap(keyadc_ctrl);   //取消LRADC_CTRL映射iounmap(keyadc_intc);   //取消LRADC_INTC映射iounmap(keyadc_ints);   //取消LRADC_INTS映射iounmap(keyadc_data);   //取消LRADC_DATA映射input_unregister_device(mykeypad_dev);     //卸载类下的驱动设备input_free_device(mykeypad_dev);           //释放驱动结构体return 0;
}static struct of_device_id mykeypad_match_table[] = {  {.compatible = "mykeypad",},
}; static struct platform_device_id mykeypad_device_ids[] = {{.name = "mykeypad",},
};static struct platform_driver mykeypad_driver=
{  .probe  = mykeypad_probe,  .remove = mykeypad_remove,  .driver={  .name = "mykeypad",  .of_match_table = mykeypad_match_table,  },  .id_table = mykeypad_device_ids,
};module_platform_driver(mykeypad_driver);MODULE_LICENSE("GPL");          //不加的话加载会有错误提醒
MODULE_AUTHOR("liefyuan@qq.com");     //作者
MODULE_VERSION("0.1");          //版本
MODULE_DESCRIPTION("mykeypad_driver");  //简单的描述
mykeypad: mykeypad@1c22800 {compatible = "mykeypad";reg = <0x01c22800 0x4>, <0x01c22804 0x4>, <0x01c22808 0x4>, <0x01c2280c 0x4>;interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;status = "okay";
};

编译.ko文件:
makefile
方式一:

KERNEL_DIR := /home/liefyuan/Liefyuan/cherry-pi/linux-zero-4.10.y
CURRENT_PATH := $(shell pwd)
obj-m := mykeypad.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_PATH) clean

方式二:

KERN_DIR = /home/liefyuan/Liefyuan/cherry-pi/linux-zero-4.10.y
all:make -C $(KERN_DIR) M=$(shell pwd) modules clean:rm -rf *.order *o *.symvers *.mod.c *.mod *.koobj-m += mykeypad.o

编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

测试程序

lradc_key.c

#include <string.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>#define  CHECK_POINT
int main(int argc, char** argv)
{int fd = open(argv[1],O_RDONLY);#ifdef CHECK_POINTprintf("fd = %d\n",fd);#endifstruct input_event t;printf("size of t = %d\n",sizeof(t));while(1){printf("while -\n");int len = read(fd, &t, sizeof(t));if(len == sizeof(t)){printf("read over\n");if(t.type==EV_KEY){printf("key %d %s\n", t.code, (t.value) ? "Pressed" : "Released");if(t.code == KEY_ESC)break;}}#ifdef CHECK_POINTprintf("len = %d\n",len);#endif}return 0;
}

编译测试程序:

arm-linux-gnueabihf-gcc lradc_key.c -o lradc_key

嵌入式Linux驱动:LRADC按键相关推荐

  1. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

    文章目录 前言 1.APP怎么读取按键值 1.1.查询方式 1.2.休眠-唤醒方式 1.3.poll方式 1.3.异步通知方式 1.5. 驱动程序提供能力,不提供策略 2.按键驱动程序框架--查询方式 ...

  2. 嵌入式linux应用层中断函数,嵌入式LINUX驱动开发(中断处理函数)

    嵌入式LINUX驱动开发(中断处理函数) 2020年08月11日 | 萬仟网网络运营 | 我要评论 嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一.函数.头文件及说明二.编译举例:一.函数. ...

  3. 嵌入式Linux驱动大全问世,十年磨一剑,视频!服务!新老客户都有大折扣!

    对于学习嵌入式Linux,韦东山老师整理了三条学习路线,适合与不同阶段的学员. 三条嵌入式学习路线 路线一:单片机路线 使用KEL.HAL库来快速的掌握单片机开发 路线二:深入学习单片机/RTOS/U ...

  4. 华清远见嵌入式Linux驱动开发培训班

    课程背景 开放的 Linux 受到广泛的欢迎,得到越来越多公司的支持,但是阻碍 Linux 在各个领域广泛应用的主要因素就是内核/驱动高端人才极度缺乏,Linux源代码中85%是设备驱动,嵌入式系统中 ...

  5. 嵌入式Linux驱动开发笔记(未完待续。。。)

    零.嵌入式Linux驱动编程思想 1.面向对象(把一个事件抽象成一个结构体) 2.分层 3.分离 一.Git仓库用法 1.linu终端输入下面命令安装 git clone https://e.codi ...

  6. 使用IDE(vs code)进行嵌入式linux驱动开发

    目录 背景 系统版本 vs code的安装和使用 1.安装 2.新建工程 3.设置vs code工程的头文件查找路径及编译器路径 4.Intelli Sense Engine Fallback设置为E ...

  7. 嵌入式Linux驱动开发【学习小结】

    文章目录 前言 一.嵌入式Linux驱动程序和单片机裸奔有啥区别? 二.为什么需要嵌入式Linux驱动开发 三.驱动程序框架大致演变过程 总结 前言 随着去嵌入式设备资源不断丰富,主频不断升高,搭载操 ...

  8. 嵌入式linux硬件成本,嵌入式Linux驱动和固件有何区别?供应商是如何用固件压缩成本的?...

    原标题:嵌入式Linux驱动和固件有何区别?供应商是如何用固件压缩成本的? 作为一个驱动开发者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地方的竞争是如此得强烈, ...

  9. 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】

    转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...

  10. linux驱动日志格式,( 转)嵌入式Linux驱动Makefile

    天气: 晴朗 心情: 高兴 ( 转)嵌入式Linux驱动开发笔记 1.1        模块的编译 Linux驱动一般以模块module的形式来加载,首先需要把驱动编译成模块的形式.简单的例子, Be ...

最新文章

  1. opencv图像灰度重心算法
  2. 十张图了解2021年中国数据中心产业链投资现状和投资并购发展趋势
  3. 3.APC的挂入过程
  4. eclipse 安装 lombok插件
  5. 95-910-332-源码-FlinkSQL-Calcite-Flink SQL 整体执行框架
  6. drupal ajax json异步调用
  7. np.array(image)的作用
  8. [No0000123]WPF DataGrid Columns Visibility的绑定
  9. java非法表达式开头,java - 我不断收到“ Battle.java:11:错误:表达式的非法开头” Heelp - SO中文参考 - www.soinside.com...
  10. python滑稽代码
  11. 几何分布的期望和方差公式推导_二项分布与负二项分布的均值与方差推导
  12. PaaS服务从heroku迁移到Fly.io小计
  13. RocketMQ5.0.0事务消息
  14. 【测试实习生基本要求】
  15. XCP实战系列介绍16-XCP标定过程指令解析
  16. 【Busybox】Busybox源码分析-02 | init程序
  17. ThingsBoard中的关系Relation
  18. Laravel repository数据仓库使用 Star.hou红楼一梦
  19. 今天学到一个新姿势(划掉)知识
  20. 电销系统不仅外显手机号,还能自选归属地

热门文章

  1. 直接下载docker镜像包
  2. 怎么用计算机算几何倍增,城市规划中对计算机仿真技术应用.doc
  3. UE4(一):游戏起点流程图
  4. 接口压力测试脚本编写
  5. ICT(计算机通信电子自动化等)专业区别和联系
  6. VMware 快照系统
  7. Python3网络爬虫,简单爬取网络小说并下载
  8. vue---批量删除表格数据
  9. 自学软件测试的面试经验(一)~
  10. CAD绘制复杂轴承图形