STM32入门——语音存储回放系统软件实现
通常学习一款新的开发板首要任务毋庸置疑的是点亮LED灯,以熟悉其相关操作。但是如果想要进一步学习了解开发板其它功能,还得通过项目实践。想起刚开始学习32的时候,和小伙伴做了一个语音存储回放系统的小题目,非常适合新手入门,因为其整个设计简单,上手容易,但是其涉及到大多数常用外设:GPIO操作,ADC,DAC,DMA,定时器,按键控制,Flash存储等。每一个模块都可以得到练习巩固。
一、系统介绍
语音存储回放系统以单片机为控制核心,话筒(声音传感器)采集到的模拟信号经过前级滤波放大等转换成单片机能够采集到的模拟信号,单片机实现系统控制,数据采集,存储并通过DAC 转换成模拟信号输出,送给后级电路处理之后通过将信号传送给扬声器播放,从而实现语音的采集、存储、回放功能。因为此文的目的是通过代码熟悉stm32单片机,所以硬件电路的设计不做过多介绍。
系统设计框图:
不难看出,单片机的主要任务是将前级电路传来的模拟信号进行转换成数字量,并进行存储,最后通过DAC转换成 模拟量输出。
通过分析,软件设计方案为:
二、主要函数介绍
1.主函数
首先定义一个标志flag为0;
当KEY1按下并且flag为0时,进入到Record()函数,对信号进行采集存储;
当flag为1(存储结束后将flag设置为1)并且KEY2按下的时候,进入到Play()函数,读取Flash中的值并通过DAC输出。
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
key_config(); //按键初始化
Init_LED_GPIO(); //LED初始化
TIM2_config(); //TIM2定时器初始化
TIM3_config(); //TIM3定时器初始化
ADC_config(); //ADC初始化
DMA_config(); //DMA初始化
DAC_config(); //DAC初始化
while(1)
{
if(key_pressed(KEY1) && !g_flag)
{
Record();
}
if(g_flag && key_pressed(KEY2))
{
Play();
if(g_readAdder == 0x0800C004)
{
TIM_Cmd(TIM2,DISABLE);
}
}
}
}
2.Record() & Play() 函数
对于Record()函数,当开始Record的时候,点亮LED2,并且使能定时器3(定时器3控制以一定的速度往Flash中存入数据)。
对于Play()函数,当开始Play的时候,点亮LED4,并且使能定时器2(定时器2控制以一定的速度读取Flash中的值并通过DAC输出)。
/***************信号采集并进行存储*************/
void Record(void)
{
turn_led(LED2,ON);//点亮LED2
TIM_Cmd(TIM3,ENABLE); //使能TIM3
}
/***********读取 Flash 中的值并通过 DAC 输出*******/
void Play(void)
{
turn_led(LED4,ON);//点亮LED4
TIM_Cmd(TIM2,ENABLE); //使能定时器2
}
3.TIM2 & TIM3 中断服务程序
Flash的写入,读取都是通过定时器控制的。我设置的以20KHz的频率向 Flash 中写入数据,本示例用到的Flash的地址空间为0x0800c008到0x08100000,总共975KB大小,按照每个数据量32位,理论存储时间为(975*1024)/(4*20K) = 12.48s,实际上我们的存储时间为12秒左右,与理论值接近。
/*******定时器2控制以一定的速度读取Flash中的值,并通过DAC输出******/
void TIM2_IRQHandler(void)//Record()
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
if(g_readAdder <=0x080FFFFF)//没有读到Flash尾地址
{
readflash=STMFLASH_Read(g_readAdder); //读取Flash中的数据
DAC_SetChannel2Data(DAC_Align_12b_R,readflash); //将读到的数据通过DAC输出
g_readAdder+=4;//地址增加4(因为存入的数字量是32位),方便下一次读取
}
else //如果已经到了尾地址
{
g_readAdder = 0x0800C004; //设置读地址为Flash首地址,方便重复读取
TIM_Cmd(TIM2,DISABLE); //关闭定时器2,读取结束
turn_led(LED4,OFF); //熄灭LED4,标志这读取结束
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
/****定时器2控制以一定的速度往Flash中写入数据**********/
void TIM3_IRQHandler(void) //Play()
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
{
if(g_writeAdder<=0x080FFFFF)//如果没有写到Flash尾地址
{
STMFLASH_Write(g_writeAdder,g_ADC_Value); //将ADC采到的值写入flash
g_writeAdder += 4;//写地址增加4(g_ADC_Value为32位),方便下个数据写入
}
else //如果已经到了尾地址
{
TIM_Cmd(TIM3,DISABLE); //关闭定时器3,停止写入数据
ADC_Cmd(ADC1, DISABLE); //关闭ADC,不再采集
ADC_DMACmd(ADC1, DISABLE); //关闭DMA通道
turn_led(LED2,OFF); //熄灭LED2,表示写入数据完成
g_flag = 1; //将flag设置为1
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
}
三、总结
以上为该系统的主要函数介绍,该代码作为学习STM32入门的实例,只是简单实现了基本功能:采集、存储、播放。必然有很多不足之处。对于初学者来讲可以练习到常用到的外设,如果朋友有兴趣,可以再在此基础上实现更多的功能。例如PCM压缩编码算法,人机交互界面等。关于更多硬件连接信息,stm32平台等大家可以下载完整代码。
完整代码下载地址: http://download.csdn.net/detail/edroid1530/9673827(需要您2个下载积分)
免费下载地址GitHub: https://github.com/Edroid/Voice_Record_Play (可直接下载)