6825按键代码分析

展讯6825平台8*8按键驱动分析

一、特性

6825双核平台有特有的按键控制器和ARMAPB通讯想交互,最大能扫描8*8的按键,能够判断出按下和松开两种状态,可以同时扫描多个按键的功能,以及长按键。

1、支持最大8*8的按键扫描矩阵

2、最多可以同时探测到4个按键被按下

3、支持长按键和单按键模式

4、具有睡眠模式来节省电量

5、按键的按下或者松开防抖时间可编程

6、按键的I/O优先级可编程

7、按键扫描周期可编程

8、内部结构

二、KEYPAD控制器

默认开的是4*3矩阵。

6825按键控制器驱动分析:

就如标准的linux设备驱动程序一样,平台设备和驱动分离的思想,主要有这样一些文件:

kernel\drivers\input\keyboard\input-hook.c

kernel\drivers\input\keyboard\sc8825-keypad.c

kernel\arch\arm\mach-sc8825\devices.c

kernel/arch/arm/mach-sc8825\board.c

代码步骤:

1)首先定义一个platmform结构体,在板级文件注册的时候将此结构注册进内核。

Devices:

struct platform_device sprd_keypad_device

platform_add_devices(devices, ARRAY_SIZE(devices));

Driver:

module_init(sci_keypad_init);

  platform_driver_register(&sci_keypad_driver);

    static int __devinit sci_keypad_probe(struct platform_device *pdev){

struct sci_keypad_t *sci_kpd;

struct input_dev *input_dev;//输入设备结构体

struct sci_keypad_platform_data *pdata = pdev->dev.platform_data;//平台devices结构体

int error;

unsigned long value;

unsigned int row_shift, keycodemax;

row_shift = get_count_order(pdata->cols);// cols = 8 rows = 8 row_shift = 7; 

keycodemax = pdata->rows << row_shift;// 8 * 2^7;

//sci_keypad 结构体其中包括  sci_keypad_t + keycodemax;

sci_kpd = kzalloc(sizeof(struct sci_keypad_t) +

  keycodemax * sizeof(unsigned short), GFP_KERNEL);

//分配一个input_dev设备

input_dev = input_allocate_device();

//失败操作

if (!sci_kpd || !input_dev) {

kfree(sci_kpd);

input_free_device(input_dev);

return -ENOMEM;

}

//设置平台数据

platform_set_drvdata(pdev, sci_kpd);

//input_dev赋值

sci_kpd->input_dev = input_dev;

//行赋值

sci_kpd->rows = pdata->rows;

//列赋值

sci_kpd->cols = pdata->cols;

//用片外RAMKPD进行复位操作

sci_glb_set(REG_GLB_SOFT_RST,BIT_KPD_RST);

//延时2ms

mdelay(2);

//清除KPD复位

sci_glb_clr(REG_GLB_SOFT_RST,BIT_KPD_RST);

//使能KPD时钟和MCU可对KPD控制器读写

sci_glb_set(REG_GLB_GEN0,BIT_KPD_EB | BIT_RTC_KPD_EB);

//对按键控制寄存器中断清除操作

keypad_writel(KPD_INT_CLR, KPD_INT_ALL);

//设置按键优先级

value = CFG_ROW_POLARITY | CFG_COL_POLARITY;

keypad_writel(KPD_POLARITY, value);//value = 0xffff

//设置按键分频参数

keypad_writel(KPD_CLK_DIV_CNT, 1);

// 设置长按键时间

keypad_writel(KPD_LONG_KEY_CNT, 0xc);

//设置防抖时间

keypad_writel(KPD_DEBOUNCE_CNT, 0x5);

//获取按键控制寄存器中断源

sci_kpd->irq = platform_get_irq(pdev, 0);

if (sci_kpd->irq < 0) {

error = -ENODEV;

dev_err(&pdev->dev, "Get irq number error,Keypad Module\n");

goto out2;

}

//申请中断

error =

    request_irq(sci_kpd->irq, sci_keypad_isr, 0, "sprd-keypad", sci_kpd);

if (error) {

dev_err(&pdev->dev, "unable to claim irq %d\n", sci_kpd->irq);

goto out2;

}

//设置input设备名字

input_dev->name = pdev->name;

input_dev->phys = "sprd-key/input0";

//设置父设备

input_dev->dev.parent = &pdev->dev;

//绑定input_dev

input_set_drvdata(input_dev, sci_kpd);

//设置总线类型

input_dev->id.bustype = BUS_HOST;

//设置版本

input_dev->id.vendor = 0x0001;

input_dev->id.product = 0x0001;

input_dev->id.version = 0x0100;

input_dev->keycode = &sci_kpd[1];

input_dev->keycodesize = sizeof(unsigned short);

input_dev->keycodemax = keycodemax;

//设置按键映射

matrix_keypad_build_keymap(pdata->keymap_data, row_shift,

   input_dev->keycode, input_dev->keybit);

/* there are keys from hw other than keypad controller */

//设置开机键

__set_bit(KEY_POWER, input_dev->keybit);

//设置为按键时间

__set_bit(EV_KEY, input_dev->evbit);

if (pdata->repeat)

__set_bit(EV_REP, input_dev->evbit);

//注册为输入设备

error = input_register_device(input_dev);

if (error) {

dev_err(&pdev->dev, "unable to register input device\n");

goto out4;

}

device_init_wakeup(&pdev->dev, 1);

//设置按键按下释放状态

value = KPD_INT_DOWNUP;

//判断是否谁知长按键

if (pdata->support_long_key)

value |= KPD_INT_LONG;

keypad_writel(KPD_INT_EN, value);

//设置按键休眠时间

value = KPD_SLEEP_CNT_VALUE(1000);

keypad_writel(KPD_SLEEP_CNT, value);

value = KPD_SLEEP_EN | (pdata->rows_choose_hw & KPDCTL_ROW_MSK) |

    (pdata->cols_choose_hw & KPDCTL_COL_MSK);

if (pdata->support_long_key)

value |= KPD_LONG_KEY_EN;

//使能按键

value |= KPD_EN;

keypad_writel(KPD_CTRL, value);d

    //设置pewer键,设置状态

gpio_request(ANA_GPI_PB, "powerkey");

gpio_direction_input(ANA_GPI_PB);

//设置power按键中断

error = request_irq(gpio_to_irq(ANA_GPI_PB), sci_powerkey_isr,

IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "powerkey", sci_kpd);

if (error) {

dev_err(&pdev->dev, "unable to claim irq %d\n",

gpio_to_irq(ANA_GPI_PB));

goto out2;

}

dump_keypad_register();

return 0;

out4:

input_free_device(input_dev);

free_irq(sci_kpd->irq, pdev);

out2:

kfree(sci_kpd);

platform_set_drvdata(pdev, NULL);

return error;

}

}

以上是probe的处理过程,其中主要的操作有一下几点:

1)分配一个sci_keypad结构体

2)申请一个input_dev设备并为其赋值

3)按键控制器寄存器初始化

4)按键控制寄存器中断申请

5)按键值和对应寄存器值映射

6)Power按键中断申请

下面分析power按键中断处理程序以及按键控制器中断处理程序

error = request_irq(gpio_to_irq(ANA_GPI_PB), sci_powerkey_isr,

IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "powerkey", sci_kpd);

上面这段是power按键中断申请,主要包括四个部分

1)将ANA_GPI_P转换为中断号

2)绑定中断处理程序

3)设置高点平触发以及设置改中断不休眠

4)传参

static irqreturn_t sci_powerkey_isr(int irq, void *dev_id)

{ //TODO: if usign gpio(eic), need add row , cols to platform data.

static unsigned long last_value = 1;

unsigned short key = KEY_POWER;//power键值116  kernel/include/linux/input.h  定义

unsigned long value = !(gpio_get_value(ANA_GPI_PB));

struct sci_keypad_t *sci_kpd = dev_id;//传参强制转化

if (last_value == value) {//判断是否和上次一样

/* seems an event is missing, just report it */

input_report_key(sci_kpd->input_dev, key, last_value);//上报按键键值

input_sync(sci_kpd->input_dev);//同步

printk("%dX\n", key);

}

if (value) {

/* Release : low level */

input_report_key_hook(sci_kpd->input_dev, key, 0);//上报键值0

input_report_key(sci_kpd->input_dev, key, 0);

input_sync(sci_kpd->input_dev);//同步

printk("Powerkey:%dU\n", key);

irq_set_irq_type(irq, IRQF_TRIGGER_HIGH);//设置高电平触发

} else {

/* Press : high level *///按下

input_report_key_hook(sci_kpd->input_dev, key, 1);//上报键值1

input_report_key(sci_kpd->input_dev, key, 1);

input_sync(sci_kpd->input_dev);//同步

printk("Powerkey:%dD\n", key);

irq_set_irq_type(irq, IRQF_TRIGGER_LOW);//设置低点平触发

}

last_value = value;//备份值

return IRQ_HANDLED;//返回IRQ

}

普通按键处理程序

static irqreturn_t sci_keypad_isr(int irq, void *dev_id)

{

unsigned short key = 0;

unsigned long value;

struct sci_keypad_t *sci_kpd = dev_id;

unsigned long int_status = keypad_readl(KPD_INT_MASK_STATUS);//读按中断寄存器的状态

unsigned long key_status = keypad_readl(KPD_KEY_STATUS);//读按键状态

unsigned short *keycodes = sci_kpd->input_dev->keycode;

unsigned int row_shift = get_count_order(sci_kpd->cols);

int col, row;

value = keypad_readl(KPD_INT_CLR);

value |= KPD_INT_ALL;

keypad_writel(KPD_INT_CLR, value);//清除

if ((int_status & KPD_PRESS_INT0)) {//判断是否是按下0

col = KPD_INT0_COL(key_status);//取出行

row = KPD_INT0_ROW(key_status);//取出列

key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];//获得对应映射键值

input_report_key(sci_kpd->input_dev, key, 1);//上报

input_sync(sci_kpd->input_dev);

printk("%03dD\n", key);

}

}

以上是6825按键中断处理程序,相比6820上的按键中断,6825的按键中断相对于简单多了,主要是展讯代码整合较为好。

你可能感兴趣的:(技术文档)