zynq ttc pwm例子
一:ttc pwm
想要使用ttc 产生pwm,需要将ttc使能主控驱动,并自己添加pwm控制驱动,比如想通过呕吐提出产生pwm控制背光
二:设备树修改
&ttc0 { //主控驱动ttc。此部分是官方内核支持的驱动,drivers\pwm\pwm-cadence.cstatus = "okay";#pwm-cells = <3>;
};usr-backlight {compatible = "pwm-bl-beep"; //backlight标签,用于设备树匹配pwms = <&ttc0 0 5000000>; //背光访问的pwm模块,ttc0,0通道,频率5000000brightness-levels = <0 4 8 16 32 64 128 255>; //背光等级支持的档位default-brightness-level = <6>; //默认背光等级status = "okay"; //设备节点状态
};
从drivers\pwm\pwm-cadence.c 内核源码知道,ttc设置pwm相关配置,采用static const struct pwm_ops ttc_pwm_ops = {
.apply = ttc_pwm_apply,
.get_state = ttc_pwm_get_state,
};
因此我们可以猜测自己的驱动应该会用到apply 和get_state相关的操作接口。因此可以查看:include\linux\pwm.h,有以下几个接口:
在新版本中需要使用
int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state);
//中断中使用
int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state);
//一般不用
int pwm_adjust_config(struct pwm_device *pwm);另外我们可以查看drivers\video\backlight\pwm_bl.c 驱动,可以看到pwm控制部分使用了pwm_apply_might_sleep和pwm_get_state。可以得出我们自己的驱动例子如下:可以根据例子改为beep驱动等
三:驱动例子
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/backlight.h>
#include <linux/pwm.h>
#include <linux/of.h>
#include <linux/of_device.h>// 复用pwm_bl.c中的私有数据结构(定义与通用逻辑一致)
struct pwm_backlight_data {struct pwm_device *pwm;const u32 *levels; // 亮度等级数组(从设备树获取)unsigned int num_levels; // 等级数量unsigned int brightness; // 当前亮度
};// 核心:更新背光亮度(复用pwm_bl的逻辑,适配PWM接口)
static void pwm_backlight_update(struct backlight_device *bl)
{struct pwm_backlight_data *data = bl_get_data(bl);struct pwm_state state;unsigned int brightness = bl->props.brightness;u64 duty_cycle;// 1. 获取当前PWM状态(周期、极性等)pwm_get_state(data->pwm, &state);if (brightness == 0) {// 关闭背光:禁用PWMstate.enabled = false;} else {// 2. 映射亮度等级到占空比(0~255 → 0~period)if (brightness >= data->num_levels)brightness = data->num_levels - 1;u32 level = data->levels[brightness]; // 取当前等级值(如128)// 占空比 = (level / 255) * 周期(保持周期不变)duty_cycle = (u64)state.period * level / 255;state.duty_cycle = clamp_val(duty_cycle, 0, state.period);state.enabled = true;}// 3. 应用PWM状态(根据内核版本选择接口)#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)pwm_apply_might_sleep(data->pwm, &state); // 新接口#elsepwm_config(data->pwm, state.duty_cycle, state.period); // 旧接口if (state.enabled)pwm_enable(data->pwm);elsepwm_disable(data->pwm);#endif
}// 背光操作集(绑定到通用框架)
static const struct backlight_ops pwm_bl_ops = {.update_status = pwm_backlight_update, // 核心更新函数};// 驱动探测函数(解析设备树,初始化资源)
static int pwm_backlight_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct device_node *np = dev->of_node;struct pwm_backlight_data *data;struct backlight_properties props;struct backlight_device *bl;int ret;// 1. 分配私有数据data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;// 2. 解析设备树:亮度等级数组ret = of_property_count_u32_elems(np, "brightness-levels");if (ret <= 0) {dev_err(dev, "missing brightness-levels\n");return -EINVAL;}data->num_levels = ret;data->levels = devm_kcalloc(dev, data->num_levels, sizeof(u32), GFP_KERNEL);if (!data->levels)return -ENOMEM;ret = of_property_read_u32_array(np, "brightness-levels", data->levels, data->num_levels);if (ret) {dev_err(dev, "failed to read brightness-levels\n");return ret;}// 3. 获取PWM设备(从设备树pwms属性)data->pwm = devm_of_pwm_get(dev, np, NULL);if (IS_ERR(data->pwm)) {dev_err(dev, "failed to get PWM: %ld\n", PTR_ERR(data->pwm));return PTR_ERR(data->pwm);}// 4. 初始化背光属性memset(&props, 0, sizeof(props));props.type = BACKLIGHT_RAW; // 原始亮度(直接映射PWM占空比)props.max_brightness = data->num_levels - 1; // 最大等级索引// 5. 解析默认亮度等级ret = of_property_read_u32(np, "default-brightness-level", &data->brightness);if (ret)data->brightness = 0; // 默认关闭data->brightness = clamp_val(data->brightness, 0, props.max_brightness);props.brightness = data->brightness;// 6. 注册背光设备bl = devm_backlight_device_register(dev, dev_name(dev), dev, data, &pwm_bl_ops, &props);if (IS_ERR(bl)) {dev_err(dev, "failed to register backlight\n");return PTR_ERR(bl);}// 7. 初始化亮度(应用默认值)backlight_update_status(bl);platform_set_drvdata(pdev, data);dev_info(dev, "PWM backlight initialized (levels: %u)\n", data->num_levels);return 0;
}// 设备树匹配表(与usr-backlight的compatible匹配)
static const struct of_device_id pwm_bl_of_match[] = {{ .compatible = "pwm-bl-beep" },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pwm_bl_of_match);// 平台驱动结构体
static struct platform_driver pwm_backlight_driver = {.driver = {.name = "pwm-bl-beep",.of_match_table = pwm_bl_of_match,},.probe = pwm_backlight_probe,
};
module_platform_driver(pwm_backlight_driver);MODULE_AUTHOR("cccc");
MODULE_DESCRIPTION("Zynq TTC PWM Backlight Driver");
MODULE_LICENSE("GPL");