exynos4412之PWM
linux kernel version:4.4.38
hardware version:exynos4412-tiny4412 1312B
涉及知识点:DTS,platform bus,PWM, pinctrl,
本次实验使用XpwmTOUT1,使用示波器观察输出波形
首先看硬件电路,LCD4的30脚 XpwmTOUT1,如果PWM正常工作,我们可以在示波器上看到预期的波形。
XpwmTOUT1对应的PCB: XpwmTOUT1/LCD_PWM/GPD0_1,可以知道是GPD0_1,继续找datasheet中GPD0
下图为GPD0CON寄存器的信息,可以看到需要将此寄存器的bit[7:4]设置为0x2,TOUT_1模式,可以使用ioremap的方式进行设置,但是这里我们使用DTS的方式,所以可以使用pinctrl子系统将引脚设置放在DTS中,让系统自动配置。
以下为DTS的信息:compatible是匹配platform driver用的,clock的部分我们使用CLK_PWM座位时钟源(100MHz),PWM寄存器的信息,对PWM电路进行设置使其输出我们预期的波形,pinctrl部分为引脚设置的部分
1 my_pwm { 2 compatible = "ethan,pwm"; 34 clocks = <&clock CLK_PWM>;
5 clock-names = "timers";
6
7 reg = <0x139d00000x100>;
8
9 pinctrl-names = "default";
10 pinctrl-0 = <&pwm1_out>;
11
12 status = "okay";
13 };
其中<&pwm1_out>的详细信息如下:注意2行和3行,就是将GPD0_1设置为0x2,TOUT_1模式,以输出PWM波形
1 pwm1_out: pwm1-out {2 samsung,pins = "gpd0-1";3 samsung,pin-function = <2>;4 samsung,pin-pud = <0>;5 samsung,pin-drv = <0>;6 };
第6行:reg = <0x139d0000 0x100>; 是PWM寄存器的信息,如下图所示
TCFG0和TCFG1:这两个寄存器是预分频时钟源的,设置相应的值,可以将时钟源降频。
TCON:主要控制自动装载,电平翻转,更新计数寄存器和比较寄存器,开始和结束PWM。
TCNTBn:存储计数值
TCMPBn:存储比较值
TCNTOn:存储当前计数值
TINT_CSTAT:中断相关,本文不涉及中断
正文代码如下;主要看probe的部分
28行:获取DTS中reg的信息
34行:获取DTS中clock的信息,并在40行使能时钟源,100MHz
45行:将28行中获取到的reg的信息,进行ioremap,为后续操作PWM电路做准备
53行:将分频器设置为0x7c,也就是124,可将时钟源分频为800KHz
55行:将分频器设置为0x30,可将时钟源分频为100KHz
这样的时钟源,可以在一秒内计数100000次,也即10微秒计数一次
59行:装载计数寄存器器100,即为从100开始递减,等减到和比较寄存器的值一样时翻转电平,减到0即为一个周期。
结合时钟源,就可以计算PWM的输出波形的周期,10微妙计数一次,一个周期计数100次,PWM输出的波形周期即为1毫秒
61行:装载比较寄存器80,计数器从100开始递减,减到80翻转电平。
64~72行:将TCON[9]置为1,把计数寄存器和比较寄存器的值更新到计数器和比较器里面,再清零(不清零PWM无法工作)
75~77行:使能自动装载和电平翻转
80~82行:使能PWM timer1
然后就可以观察示波器的波形
1 #include <linux/err.h>2 #include <linux/gpio.h>
3 #include <linux/fs.h>
4 #include <linux/gpio/consumer.h>
5 #include <linux/kernel.h>
6 #include <linux/leds.h>
7 #include <linux/module.h>
8 #include <linux/of.h>
9 #include <linux/of_gpio.h>
10 #include <linux/of_irq.h>
11 #include <linux/platform_device.h>
12 #include <linux/property.h>
13 #include <linux/slab.h>
14 #include <linux/workqueue.h>
15 #include <linux/interrupt.h>
16 #include <linux/acpi.h>
17 #include <linux/clk.h>
18 #include <linux/delay.h>
19
20void __iomem *pwm_reg_base = NULL;
21staticstruct clk *my_pwm_clock;
22
23staticint hello_probe(struct platform_device *pdev)
24{
25 unsigned int tmp;
26struct resource *mem_res;
27
28 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
29if (mem_res == NULL) {
30 dev_err(&pdev->dev, "Unable to get PWM MEM resource
");
31return -ENXIO;
32 }
33
34 my_pwm_clock = devm_clk_get(&pdev->dev, "timers");
35if (IS_ERR(my_pwm_clock)) {
36 dev_err(&pdev->dev, "Unable to acquire clock "my_pwm_clock"
");
37return0;
38 }
39
40if (clk_prepare_enable(my_pwm_clock)) {
41 dev_err(&pdev->dev, "Couldn"t enable clock "my_pwm_clock"
");
42return0;
43 }
44
45 pwm_reg_base = devm_ioremap_resource(&pdev->dev, mem_res);
46if (IS_ERR(pwm_reg_base)) {
47 dev_err(&pdev->dev, "Couldn"t ioremap resource
");
48 clk_disable_unprepare(my_pwm_clock);
49return0;
50 }
51
52/*input clock = 100MHz / 125 = 800KHz*/
53 writel(0x7c, pwm_reg_base);
54/*input clock = 800KHz / 8 = 100KHz*/
55 writel(0x3 << 4, pwm_reg_base + 0x4);
56
57
58/*time 1 count buffer: 100*/
59 writel(0x64, pwm_reg_base + 0x18);
60/*time 1 compare buffer: 80*/
61 writel(0x50, pwm_reg_base + 0x1c);
62
63
64 writel(0, pwm_reg_base + 0x8);
65/*update timer1 count & compare*/
66 tmp = readl(pwm_reg_base + 0x8);
67 tmp |= (1 << 9);
68 writel(tmp, pwm_reg_base + 0x8);
69
70/*need clear*/
71 tmp &= ~(1 << 9);
72 writel(tmp, pwm_reg_base + 0x8);
73
74/*auto-load and inverter-on */
75 tmp = readl(pwm_reg_base + 0x8);
76 tmp |= (1 << 10) | (1 << 11);
77 writel(tmp, pwm_reg_base + 0x8);
78
79/*enable timer1*/
80 tmp = readl(pwm_reg_base + 0x8);
81 tmp |= (0x1 << 8);
82 writel(tmp, pwm_reg_base + 0x8);
83
84 printk(KERN_ALERT "%s %d success===
", __FUNCTION__, __LINE__);
85return0;
86}
87
88staticint hello_remove(struct platform_device *pdev)
89{
90 clk_disable_unprepare(my_pwm_clock);
91 printk(KERN_ALERT "%s %d success===
", __FUNCTION__, __LINE__);
92return0;
93}
94
95staticstruct of_device_id of_platform_hello_match[] = {
96 { .compatible = "ethan,pwm",},
97 { },
98};
99MODULE_DEVICE_TABLE(of, of_platform_hello_match);
100
101staticstruct platform_driver platform_hello_driver = {
102 .probe = hello_probe,
103 .remove = hello_remove,
104 .driver = {
105 .name = "my_pwm",
106 .of_match_table = of_platform_hello_match,
107 },
108};
109
110module_platform_driver(platform_hello_driver);
111
112 MODULE_AUTHOR("EthanDL");
113 MODULE_DESCRIPTION("platform pwm");
114 MODULE_LICENSE("GPL");
可以看到周期为1ms,20%时翻转电平~
以上是 exynos4412之PWM 的全部内容, 来源链接: utcz.com/z/519737.html