您的位置:首页 > 运维架构 > Linux

AT91SAM9G45 PWM应用

2013-04-07 22:52 344 查看
最近在玩AT91SAM9G45的板子, 发现Linux系统下的PWM输出频率仅有100Hz. 通常情况下PWM频率可调是个必要功能, 现在和大家分享一下实现方法.

 

环境: AT91SAM9G45 +linux-3.5

 

步骤1:

make menuconfig配置内核, 开启PWM输出功能.

Device Drivers --->
Misc devices  --->
<*>Atmel AT32/AT91 PWM support
[*] LEDSupport  --->
<*>   LED Support usingAtmel PWM outputs


步骤2:

修改arch/arm/mach-at91/board-sam9m10g45ek.c

static struct gpio_ledek_pwm_led[] = {
#ifdefined(CONFIG_LEDS_ATMEL_PWM) || defined(CONFIG_LEDS_ATMEL_PWM_MODULE)
{  /* "right" led, green, userled1, pwm1 */
.name           = "d7",
.gpio           = 1<< AT91_PWM1,    /* is PWM channel number */
.active_low     = 1,
.default_trigger    = "none",
},
#endif
};


----------------------------------------

修改.gpio成员指定PWM通道, AT91SAM9G45一共有四个PWM通道.

 

步骤3:

编译内核, 下载烧写. 如果一切顺利, 输入下列命令即可从PD31输出越100Hz的方波.

echo127 > /sys/class/leds/pd7/brightness


占空比为127/256 ≈ 49.6%

 

步骤4:

为了修改PWM输出频率, 我们现在分析下驱动源码.

打开驱动文件"drivers/leds/leds-atmel-pwm.c",找到pwmled_probe()函数, 重点关注下面几条语句

tmp = 5;
if (!led->active_low)
tmp |= PWM_CPR_CPOL;
pwm_channel_writel(&led->pwmc,PWM_CMR, tmp);

/*
* Pick a period so PWM cycles at 100+ Hz; anda multiplier
* for scaling duty cycle:  brightness * mult.
*/
tmp = (led->pwmc.mck / (1 << 5))/ 100;
tmp /= 255;
led->mult = tmp;
pwm_channel_writel(&led->pwmc,PWM_CDTY,
led->cdev.brightness * 255);
pwm_channel_writel(&led->pwmc,PWM_CPRD,
LED_FULL * tmp);


--------------------------------

a.led->pwmc.mck = 133333333, 外部总线时钟

b.标蓝部分设置的是PWM模块的时钟分频系数, 参照手册PWM_CMR设置得知tmp=5(二进制0101)为MCK/32

c.tmp = (led->pwmc.mck / (1 << 5))/ 100;

这里的100为将要设置的目标频率. 如果分频系数为5, 那么最大可达到的目标频率约为16kHz.

 

好的, 我们已经找到了这两个关键的设置点, 下面以设置PWM输出38kHz为例, 计算参数

CPRD= 总线时钟/分频系数/目标频率

 


由上表可以看出, 分频系数2的小数部分最小, 误差也最小, 为最优选择. 但最终的目标频率不是精确的38kHz, 有误差的哟请务必注意.

 

代码如下

tmp = 2;
if (!led->active_low)
tmp |= PWM_CPR_CPOL;
pwm_channel_writel(&led->pwmc,PWM_CMR, tmp);

/*
* Pick a period so PWM cycles at 100+ Hz; anda multiplier
* for scaling duty cycle:  brightness * mult.
*/
tmp = (led->pwmc.mck / (1 << 2))/38000;
tmp /= 255;
led->mult = tmp;
pwm_channel_writel(&led->pwmc,PWM_CDTY,
led->cdev.brightness * 255);
pwm_channel_writel(&led->pwmc,PWM_CPRD,
LED_FULL * tmp);
------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  AT91SAM9G45 linux PWM