CSR8675的学习笔记:驱动正交编码器
2018-02-19 23:40
211 查看
1. 引言
项目需要使用正交编码器作为音量旋钮。当旋钮顺时针旋转一格,音量即增大一格;当旋钮逆时针旋转一格,音量即减小一格。2. 正交编码器工作原理
正交编码器又名增量式编码器或光电式编码器,用于检测旋转运动系统的位置和速度。下图是增量式正交编码器的实物图:从上图可以看出,正交编码器的左下角有三个引脚,分别是A、B、C三个端子,其等效电路如下:
正交编码器的C端子需接地,A、B端子需接5V上拉电阻。当用户旋转正交编码器时,A、B端子的输出信号的时序图如下(图中红色波形代表A端子、黄色波形代表B端子):
上图4个脉冲分别对应用户旋转编码器的4个基本动作:
第一个脉冲代表顺时针旋转1格
第二个脉冲代表逆时针旋转1格
第三个脉冲代表顺时针旋转未到1格又逆时针转回至起始位置
第四个脉冲代表逆时针旋转未到1格又顺时针转回至起始位置
3. 解码器工作原理
解码器由采样、量化两个基本步骤组成。3.1. 采样
假设用户旋转正交编码器所能产生的脉冲的最高频率是fsfs,可通过香农定理得知解码器的采样频率最低应不小于2fs2fs,此时可以捕捉到用户的动作1、2。为了捕捉到动作3、4,即需捕捉每个脉冲的2个边沿变化,则需要解码器的采样频率达到2fs∗2=4fs2fs∗2=4fs以上。3.2. 量化
对章节2中的输出信号时序图进行4倍频采样,可以得到如下一组数据(省略同值样点):Signal | S01 | S02 | S03 | S04 | S05 | S06 | S07 | S08 | S09 | S10 | S11 | S12 | S13 | S14 | S15 | S16 | S17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
B(yellow) | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 |
A(red) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
其中红色方框数据对应图上红线超前黄线变化前后的采样值,代表顺时针旋转;蓝色方框对应图上黄线超前红线变化前后的采样值,代表逆时针旋转。
程序上只需用定时器以4fs4fs为频率采样A、B两端子,并在每次采样时将本次采样值连同上次采样值与上述8组值相匹配,即可确定当前的旋转方向和旋转格数。比如本次采样值为B=A=1,上次采样值为B=0、A=1,与S02、S03相匹配,则判定开始顺时针旋转1格;当本次采样值为B=A=0,上次采样值为B=1、A=0时,与S04、S05相匹配,则判定顺时针旋转1格结束。
再观察8组值的特征,有如下几个特点:
PreA ≠ PreB
CurA = CurB
顺时针旋转:CurA = PreA
逆时针旋转:CurA ≠ PreA
PreA:上一次采样时A端子的值
PreB:上一次采样时B端子的值
CurA:本次采样时A端子的值
CurB:本次采样时B端子的值
在编程时利用上述特点可大幅简化程序。
4. 解决方案
4.1. 外部解码芯片+CSR8675
Proto阶段用Cypress的CY8C40xx实现了驱动正交编码器的功能,方案框图如下:上图中左侧为正交编码器的等效电路。当用户旋转正交编码器时,其A、B两引脚输出正交编码信号。CY8C40xx内部的正交解码器单元支持将正交编码信号转换成旋转方向和旋转格数,通过UART串口发送给CSR8675。CSR8675接收到此数据后将旋转方向和格数转换成音量增大或减小操作。
串口数据格式很简单,如下:
序号 | 命令名称 | 数据格式 |
---|---|---|
1 | 音量增[x]格 | 0x55, 0x01, [x], 0xaa |
2 | 音量减[x]格 | 0x55, 0x02, [x], 0xaa |
case 0x55: /* cypress uart message */ DEBUG_QUAD_ENC(("QUAD: recv msg %x, %x, %x\n", receiveData[1],receiveData[2],receiveData[3])); if (receiveData[3]) { for (ucIdx = 0; ucIdx < receiveData[2]; ucIdx++) { if (receiveData[1] == 0x01) { MessageSend(&theSink.task, EventUsrMainOutVolumeUp, 0); } else if (receiveData[1] == 0x02) { MessageSend(&theSink.task, EventUsrMainOutVolumeDown, 0); } } } else { DEBUG_QUAD_ENC(("QUAD: msg not support\n")); } break;
4.2. 单CSR8675
上述方案的优点是实现起来简单可靠,支持最多4倍频计数。缺点是成本较高、PCB占用面积大、系统集成度低。为了克服上述方案的缺点,用CSR8675的DSP驱动正交编码器是一个很好的方案,框图如下:
高通释放的ADK中包含一个DSP驱动正交编码器的库(rotary_enc.asm)。这个库实现的解码器也分采样和量化两部分。采样是通过捕捉PIO的电平边沿变化触发一个定时事件,在定时事件到来时采集PIO的电平,只支持2倍频率采样。这种机制的好处是降低中断事件的次数,坏处是当程序执行到临界区时无法捕捉电平边沿变化,导致丢失用户动作,手感不佳。
在库代码的基础上,改用以2ms为固定采样周期的定时采样机制,不仅克服了上述缺点,还实现了4倍频率采样。解码器的采样、量化和消息发送都在都在定时器中断服务程序里完成,代码如下:
.MODULE $M.rotary_enc.service_routine; .CODESEGMENT ROTARY_ENC_SERVICE_ROUTINE_PM; .DATASEGMENT DM; $rotary_enc.service_routine: // push rLink onto stack $push_rLink_macro; r0 = M[$PIO_IN]; r1 = 0x800; r2 = 0x80; // read PIO 11 r3 = 1; null = r0 AND r1; if Z r3 = 0; // read PIO 7 r4 = 1; null = r0 AND r2; if Z r4 = 0; // get last sample pio value r1 = M[$pio_a_last_value]; r2 = M[$pio_b_last_value]; // save current pio value M[$pio_a_last_value] = r3; M[$pio_b_last_value] = r4; // check if pre and cur value is match, // otherwise register timer event // pre pio A != pio B null = r1 - r2; if Z jump register_event; // cur pio A = pio B null = r3 - r4; if NZ jump register_event; // check rotate direction // 1: rotation, -1; anti-rotation // cur = pre pio A r0 = 1; null = r3 - r1; if Z r0 = -1; // send rotation number r2 = $KALIMBA_MSG_ROTARY_ENCODER_RETURN_ID_1; r3 = r0; r4 = 1; call $message.send; register_event: // fire a timer in 'MAX_BOUNCE_US' time r1 = &$timer_struc; r2 = 1000; r3 = &$rotary_enc.service_routine; call $timer.schedule_event_in; // pop rLink from stack jump $pop_rLink_and_rts; .ENDMODULE;
VM收到此消息后,将旋转格方向和旋转数转换成音量控制消息。
if (dspind->id == KALIMBA_MSG_ROTARY_ENCODER_RETURN_ID_1) { ENC_DEBUG(("HS : rotary encoder 1 [val: %x]\n", dspind->value[0])); if (dspind->value[0] < 10) { MessageCancelAll(&theSink.task, EventUsrMainOutVolumeUp); MessageSend(&theSink.task, EventUsrMainOutVolumeUp, 0); } else if (dspind->value[0] > 0xfff0) { MessageCancelAll(&theSink.task, EventUsrMainOutVolumeDown); MessageSend(&theSink.task, EventUsrMainOutVolumeDown, 0); } }
MessageCancelAll函数的目的是防止太多同类消息堆积,耗尽系统资源。
5. 总结
CSR8675驱动正交编码器的方案硬件成本低、可支持多组编码器、消耗的RAM资源少,实测响应速度快、不丢脉冲、不影响音频播放,是蓝牙平台深度开发的又一成果。从另一个角度说,CSR8675的芯片架构设计值得认真学习。这种架构将快速、实时的任务交给DSP,将慢速、非实时任务交给VM,实现了系统的高效率运行。当然这种架构对开发人员的能力提出了更高的要求,这也不断鞭策我们努力提升自己的业务水平,向着更深更难处进军。
相关文章推荐
- AM335x(TQ335x)学习笔记——GPIO关键驱动移植
- LCD驱动学习笔记
- 【学习笔记】DM9000裸机驱动(一)
- Linux驱动学习笔记----------IIC框架与流程(一)
- input子系统学习笔记九 evdev输入事件驱动分析
- JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接
- [linux驱动]linux块设备学习笔记(二)
- nginx 源码学习笔记(二十一)—— event 模块(二) ——事件驱动核心ngx_process_events_and_timers
- Linux驱动学习笔记之触摸屏驱动
- 领域驱动设计学习笔记
- VxWorks BSP学习笔记_串口驱动
- Android深度探索:HAL与驱动开发学习笔记--并发控制之原子操作
- davinci sd卡驱动学习笔记(五)
- 境界篇:linux 驱动开发的境界(学习笔记)
- LINUX驱动学习笔记一
- 韩顺平 javascript教学视频_学习笔记27_dom对象(window对象3.history.location.navigator.screen.event)_js事件驱动编程
- linux驱动学习笔记7
- EEPlat学习笔记---元数据驱动(三)
- [linux驱动]proc学习笔记(一)