CSR8675的学习笔记:驱动正交编码器

Posted NiceBT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSR8675的学习笔记:驱动正交编码器相关的知识,希望对你有一定的参考价值。

为了方便大家学习,现与我爱蓝牙网联合推出【QCC300x/CSR867x/QCC30xx/QCC51xx开发板】

技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–

1. 引言

项目需要使用正交编码器作为音量旋钮。当旋钮顺时针旋转一格,音量即增大一格;当旋钮逆时针旋转一格,音量即减小一格。

2. 正交编码器工作原理

正交编码器又名增量式编码器或光电式编码器,用于检测旋转运动系统的位置和速度。下图是增量式正交编码器的实物图:

从上图可以看出,正交编码器的左下角有三个引脚,分别是A、B、C三个端子,其等效电路如下:
正交编码器的C端子需接地,A、B端子需接5V上拉电阻。当用户旋转正交编码器时,A、B端子的输出信号的时序图如下(图中红色波形代表A端子、黄色波形代表B端子): 上图4个脉冲分别对应用户旋转编码器的4个基本动作:

  • 第一个脉冲代表顺时针旋转1格
  • 第二个脉冲代表逆时针旋转1格
  • 第三个脉冲代表顺时针旋转未到1格又逆时针转回至起始位置
  • 第四个脉冲代表逆时针旋转未到1格又顺时针转回至起始位置

3. 解码器工作原理

解码器由采样、量化两个基本步骤组成。

3.1. 采样

假设用户旋转正交编码器所能产生的脉冲的最高频率是 f s fs fs,可通过香农定理得知解码器的采样频率最低应不小于 2 f s 2fs 2fs,此时可以捕捉到用户的动作1、2。为了捕捉到动作3、4,即需捕捉每个脉冲的2个边沿变化,则需要解码器的采样频率达到 2 f s ∗ 2 = 4 f s 2fs*2=4fs 2fs2=4fs以上。

3.2. 量化

对章节2中的输出信号时序图进行4倍频采样,可以得到如下一组数据(省略同值样点):

SignalS01S02S03S04S05S06S07S08S09S10S11S12S13S14S15S16S17
B(yellow)00110110001001110
A(red)01100011011100100

上表数据分组如下: 其中红色方框数据对应图上红线超前黄线变化前后的采样值,代表顺时针旋转;蓝色方框对应图上黄线超前红线变化前后的采样值,代表逆时针旋转。 程序上只需用定时器以 4 f s 4fs 4fs为频率采样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

CSR8675的串口接收程序很简单,如下:

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,实现了系统的高效率运行。当然这种架构对开发人员的能力提出了更高的要求,这也不断鞭策我们努力提升自己的业务水平,向着更深更难处进军。

以上是关于CSR8675的学习笔记:驱动正交编码器的主要内容,如果未能解决你的问题,请参考以下文章

CSR8675项目实战:BlueHiFi蓝牙音乐收发器

CSR8675项目实战:BlueHiFi蓝牙音乐收发器

CSR8675项目实战:BlueBrowsing蓝牙播放器

CSR8675项目实战:BlueAg蓝牙一拖二发射器

CSR8675项目实战:BlueEarphone 左右声道各10个Speaker EQ

关于stm32的正交解码