linux pwm 调屏_Linux驱动学习之:PWM驱动

PWM(Pulse Width Modulation)——脉宽调制,它是利用微控制器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量、通信、功率控制与变换等许多领域。

s3c2440芯片中一共有5个16位的定时器,其中有4个定时器(定时器0~定时器3)具有脉宽调制功能,因此用s3c2440可以很容易地实现PWM功能。载有s3c2440芯片的Mini2440 板子带有一个蜂鸣器,它是由 PWM 控制的,下面是它的连接原理图:

操控PWM主要分以下四步:

1、PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。

2、再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器TCFG1配置的divider参数。

3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。因此这一步的关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比。由于s3c2440的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,它会在下个周期开始有效。

4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):启动/终止位,用于启动和终止定时器;手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;输出反转位,用于改变输出的电平方向,使原先是高电平输出的变为低电平,而低电平的变为高电平;自动重载位,用于TCNTn减为零后重载TCNTBn里的值,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。

因此,我们需要在驱动程序中,按照上述的操控序列就可以控制 PWM 的输出频率了。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DEVICE_NAME "pwm" //设备名

#define PWM_IOCTL_SET_FREQ 1 //定义宏变量,用于后面的 ioctl 中的控制命令

#define PWM_IOCTL_STOP 0     //定义宏变量,用于后面的 ioctl 中的控制命令

//定义信号量 lock用于互斥,因此,改驱动程序只能同时有一个进程使用

static struct semaphore lock;

/* freq: pclk/50/16/65536 ~ pclk/50/16

* if pclk = 50MHz, freq is 1Hz to 62500Hz

* human ear : 20Hz~ 20000Hz

*/

//设置 pwm 的频率,配置各个寄存器

static void PWM_Set_Freq( unsigned long freq )

{

unsigned long tcon;

unsigned long tcnt;

unsigned long tcfg1;

unsigned long tcfg0;

struct clk *clk_p;

unsigned long pclk;

//set GPB0 as tout0, pwm output 设置 GPB0 为 tout0,pwm 输出

s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);

tcon = __raw_readl(S3C2410_TCON); //读取寄存器 TCON 到 tcon

tcfg1 = __raw_readl(S3C2410_TCFG1); //读取寄存器 TCFG1 到 tcfg1

tcfg0 = __raw_readl(S3C2410_TCFG0); //读取寄存器 TCFG0 到 tcfg0

//设置TCFG0寄存器,prescaler = 50

tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; // S3C2410_TCFG_PRESCALER0_MASK 定时器 0 和1 的预分频值的掩码,清除TCFG[0~8]

tcfg0 |= (50 - 1); // 设置预分频为 50

//设置TCFG1寄存器,mux = 1/16

tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; //S3C2410_TCFG1_MUX0_MASK 定时器 0 分割值的掩码:清除TCFG1[0~3]

tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; //定时器 0 进行 16 分割

__raw_writel(tcfg1, S3C2410_TCFG1); //把 tcfg1 的值写到分割寄存器 S3C2410_TCFG1 中

__raw_writel(tcfg0, S3C2410_TCFG0); //把 tcfg0 的值写到预分频寄存器 S3C2410_TCFG0 中

clk_p = clk_get(NULL, "pclk"); //得到 pclk

pclk = clk_get_rate(clk_p);

tcnt = (pclk/50/16)/freq; //得到定时器的输入时钟,进而设置 PWM 的调制频率

__raw_writel(tcnt, S3C2410_TCNTB(0)); //PWM 脉宽调制的频率等于定时器的输入时钟,确定一个计数周期的时间长度

__raw_writel(tcnt/2, S3C2410_TCMPB(0)); //占空比是 50%

tcon &= ~0x1f;    //清空低5位,其中:TCON[4] --Dead zone enable, TCON[3] -- Timer 0 auto reload on/off, TCON[2] -- Timer 0 output inverter on/off, TCON[1] -- Timer 0 manual update, TCON[0] -- Timer 0 start/stop      /*

* 0xb: 0000 1011

* disable dead zone, auto reload for Timer 0, output inverter off, Update TCNTB0&TCMPB0, start for Timer 0

*/

tcon |= 0xb;

__raw_writel(tcon, S3C2410_TCON); //把 tcon 写到计数器控制寄存器 S3C2410_TCON 中

tcon &= ~2;   //clear manual update bit

__raw_writel(tcon, S3C2410_TCON);

}

static void PWM_Stop(void)

{

s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); //设置 GPB0 为输出

s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //设置 GPB0 为低电平,使蜂鸣器停止

}

static int s3c24xx_pwm_open(struct inode *inode, struct file *file)

{

if (!down_trylock(&lock)) //是否获得信号量,是 down_trylock(&lock)=0,否则非 0

return 0;

else

return -EBUSY; //返回错误信息:请求的资源不可用

}

static int s3c24xx_pwm_close(struct inode *inode, struct file *file)

{

PWM_Stop();

up(&lock); //释放信号量 lock

return 0;

}

/*cmd 是 1,表示设置频率;cmd 是 2 ,表示停止 pwm*/

static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

switch (cmd) {

case PWM_IOCTL_SET_FREQ: //if cmd=1 即进入 case PWM_IOCTL_SET_FREQ

if (arg == 0) //如果设置的频率参数是 0

return -EINVAL; //返回错误信息,表示向参数传递了无效的参数

PWM_Set_Freq(arg); //否则设置频率

break;

case PWM_IOCTL_STOP: // if cmd=2 即进入 case PWM_IOCTL_STOP

PWM_Stop(); //停止蜂鸣器

break;

}

return 0; //成功返回

}

/*初始化设备的文件操作的结构体*/

static struct file_operations dev_fops = {

.owner = THIS_MODULE,

.open = s3c24xx_pwm_open,

.release = s3c24xx_pwm_close,

.ioctl = s3c24xx_pwm_ioctl,

};

static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};

static int __init dev_init(void)

{

int ret;

init_MUTEX(&lock); //初始化一个互斥锁

ret = misc_register(&misc); //注册一个 misc 设备

printk (DEVICE_NAME"\tinitialized\n");

return ret;

}

static void __exit dev_exit(void)

{

misc_deregister(&misc); //注销设备

}

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("FriendlyARM Inc.");

MODULE_DESCRIPTION("S3C2410/S3C2440 PWM Driver");测设程序如下:

#include

#include

#include

#include

#define PWM_IOCTL_SET_FREQ 1

#define PWM_IOCTL_STOP 2

#define ESC_KEY 0x1b

static int getch(void)

{

struct termios oldt,newt;

int ch;

if (!isatty(STDIN_FILENO)) {

fprintf(stderr, "this problem should be run at a terminal\n");

exit(1);

}

// save terminal setting

if(tcgetattr(STDIN_FILENO, &oldt) < 0) {

perror("save the terminal setting");

exit(1);

}

// set terminal as need

newt = oldt;

newt.c_lflag &= ~( ICANON | ECHO );

if(tcsetattr(STDIN_FILENO,TCSANOW, &newt) < 0) {

perror("set terminal");

exit(1);

}

ch = getchar();

// restore termial setting

if(tcsetattr(STDIN_FILENO,TCSANOW,&oldt) < 0) {

perror("restore the termial setting");

exit(1);

}

return ch;

}

static int fd = -1;

static void close_buzzer(void);

static void open_buzzer(void)

{

fd = open("/dev/pwm", 0);

if (fd < 0) {

perror("open pwm_buzzer device");

exit(1);

}

// any function exit call will stop the buzzer

atexit(close_buzzer);

}

static void close_buzzer(void)

{

if (fd >= 0) {

ioctl(fd, PWM_IOCTL_STOP);

close(fd);

fd = -1;

}

}

static void set_buzzer_freq(int freq)

{

// this IOCTL command is the key to set frequency

int ret = ioctl(fd, PWM_IOCTL_SET_FREQ, freq);

if(ret < 0) {

perror("set the frequency of the buzzer");

exit(1);

}

}

static void stop_buzzer(void)

{

int ret = ioctl(fd, PWM_IOCTL_STOP);

if(ret < 0) {

perror("stop the buzzer");

exit(1);

}

}

int main(int argc, char **argv)

{

int freq = 1000 ;

open_buzzer();

printf( "\nBUZZER TEST ( PWM Control )\n" );

printf( "Press +/- to increase/reduce the frequency of the BUZZER\n" ) ;

printf( "Press 'ESC' key to Exit this program\n\n" );

while( 1 )

{

int key;

set_buzzer_freq(freq);

printf( "\tFreq = %d\n", freq );

key = getch();

switch(key) {

case '+':

if( freq < 20000 )

freq += 10;

break;

case '-':

if( freq > 11 )

freq -= 10 ;

break;

case ESC_KEY:

case EOF:

stop_buzzer();

exit(0);

default:

break;

}

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/541196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2019聊大考研计算机调剂,2019年聊城大学硕士研究生预调剂工作说明

教育部2019年分数线与招生计划尚未确定&#xff0c;根据往年分数线与报考情况&#xff0c;现将我校2019年硕士研究生预调剂工作说明如下&#xff1a;一、调剂要求:(一)只接受学历为本科的考生调剂&#xff0c;应届本科生优先;(二)初试成绩达到报考专业与拟调剂专业教育部A区分数…

SylixOS磁盘高速传输

SylixOS管线模型分析前文主要介绍了SylixOS中的块设备CACHE管理&#xff0c;本章主要介绍磁盘高速传输。在CAHCE回写中SyilxOS采取了两种方式&#xff0c;即直接回写和多管线并发回写。并发写管线通过多线程并发处理CACHE提交的写请求&#xff0c;实现磁盘高速传输。SylixOS中通…

kotlin int最大值_Kotlin程序查找三个数字中的最大值

kotlin int最大值Input 3 integer numbers, we have to find the largest of these input numbers. 输入3个整数&#xff0c;我们必须找到这些输入数字中最大的一个。 Example: 例&#xff1a; Input:First number: 10Second number: 20Third number: 30Output:Largest numbe…

计算机社团活动展望未来,社团展望未来寄语简短,如何写社团未来计划

呵呵&#xff0c;你不会是社团的会长吧&#xff0c;不会写这个的话说不过去了&#xff0c;而且没有你想象的那么复杂&#xff0c;有条理&#xff0c;分类的写下去就可以了&#xff0c;但这里不要太多的口水话&#xff01;再者你可以去问一下其他社团的负责人的&#xff01;&…

判断五个分数等级划分_压力表精度等级怎么算?压力表精度等级划分及检验项目...

压力表是一种典型的轴向精密压力表&#xff0c;是用来测量介质压力的仪表&#xff0c;在工业生产也是常用的仪表&#xff0c;对生产起到了非常重要的作用&#xff0c;压力表的所显示压力的精度&#xff0c;就成了我们非常重要的一个参数。今天小编就来为大家说说压力表精度等级…

stl make_heap_通过使用make_heap()创建堆| C ++ STL

stl make_heapWhat is Heap Data structure? 什么是堆数据结构&#xff1f; Heap is a tree-based which is used for fast retrieval of largest (max heap) or smallest (min heap) element. This DS is used in the priority queue, prims algo, heap sort and many more.…

cad安装日志文件发生错误_苹果电脑Mac os系统重装时出现“准备安装时发生错误”解决方案...

题外话&#xff1a;自己是个电脑小白&#xff0c;因为自己的MacBook Air 不那么流畅了&#xff0c;就按照其他知乎大佬们说的步骤进行重装电脑。&#xff08;2017款 MacBook Air 以下就用Air代替&#xff09; 自己先把电脑重新启动&#xff0c;按照步骤按住commandR&#xff0c…

mac mail 删除邮件服务器,Mac邮件应用程序Mail设置

1、 点击Mail。如果从未使用Mail 设置任何电子邮件账户&#xff0c;则出现“欢迎使用 Mail”页&#xff1b;如果已使用Mail创建电子邮件账户&#xff0c;则在“邮件”菜单上单击“首选项”&#xff0c;在“账户”选项卡上单击导航窗格底部的加号 ()&#xff0c;打开“添加账户”…

sass使用相关报错

1. 移动端一像素边框的缩放&#xff0c;我创建了三个文件&#xff1a;mixin.scss &#xff0c; base.scss&#xff0c;index.scss 在index.scss里面引入全局样式文件&#xff0c; 在base.scss文件中编写 根据媒体查询设置不同的缩放比例&#xff0c;报错如下&#xff1a; media…

client netty 主动发数据_netty案例,netty4.1基础入门篇十一《netty udp通信方式案例Demo》...

小傅哥 | https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获。专注于原创专题案例编写&#xff0c;目前已完成的专题有&#xff1b;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。你…

服务器硬件oid,HPE ProLiant DL580 Gen10 服务器

用可扩展 4U 外形提供可扩展性能HPE ProLiant DL580 Gen10 服务器支持多达四个英特尔至强铂金和金牌处理器&#xff0c;与上一代英特尔 至强 可扩展处理器相比&#xff0c;每个内核的性能提升高达 11%[5]&#xff0c;可借助 4U 可扩展机箱&#xff0c;实现 4P 计算能力。多达 4…

php-对银行卡号做掩码处理

1.实现代码如下/*** 对银行卡号进行掩码处理* param string $bankCardNo 银行卡号* return string 掩码后的银行卡号*/function formatBankCardNo($bankCardNo){//截取银行卡号前4位$prefix substr($bankCardNo,0,4);//截取银行卡号后4位$suffix substr($bankCa…

最长递增子序列 子串_最长递增奇偶子序列

最长递增子序列 子串Problem statement: 问题陈述&#xff1a; Given a sequence of numbers you have to find out the length of the longest increasing odd even subsequence and print the length of the subsequence. The sequence will be maintaining like, (odd ) -&…

echarts 柱状图不显示y坐标轴_Python+matplotlib自定义坐标轴位置、颜色、箭头

图书推荐&#xff1a;《Python程序设计基础与应用》(ISBN&#xff1a;9787111606178)&#xff0c;董付国&#xff0c;机械工业出版社图书详情&#xff1a;用书教师可以联系董老师获取教学大纲、课件、源码、教案、考试系统等配套教学资源。使用Pythonnumpymatplotlib这样的组合…

css3浏览,css3支持哪些浏览器?

CSS3 带来众多全新的设计体验&#xff0c;但有一个问题值得考虑&#xff1a;浏览器对 CSS3 特性的兼容情况如何&#xff1f;因为页面最终离不开用浏览器来渲染&#xff0c;并不是所有浏览器都完全支持 CSS3 的特性。有时花时间写的效果只能在特定的浏览器下有效&#xff0c;这意…

print函数python_带有结束参数的Python print()函数

print函数pythonprint()函数 (print() function) print() function is used to print message on the screen. print()函数用于在屏幕上打印消息。 Example: 例&#xff1a; # python print() function example# printing textprint("Hello world!")print("He…

python各位数字之和为5的数_『Python基础-5』数字,运算,转换

『Python基础-5』数字,运算,转换目录基本的数字类型二进制,八进制,十六进制数字类型间的转换数字运算1. 数字类型Python 数字数据类型用于存储数学上的值&#xff0c;比如整数、浮点数、复数等。数字类型在python中是不可变类型&#xff0c;意思是一个变量被赋予了一个不一样的…

移动游戏加载性能和内存管理全解析 学习

https://v.qq.com/iframe/player.html?vido0512etq2vm&tiny0&auto0 转载于:https://www.cnblogs.com/revoid/p/7039232.html

css 轨道,html-当其他轨道增加时,CSS网格的轨道不会缩...

由于行和列定义中都包含1fr,因此水平和垂直空间受到限制-因此网格项目将平均共享它们.尝试将其更改为自动用于行和列,您可以看到一切正常,但还不完美-请注意,悬停的网格项周围存在空格&#xff1a;.grid--container {height: 100vh;width: 100vw;max-height: 100%;max-width: 1…

带有示例的Python File readline()方法

文件readline()方法 (File readline() Method) readline() method is an inbuilt method in Python, it is used to get one line from the file, the method is called with this object (current file stream/IO object) and returns one line from the file, we can also sp…