宿主机 : 虚拟机 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-24 20:04:33
作者: SY
开发板的 LCD
背光通过 CPLD(EPM240T100)
控制,不能直接通过操作 GPIO
电平,控制背光亮度。根据友善之臂的说法,支持 128
级背光调节
参考友善之臂的驱动 linux-3.5\drivers\input\touchscreen\tiny4412_1wire_host.c
#define GPIO_1WIRE EXYNOS4_GPX1(2)
可以推出背光相关的引脚:GPX1_2
,这个引脚在使用时,作为一个双向 IO
,既需要输入,也需要输出。
而且触摸屏的输入也使用该引脚,暂时我们先实现 backlight
功能。
开发板和 CPLD
通讯使用了特殊的协议,设置一次背光值,需要读写 IO
多次,因此,需要使用定时器功能,我们使用定时器 3
,也就是 PWM3
参考:PWM
设备树
pwm: pwm@139D0000 {
compatible = "samsung,exynos4210-pwm";
reg = <0x139D0000 0x1000>;
interrupts = 37 IRQ_TYPE_LEVEL_HIGH>,
38 IRQ_TYPE_LEVEL_HIGH>,
39 IRQ_TYPE_LEVEL_HIGH>,
40 IRQ_TYPE_LEVEL_HIGH>,
41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clock CLK_PWM>;
clock-names = "timers";
#pwm-cells = <3>;
status = "disabled";
};
实现我们的 dts
backlight {
compatible = "tiny4412, onewire_backlight";
reg = <0x139D0000 0x1000>;
pinctrl-names = "backlight_in", "backlight_out";
pinctrl-0 = <&backlight_in>;
pinctrl-1 = <&backlight_out>;
gpios = <&gpx1 2 GPIO_ACTIVE_HIGH>;
clock-names = "timers";
clocks = <&clock CLK_PWM>;
interrupts = 40 IRQ_TYPE_LEVEL_HIGH>;
pwms = <&pwm 0 1000000000 0>;
};
&pinctrl_1 {
backlight_out: backlight_out {
samsung,pins = "gpx1-2";
samsung,pin-function = ;
samsung,pin-pud = ;
samsung,pin-drv = ;
};
backlight_in: backlight_in {
samsung,pins = "gpx1-2";
samsung,pin-function = ;
samsung,pin-pud = ;
samsung,pin-drv = ;
};
};
使用 pinctrl-0
pinctrl-1
可以将同一个引脚设置为 2
种配置。
root@ubuntu:/opt/linux-4.12# cat drivers/input/touchscreen/Makefile
obj-$(CONFIG_TOUCHSCREEN_1WIRE) += tiny4412_1wire.o
root@ubuntu:/opt/linux-4.12# cat drivers/input/touchscreen/Kconfig
config TOUCHSCREEN_1WIRE
tristate "Tiny4412 1wire touchscreen"
help
Say Y here to support touchscreen for tiny4412.
If unsure, say N.
Device Drivers --->
Input device support --->
[*] Touchscreens --->
<*> Tiny4412 1wire touchscreen
新建一个tiny4412_1wire.c
源文件,参考:学习设备树之(十一)Backlight
/*
* lcd_key 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define KEY_HOME 102
#define KEY_MENU 139
#define KEY_BACK 158
struct input_event {
struct timeval time;
unsigned short int type;
unsigned short int code;
signed int value;
};
#if 0
static void help(void)
{
printf("Usage: ./key \n" );
}
#endif
bool esc = false;
static void sigint_handler(int dunno)
{
switch (dunno) {
case SIGINT:
esc = true;
printf("< Ctrl+C > Press.\n");
break;
default:
break;
}
}
static void* read_handler(void* data)
{
printf("thread run.\n");
int epfd = epoll_create1(0);
if (epfd < 0) {
perror("epoll_create1");
return NULL;
}
int evfd = open("/dev/input/event0", O_RDONLY);
if (evfd < 0) {
perror("[open]");
esc = true;
}
struct epoll_event epoll_event;
epoll_event.events = EPOLLIN;
epoll_event.data.fd = evfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, evfd, &epoll_event) < 0) {
perror("[epoll_ctl]");
esc = true;
}
printf("start epoll...\n");
struct input_event event;
const int MAX_EVENT_NUMS = 10;
const int TIMEOUT = 100;
struct epoll_event *events = calloc(MAX_EVENT_NUMS, sizeof(struct epoll_event));
if (!events) {
perror("mem calloc");
esc = true;
}
while (esc == false) {
int nums = epoll_wait(epfd, events, MAX_EVENT_NUMS, TIMEOUT);
for (int i=0; iif (events[i].events & (EPOLLERR | EPOLLHUP)) {
perror("epoll");
continue;
} else if ((events[i].data.fd == evfd) && (events[i].events & EPOLLIN)) {
int ret = read(evfd, &event, sizeof(event));
if (ret < 0) {
break;
}
//printf("[key] nums=%d code=%d value=%d\n", nums, event.code, event.value);
switch (event.code) {
case KEY_HOME:
if (event.value) {
printf("[HOME] Press.\n");
} else {
printf("[HOME] Release.\n");
}
break;
case KEY_MENU:
if (event.value) {
printf("[MENU] Press.\n");
} else {
printf("[MENU] Release.\n");
}
break;
case KEY_BACK:
if (event.value) {
printf("[BACK] Press.\n");
} else {
printf("[BACK] Release.\n");
}
break;
default:
break;
}
}
}
}
if (events) {
free(events);
}
close(epfd);
close(evfd);
printf("thread exit.\n");
pthread_exit(NULL);
return NULL;
}
int main(int argc, char **argv)
{
pthread_t thread_read;
int ret = pthread_create(&thread_read, NULL, read_handler, NULL);
if (ret) {
perror("[thread_create]");
return 1;
}
/* Register signal */
signal(SIGINT, sigint_handler);
pthread_join(thread_read, NULL);
printf("done!\n");
return 0;
}
在申请 IO
资源时,由于我们的驱动中在 pwm-samsung.c
的 pwm_samsung_probe
中已经申请过,而且不会释放资源,因此:
/* Get res */
priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!priv->res) {
dev_err(dev, "get res error!\n");
return -EINVAL;
}
dev_err(dev, "start=%x, end=%x name=%s\n", priv->res->start, priv->res->end, priv->res->name);
/* Get ioremap */
priv->pwm = (volatile struct pwm_base *)devm_ioremap_resource(dev, priv->res);
if (IS_ERR((struct pwm_base *)priv->pwm)) {
dev_err(dev, "devm_ioremap_resource error!\n");
return -EINVAL;
}
如果再次申请就会失败。
我们采用另一种方式获取资源,devm_pwm_get
struct pwm_device *pwm;
pwm = devm_pwm_get(dev, NULL);
查看结构体:
struct pwm_device {
const char *label;
unsigned long flags;
unsigned int hwpwm;
unsigned int pwm;
struct pwm_chip *chip;
void *chip_data;
struct pwm_args args;
struct pwm_state state;
};
struct pwm_chip {
struct device *dev;
struct list_head list;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device *pwms;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
};
关键的字段是 base
,搜索该字段的内容:
static int pwm_samsung_probe(struct platform_device *pdev)
{
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->base = devm_ioremap_resource(&pdev->dev, res);
}
可以看出,在这个驱动中,已经获取过 IO
资源,并把 指针
存储到 base
中,这样就好办了
chip = (struct samsung_pwm_chip *)pwm->chip;
priv->pwm = (volatile struct pwm_base *)chip->base;
if (IS_ERR((struct pwm_base *)priv->pwm)) {
return -EINVAL;
}
这样,定时器的寄存器基地址被存储到 priv->pwm
中,后面我们就可以自由读写定时器寄存器了。
/*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if 0
static void help(void)
{
printf("Usage: ./key \n" );
}
#endif
int main(int argc, char **argv)
{
int evfd = open("/dev/backlight", O_RDWR);
if (evfd < 0) {
perror("[open]");
}
for (int i=0; i<=127; i++) {
uint8_t buff = i;
if (write(evfd, &buff, 1) < 0) {
perror("write");
}
printf("backlight: %d\n", i);
usleep(50000);
}
close(evfd);
printf("done!\n");
return 0;
}
# Linux modules compile
# Author : SY
# Time : 2017-8-10 22:29:24
#############################################################################
CC = arm-none-linux-gnueabi-gcc
CFLAGS = -Wall -std=gnu99 -pthread
TARGET = backlight
OBJS = $(TARGET).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)
查看内核消息
[ 2.653749] tiny4412_1wire 139d0000.backlight: probe.
[ 2.658219] tiny4412_1wire 139d0000.backlight: clk = 100000000 Hz
[ 2.664248] of_get_named_gpiod_flags: parsed 'gpios' property of node '/backlight[0]' - status (0)
[ 2.673202] tiny4412_1wire 139d0000.backlight: TCFG0: 00000f01
[ 2.678969] tiny4412_1wire 139d0000.backlight: TCFG1: 00020000
找到应用程序
[root@TINY4412:~]# cd tmp/
[root@TINY4412:/tmp]# ./backlight
[ 156.325821] backlight_open
backlight: 0
backlight: 1
backlight: 2
backlight: 3
backlight: 4
backlight: 5
backlight: 6
backlight: 7
backlight: 8
backlight: 9
backlight: 10
backlight: 11
backlight: 12
backlight: 13
backlight: 14
backlight: 15
backlight: 16
backlight: 17
backlight: 18
backlight: 19
backlight: 20
backlight: 21
backlight: 22
backlight: 23
backlight: 24
backlight: 25
backlight: 26
backlight: 27
backlight: 28
backlight: 29
backlight: 30
backlight: 31
backlight: 32
backlight: 33
backlight: 34
backlight: 35
backlight: 36
backlight: 37
backlight: 38
backlight: 39
backlight: 40
backlight: 41
backlight: 42
backlight: 43
backlight: 44
backlight: 45
backlight: 46
backlight: 47
backlight: 48
backlight: 49
backlight: 50
backlight: 51
backlight: 52
backlight: 53
backlight: 54
backlight: 55
backlight: 56
backlight: 57
backlight: 58
backlight: 59
backlight: 60
backlight: 61
backlight: 62
backlight: 63
backlight: 64
backlight: 65
backlight: 66
backlight: 67
backlight: 68
backlight: 69
backlight: 70
backlight: 71
backlight: 72
backlight: 73
backlight: 74
backlight: 75
backlight: 76
backlight: 77
backlight: 78
backlight: 79
backlight: 80
backlight: 81
backlight: 82
backlight: 83
backlight: 84
backlight: 85
backlight: 86
backlight: 87
backlight: 88
backlight: 89
backlight: 90
backlight: 91
backlight: 92
backlight: 93
backlight: 94
backlight: 95
backlight: 96
backlight: 97
backlight: 98
backlight: 99
backlight: 100
backlight: 101
backlight: 102
backlight: 103
backlight: 104
backlight: 105
backlight: 106
backlight: 107
backlight: 108
backlight: 109
backlight: 110
backlight: 111
backlight: 112
backlight: 113
backlight: 114
backlight: 115
backlight: 116
backlight: 117
backlight: 118
backlight: 119
backlight: 120
backlight: 121
backlight: 122
backlight: 123
backlight: 124
backlight: 125
backlight: 126
backlight: 127
[ 162.758208] backlight_exit
done!
[root@TINY4412:/tmp]#
可以看到 LCD
从暗慢慢变亮。