x210-2023-04-18

1、在定时器选值过程中,为了尽量减少公式记忆加快直觉判断,算出来2014Hz说明要数2014次就可以得到1s,算出来625000Hz说明要数625000次就可以得到1s,但是这里625000Hz一般不会按s来使用,而是按us使用,所以通过看到625000Hz时要能直接得到大概是多少us才行,625000Hz=0.625MHz,也就是数0.625次就可以得到1us,但是定时器不可能只数0.几次,最少也要1次,所以这里要能得到定时器数够1次耗时多长,也就是0.625次*2=1.25次,此时能满足至少1次的要求了,同时也可以得出定时器数够至少1次需要耗时1us*2=2us;上面只是加快了根据Hz数直接得到s/us的直觉判断,但是定时器还有一个关键因素,那就是分频系数,定时单位为s为什么通常直接选最大分频?定时us为什么通常直接选最小分频?一般估算时只是代入分频系数能够算到一个频率值,但是对于得到的频率值无法知道是更偏向s还是更偏向us,譬如上面2014Hz、625000Hz,值越小越偏向s值越大越偏向us这样记忆?时间一长记忆容易出错以及混乱,但是有一个普遍直觉是,频率越快时间越短,所以频率数值更大的625000Hz其定时单位更偏向us,也就是可以通过频率值数字的大小直接推测大概单位;如果这时分频系数都选好之后算出来定时器的(参考)频率为66000Hz(即0.066MHz),要满足至少一次的要求,则要0.066*100,目的是将第一个6剥离出来,但是还不够,因为需要接近1次所以除以6,即0.066*100/6=6.6/6=1.1次,于是按上面最开始的思路可以得出定时器数够至少1次需要耗时为1us*(100/6)≈1us*(16)=16us,这时如果给出初始值和重装载值为1000(次),那就很容易得到定时器总的耗时时间大约为16us*1000=16000us=16ms,这些计算里如果纯脑力计算不依靠纸笔的话,相对麻烦的也只是算100/6了,想要全部通过心算完成的话是可以接受的,但是这里要说的是另一个问题,因为有些场合并不是要定时器这段总的耗时时间,而是要这段总耗时时间对应的频率,譬如蜂鸣器这种以频率为指标的器件(至于为什么要的是频率,是因为大部分定时器同时具有PWM输出功能,如果单纯用作定时器功能,那么只有当倒计时为0之后触发定时器中断,而用作PWM输出时,一般还需要设置极性和比较值,极性最重要的一点是负责初始输出电平是高还是低,但是只有当计数值递减到和比较值相同时才翻转电平,翻转电平是芯片自动完成的,假如在初始值和重装载值为1000次的情况下,设定了比较值为500,那么高低电平的比例为1:1,即高电平数500次:低电平数500次,或者低电平数500次:高电平数500次,所以这里也看出如果不设定极性就会得到两种情况,但是总的来说,这1000次就构成了方波的一个周期,而通过这一个周期的总耗时就能反映频率,为什么?因为频率是指一秒内有多少个重复的周期,这也就是在问一秒内有多少个16ms,那不就刚好是在求频率吗?1s/16ms),回到原问题,先拿上面2014Hz为例,由于数够2014次就可以得到1s,所以每数一次需要耗时(1/2014)s,如果给出的初始值和重装载值也为1000次,那么总共耗时为(1000/2014)s(那这里我们就应该直接使用背诵来的时间和频率是倒数这个关系,进而得到该段总耗时对应的频率为(2014/1000)Hz=2.014Hz吗?如果实际应用多了之后,这种依赖背诵来的算术关系很影响大脑计算的连贯性,特别是这种需要颠倒用倒数的时候),既然已经得到1000次总耗时为(1000/2014)s,也就是说方波的一个周期的总耗时就是(1000/2014)s,那么再根据频率的含义:一秒内有多少个重复的周期,也就是一秒内有多少个(1000/2014)s,可以得到1s/[(1000/2014)s],分子分母的s互相抵消,得到最终次数,所以结果为(2014/1000)Hz=2.014Hz,通过推算的过程得到最有利用价值的结论是,总耗时对应的频率可以通过定时器频率直接除以计数值(也就是上面提的初始值和重装载值)而快速得到,简单点说也就是将计数值直接作为分母进行计算,就可以快速估算总耗时对应的频率值,例如上面定时器频率为66000Hz、计数值为1000次的情况下,这1000次数完所对应的频率值就是66000/1000=66Hz。

2、按键事件发生后,一般会在read接口(驱动层)中使用copy_to_user()上报按键键值数据到用户空间,如果应用层通过一直开着read来等待按键事件发生,这明显是不应该的,但是应用层又不知道底层什么时候会发生按键事件,所以这时就需要有一个东西来控制同步,在32按键中断中一般会使用一个flag,进入中断置一,然后应用程序main中轮询该flag是否被置位了,如果还没置位那就去干其它事情先,到了linux这,实现相同机制的这个东西就是等待队列,用等待队列来解决“应用层通过一直开着read来等待按键事件发生”这个问题需要考虑到应用层read也是具有一定程度的随机性的,也就是你不能限制用户在应用层使用read的时机,所以这也就说明了应用层read很可能就read一次,而不是像32那样的main可以一直轮询标志位flag,所以在应用层第一次read时,驱动层相应的read接口在开头处就需要做好等待队列的准备,即调用wait_event_interruptible(),这样一来,应用层即使只read一次而且在读的那一时刻还没有碰上key中断发生,在往后的时间里也不用担心错过中断了。

3、在使用source insight的search下的replace...虽然可以方便快速地对框架代码按照新工程名称一键替换所有变量以及自定义函数名称,但是也容易出现某些非必要的替换,例如下图头文件的platform_device.h被替换成了platform_driver.h导致编译出错,所以只能在一键替换全部之后,最好着重观察一下头文件所在区域是否出现黄色高亮(因为source insight修改内容之后会在行号左侧显示黄色高亮),如果有那么就需要注意检查可能出现了非必要替换,这时将其改回再保存即可。

 4、在《第十二天 01 platform之pwm实现》的0:35:53中所用DEFINE_RES_MEM()在2.6.35版本kernel下查找过但不存在,但是由于思路是一致的,只是resource数组元素内容被DEFINE_RES_MEM()平替了,所以还是可以按照.start、.end、.flags逐个赋值这种相对原始的方式进行实现。

5、在《第十二天 01 platform之pwm实现》的1:50:50处实现pwm输出频率可在应用层传值来修改的功能,如果只按视频所说在SET_CNT执行过程仅重新赋值TCNTB2和TCMPB2,经过实际测试发现并不能使蜂鸣器响,还需要加上开启定时器的操作语句才行(即下图130、131两行),另外,如果执行完初始化进来用户首先使用的是前两个命令,但是由于第二条命令是关闭定时器,所以这时再继续执行第三条命令SET_CNT(这时还没有加入开启定时器的操作语句)那么还是会存在无法使蜂鸣器鸣叫的漏洞;再说回这次调试自己出现的错误,因为没有注意到TCNTB2和TCMPB2是32bit的,开始时只清除了前16bit(即下图124和126行,之所以只清了16bit是由于图方便所以直接使用了前面配置初始化时的语句,然后改造时重点放在了后面赋值的修改而忽略了检查前半句)导致值没能完全写入导致不能鸣叫,排查时通过打印应用层和驱动层ioctl的传入和接收值,发现都一样说明不是传递过程时出错,为了更方便排查,直接将cmd_data变量名换成设置值,也就是0x101d0,但发现还是不响,这时开始排查逻辑错误了,也就是找到了前面说的缺少开启定时器语句,为了保险起见,还把手动更新(也就是下图128和129两行)也加了进来(后面已经测试正常之后有专门测试过这里的影响,在测试的头一次可能会出现值没有修改过来的感觉,在后面测试播放音乐时也出现了第一次不能播放的现象,虽然在初始化过程已经有这个操作但是按理论说应该尽量保留该操作,即手动更新位开合一下以协助写入,但最重要的还是不能缺少开启定时器语句),但是发现还是不对(这时其实已经能响了但是并没有按新赋值所对应的频率来响),所以这时想起要仔细对比一下TCNTB2和TCMPB2的手册描述和124、126两行操作了,这时开始注意到了要写入数据0x101d0是20bit的,但是发现到只清除了16bit,最后将0xffff换成0xffffffff将32bit都清除这样写入的值就完全是要设置的0x101d0。

 

posted @ 2023-04-18 16:02  migui  阅读(20)  评论(0编辑  收藏  举报