第一节 Linux休眠唤醒
设备休眠模式,是指设备通过固定的操作,或手动或自动地进入省电模式,挂起或者冻结程序、外设甚至cpu,从而进入一种待机状态,使设备在这种待机状态下尽可能少地消耗电量和功耗,从而满足家用手持设备对待机时长的性能要求。设备在进入休眠状态之后需要能够通过固定的操作进行唤醒,这个唤醒操作使设备能快速地恢复到休眠之前的状态,并正常工作。
首先,关于省电模式,Linux内核支持三种省电模式:standby、STR(suspendto ram)、STD(suspendtodisk),几乎所有的方案都只支持STR模式(也有同时支持standby模式的),但是在使用电池供电的手持设备时,由于STD模式需要有交换分区的支持,而我们的嵌入式设备一般文件系统中是不提供交换分区的,所以这类设备上的Linux都没有支持STD省电模式。在设备中如果已经有对电源管理PM模块的支持,我们就可以查看设备支持的省电模式类型,查看方法是输入命令“cat/sys/power/state”。
先从内核设置开始说起,关于内核的配置,在整个调测过程中是以海思Hi3516AV100R001C01SPC030的SDK提供的内核版本为基础,内核版本号是3.4.35,内核源码中有提供默认的配置文件:hi3516a_full_defconfig,在menuconfig中查看powermanagement options,配置中电源管理配置默认是打开的。
从以上配置我们可以看到,挂起方式支持RAM和standby两种方式。除此之外,配置默认打开了snapshot快照功能,Hiberanation即STD功能和DEBUG相关的功能。所以我们默认的休眠模式至少支持STR和standby两种方式,这里STD模式虽然打开了,但是由于对交换分区的限制,该功能是否能正常使用,我在后面有做调测和分析。
在如图示的配置中,PowerManagement DebugSupport功能被打开,这个配置被打开之后在系统的/sys/power目录下会多出一个pm_test文件,catpm_test后,列出的测试选项有:[none]core processors platform devicesfreezer。这里有六个参数,代表pm调试的六个级别,跟踪Linux内核里面的相关代码,我发现这六个参数代表了系统挂起的六个阶段,所以,我们可以通过设置这个参数来分别对每一阶段的挂起操作进行调试查看。其中,freezer是级别最低的,none是表示不进行pm_test,也就是完成所有的挂起操作。
有了以上内核的配置支持之后,我们就可以进行简单的休眠唤醒调试了,只要在系统中输入两条命令即可:
echoplatform > /sys/power/pm_test echomem > /sys/power/state
表示设置采用STR的方式进行休眠,休眠到platform阶段为止,后面的休眠操作不再进行。输入命令之后在设备终端是可以看到相关的操作流程的打印信息的。
第二节 休眠流程
从整个挂起流程来看,系统最终需要在函数hi_pm_ops.enter回调函数中进行系统挂起,并设置唤醒方式,等待唤醒。enter函数的堵塞流程跳出之后,suspend_enter会继续向下执行,这个时候就进入到resume唤醒流程了。所以,由于目前3516A的代码中没有实现enter函数的堵塞操作,suspend_enter函数就会在挂起完成之后直接进入到resume流程,系统自动被唤醒。
挂起流程在每个阶段结束之后都会去检测suspend_test的状态,这个状态就是我们在文件/sys/power/pm_test中设置的参数,如果检测到用户设置的级别跟当前级别一致了,就会直接等待5S,然后进入唤醒流程。那么从整个流程来看,我们大概可以知道这5个级别都分别进行了哪些处理工作:
第一阶段freezer:首先是suspend前的一些准备工作,比如同步文件系统(sys_sync)、给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,这些进程也包括也包括workqueue/kthread进程,最后会尝试释放一些内存附录。 第二阶段device:这个阶段主要是用来休眠外设,调用suspend_devices_and_enter()进行操作,外设休眠的时候会调用所有外设的suspend函数。在这个函数中会调用mach-3516a/pm.c中suspend_ops->begin(),3516A方案中并没有实现begin函数,然后driver/base/power/main.c中的 device_suspend()->dpm_suspend()会被调用,他们会依次调用驱动的suspend()回调来休眠掉所有的设备。 第三阶段platform:这个阶段主要是调用mach-3516a/pm.c中suspend_ops->prepare ()函数中实现的一些平台相关的准备工作,目前未实现该函数。 第四阶段processors:这个阶段只做了一件事,就是调用disable_nonboot_cpus()函数来关掉多核中非启动的cpu。 第五阶段core:在这个阶段就是真正的休眠阶段了,这个时候会调用平台注册的enter函数来进行将cpu切换到省电状态等操作,并需要将代码执行停止在这里。3516A平台对此未做实现,需要我们去实现这个函数。第三节 简单测试
根据上面的流程,我做了一个简单的测试,编写一个pm的驱动模块,去实现suspend_ops->enter()函数,在该函数中使用while循环将函数进行堵塞,并轮询我指定的一个GPIO0的bit3电平变化,GPIO0_3作为输入时通过电路设置常态为高电平,通过该GPIO来模拟唤醒按钮操作,需要唤醒时将该GPIO输入低电平,enter函数中检测到持续的低电平之后函数跳出,进入唤醒流程。
通过这个简单的测试,来查看一下休眠唤醒机制的基本工作情况。
查看系统休眠前后的功耗变化:关于功耗的变化,测试了两组数据,第一组数据是只加载系统的时候,休眠前后的功耗变化,休眠前在系统完全运行起来之后电流为0.12A,休眠后变成0.09A,唤醒之后恢复0.12A;第二组数据是加载CVBS实时视频的时候,休眠前电流值为0.22A,休眠后电流为0.17A。两种情况下,电流都有一定的跌幅,所以可以看出目前的休眠机制是有一定的省电作用。
查看休眠前后用户态进程状态:编写一个简单的测试程序,使程序一直循环打印累加的计数值,在休眠之后可以看到串口是没有任何打印信息输出了,并且在此之前通过测试发现设备休眠之后串口是没有休眠的,仍然能输出打印信息。那么休眠一段时间之后,使用按键将系统唤醒,查看唤醒之后的状态。设备唤醒之后,网卡会重新连接,测试程序的打印信息会在休眠前的状态之下执行,因此说明,休眠时应用程序的状态以及现场的保存都是正常的。同时,还发现唤醒后系统的时间和休眠前的系统时间几乎是没有差异的。但是,测试发现RTC时钟还在正常运行,所以在实际开发的过程中在休眠唤醒之后还需要根据RTC时钟的时间对系统进行对时。
视频输入输出休眠测试:启动VIO应用程序,并使开发板能正常显示适时视频,程序运行正常之后,将系统休眠,查看休眠之后的状态以及休眠唤醒之后是否能正常显示适时视频。通过测试发现,休眠之后,适时视频输出端画面被冻结,整个画面已经处于静止状态,在被唤醒之后,视频恢复正常,可适时显示视频变化。并且,针对在线模式和离线模式的测试都能正常通过。并且,我将休眠时间增加,测试经过长时间地休眠之后,系统是否还能正常被唤醒,目前的测试情况休眠2小时,系统能正常唤醒,但是到10小时以上之后会出现内核崩溃。
使用中断源唤醒系统:在上面的这些测试中,我使用的唤醒机制是轮询检测GPIO的输入电平,当电平由高变低的时候,触发唤醒。但是,内核提供一种中断唤醒源的方式对挂起的系统进行唤醒操作,这种操作的实现是需要将某个中断注册为唤醒源,一旦该中断被触发,则进入唤醒流程。实现该操作的时候我仍然是用GPIO0的bit3设置为中断方式,使用接口enable_irq_wake()来注册该中断为唤醒源。测试结果是在中断能正常检测到的情况下,触发该中断之后并不能进入到唤醒流程。究其原因,在内核中每个平台需要实现irq_set_wake的回调函数,在该函数中将注册的中断号保存到cpu的中断唤醒掩码中,这个中断唤醒掩码保存到寄存器中,当唤醒源中断到来的时候,进行唤醒操作。从海思H3516A的代码来看,并未实现这块功能,所以目前的内核并不支持。
综上所述,海思H3516A内核中已有的功能对休眠和唤醒支持地并不是很完善,那么就需要做较多的内核编码工作来完善这块功能。最首要的是需要完善设备休眠时的低功耗省电模式的切换。
第四节 省电模式
海思3516A方案系统工作模式分为两种模式:正常工作模式和待机工作模式,待机工作模式对应系统运行模式控制中的SLOW模式和DOZE模式,待机工作模式工作系统在极低工作时钟下,并且关闭了大部分不使用的模块的时钟,功耗较低,待机工作在SLOW或DOZE模式下,可以通过关闭不工作模块的电源来直接减少待机的功耗。
NORMAL模式:系统正常工作在NORMAL模式下。在此模式下,系统由片内PLL的输出时钟驱动。所有的模块均能正常工作。 SLOW模式:LOW模式是一种慢速模式。在此模式下,系统由外接晶振时钟驱动,只有部分片内外设(如系统控制器、Timer、NANDC、SFC等)可以工作。所有对高速时钟有要求的模块在此时钟下无法工作,如DDRC等。 DOZE模式:DOZE模式是一种低速模式。只有少量片内外设可以工作于DOZE模式。在此模式下,系统由外接晶振分频的46.875kHz低频时钟驱动。大部分片内外设无法工作,存储器接口无法工作,CPU和少量模块(如系统控制器、Timer和IR等)可以工作于该模式。 第七部分 音频编解码第一节 语音流程
海思的音频编码提供了设备端和pc端用于编解码的库文件,封装了语音编解码的接口。对于语音输出入输出海思引入了几个概念:ai、aenc、adec、ao,分别代码输入通道、编码通道、解码通道和输出通道,应用程序通过对这几个通道进行绑定来实现语音操作功能,绑定方式有以下几种:
Ai和ao直接绑定:这种直接输入输出的绑定关系提供了设备端mic采样,直接从spk输出,中间不进行数据编码压缩,PCM帧进入PCM帧输出。这种方式不太常用,也很简单。 Ai和aenc绑定、adec和ao绑定,这两种绑定关系是相互依赖存在的,我们在对讲中使用的这种方式,设备端采集PCM帧,然后发送到aenc,编码完成之后发送到应用层,应用层进行解码;应用层发送编码帧,编码库将编码帧发送到adec,adec进行解码然后发送到ao进行放音。第二节 输入输出设备
Ai和ao要想正常工作,需要对其属性参数进行配置,然后调用HI_MPI_AI_SetPubAttr接口设置其属性并使能才行,否则是无法正常接收到采样中断的。这些属性参数具体内容可参考文档中关于AIO_ATTR_S结构体的详细说明。
设备属性中的相关内容有采样率、比特位宽、工作模式等,其中需要注意的是工作模式的配置,工作模式配置的是主从模式以及时序的选择,AI/AO设备支持主模式和从模式,主模式即AI/AO设备提供时钟,从模式即AudioCodec提供时钟。主模式时,如果AI设备与AO设备同时对接同一个Codec,则时钟供输入和输出共同使用,其他情况没有此限制。而从模式时的输入输出时钟可以分别由外围AudioCodec提供。在AI/AO设备从模式下,先配置好对接的Codec,再配置AI或AO设备;而在AI/AO设备主模式下,先配置好AI或AO设备,再配置对接的Codec。目前对于3516A来说,使用的是一个内置的audiocodec,所以只支持I2S主模式。而且,ai和ao对接同一个内置codec时,时钟要采用ai和ao复用。
调测的过程中,我们可以使用海思方案提供的通用调试方法来查看相关配置是否有效,通过cat/proc/umap/ai 等来分别查看通道绑定是否成功,是否有中断到来之类的,详细描述查看文档《HiMPP用户手册》
第三节 语音帧结构
海思的编码语音帧结构与之前我们在TI平台中使用的语音帧结构有所区别
前4byte的数据信息为海思独有的帧头,帧头信息里面实际有用的信息为0:[15:8]= 01,1:[15:8]=帧长/2。其他位为0,唯一需要在应用层中需要注意的是这个帧长为编码之后的语音帧的实际长度,如g711A编码时,如果采样数为320,则每帧PCM长度为640byte,编码之后每帧长度为320byte,这里填充到数据头里面的长度则为160,数据头之后的320byte为解码数据。
第四节 编解码协议
以下针对几种编解码方式在对接过程中的一些操作分别进行描述。
海思目前提供了几种编解码方式:G711(A律与µ律)、G726、ADPCM(ADPCM_IMA4、ADPCM_ORG_DVI4和ADPCM_DVI),这三种编解码的方式在海思的开发文档里面有详细的比较,三种编码方式压缩率分别是:2:1、8~3.2:1、4:1。
使用海思的编解码库时每种编码方式都会在语音帧前面增加一个帧头。
第一步 G711编码
G.711提供A律与µ律压缩编码,适用于综合业务网和大多数数字电话链路。北美与日本通常采用µ律编码,欧洲和其他地区大都采用A律编码。 G711编解码和TI保持一致,这里不详述。
第二步 ADPCM编码
ADPCM有三种编解码方式,ADPCM_IMA4、ADPCM_ORG_DVI4和ADPCM_DVI。三种编码方式的压缩比率都是4:1,压缩后码率为32kbps,关于这三种编码方式的详细介绍可以参考RFC3551.pdf文档,有很明确的说明,总结起来,三种编码方式的区别在于,预测值上面的差别,这里简单归纳一下:
ADPCM_ORG_DVI4:这种编码方式是不会把预测值保存在压缩帧数据中的,编码时,每次会将上一帧计算出来的值作为预测值,解码时,同样将上一帧计算出来的值作为预测值,在当前帧中用来解码。这种方式存在的问题是:在网络丢包时,可能会引起异常。所以不推荐使用这种方式。 ADPCM_DVI:这种编码方式会把预测值保存在编码帧的头四个字节,每一帧通过计算出来的值作为下一帧的预测值,解码时,通过传过来的预测值进行解码,这种方式就比较安全,不会因为丢失帧而造成语音异常。这个预测值信息即为算法中的adpcm_state结构体的内容。 ADPCM_IMA4:这种编码方式目前没使用,这种编码方式是将第一个采样点作为预测值,所以在进行采样时,要多采一个,所以输入采样点个数为81/161/241/321/481。我们在和TI平台对接时使用ADPCM_DVI的方式。
附录
FAQ
对于调试过程中出现的一些现象在此做一个简单的记录:
1、Q:运行load3516a时出现如下错误:
A:这个问题是前面提到的himm执行段错误的问题,按前面调到的方法进行修改。解决方法查看这里。
2、Q:运行load3516a时出现如下打印
A:系统运行会一直堵塞在这里,等待I2C响应,这个错误就是前面提及的需要删除sil9024驱动的问题。解决方法查看这里。
Q:运行load3516a时出现如下打印,设备挂死
A:系统运行在这里会一直挂着,串口输入也没反应,必须要重新上电,这个是DDR时钟配置的问题,需要按前面提到的方法进行配置。解决方法查看这里。
原文链接:https://blog.csdn.net/bobpeter84/article/details/79319000?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:海思3516A开发板调试整理(三) https://www.yhzz.com.cn/a/14433.html