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";

3

4 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

回到顶部