【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 <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;
}
}
原来的 code
为SND_BELL
,因此设置的频率始终是beeper->bell_frequency
,默认为bell_frequency = 1000;
需要修改:
#define SND_TONE 0x02
event.code = SND_TONE;
这样就可以自定义周期,自定义周期范围:0 ~ 1000000000
重新测试 OK
!