【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 <[email protected]>
 *
 * 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蜂鳴器驅動

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章