【TINY4412】LINUX移植笔记:(18)设备树BEEP驱动

【TINY4412】LINUX移植笔记:(18)设备树 BEEP驱动

宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-8-10 21:50:02
作者: SY

简介

例程使用PWM控制开发板上的蜂鸣器,使用linux内核的通用pwm-beep模块实现。

设备树

&pwm {
    samsung,pwm-outputs = <0>;
    pinctrl-0 = <&pwm0_out>;
    pinctrl-names = "default";
    status = "okay";
};

buzzer {
    compatible = "pwm-beeper";
    pwms = <&pwm 0 1000000000 0>;
};

其中pwms含义参考

struct pwm_device *
of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) 
{
    //第一个参数表示第几个pwm
    pwm = pwm_request_from_chip(pc, args->args[0], NULL);

    //第二个参数表示周期
    pwm->args.period = args->args[1];

    //第三个参数表示电平翻转,取决于电路板设计,是高电平鸣叫还是低电平鸣叫
    if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
        pwm->args.polarity = PWM_POLARITY_INVERSED;
}

源文件

实现pwm-beep主要依赖于./driver/input/misc/pwm-beeper.c

首先执行probe

static int pwm_beeper_probe(struct platform_device *pdev)
{
    //该函数获取一个pwm设备
    beeper->pwm = devm_pwm_get(dev, NULL);

    //初始化工作队列,工作队列起作用时调用 pwm_beeper_work
    INIT_WORK(&beeper->work, pwm_beeper_work);

    //从设备树读取蜂鸣器固定鸣叫频率,如果找不到使用默认值1000HZ
    error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
    if (error) {
        bell_frequency = 1000;
        dev_dbg(dev,
            "failed to parse 'beeper-hz' property, using default: %uHz\n",
            bell_frequency);
    }

    //收到输入事件时,执行 pwm_beeper_event
    beeper->input->event = pwm_beeper_event;
    beeper->input->close = pwm_beeper_close;

    //注册为输入子系统,将在 /dev/input/eventx 生成设备对象,读写该设备就可以操作蜂鸣器
    error = input_register_device(beeper->input);
}

应用层读写设备时,pwm_beeper_event函数被调用:

static int pwm_beeper_event(struct input_dev *input,
                unsigned int type, unsigned int code, int value)
{
    struct pwm_beeper *beeper = input_get_drvdata(input);

    if (type != EV_SND || value < 0)
        return -EINVAL;

    switch (code) {
    case SND_BELL:
        value = value ? beeper->bell_frequency : 0;
        break;
    case SND_TONE:
        break;
    default:
        return -EINVAL;
    }

    if (value == 0)
        beeper->period = 0;
    else
        beeper->period = HZ_TO_NANOSECONDS(value);

    if (!beeper->suspended)
        schedule_work(&beeper->work);

    return 0;
}

只要蜂鸣器没有挂起,将执行schedule_work,工作队列起作用后,调用pwm_beeper_work

static void pwm_beeper_work(struct work_struct *work)
{
    struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work);
    unsigned long period = READ_ONCE(beeper->period);

    if (period)
        pwm_beeper_on(beeper, period);
    else
        pwm_beeper_off(beeper);
}

这样,蜂鸣器就可以鸣叫。

Device Drivers  --->
    Input device support  --->
        [*]   Miscellaneous devices  ---> 
            <*>   PWM beeper support 

测试

[    2.690985] pwm-beeper beep: beep supply amp not found, using dummy regulator
[    2.697795] input: pwm-beeper as /devices/platform/beep/input/input0

可以看出蜂鸣器作为内核的输入事件0,查看设备:

[root@TINY4412:~]# cat proc/bus/input/devices
I: Bus=0019 Vendor=001f Product=0001 Version=0100
N: Name="pwm-beeper"
P: Phys=pwm/input0
S: Sysfs=/devices/platform/beep/input/input0
U: Uniq=
H: Handlers=kbd event0 
B: PROP=0
B: EV=40001
B: SND=6

dev目录对应的设备

[root@TINY4412:~]# ls /dev/input/event0 
/dev/input/event0

应用程序

测试蜂鸣器需要写一个应用程序,通过用户态访问蜂鸣器。

应用程序包含两个文件,一个是用户态源文件,一个是Makefile文件。 这两个文件可以放在任意位置编译。

app_beep.c

/*
 * beep driver for tiny4412
 *
 * Copyright (c) 2017
 * Author: SY <1530454315@qq.com>
 *
 * 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. 
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> 

#define EV_SND                  0x12
#define SND_BELL                0x01
#define SND_TONE                0x02

struct input_event {
        struct timeval time;
        unsigned short int type;
        unsigned short int code;
        signed int value;
};

static void help(void)
{
        printf("Usage: ./app_beep <duty>");
}


int main(int argc, char **argv)
{
        if (argc < 2) {
                help();
                return 1;
        }

        int fd = open("/dev/input/event0", O_RDWR);
        if (fd < 0) {
                perror("[open]");
                return 1;
        }

        int duty = atoi(argv[1]);

        struct input_event event;
        event.type = EV_SND;
        event.code = SND_BELL;
        event.value = duty;

        if (write(fd, &event, sizeof(event)) < 0) {
                perror("[write]");
                goto error;
        }
        close(fd);

        printf("done!\n");

        return 0;

error:
        close(fd);
        return 1;
}

Makefile

# Linux modules compile
# Author : SY
# Time   : 2017-8-10 22:29:24
#############################################################################

CC            = arm-none-eabi-gcc
CFLAGS        += -Wall -std=gnu99


TARGET        = app_beep
OBJS          = app_beep.o
INSTALL_DIR   = /opt/fs/rootfs/rootfs/tmp/

$(TARGET) : $(OBJS)
        $(CC) $(CFLAGS) -o $@ $<

clean:
        rm -rf *.o 

调用make时,提示:

arm-none-eabi-gcc -Wall -std=c99 -o app_beep app_beep.o
/opt/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/libc.a(lib_a-exit.o): In function `exit':
exit.c:(.text.exit+0x2c): undefined reference to `_exit'
/opt/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/libc.a(lib_a-openr.o): In function `_open_r':
openr.c:(.text._open_r+0x24): undefined reference to `_open'
/opt/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/libc.a(lib_a-sbrkr.o): In function `_sbrk_r':

网上找到答案:exit.c:(.text+0x18): undefined reference to `_exit’ when using arm-none-eabi-gcc

添加字段--specs=nosys.specs

# Linux modules compile
# Author : SY
# Time   : 2017-8-10 22:29:24
#############################################################################

CC            = arm-none-eabi-gcc
CFLAGS        += -Wall -std=gnu99 --specs=nosys.specs


TARGET        = app_beep
OBJS          = app_beep.o
INSTALL_PATH  = /opt/fs/rootfs/rootfs/tmp/

$(TARGET) : $(OBJS)
        $(CC) $(CFLAGS) -o $@ $<

install:
        chmod 755 $(TARGET)
        cp $(TARGET) $(INSTALL_PATH) 

clean:
        rm -rf *.o $(TARGET)
root@ubuntu:/opt/temp/temp/beep/app# make
root@ubuntu:/opt/temp/temp/beep/app# make install

使用NFS挂载Linux,在开发板执行

[root@TINY4412:/tmp]# ./app_beep 1000
Segmentation fault

应该是内存越界等问题,在本地测试

root@ubuntu:/opt/temp/temp/beep/app# gcc -g app_beep.c -o app_beep
root@ubuntu:/opt/temp/temp/beep/app# ./app_beep 1000
Done!

没毛病啊!估计是编译器的问题,修改Makefile

root@ubuntu:/opt/temp/temp/beep/app# more Makefile 
# Linux modules compile
# Author : SY
# Time   : 2017-8-10 22:29:24
#############################################################################

CC            = arm-none-linux-gnueabi-gcc
CFLAGS        += -Wall -std=gnu99


TARGET        = app_beep
OBJS          = app_beep.o
INSTALL_PATH  = /opt/fs/rootfs/rootfs/tmp/

$(TARGET) : $(OBJS)
        $(CC) $(CFLAGS) -o $@ $<

install:
        chmod 755 $(TARGET)
        cp $(TARGET) $(INSTALL_PATH) 

clean:
        rm -rf *.o $(TARGET)

重新编译,安装

root@ubuntu:/opt/temp/temp/beep/app# make
arm-none-linux-gnueabi-gcc -Wall -std=gnu99   -c -o app_beep.o app_beep.c
arm-none-linux-gnueabi-gcc -Wall -std=gnu99 -o app_beep app_beep.o
root@ubuntu:/opt/temp/temp/beep/app# make install
chmod 755 app_beep
cp app_beep /opt/fs/rootfs/rootfs/tmp/

测试

[root@TINY4412:/tmp]# ./app_beep 1000
done!

蜂鸣器叫了!果然是编译器问题,查找资料交叉编译工具链

arm-none-eabi-gcc

(ARM architecture,no vendor,not target an operating system,complies with the ARM EABI)
用于编译 ARM 架构的裸机系统(包括 ARM Linux的 boot、kernel,不适用编译 linux 应用 Application),一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数,比如fork(2),他使用的是 newlib 这个专用于嵌入式系统的C库。

说的非常清楚,不适合编译应用程序!!! 以后统一使用编译器arm-none-linux-gnueabi-gcc

虽然蜂鸣器正常驱动,但是修改占空比没效果,查看源码:

static int pwm_beeper_event(struct input_dev *input,
                unsigned int type, unsigned int code, int value)
{
    switch (code) {
    case SND_BELL:
        value = value ? beeper->bell_frequency : 0;
        break;
    case SND_TONE:
        break;
    default:
        return -EINVAL;
    }
}

原来的 codeSND_BELL,因此设置的频率始终是beeper->bell_frequency,默认为bell_frequency = 1000; 需要修改:

#define SND_TONE        0x02
event.code = SND_TONE;

这样就可以自定义周期,自定义周期范围:0 ~ 1000000000

重新测试 OK !

参考

Linux如何查看与/dev/input目录下的event对应的设备

基于S3C2440的Linux-3.6.6移植——PWM蜂鸣器驱动

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值