一、PWM模式

脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。

PWM本质是一个定时器,ARR寄存器中填充一个最大值,计数器从0计数至ARR,然后再跳变至0开始重新计数。

CCRx寄存器中存放一个阈值,当计数器中的值小于CCRx时,PWM输出低电平,当大于CCRx时输出高电平。

二、用途

可以实现调节LCD的背光度、蜂鸣器的音调、LED的亮度等。

在S5PV210中,PWM定时器的特点如下:

  • 拥有5个PWM定时器,其中,Timer0~Timer3各拥有一个PWM输出Pin。
  • 时钟输入源为APB-PCLK,我在时钟初始化时将PCLK配为66.7MHz,通过一系列的分频,实现预期的tick周期。
  • PWM的每次tick结束都会产生一个内部中断,我们可以在中断处理函数中添加需要处理的功能。

三、内部流程

以Timer0为例(XpwmTOUT0):

输入时钟为PCLK,先经过一个8Bit的预分频,随后在进行一次分频(1/1, 1/2, 1/4, 1/8, 1/16选1个),随后进入核心部分的控制单元(Control Logic0),最后输出PWM波形xPWMTOUT0。其中,核心的部分就在图中的Control Logic的部分。

四、PWM Cycle

先看一个简单的PWM周期,如下图:

首先,上图中的位置2到位置5的部分,成为一个PWM周期。依次列出每个时刻做了什么事情。

1. 设置寄存器TCNTBn=159(50+109), TCMPBn=109, 设置manual-update on,此时,TCNTBn中的值被拷贝到TCNTn中,TCMPBn中的值被拷贝到TCMPn中。

2. 设置manual-update off, 设置start位,表示开始定时,此时,TCNTn开始倒计时,每隔1个tick减一,即:159,、158、157......

3. 当TCNTn中的值从159减少到109时,即:TCNTn的值 == TCMPn的值时,电平跳转,由0变为1。

4. 到TCNTn继续递减为0时,产生内部中断。

5. 又过了1个tick,若此时auto-reload被置位,则TCMPBn和TCNTBn被重新加载到TCNTn和TCMPn中,开始一个新的PWM周期。

上面涉及到几个关键点再次提出:

1. 当我们设定了auto-reload时,只有当TCNTn的值递减为0时,TCMPBn和TCNTBn才会被重新加载到TCNTn和TCMPn中。

2. 一开始,当我们设定完TCMPBn和TCNTBn后,只有将manual-update打开,才能手动将TCMPBn和TCNTBn加载到TCNTn和TCMPn中,随后我们又将manual-update关闭

3. PWM周期开始时,默认的初始电平为低电平,但这是可以通过寄存器修改的。

4. 当TCNTn  == TCMPn时,电平跳转,所以,我们可以通过修改TCMPBn的值,来修改高电平的占空比。

再看一下下面这张图,描述了PWM的double buffer机制:

流程和原理与上面类似,重点看一下开头连续两次的设定:

1. 第一次设定TCNTBn=3,TCMPBn=1,将manual update置1,这样,就会使TCNTn=TCNTBn=3,TCMPn=TCMPBn=1

2. 第二次设定TCNTBn=2,TCMPBn=0,将manual update置0,这样,虽然PWM cycle还没开始,但是第二个PWM周期的值已经被设定好,这就是所谓的double buffer机制。

3. 在第二个PWM周期开始时,auto-reload被置0,因此在第二个PWM结束后,就不会继续reload,就结束了。

五、寄存器

以下均以Timer1为例。

两次分频

TCFG0,0xE250_0000

可以进行1~255的预分频。

TCFG1,0xE250_0004

可以进行1/1, 1/2, 1/4, 1/8, 1/16 分频,或者直接选择SCLK_PWM。

TCNTBn和TCMPBn

TCNTB1, 0xE250_0018

TCMPB1, 0xE250_001C

控制寄存器

TCON, 0xE250_0008

实例

下面的例子是通过Timer1输出的PWM波形来控制蜂鸣器发声,控制的方式有两种,通过用户空间的ioctl操作和文件系统的echo操作,可调节的参数有蜂鸣器的频率、响度、持续时间。

注:蜂鸣器的频率即PCLK分频后的输出频率,响度即PWM的占空比。

/** 蜂鸣器的字符设备驱动* 两种方式打开蜂鸣器* 方法1: 通过ioctl操作/dev/my_beep,代码实例:test/beep_test.c* 方法2: 通过文件系统操作:*             step1:向/sys/devices/virtual/my_beep/my_beep路径下的frequency、volume、sec文件写入参数*             step2:echo 1 > /sys/devices/virtual/my_beep/my_beep/do_beep,来启动蜂鸣器*/#include <linux/delay.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>
#include <plat/regs-timer.h>
#include <plat/clock.h>#define SOUND_FREQENCY_BASE  667volatile int *CLK_GATE_IP3 = NULL;
static dev_t devno;
static struct cdev *beep_dev;
static struct class *beep_class;
static struct device *beep_class_dev;
static unsigned long pclk;
static int beep_irq;
static unsigned long beep_cnt = 0;static int fs_beep_freq;
static int fs_beep_volume;
static long fs_beep_sec;static int beep_on(unsigned int snd_volume, unsigned int snd_freq, unsigned long beep_sec)
{unsigned long tcon;unsigned long tcfg0;unsigned long tcfg1;unsigned long tcntb1;unsigned long tcmpb1;unsigned long freq;/*clear relate bits*/tcon = __raw_readl(S3C2410_TCON);tcfg0 =__raw_readl(S3C2410_TCFG0);tcfg1 =__raw_readl(S3C2410_TCFG1);tcfg0 &= ~0xFF;__raw_writel(tcon, S3C2410_TCFG0);tcfg1 &= ~(0xF << 4);__raw_writel(tcon, S3C2410_TCFG1);tcon &= ~(0xF << 8);__raw_writel(tcon, S3C2410_TCON);/*Timer Input Clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider value}*set prescaler    = 1*set divider = 2*so , input clock frequency = 66.7MHz / 2 / 2 = 16.675MHz*/tcfg0 |= 0x01;__raw_writel(tcfg0, S3C2410_TCFG0);tcfg1 |= 1 << 4;__raw_writel(tcfg1, S3C2410_TCFG1);/*enable auto-reload, set TCNTB1 & TCMPB1, then disable manual-update*/tcon |= 1 << 11;tcntb1 = 101 * SOUND_FREQENCY_BASE * (10 - snd_freq);tcmpb1 = snd_volume * SOUND_FREQENCY_BASE * (10 - snd_freq);freq = pclk/4/(tcntb1+1);beep_cnt = freq * beep_sec;printk("beep frequency = %ldHz \n" , freq);__raw_writel(tcon, S3C2410_TCON);__raw_writel(tcntb1, S3C2410_TCNTB(1));__raw_writel(tcmpb1, S3C2410_TCMPB(1));/*enable manual-update and then disable manual-update, let TCNTB1 and TCMPB1 load to TCNT1 and TCMP1*/tcon |= 1 << 9;__raw_writel(tcon, S3C2410_TCON);tcon &= ~(1 << 9);__raw_writel(tcon, S3C2410_TCON);/*start timer*/tcon |= 1 << 8;__raw_writel(tcon, S3C2410_TCON);return 0;
}static int beep_off(void)
{unsigned long tcon;tcon = __raw_readl(S3C2410_TCON);tcon &= ~(1 << 8);tcon &= ~(1 << 9);__raw_writel(tcon, S3C2410_TCON);return 0;
}static int beep_dev_open(struct inode *inode, struct file *file)
{/*open函数中没什么要做的,在此我们打印PCLK(pwm输入时钟)的频率*/struct clk *clk_p;clk_p = clk_get(NULL, "pclk");pclk = clk_get_rate(clk_p);printk("pclk rate : %ldHz\n", pclk);return 0;
}static int beep_dev_release(struct inode *inode, struct file *file)
{/*stop beep*/
//    beep_off();return 0;
}/** snd_lvl: 1 ~ 100* snd_freq: 0 ~ 9*/
static int beep_dev_ioctl(struct file *file, unsigned int beep_on_off, unsigned int (*beep_args)[])
{unsigned int snd_lvl = (*beep_args)[0];unsigned int snd_freq = (*beep_args)[1];unsigned long beep_sec = (*beep_args)[2];if(beep_on_off > 0){if(snd_lvl < 1 || snd_lvl > 100){printk("error: sound volume should from 1 ~ 100 !\n");return -1;}if(snd_freq < 0 || snd_freq > 9){printk("error: sound frequency level should from 0 ~ 9 !\n");return -1;}if(beep_sec <= 0){printk("error: beep seconds should > 0 !\n");return -1;}printk("snd_lvl = %d, snd_freq= %d\n", snd_lvl, snd_freq);beep_on(snd_lvl, snd_freq, beep_sec);}elsebeep_off();return 0;
}static irqreturn_t my_beeq_irq_handler(int irq,void *dev_id,struct pt_regs *regs)
{beep_cnt--;int tint_cstat = __raw_readl(S3C64XX_TINT_CSTAT);tint_cstat |= 1 << 6;__raw_writel(tint_cstat, S3C64XX_TINT_CSTAT);if(beep_cnt == 0)beep_off();return (IRQ_HANDLED);
}static size_t show_beep_frequency(struct device *dev, struct device_attribute *attr, char *buf)
{ssize_t ret = 0;sprintf(buf, "%d (value:0~9)\n", fs_beep_freq);ret = strlen(buf) + 1;return ret;
}static size_t store_beep_frequency(struct device *dev, struct device_attribute *attr, char *buf, size_t len)
{int freq = (unsigned int)simple_strtoull(buf, NULL, 10);if(freq < 0 || freq > 9){printk("error: sound frequency level should from 0 ~ 9 !\n");return -1;}fs_beep_freq = freq;return len;
}
static DEVICE_ATTR(frequency, 0666, show_beep_frequency, store_beep_frequency);static size_t show_beep_volume(struct device *dev, struct device_attribute *attr, char *buf)
{ssize_t ret = 0;sprintf(buf, "%d (value:1~100)\n", fs_beep_volume);ret = strlen(buf) + 1;return ret;
}static size_t store_beep_volume(struct device *dev, struct device_attribute *attr, char *buf, size_t len)
{int volume = (unsigned int)simple_strtoull(buf, NULL, 10);if(volume < 1 || volume > 100){printk("error: sound volume should from 1 ~ 100 !\n");return -1;}fs_beep_volume = volume;return len;
}
static DEVICE_ATTR(volume, 0666, show_beep_volume, store_beep_volume);static size_t show_beep_sec(struct device *dev, struct device_attribute *attr, char *buf)
{ssize_t ret = 0;sprintf(buf, "%d (value: > 0)\n", fs_beep_sec);ret = strlen(buf) + 1;return ret;
}static size_t store_beep_sec(struct device *dev, struct device_attribute *attr, char *buf, size_t len)
{unsigned long sec = simple_strtoull(buf, NULL, 10);if(sec <= 0){printk("error: beep seconds should > 0 !\n");return -1;}fs_beep_sec = sec;return len;
}
static DEVICE_ATTR(sec, 0666, show_beep_sec, store_beep_sec);static size_t fs_do_beep(struct device *dev, struct device_attribute *attr, char *buf, size_t len)
{int is_beep = (unsigned int)simple_strtoull(buf, NULL, 10);if(is_beep > 0){if(fs_beep_freq < 0 || fs_beep_freq > 9|| fs_beep_volume < 1 || fs_beep_volume > 100|| fs_beep_sec < 1){printk("error: condition not match! \n");return len;}struct clk *clk_p;clk_p = clk_get(NULL, "pclk");pclk = clk_get_rate(clk_p);beep_on(fs_beep_volume, fs_beep_freq, fs_beep_sec);}return len;
}
static DEVICE_ATTR(do_beep, 0222, NULL, fs_do_beep);static struct file_operations beep_ops =
{.owner = THIS_MODULE,.open = beep_dev_open,.release = beep_dev_release,.unlocked_ioctl = beep_dev_ioctl
};static int tq210_beep_init(void)
{int major;unsigned long tint_cstat;/*确保PWM的clock gating打开*/CLK_GATE_IP3 = (int *)ioremap(0xE010046C, 4);*CLK_GATE_IP3 |= 1 << 23;/*设置gpio控制寄存器*/s3c_gpio_cfgpin(S5PV210_GPD0(1), S3C_GPIO_SFN(2));/*不使用上拉下拉电阻*/s3c_gpio_setpull(S5PV210_GPD0(1), S3C_GPIO_PULL_NONE);/*下面是创建、添加一个字符设备*/if(alloc_chrdev_region(&devno, 0, 1, "my_tq210_beep") < 0){printk("alloc_chrdev_region 'my_beep' fail ! \n");return -1;}major = MAJOR(devno);beep_dev = kmalloc(sizeof(struct cdev), GFP_KERNEL);cdev_init(beep_dev, &beep_ops);beep_dev->owner = THIS_MODULE;if(cdev_add(beep_dev, devno, 1) < 0){printk("cdev_add 'beep_dev' fail ! \n");return -1;}/*创建设备节点*/beep_class = class_create(THIS_MODULE, "my_beep");beep_class_dev = device_create(beep_class, NULL, MKDEV(major, 0), NULL, "my_beep", 0);if (device_create_file(beep_class_dev, &dev_attr_frequency) < 0){printk("error: create attr frequency error!\n");return -1;}if (device_create_file(beep_class_dev, &dev_attr_volume) < 0){printk("error: create attr volume error!\n");return -1;}if (device_create_file(beep_class_dev, &dev_attr_sec) < 0){printk("error: create attr seconds error!\n");return -1;}if (device_create_file(beep_class_dev, &dev_attr_do_beep) < 0){printk("error: create attr do_beep error!\n");return -1;}/*打开中断,请求中断*/tint_cstat = __raw_readl(S3C64XX_TINT_CSTAT);tint_cstat |= 1 << 1;tint_cstat |= 1 << 6;__raw_writel(tint_cstat, S3C64XX_TINT_CSTAT);beep_irq = request_irq(IRQ_TIMER1, my_beeq_irq_handler, IRQF_DISABLED, "my_beep", NULL);if(beep_irq < 0){printk("request timer 1 irq (no = %d) fail!\n", IRQ_TIMER1);return -1;}printk("my tq210 beep init finish! \n");return 0;
}static void tq210_beep_exit(void)
{cdev_del(beep_dev);kfree(beep_dev);unregister_chrdev_region(devno, 1);device_unregister(beep_class_dev);class_destroy(beep_class);free_irq(beep_irq, NULL);
}module_init(tq210_beep_init);
module_exit(tq210_beep_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("chuck_huang / huangch28@sina.com");
MODULE_DESCRIPTION("PWM for beep driver");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>int main(int argc, char *argv[])
{if (argc != 5){printf("arg format:\n\argv[1]: 0-> sound off / 1-> sound on\n\argv[2]: sound volume, from 1 to 100\n\argv[3]: sound frequency level, from 0 to 9\n\argv[4]: beep secounds\n\e.g.: beep_test 1 50 5 2\n");return -1;}int fd;int beep_args[3];beep_args[0] = atoi(argv[2]);beep_args[1] = atoi(argv[3]);beep_args[2] = atoi(argv[4]);fd = open("/dev/my_beep", O_RDWR);if(fd < 0){printf("open /dev/my_beep error ! \n");return -1;}/** ioctl (*file, int cmd, int (*beep_args)[2])* cmd: 0-> sound off / 1-> sound on* (*beep_args)[0] : snd_lvl : from 1 to 100* (*beep_args)[1] : snd_freq : from 1 to 10* (*beep_args)[2] : beep_sec : from 1 to 10*/ioctl(fd, atoi(argv[1]), &beep_args);close(fd);return 0;
}

PWM脉冲宽度调制(一)相关推荐

  1. 微雪树莓派PICO笔记——3.PWM(脉冲宽度调制)

    文章目录 什么是PWM PWM的应用 RP2040 PWM框架图 PWM流程图 PWM内部框架图 [MicroPython]machine.PWM类函数详解 代码实现 什么是PWM 脉冲宽度调制 (P ...

  2. 蓝桥杯模块学习9——PWM脉冲宽度调制(深夜学习——单片机)

    一.什么是PWM脉冲宽度调制: 对PWM简单理解(佛科院--深夜学习)_佛科院深夜学习的博客-CSDN博客 二.脉冲宽度调制实验: 1.代码思路: (1)我们可以利用定时器规定周期为10ms(100H ...

  3. 拓展模块使用教程和心得(四):PWM脉冲宽度调制及普通有刷马达和空心杯电机(测试平台:STC8A8K,STM32F103)

    成就更好的自己 这次内容是给未来的新专栏(控制算法)打一个小基础,而且是为了完善上一期拓展模块教程三:步进电机的内容(https://blog.csdn.net/qq_36098477/article ...

  4. 嵌入式STM32入门之定时器控制LED闪烁与产生PWM脉冲宽度调制信号

    定时器控制LED闪烁与产生PWM脉冲宽度调制信号 一.前言 二.定时器基本介绍 (一)STM32定时器 (二)通用定时器主要功能 (三)计数器模式 (四)定时器工作原理 三.实验(1)初识定时器 (一 ...

  5. 启明智显分享| ESP32学习笔记参考--PWM(脉冲宽度调制) 篇,配PWM控制 LED呼吸灯代码示例参考

    提示:启明智显专为智能产品提供一站式彩屏显示+连接+云端服务+APP软件开发.维护等解决方案,帮厂商快速实现硬件的智能化.作为启明云端旗下方案公司,我们用心整理了开发小伙伴在开发过程中可能会遇到的问题 ...

  6. 脉冲宽度调制 matlab,关于PWM脉冲宽度调制的点滴总结

    基本原理 PWM的全称是脉冲宽度调制(Pulse-width modulation),是通过将有效的电信号分散成离散形式从而来降低电信号所传递的平均功率的一种方式: 所以根据面积等效法则,可以通过对改 ...

  7. PWM脉冲宽度调制技术控制LED亮度

    1.PWM控制 PWM控制--脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要的波形.PWM是一种对模拟信号电平进行数字编码的方法.通过高分辨率计数器的使用,方波的占空比被调制用来 ...

  8. PWM脉冲宽度调制——它是什么?

    脉冲宽度调制 (PWM) 的良好定义就在名称本身.为了更好地理解 PWM 是什么,让我们首先看一些基本术语. 微控制器是基于二进制信号的智能数字元件.二进制信号的最佳表示是方波(高低电平).下图解释了 ...

  9. PWM脉冲宽度调制,实现呼吸灯_领航者开发板

    1.脉冲宽度调制(Pulse Width Modulation,PWM) 仅从本次实验(呼吸灯)的角度进行解释:通过不断调节信号的占空比来改变LED灯的亮度,如占空比逐渐递增会使得LED逐渐变亮,占空 ...

最新文章

  1. 希尔排序算法实现思想个人理解
  2. js Object.keys()
  3. 镜像浏览器_Docker 企业级私有镜像仓库 Harbor 部署
  4. [ARM异常]-图解armv7/armv8的异常向量表和基地址
  5. luogu_1002 过河卒
  6. 不用正则表达式,用javascript从零写一个模板引擎(一)
  7. 当CNI遇上Kata-KataNative的CNI扩展
  8. Python使用exec自动生成代码并执行,同时得到返回的变量
  9. Editplus 的配色方案
  10. SQLServer 使用sp_repldone标识所有未分发的事务为已分发
  11. 中国各地高考难度地图:最难的省份不出所料!
  12. 主成分分析法(PCA方法)计算OBB包围盒
  13. 泰克Tektronix示波器软件TDS210|TDS220|TDS224上位机软件NS-Scope
  14. hdu5020 Revenge of Collinearity 求三点共线的点对个数
  15. ctfshow密码easyrsa5
  16. Photoshop - 批量处理(以批量修改图片像素为例)
  17. 一个微信小程序的案例
  18. rstudio 导出结果_R语言数据导入与导出
  19. 时艳强对话Ricky Ng:交易平台新势力 引领行业新变革
  20. 如何使用ArcGIS从天地图中提取水系

热门文章

  1. JAXWS CXF Spring + MyEclipse + Maven + Tomcat Byron自學視頻02
  2. emacs 启动页面定制
  3. 中国大学MOOC课程《程序设计入门——C语言》 第8周编程练习
  4. 联想Y7000如何切换性能模式
  5. C# 定时任务 调度框架 WebWork (Quartz.NET) Web版的Windows服务
  6. 模型量化论文阅读#1----综述:A Survey of Quantization Methods for Efficient Neural Network Inference
  7. tornado完成一个简单的登录界面/图片的上传
  8. matlab 多项式相减,matlab多项式计算与数据处理
  9. python 大智慧接口_大智慧股票本地数据读取接口(含源码)
  10. microbit积木块菜单图标