后续内容更新:
1,源码更新(
2,工程讲解更新:
一个完整的歌曲音调查找表数据结构如下图所示:段内数据有三部分组成,标识数据(段起始及结束,歌曲标题起始结束,段长度),歌曲标题数据以及音调记录数据组成。
一个完整的段,第一个字必然是段起始标志0xFFFFFF3C,第二个字必然是段长度,以字为单位。随后第三个字一定是0xFFFFFF69表示歌曲标题数据开始,歌曲数据结束则用0xFFFFFF96表示。段尾的最后一个数据一定是0xFFFFFFC3代表当前段结束。
歌曲数据则是由ASCII按照小端模式依次填充到每一个字中,字符串从左向右,最左边填充最低的字节地址,最右边填充最高的字节地址,不是4的倍数不足的字节数填充0补齐一个字,正好是4的倍数则在末尾额外增加一个全0的字标志字符串终止。
在标志0xFFFFFF69与0xFFFFFFC3之间的数据全部是音调记录数据,其总字数总是2的倍数。
5,程序架构行为分析
i.程序运行环境初始化及整体框架行为
程序起始需要解决两个问题,变量初值指定,关键外设初始化。所以在程序起始阶段需要做的事是对全局变量进行初始化使之为合适初值,使得后续程序正常运行。并且做一些内核密切的配置,如系统时钟的选择,中断优先级分配。
整体框架行为如右图所示,上电后Cortex-M3自动从FLASH加载两个字的数据,分别是PC(Programcounter)初值和堆栈指针初值(R13or SP),随后跳转Reset_Handler(复位向量),再由复位句柄引发跳转,开始执行SysInit,该函数负责对程序整体运行环境的初始化,做了上述的准备工作。
随后由SysInit控制跳转到main函数开始执行用户程序。用户程序中,先初始化用户程序需要的外设,由PeripheralInit完成,然后开始正式进行歌曲准备播放工作。
歌曲播放之前,需要知道FLASH中定义的歌曲段信息,所以调用SongSegBaseAddrSeek扫描指定地址和长度的FLASH区域,得到所有定义的歌曲信息,创建索引记录并保存在私有空间中,随后进入一个循环。循环中不断读取当前播放的状态,如果是空闲状态,就调用PlaySongs函数,传入歌曲索引号触发播放,并且索引号在每次播放后自增指向下一首歌,到头后重置。
ii.SysTick中断服务程序行为分析
如右图示,在每个中断(使用片内DAC则在SysTick中断中触理,使用片外的IISDAC则在对应的SPI中断中处理)发生后,都会固定地调用音调生成函数ToneDatBufFeed处理完音调生成并输出,随后返回。
音调生成函数在每次执行时都会检查特定标志位决定是否允许音调生成,随后调用实时采样值生成,输出函数TonesGenerate,进行当前音调的实时采样值的生成并输出。TonesGenerate返回后,判断返回值,如果是0无需特殊处理直接结束本次处理;如果是1则进行进一步处理。
非0的返回值意味着当前音调结束,下一个音调应该被播放,随后直接从指定的地址加载两个字的数据,并判断第一个字是否为段结束标志0xFFFFFFC3,未结束时直接将加载的数据写入指定的buff结束处理;发现当前段结束时,调用ToneGenCtrl关闭指定中断以及相关硬件,设置特定标准位禁止后续音调发生,结束处理。
实际上,使用IISDAC输出还面临则左右声道的问题,历程中只在某个声道传输时调用音调生成,另一个声道固定填充静音数据。
iii.实时采样值生成输出函数行为分析
如右图示,每次执行后都检查音调剩余持续时间计数器,并决定解析来的动作。
计数器为0时当前音调结束,计数器减一变为-1,函数直接返回,返回值为1通知上层函数重装新的音调记录到指定buff。
计数器不为0且为正时,计数器减1,进入正常流程,得到各次谐波混合体的实时采样值与幅度实时采样值后就调制并输出。
计数器为-1时,则调用音调参数设置函数重设音调发生参数,随后进入正常流直到结束。
|