linux驱动开发:背光设备
标签: linux驱动开发 2016-08-26 10:54 49人阅读 评论(0)收藏举报 本文章已收录于: 分类: 作者同类文章X版权声明:学习记录,积少成多
上一篇的pwm 驱动,加上第一篇的led灯控制,合起来就是背光设备的驱动!
背光设备定位于仅仅对于lcd的背光进行控制,其中主要控制背光开关脚(GPIO),背光调节脚(PWM)!
由于很类似,在这里就不做赘述
显而易见,pwm的配置和上一篇一样,具体代码如下:
/*
* linux/drivers/char/smart210_pwm.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#define DEVICE_NAME "pwm-backlight"
#define PWM_IOCTL_SET_BL_POWER 0
#define PWM_IOCTL_SET_BL_FREQ 1
#define PWM_IOCTL_SET_BL_DUTY 2
#define NS_IN_1HZ (1000000000UL)
#define BACKLIGHT_PWM_ID 1
#define BACKLIGHT_PWM_GPIO S5PV210_GPD0(1)
#define BACKLIGHT_CON_GPIO S5PV210_GPH1(2)
#define _BL_ON 1
#define _BL_OFF 0
struct backlight_data{
unsigned char bl_power;//backlight power pin state
unsigned char bl_duty;//backlight con for brightness
unsigned int bl_freq;//backlight con for freq
};
static struct pwm_device *pwm1backlight;
static struct semaphore lock;
//default setting for backlight ,backlight power on,bl bri is 90,bl freq is 1000hz
static struct backlight_data smart210_bl ={
.bl_power = _BL_ON,
.bl_duty = 90,
.bl_freq = 1000,
};
static void bl_set_power(unsigned char blpower) {
smart210_bl.bl_power = blpower;
if (gpio_request(BACKLIGHT_CON_GPIO, "BacklightPower")) {
printk("request GPIO %d for bl power failed\n", BACKLIGHT_CON_GPIO);
return;
}
gpio_set_value(BACKLIGHT_CON_GPIO, blpower);
s3c_gpio_cfgpin(BACKLIGHT_CON_GPIO, S3C_GPIO_OUTPUT);
gpio_free(BACKLIGHT_CON_GPIO);
}
static void pwm_set_data(unsigned int freq,unsigned char duty) {
unsigned long period_ns = NS_IN_1HZ / freq;
unsigned long duty_ns =period_ns/100*(100-duty);
smart210_bl.bl_freq =freq;
smart210_bl.bl_duty =duty;
//(100,100)黑色
//(0,100)白色
pwm_config(pwm1backlight, duty_ns, period_ns);
pwm_enable(pwm1backlight);
s3c_gpio_cfgpin(BACKLIGHT_PWM_GPIO, S3C_GPIO_SFN(2));
}
static int smart210_pwm_open(struct inode *inode, struct file *file) {
if (!down_trylock(&lock))
return 0;
else
return -EBUSY;
}
static int smart210_pwm_close(struct inode *inode, struct file *file) {
up(&lock);
return 0;
}
/*cmd--duty;arg--freq*/
static long smart210_bl_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
//if(cmd ==100) cmd=99;
//pwm_set_data(arg,cmd);
switch (cmd) {
case PWM_IOCTL_SET_BL_POWER:
bl_set_power(arg);
break;
case PWM_IOCTL_SET_BL_FREQ:
pwm_set_data(arg,smart210_bl.bl_duty);
break;
case PWM_IOCTL_SET_BL_DUTY:
pwm_set_data(smart210_bl.bl_freq,arg);
break;
default:
break;
}
return 0;
}
static ssize_t smart210_bl_write(struct file *file, const char *buffer,
size_t count, loff_t *ppos)
{
unsigned char *k_buf =(char *)smart210_bl;
int ret;
if (count == 0) {
return count;
}
ret = copy_from_user(k_buf, buffer, sizeof(struct backlight_data)) ? -EFAULT : 0;//to_kernel,from_usr,length
if (ret) {
return ret;
}
bl_set_power(smart210_bl.bl_power);
pwm_set_data(smart210_bl.bl_freq,smart210_bl.bl_duty);
return count;
}
static ssize_t smart210_bl_read(struct file *filp, char *buffer,
size_t count, loff_t *ppos)
{
int ret;
unsigned char *k_buf =(char *)smart210_bl;
if (count == 0) {
return 0;
}
ret = copy_to_user(buffer, k_buf, sizeof(struct backlight_data)) ? -EFAULT : 0;//to_user,from,length
if (ret) {
return ret;
}
return sizeof(struct backlight_data);
}
static struct file_operations smart210_pwm_ops = {
.owner = THIS_MODULE,
.open = smart210_pwm_open,
.release = smart210_pwm_close,
.unlocked_ioctl = smart210_bl_ioctl,
.read = smart210_bl_read,
.write = smart210_bl_write,
};
static struct miscdevice smart210_misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &smart210_pwm_ops,
};
static int __init smart210_pwm_dev_init(void) {
int ret;
ret = gpio_request(BACKLIGHT_PWM_GPIO, DEVICE_NAME);
if (ret) {
printk("request GPIO %d for pwm failed\n", BACKLIGHT_PWM_GPIO);
return ret;
}
gpio_set_value(BACKLIGHT_PWM_GPIO, 0);
s3c_gpio_cfgpin(BACKLIGHT_PWM_GPIO, S3C_GPIO_OUTPUT);
pwm1backlight = pwm_request(BACKLIGHT_PWM_ID, DEVICE_NAME);
if (IS_ERR(pwm1backlight)) {
printk("request pwm %d for %s failed\n", BACKLIGHT_PWM_ID, DEVICE_NAME);
return -ENODEV;
}
sema_init(&lock, 1);
ret = misc_register(&smart210_misc_dev);
printk(DEVICE_NAME "\tinitialized\n");
return ret;
}
static void __exit smart210_pwm_dev_exit(void) {
misc_deregister(&smart210_misc_dev);
gpio_free(BACKLIGHT_PWM_GPIO);
}
module_init(smart210_pwm_dev_init);
module_exit(smart210_pwm_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
MODULE_DESCRIPTION("S5PV210 PWM Backlight Driver");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
背光设备,根据实际情况来讲,一般都是固定 背光Adj脚的Freq,仅仅改变它的duty来控制亮度。另外背光的开关也可以单独控制。这里我们实现3个功能,背光开关,freq控制和duty控制。
嗯,实际上有时我们也需要获得当前背光的亮度,背光的开光状态等,所以我们需要读设备。同样我们可以对设备进行写!所以这里在初始化smart210_pwm_ops 这边时加入了设备读写接口
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//file,user buffer,length,相对文件头的偏移量,将设备的资讯抓到用户空间
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//file,user buffer,length,相对文件头的偏移量,将用户空间的资讯写到设备中
内核空间和用户空间
驱动是运行在内核空间的,测试程序是运行在用户空间的。所以在从内核空间copy资料到用户空间,或者把用户空间的资料copy到内核空间时,便需要专用的函数。
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
//用户空间buffer,内核空间buffer,长度
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
//内核空间buffer,用户空间buffer,长度
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
同样,pwm需要先初始化它的timer
在arch/arm/mach-s5pv210/mach-smdkv210.c中添加
static struct platform_device *smdkv210_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&s3c_device_hsmmc3,
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_i2c2,
&s3c_device_rtc,
&s3c_device_ts,
&s3c_device_usb_hsotg,
&s3c_device_wdt,
&s3c_device_timer[0],// for pwm buzzer
&s3c_device_timer[1],// for pwm backlight
&s5p_device_fimc0,
&s5p_device_fimc1,
&s5p_device_fimc2,
&s5p_device_fimc_md,
&s5p_device_jpeg,
&s5p_device_mfc,
&s5p_device_mfc_l,
&s5p_device_mfc_r,
&s5pv210_device_ac97,
&s5pv210_device_iis0,
&s5pv210_device_spdif,
&samsung_asoc_idma,
&samsung_device_keypad,
&smdkv210_dm9000,
&smdkv210_lcd_lte480wv,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
这里同样将驱动关联进内核,编译,加载,在根文件系统的dev目录下会自动生成pwm-backlight设备文件,这里已经加载成功!
测试文件:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>//close read,write func
#include <stdlib.h>
#include <sys/ioctl.h>
/*open head need*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PWM_IOCTL_SET_BL_POWER 3
#define PWM_IOCTL_SET_BL_FREQ 4
#define PWM_IOCTL_SET_BL_DUTY 5
#define CMD_MIN PWM_IOCTL_SET_BL_POWER
#define CMD_MAX PWM_IOCTL_SET_BL_DUTY
#define _BL_ON 1
#define _BL_OFF 0
struct backlight_data{
unsigned char bl_power;//backlight power pin state
unsigned int bl_duty;//backlight con for brightness
unsigned int bl_freq;//backlight con for freq
};
struct backlight_data us_bl_data;
static int fd = -1;
/*cmd data*/
int main(int argc, char **argv)
{
unsigned int cmd,data;
int ret;
if (argc != 3) {
fprintf(stderr, "Usage: pwm_bl_test cmd data.\n");
exit(1);
}
else
{
sscanf(argv[1], "%d", &cmd);
sscanf(argv[2], "%d", &data);
printf("cmd:%d,data:%d\n",cmd,data);
if(cmd >CMD_MAX || cmd <CMD_MIN){
fprintf(stderr, "Usage: pwm_bl_test cmd range: 3-5\n");
exit(1);
}
//cmd =0,check data range
switch(cmd)
{
case PWM_IOCTL_SET_BL_POWER:
if(data >1 || data <0){
fprintf(stderr, "Usage: pwm_bl_test data range: 0 - 1\n");
exit(1);
}
break;
case PWM_IOCTL_SET_BL_FREQ:
if(data >2000 || data <10){
fprintf(stderr, "Usage: pwm_bl_test data range: 10 - 2000\n");
exit(1);
}
break;
case PWM_IOCTL_SET_BL_DUTY:
if(data >99 || data <0){
fprintf(stderr, "Usage: pwm_bl_test data range: 0 - 99\n");
exit(1);
}
break;
default:
break;
}
fd = open("/dev/pwm-backlight", 0);
if (fd < 0) {
perror("open pwm_backlight device");
exit(1);
}
ret = ioctl(fd,cmd,data);
//从设备读状态
read(fd, (char*)(&us_bl_data), sizeof(struct backlight_data));
printf("freq=%d,duty=%d,power=%d\n",us_bl_data.bl_freq,us_bl_data.bl_duty,us_bl_data.bl_power);
if(ret < 0) {
perror("set the backlight");
exit(1);
}
close(fd);
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
经过测试,可以正常控制背光开关,freq,duty。并可以从设备读取到已经设定的背光设备参数,至此,背光设备的驱动基本完成!
- 顶
- 0
- 踩
- 0
- 上一篇linux驱动开发之pwm蜂鸣器
- 下一篇linux驱动:按键
我的同类文章
http://blog.csdn.net- •mmap函数分析使用实例2016-12-17
- •内核资料:ALSA资料2016-12-03
- •linux内核札记2016-11-26
- •ubuntu下开发环境配置:tftp服务器的搭建2016-11-22
- •linux驱动开发:重力传感器的了解2016-11-13
- •linux驱动开发:ft5x06的touch screen的IIC驱动程序编写2016-11-13
- •linux驱动开发: wm8960 codec代码分析2016-12-09
- •linux内核学习:内核链表2016-11-26
- •ubuntu下开发环境配置:nfs服务器的搭建2016-11-22
- •linux驱动开发:mma7660 sensor的配置2016-11-15
- •linux驱动开发:串口协议2016-11-13