做客

昨天爸爸的同事请我们全家去他家做客,这是我第一次上日本人的家里。他家里有两个女儿,大女儿读小学二年级,小女儿才两岁。爸爸说他一想到我再过一年多就会和那小女儿一样活蹦乱跳,就抑制不了激动。

我是被公认的皮肤白,长得十分可爱,所以也免不了被好好夸奖一番。我当然也不能辜负这样的夸奖,所以昨天我一直表现得都非常乖。他家里玩具很多,搞得我眼花缭乱,家里的那几个我早就玩厌了,所以我对他们的玩具十分感兴趣。等我回武汉了,我也得弄一大堆玩具好好玩玩。

姑奶奶回国

时间过得真快,转眼姑奶奶来日本就快半年了。今天姑奶奶已经坐上了回国的新鉴真号轮船。我一大早也起来为姑奶奶送行。这半年来,从我还在娘胎内开始,姑奶奶就无微不至地照顾着我。今天两次从轮船上打来的电话,第一句都是询问我的情况。
 
姑奶奶这一回国,爸爸妈妈的负担可加重了不少,特别是妈妈白天一个人在家照顾我,会很辛苦的。为了配合妈妈的工作,所以今天我表现得非常乖。

4个月体检

4个月体检胜利结束,几乎一切正常。就是有一样,医生说我腿比较硬,张得不够开,让我去专门的医院做进一步的检查,看看是不是有毛病。妈妈说大概是日本小孩做婴儿体操做得多些,所以比我的软一些。关于这一点,等我做了检查之后再来报告。

打疫苗的时候我没有哭,就只是眼眶里充了几滴眼泪,然后我就靠吃手指把疼痛忍过去了。可不像其她孩子那样,大哭大闹,完全没有淑女的形象。

目前我的身材是:6690克,63.4厘米,属于较苗条型。我虽然不愿意和别人进行攀比,不过除了头发比别人少了些,我对我自己的形象还是很满意的。
 
顺便说一句,2月16日妈妈带我去了医院检查腿,结果一切正常。

元宵节

转眼狗年过了半个月,今天正好是元宵节,我也已经成长到4个月了。妈妈和姑奶奶一致夸奖我从新年开始就突然变得特别乖,说即使姑奶奶回国了,妈妈一个人也应该能够带好我的。姑奶奶下个星期二就要走了,说起来我真是舍不得,所以当然要表现得乖些,留个好印象。
 
昨天爸爸弄了个摄像机,给我摄了像。在明石大桥下,我戴着顶红色的西瓜帽,显得特别出众。姑奶奶会把这段录像带回武汉给爷爷奶奶看,我想他们看到网络摄像头中的我都已经高兴得合不拢嘴,如果看到这段录像的话... ...
 
明天要去区役所进行四个月体检,还要打疫苗,明天再报告一下成绩吧。

DMA的自动初始化

RY DSP开发 2006/02/28

最近一个老项目的程序出了问题,在某种特定的情况下声音输出会中断。这个项目使用的芯片是C5416,声音输出使用的是DMA4。经过调试发现声音 中断之后,DMCTR4寄存器的数值为初始化时的值:0x007f。这说明DMA4根本没有传输数据到McBSP的DX寄存器中去。这个问题我遇到过很多 次,原因如下:DMA4的传输的启动由McBSP控制,当McBSP的DX寄存器中的数据被McBSP读取之后, McBSP通知DMA传输下一个数据。如果DMA初始化时DX寄存器已经被读取,那么DMA就永远等不到McBSP的通知了,这样DMA就停止了。更具体 一点,让我们考虑DMA传输完当前传输块的最后一个数据之后,DSP内都会发生些什么情况:

1. 传输块的最后一个数据被DMA复制到McBSP的DX寄存器中
2. DMA向DSP提出中断
3. DSP设置DMA的下一个传输块
4. DX中的数据被McBSP输出,并且McBSP向DMA提出通知
5. DMA传输刚刚被设置好的传输块中的第一个数据到DX中

上面是正常运作时的情况,由于McBSP的传输是非常快的,例如如果是传输双声道44.1kHz的声音信号的话,那么McBSP一秒钟传输88.2k个数据,每两个数据之间的间隔大约是0.00001秒。也就是说,上面的从步骤1到步骤4的时间间隔必须小于这传输时间。
当DSP繁忙的时候,有时不能够立即响应DMA的中断,则很可能出现如下情况:

1. 传输块的最后一个数据被DMA复制到McBSP的DX寄存器中
2. DX中的数据被McBSP输出,并且McBSP向DMA提出通知
3. 由于DMA的DMCTR寄存器为0,DMA不向DX传输数据
4. DMA向DSP提出中断
5. DSP设置DMA的下一个传输块
6. DMA永远等待McBSP的传输通知

所以,DMA停止传输的根本原因是DSP无法及时地更新DMA的设置。这是一个普遍的问题,因此TI公司早就有解决方案了,解决办法就是使用DMA的自动初始化。
设置了自动初始化之后,DMA在传输完毕时直接读取自动初始化寄存器中的设置,设置好下一个传输块。采用自动初始化之后,就完全地解决了上述问题:

1. 传输块的最后一个数据被DMA复制到McBSP的DX寄存器中
2. DMA复制自动初始化寄存器的设置到本身的工作寄存器中 (本步骤不需要DSP介入)
3. DX中的数据被McBSP输出,并且McBSP向DMA提出通知
4. DMA传输刚刚被设置好的传输块中的第一个数据到DX中
5. DMA向DSP提出中断
6. DSP设置DMA的下一次自动初始化的设置

我 们看到,DSP响应DMA中断的时刻已经不重要了,只要在当前传输块传输完之前能够设置好自动初始化寄存器即可。C54x系列的某些芯片是多个DMA共享 一组自动初始化寄存器,在使用的时候请查阅相关芯片的参考手册。C55x系列的自动初始化功能有很大的改进,请参见我以前的文章:C5510DSK例子程 序的DMA设置问题。

用Vpython显示Blender设计的3D模型

vpython(http://www.vpython.org)是python的一个实时3D开发库。它十分简单易用,用户可以抛开OpenGL那样复杂的命令,把注意力集中到模型和动画的设计上,因此用它可以快速地开发实时3D演示程序。

Blender(http://www.blender3d.org )是一个开源的3D设计软件,功能强大,短小精悍。
本文介绍如何用vpython读取用blender设计好的3D模型。

Vpython除了提供一些立方体、圆柱、球体等基本形体之外,还提供了faces函数用于建造复杂的模型。手工写模型的点、面以及颜色等信息几乎是不可能的,因此需要3D设计软件设计好模型之后,再用程序转换为vpython的faces。

Blender 可以把模型输出为许多格式,其中的directX(.x)格式最接近于vpython的faces构造,因此我决定用directX(.x)格式连接 blender和vpython。由于vpython只支持3点构成的面,而blender设计的模型可能有4点构成的面,因此在输出模型之前,应该先把 模型的所有4点面转换为3点面,blender的菜单中直接就有这个功能。

剩下的就是写个小python程序读取.x文件中的信息,转换为faces所需要的格式了。Vpython中定义的面是单向的,只能从一个方向看到它,如果需要建造双向面的话,就构造两个3点面,它们的点重合,但是点的顺序相反:p1,p2,p3和p1,p3,p2。

另外关于模型的材质颜色,由于blender输出的文件按照材质的名称排列材质,而面所使用的材质信息却是按照blender内部的材质添加顺序进行的。因此直接转换材质信息会造成材质错位,为了解决这个问题,需要手工调整材质的顺序。

采用上述办法,我用vpython实现了一个实时汽车3D演示程序。这段程序和Labview的实时计算部分之间用TCP/IP通信,获取汽车各个部分的位置信息。

Frequency Shifter (频率偏移器)

RY DSP开发 2006/02/16

在阅读啸叫消除算法的文章中,提到一种办法叫做frequency shift。所谓frequency shift就是把输入信号的频率增加或者减少df,这样一来在音箱和麦克风之间就不可能出现某个固定的频率的增益总是大于1,因此也就消除了啸叫声。下面 来简单看看frequency shift的算法。

假设我们的输入信号是频率为f的正弦波:sin(Wf*t),其中Wf=2*PI*f。我们想通过某种算法将其频率增加df。也就是说输出的信号应该是:sin(Wf*t+Wdf*t)。这个问题不太困难,马上就让我们回想起高中的三角公式:

sin( a + b) = sin(a)*cos(b) + cos(a)*sin(b)
sin(Wf*t+Wdf*t) = sin(Wf*t) * cos(Wdf*t) + cos(Wf*t) * sin(Wdf*t) (1)

其中,sin(Wf*t)是输入信号,cos(Wdf*t)和sin(Wdf*t)是我们可以产生的信号,就只有cos(Wf*t)需要计算。

那 么下面的问题就是如何通过sin(Wf*t)计算出cos(Wf*t)。这种变换叫做Hilbert transformer。Hilbert transformer将输入信号的相位延时90度,而振幅不变。精确的Hilbert transformer是不可能的,可以用FIR滤波器来近似,滤波器的系数可以用matlab来计算,也可以到这个地址来计算系数:http://www-users.cs.york.ac.uk/~fisher/mkfilter 。经过FIR滤波器之后产生的信号会产生延时,因此需要把原始信号sin(Wf*t)也经过相同的延时之后,再用公式(1)进行计算。
我们知道任何信号都可以表达成许多正弦波的叠加,因此上面频率偏移算法对于任意的波形都是适用的。

由 于这种偏移是固定的,也就是说对于所有的频率都偏移df,因此得到的结果会破坏原始声音信号的成分,听上去会怪怪的,音乐上的变调是不能用 Frequency Shifter来实现的。因为变调是把所有的频率都扩大或缩小某个固定的值a,也就是把sin(Wf*t)变成sin(a*Wf*t)。

Labview无法实现精确实时

当我把读取模拟输入信号和控制电动机的程序和实时运算程序做到一块儿去的时候,问题出现了。实时计算5000次大约需要5400ms,也就是说无法 实现1ms运算一次。经过profile的分析,我发现实际上CPU并没有满负荷运转。而是因为读取模拟信号平均需要1ms时间,如果这1ms的时间是无 法被打断的,也就是说无法被强占的话,那么实时运算就无法在这1ms之内运行。在上述运算中,大约读取模拟输入信号800次,其中400次左右导致实时运 算程序被延时。因此5000次的循环花了5400ms。目前我没有找到很好地解决办法,只能把实时运算的while循环修改为timed loop,并且设置它当被延时的时候,不忽略周期。这样一来5000ms能计算5000次,但是每两次的计算之间的间隔是变动的,或大于或小于1ms,平 均下来是每1ms运算一次,也不知道这样是否满足要求。

从串口读数据的程序和实时运算是放在一个循环中的,为了保证每次读取的时候都是缓存 中最新的数据,所以我的程序读串口缓存中最后一个数据包并把剩下的未满一个数据包的数据传到下一次循环中,并把这些数据和下一次读取的数据连接起来,然后 在其中再找最后一个完整的数据包,如此反复。由于每两次读取的间隔可能小于1ms,就有可能在相隔很短的两次循环中读不到完整的数据包,这种情况下只能用 上一次读取的数据。这段复杂的逻辑花了我一上午的时间才调试出来。

和主机之间的TCP/IP通信由于速度要求不高,大约1秒钟发20次数据即可,因此我想对实时运算影响不大。

Labview实时控制系统开发

最近要用labview做一个实时控制系统,它实时地读取两种数据,一个是模拟电压信号,一个是从RS422串口以460.8kbps的速度读取的 数据,然后把这两个数据提交给C语言写的计算内核进行计算,输出数据用来控制电机,以及在另外一台电脑上实现3D动画演示。整个系统要求一秒钟计算 1000次。

实时数据采集、运算以及控制部分的程序在NI公司的realtime target上运行,3D动画演示在主机的windowsXP系统下运行。主机和realtime target之间使用网线相连,最终可用采用TCP/IP通信。
在配置realtime target的时候,遇到了不小的麻烦,其原因为NI-motion的驱动程序和串口的驱动程序相互冲突,升级到NI-motion之后问题解决。

实时处理程序也遇到了一些问题。

串 口传送的数据包由15个字节组成,其中包括1个字节的开始符、结束符和命令符,其余的12个字节表示6组数据。由于串口的传输速度和实时处理循环的速度不 一致(大约一次循环会接收到2.5组数据),因此需要对串口接收到的数据进行同步和裁剪。这样复杂的计算如果用labview的图形语言来做的话,对我来 说简直就是一个灾难,幸好有个脚本VI,可以执行类似c语言的小程序。于是我就写了一个小程序能够每次读取串口接收缓存中的最后一组数据(时间上最近的一 组),这个程序能够自动通过开始符和结束符对数据包进行同步,使得数据不至于错位。

串口数据处理子模块中用到了6个演算VI来计算 x1*256+x2这样简单的算式,也就是说一秒钟计算6000次。结果程序无法实时运行,需要花6.5秒才能运行5000次。当我把演算VI改为原始的 乘法和加法VI之后,程序就可以实时运行了。看来Labview无法优化演算VI,还是的用最麻烦的配线来加快速度。

为了能够重复试验,需 要实时地把接收到的数据和结果储存到文件中。但是在实时处理循环中如果添加文件操作的话,就会影响到实时性。这类问题NI的资料说需要用“多线程+队列” 来实现,可是我多方尝试也未能实现实时储存文件。我让实时运算循环把需要写入文件的数据存放到一个队列中,然后用一个低优先级的循环读出队列中的数据并保 存到文件中。但是似乎在写文件的时候,实施运算的线程似乎无法抢占CPU,这样若文件操作超过1ms的时间,实施运算就只好被延迟了。万般无奈,最后我的 解决办法是在实时运算的时候把结果保存到内存中,实时处理结束之后一下子把内存中的数据全部写入文件。反正内存够大,储存几百兆的数据不成问题。

虽然目前还没有开发到与主机的通信部分,但是我担心TCP/IP会不会和写文件一样,不能被高优先级的线程打断,这样一来如果TCP/IP的执行时间大于1ms的话,实时运算又会受影响了。
由于Labview已经提供了许多可用的VI,所以开发这样的程序速度还是很快的,但是若真要实现点什么算法,我觉得还是C语言来得上手。