【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);
}
這樣,蜂鳴器就可以鳴叫。
menuconfig
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;
}
}
原來的 code
爲SND_BELL
,因此設置的頻率始終是beeper->bell_frequency
,默認爲bell_frequency = 1000;
需要修改:
#define SND_TONE 0x02
event.code = SND_TONE;
這樣就可以自定義週期,自定義週期範圍:0 ~ 1000000000
重新測試 OK
!