龙华网站公司,ps网页模板,wordpress可以放视频吗,室内设计平面图案例引子#xff1a;使用OK6410 通过 IMU_EV30 采集 ADIS16405 的数据#xff0c;采集频率为100Hz#xff0c;需要10ms的定时器。首先考虑了a POSIX per-process timer#xff0c;后发现板子负担轻的时候还行#xff0c;负担重了定时很不精确#xff0c;最后使用PWM定时器解决…引子使用OK6410 通过 IMU_EV30 采集 ADIS16405 的数据采集频率为100Hz需要10ms的定时器。首先考虑了a POSIX per-process timer后发现板子负担轻的时候还行负担重了定时很不精确最后使用PWM定时器解决了问题。现总结一下。
1、a POSIX per-process timer
include signal.h // 信号 includetime.h includesys/time.h
timer_t timer; //定时器 a POSIX per-process timer timer_t其实是个long型
struct sigevent evp; // 事件,定时器到期时的事件 memset(evp, 0, sizeof(evp)) evp.sigev_notify SIGEV_THREAD; SIGEV_NONE什么都不做只提供通过timer_gettime和timer_getoverrun查询超时信息。 SIGEV_SIGNAL: 当定时器到期内核会将sigev_signo所指定的信号传送给进程。 SIGEV_THREAD: 当定时器到期内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程并且让它执行sigev_notify_function传入sigev_value作为一个参数。 evp.sigev_notify_function writeCom; //线程函数:void writeCom (union sigval v) evp.sigev_value.sival_int 3; //作为,函数:void writeCom (union sigval v) 的参数
ret timer_create(CLOCK_REALTIME, evp, timer); // create a POSIX per-process timer CLOCK_REALTIME :Systemwide realtime clock. 实时时钟 CLOCK_MONOTONIC:Represents monotonic time. Cannot be set. CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer. CLOCK_THREAD_CPUTIME_ID :Thread-specific timer. CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME. CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC 如果evp为NULL那么定时器到期会产生默认的信号对 CLOCK_REALTIMER来说默认信号就是SIGALRM。如果要产生除默认信号之外的其它信号程序必须将 evp-sigev_signo设置为期望的信号码。struct sigevent 结构中的成员evp-sigev_notify说明了定时器到期时应该采取的行动。
struct itimerspec ts; //定义时间间隔 ts.it_interval.tv_sec 0; ts.it_interval.tv_nsec 10000000; //纳秒 e-9s ts.it_value.tv_sec 3; //定时器初始值 ts.it_value.tv_nsec 0; ret timer_settime(timer, TIMER_ABSTIME, ts, NULL); //配置并开启 如果flags的值为TIMER_ABSTIME则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。 如果ovalue的值不是NULL则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态则此结构的成员全都会被设定成0。
2、PWM定时器
需要设置PWM寄存器的值寄存器的相关说明s3c6410手册中很详细这里重点说一下实现过程。先来看一下需要解决的问题 1. 如何对寄存器进行操作 2. 如何注册中断函数 3. 前两个问题不能在用户程序中解决那么就有了第3个问题如何编写类似驱动的东西即内核模块开发 其实不想一下子学习这么多但是没办法一个定时器引出了这么多问题。
2.1 内核模块的框架
详见文章《Linux编译内核模块来自国嵌的视频教学》(http://blog.csdn.net/leaglave_jyan/article/details/6652435)
2.2 读写寄存器
static unsigned long PWM_Base_addr; PWM_Base_addr (unsigned long)ioremap(ox7F006000, ox44); // 将ox7F006000 开始的 ox44 个字节映射到内存将映射后的地址赋给PWM_Base_addr
例如对于寄存器 TCON
volatile unsigned int *pTCON (volatile unsigned int *)(PWM_Base_addr ox08); 使用 *pTCON oxFFFFFFF0; 对其进行操作
2.3 配置并开启定时器
2.3.1 分频
不考虑使用外部时钟的情况定时器使用的时钟为PCLK开发板启动时会在终端显示PCLK的频率 PCLK会通过经过两次分频降低频率供定时器使用。 第一级分频使用8位时钟预定标器进行预分频对于s3c6410定时器0和定时器1共用一个预定标器定时器2、3、4共用第二个预分频后的输出将会进入第二级分频器。 第二级的分频器是针对每个定时器的并不像预分频存在共用的情况但是对于每个定时器第二级的分频只有5种选择2分频、4分频、8分频、16分频和使用外部时钟 定时器输入时钟频率 PCLK/(预定标器值1)/(第二级的分频值) 预定标器1和预定标器2的值由寄存器TCFG0设定 第二级分频由寄存器TCFG1设定
2.3.2 定时器工作过程 转自http://blog.csdn.net/luoamforever/article/details/5483772
1、设定TCMPBn、TCNTBn两个寄存器它们表示定时器n的比较值初始计数值 2、启动定时器n通过设置TCONTCMPBn、TCNTBn的值被装入TCMPn、TCNTn中在定时器n的工作频率下TCNTn开始减1计数其值可以通过TCNTOn寄存器读取 3、当TCNTn的值等于TCMPn的值时定时器n的输出管脚TOUTn反转TCNTn继续减1计数 4、当TCNTn的值到达0时其输出管脚TOUTn再次反转并触发定时器n的中断如果中断使能 5、如果在TCON寄存器中将定时器n设为“自动加载”则TCMPBn和TCNTBn寄存器的值被自动装入TCMPn和TCNTn中开始下一个计数流程。
2.3.3 相关代码
使用定时器0经读TCON寄存器发现定时器4在使用着 *pTCFG0 | ox7C; // 定时器0 预分频设为124 *pTCFG1 | ox03; //定时器0 8分频分频后 PCLK/(1241)/8 65500Hz *pTCNTB0 664; // 定时间隔 10ms 特别注意是664不是665好多地方没讲到一开始使用665一直觉得差一点结果发现了定时器的频率为 65500/(6641)。 *pTCON | ox02; // 启动手动更新 *pTCON 0xFFFFFFFD; //关闭手动更新 *pTCON | ox09; // 启动自动更新 *pTINT_CSTAT | 0x01; //定时器0中断使能
2.4 中断处理函数注册
ret request_irq(IRQ_TIMER0, timer_handl, IRQF_DISABLED,Timer_0,NULL); IRQ_TIMER0 定时器0 的中断号Timer_0 定时器名称。 * IRQF_DISABLED - keep irqs disabled when calling the action handler * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator * IRQF_SHARED - allow sharing the irq among several devices * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur * IRQF_TIMER - Flag to mark this interrupt as timer interrupt * IRQF_PERCPU - Interrupt is per cpu * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is * registered first in an shared interrupt is considered for * performance reasons) static irqreturn_t timer_handl(int irq, void *dev_id, struct pt_regs *regs); // 中断处理函数 中断程序的返回值是一个特殊类型——irqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONE和IRQ_HANDLED。 IRQ_NONE中断程序接收到中断信号后发现这并不是注册时指定的中断原发出的中断信号 IRQ_HANDLED接收到了准确的中断信号,并且作了相应正确的处理。
irqreturn.h 中有如下宏定义 typedef int irqreturn_t #define IRQ_NONE (0) #define IRQ_ HANDLED (1)
中断处理程序断处理程序不能访问用户空间地址访问用户空间的代码都是可能导致睡眠的因为没人保证用户空间的页面就在内存中可能被交换出去了这样在临界区当然不能访问
2.5 设备注册和添加
#define MYTIMER_MAJOR 235 static stuct file_operations timer_fops { .ower THIS_MODULE, .ioctl timer_ioctl, }; // 这里仅实现了一个函数ioctl ()在函数ioctl ()中实现对定时器寄存器的操作和定时中断的注册虽然这没有open但是可以调用open打开设备并且始终会成功打开。
dev_t devno MKDEV(MYTIMER_MAJOR, 0); ret register_chrdev_region(devno, 1 ,timer);
struct cdev cdev; sruct class *PWM_Class; cdev_init(cdev, timer_fops); cdev.owner THIS_MODULE; cdev.ops timer_fops; cdev_add(cdev, devno, 1);
PWM_Class class_create(THIS_MODULE;, PWMTimerclass); device_create(PWM_Class, NULL, MKDEV(MYTIMER_MAJOR, 0), NULL, timer%d, 0);// 这里的 timer%d 将会出现在开发板 /dev 目录下即/dev/timer0
相反的操作
device_destroy(PWM_Class, MKDEV(MYTIMER_MAJOR, 0),); class_destroy(PWM_Class); cdev_del(cdev); unregister_chrdev_region(MKDEV(MYTIMER_MAJOR, 0), 1);