系列 | 内容 |
---|---|
深入理解linux内核 | 深入理解PWM子系统 |
#!/system/bin/sh
i=0
while [ $i -le 255 ]
do
echo $i
echo $i > /sys/class/backlight/backlight/brightness
i=$((i+1))
usleep 100000
done
backlight_device
目录:kernel/include/linux/backlight.h
static inline void * bl_get_data(struct backlight_device *bl_dev)
{
/*********返回driver的私有数据********/
return dev_get_drvdata(&bl_dev->dev);
}
其对应的私有数据是在prob
函数中赋值:
/*获取私有数据 return dev->platform_data; */
struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
struct platform_pwm_backlight_data defdata;
struct pwm_bl_data *pb;
/***设备树的解析***/
ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
data = &defdata;
pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
if (data->levels) {
for (i = 0; i <= data->max_brightness; i++)
if (data->levels[i] > pb->scale)
pb->scale = data->levels[i];
pb->levels = data->levels;
} else
pb->scale = data->max_brightness;
pb->notify = data->notify;
pb->notify_after = data->notify_after;
pb->check_fb = data->check_fb;
pb->exit = data->exit;
pb->dev = &pdev->dev;
pb->enabled = false;
//devm_gpiod_get_optional只是对gpiod_get_index
//gpiod_get_index()本质上和gpio_request()是一个功能,是申请gpio的,只是它是从device tree去查找,
pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",GPIOD_ASIS);
if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
// 申请这个GPIO
ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,GPIOF_OUT_INIT_HIGH,"enable");
//根据gpio序号获取相应的描述gpio的结构体desc
pb->enable_gpio = gpio_to_desc(data->enable_gpio);
}
if (pb->enable_gpio) {
if (node && node->phandle &&
gpiod_get_direction(pb->enable_gpio) == GPIOF_DIR_OUT && gpiod_get_value(pb->enable_gpio) == 0)
initial_blank = FB_BLANK_POWERDOWN;
else
gpiod_direction_output(pb->enable_gpio, 1);
//通过regulator_get就可以获得regulator设备。
pb->power_supply = devm_regulator_get(&pdev->dev, "power");
//core.c//获得一个pwm
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
pwm_get ->
of_pwm_get ->
of_parse_phandle_with_args 解析上面dts中的pwms属性.
of_node_to_pwmchip
pwm = pc->of_xlate //最终生成struct pwm_device类型.
//linux/drivers/pwm/core.c
//adjust the current PWM config to the PWM arguments
pwm_adjust_config(pb->pwm);
pwm_get_args(pb->pwm, &pargs);
pb->period = pargs.period;
pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = data->max_brightness;
//这个注册函数接口在/sys/class/backlight/下又创建了一个设备接口, 名字是通过dev_name(&pdev->dev)实参传递的.
bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, &pwm_backlight_ops, &props);
bl->props.brightness = data->dft_brightness;
bl->props.power = initial_blank;
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
static int pwm_backlight_parse_dt(struct device *dev,
struct platform_pwm_backlight_data *data)
{
struct device_node *node = dev->of_node;
struct property *prop;
/* determine the number of brightness levels */
prop = of_find_property(node, "brightness-levels", &length);
data->max_brightness = length / sizeof(u32);
/* read brightness levels from DT property */
if (data->max_brightness > 0) {
size_t size = sizeof(*data->levels) * data->max_brightness;
data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
ret = of_property_read_u32_array(node, "brightness-levels",data->levels,data->max_brightness);
ret = of_property_read_u32(node, "default-brightness-level",
&value);
data->dft_brightness = value;
data->max_brightness--;
}
我们重点看pwm_backlight_update_status
和compute_duty_cycle
当向brightness
中写入合法的亮度索引后,就会调用到pwm_backlight_update_status
,其中的变量brightness
就是写入的所用值,如果不为0
的话(为0
的话,会调用pwm_backlight_power_off
关闭背光),就会调用compute_duty_cycle
,这个函数将索引转换为duty_cycle
(在normal
模式下表示的是高电平的持续时间,用来控制占空比),然后调用pwm_config
配置tcmpb
寄存器,实现占空比的改变。
static int pwm_backlight_update_status(struct backlight_device *bl)
{
struct pwm_bl_data *pb = bl_get_data(bl);
int brightness = bl->props.brightness;
int duty_cycle;
/********其中FB_BLANK_UNBLANK是亮屏操作;********/
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & BL_CORE_FBBLANK)
brightness = 0;
if (pb->notify)
brightness = pb->notify(pb->dev, brightness);
if (brightness > 0) {
//计算占空比
duty_cycle = compute_duty_cycle(pb, brightness);
pwm_config(pb->pwm, duty_cycle, pb->period);
pwm_backlight_power_on(pb, brightness);
} else{
//props.brightness为0则关闭显示屏
pwm_backlight_power_off(pb);
}
if (pb->notify_after)
pb->notify_after(pb->dev, brightness);
return 0;
}
static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
{
/*一般情况下这个值都为0*/
unsigned int lth = pb->lth_brightness;
int duty_cycle;
/*pb->levels这个表格就是从dts节点brightness-levels中获取的*/
if (pb->levels)
duty_cycle = pb->levels[brightness];
else
duty_cycle = brightness;
/*duty_cycle * pb->period / pb->scale*/
/*pb->scale为pb->levels数组中的最大值*/
/*pb->period也就是dts节点pwms的第三个参数周期值为25000*/
return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}
函数只是pwm_apply_state()
的封装
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
linux
中pwm
接口位于
参数:
duty_ns
为pwm
最终输出脉冲周期中占空比时间
period_ns
为pwm
最终输出脉冲的周期
static inline int pwm_config(struct pwm_device *pwm, int duty_ns,int period_ns)
{
struct pwm_state state;
if (!pwm)
return -EINVAL;
if (duty_ns < 0 || period_ns < 0)
return -EINVAL;
pwm_get_state(pwm, &state);
if (state.duty_cycle == duty_ns && state.period == period_ns)
return 0;
state.duty_cycle = duty_ns;
state.period = period_ns;
return pwm_apply_state(pwm, &state);
}