#include "stm32f10x_lib.h" #include "COMM.h" unsigned char Buff[512]; unsigned char Channel,SampleBit; // 音乐通道数目 /采样分辨率 unsigned int SampleRate; //采样率 unsigned int FifoWait,FifoRead; //等待队列位置 / 读取队列位置 unsigned int FifoCnt; // 等待队列个数 unsigned char DataOffset; u8 music_info=0; // bit 1,0 循环模式: 00-不循环 01-单曲循环 10-全部循环 11-随机循环 // bit 3,2 歌词模式: 0x-不显示歌词 1x-显示歌词 x0-无歌词 x1-有歌词 // bit 4-7 保留 u8 vol=6; //音量设置 void WAV_Init(void) { RCC->APB1ENR |= 1<<0; // TIM2时钟使能 RCC->APB1ENR |= 1<<1; // TIM3时钟使能 RCC->APB2ENR |= 1<<2; // PA时钟使能 GPIOA->CRL &= 0xFFFFFF00; // PA0/1输出 GPIOA->CRL |= 0x000000BB; // PA0/1复用功能输出 GPIOA->ODR |= 0x03; // PA0/1上拉 //TIM2 PWM1模式 作为音频输出 TIM2->ARR = 0xFF; // 设定计数器自动重装值 TIM2->PSC = 0; // 预分频器不分频 TIM2->CCMR1 = 0x6868; // PWM1模式/预装载使能 (CH1/CH2) TIM2->CCR1 = 0xFFFF; // 高电平 TIM2->CCR2 = 0xFFFF; // 高电平 TIM2->CR1 = 0x0080; // 自动重装载使能 //TIM3 更新事件中断 其值为音频采样率 TIM3->ARR = 0xFFFF; // 设定计数器自动重装值 TIM3->PSC = 0; // 预分频器不分频 TIM3->CCR1 = 0xFFFF; // 高电平 TIM3->CCMR1 = 0x0038; // 输出冻结模式 TIM3->CR1 = 0x0080; // 自动重装载使能 vol=6; //音量设置 } void TIM3_IRQHandler(void) { unsigned char tempA,tempB; TIM3->SR &= 0xFFFE; // 更新中断标志位 if(FifoCnt>Channel) { if(Channel==1) //如果音源只有一个通道,那么两个喇叭的信号是相同的 { tempA=Buff[FifoRead]; FifoRead++; if(FifoRead==512) FifoRead=0; FifoCnt--; TIM2->CCR1=tempA; TIM2->CCR2=tempA; } else //如果音源有两个通道,那么两个喇叭的信号是不相同的 { tempA=Buff[FifoRead]; FifoRead++; if(FifoRead==512) FifoRead=0; FifoCnt--; tempB=Buff[FifoRead]; FifoRead++; if(FifoRead==512) FifoRead=0; FifoCnt--; TIM2->CCR1 = tempA; TIM2->CCR2 = tempB; } } } unsigned long int load_head(void) { unsigned long int data_size,id; union{ unsigned long int ul; unsigned char dt[4]; }utemp; unsigned char i; utemp.dt[3]=main_buffer[8]; //'W' utemp.dt[2]=main_buffer[9]; //'A' utemp.dt[1]=main_buffer[10]; //'V' utemp.dt[0]=main_buffer[11]; //'E' if(utemp.ul!=0x57415645 ) //"WAVE"="0x57415645"如果utemp.ul不等于"WAVE"则错误,退出. return 1; i=12; //下一个Chunk的ID首位置 while(i<200) { utemp.dt[3]=main_buffer[i]; //'?' 获取ID utemp.dt[2]=main_buffer[i+1]; //'?' utemp.dt[1]=main_buffer[i+2]; //'?' utemp.dt[0]=main_buffer[i+3]; //'?' id=utemp.ul; utemp.dt[0]=main_buffer[i+4]; //'?' 获取该Chunk大小(小端模式) utemp.dt[1]=main_buffer[i+5]; //'?' utemp.dt[2]=main_buffer[i+6]; //'?' utemp.dt[3]=main_buffer[i+7]; //'?' data_size=utemp.ul; i+=8; //转到该Chunk的数据开始位置 //'WAVE'=0x57415645 //'fmt '=0x666d7420 //'fact'=0x66616374 //'data'=0x64617461 switch(id) { case 0x666d7420: //'fmt '=0x666d7420 if(data_size>100 || data_size<16) return 2; if(main_buffer[i+0]!=1) return 3; //2 Bytes 编码方式,一般为0x0001 if(main_buffer[i+2]!=1 && main_buffer[i+2]!=2) return 4;//2 Bytes 声道数目,1--单声道;2--双声道 Channel=main_buffer[i+2]; //获取音乐通道数目 if(main_buffer[i+14]!=8 && main_buffer[i+14]!=16) return 5;//2 Bytes 每个采样需要的bit数 SampleBit=main_buffer[i+14];//获取采样分辨率(采样位数),4 Bytes 采样频率 utemp.dt[0]=main_buffer[i+4]; //'?' 获取采样分辨率 utemp.dt[1]=main_buffer[i+5]; //'?' 小端模式 utemp.dt[2]=main_buffer[i+6]; //'?' utemp.dt[3]=main_buffer[i+7]; //'?' //设置定时器2匹配中断频率为音乐的采样频率 // PSC=1 8000Hz ARR 4500 // PSC=0 11025Hz ARR 6530 22050Hz ARR 3265 44100Hz ARR 1632 // 16000Hz ARR 4500 24000Hz ARR 3000 32000Hz ARR 2250 if((utemp.ul<7000)&&(utemp.ul>25000)) return 6; if(utemp.ul<11000) { TIM3->PSC=1; TIM3->PSC=1; TIM3->ARR=(unsigned int)36000000/utemp.ul-1; } else { TIM3->PSC=0; TIM3->PSC=0; TIM3->ARR=(unsigned int)72000000/utemp.ul-1; } SampleRate = utemp.ul; break; case 0x66616374: //'fact'=0x66616374 break; case 0x64617461: //'data'=0x64617461 DataOffset=i;//设置wav文件有用数据开始位置 return data_size; //返回wav文件有用数据大小 default: return 7; } i+=data_size; //查询下一个Chunk,设置偏移位置 } return 8; } // 歌曲标题,时间,比特率 等信息显示 //void Mus_Info(u32 file_size,u8 info,FileInfoStruct *FileName,u16 index,u16 total) void Mus_Info(FileInfoStruct *FileName,u32 file_size,u8 info,u16 cur_mus,u16 all_mus) { u16 play_time;//播放时间 u16 temp=0; POINT_COLOR=WHITE; TFT_Fill(0,29,239,47,BLACK); Show_Str(0,30,FileName->F_Name,0x01); //显示歌曲名字 //显示但前文件夹下的歌曲数目,及当前歌曲的索引 POINT_COLOR=GREEN; BACK_COLOR=GRAY; TFT_Show3Num(10,62,cur_mus,16,0); TFT_ShowChar(34,62,'/',16,0); TFT_Show3Num(42,62,all_mus,16,0); TFT_ShowButton( 72,58,(info&0x03)+8); //显示循环模式 //if(info&0x04) TFT_ShowButton(108,58,14); //有歌词 //else TFT_ShowButton(108,58,15); //无歌词 //if(!(info&0x08)) TFT_Fill(108,58,132,82,GRAY);//是否允许显示歌词 if(Channel==1) TFT_ShowButton(108,58,12); //单声道 144 else TFT_ShowButton(108,58,13); //双声道 144 //显示采样率 BACK_COLOR=0X380E; temp = SampleRate / 1000; if(temp>10) { TFT_ShowChar(90,86,(temp/10)%10+'0',12,0); TFT_ShowChar(96,86,temp%10+'0',12,0); } else { TFT_ShowChar(90,86,' ',12,0); TFT_ShowChar(96,86,temp%10+'0',12,0); } TFT_ShowChar(102,86,'.',12,0); temp = SampleRate % 1000; TFT_Show3Num(108,86,temp,12,0); TFT_ShowChar(126,86,' ',12,0); TFT_ShowChar(132,86,'K',12,0); TFT_ShowChar(138,86,'H',12,0); TFT_ShowChar(144,86,'Z',12,0); TFT_Fill(20,98,219,107,GBLUE); // 画空进度,180*10大小 //显示总时间 play_time = file_size/SampleRate/Channel; //秒数=文件长度(字节)/采样率/声道数/量化字节数 if(SampleBit==16) play_time >>=1; TFT_ShowNum(190,108,play_time/60,12,0); //分钟 TFT_ShowChar(202,108,':',12,0); TFT_ShowNum(208,108,play_time%60,12,0);//秒钟 } //播放选定的歌曲 //index:当前播放的歌曲索引 //total:总共音乐文件的个数 u8 Mus_Play(FileInfoStruct *FileName,u16 index,u16 total) { u8 key; u8 *p=0; //指向文件 // u8 keycnt; //按键计数器 //u8 vol=6; //音量设置 u8 pause_flag=1; //暂停标志 u16 count; unsigned long int total_size; unsigned long int size; unsigned long int i; u8 bar_value; // 进度数值 u16 bar_coeff; // 进度条系数 u8 bar_flag; // 进度条更新标志 u16 bar_cnt; // 进度条计数器 u16 n; //timer3_init();//初始化歌词显示定时器 Mus_Gui(); // 界面初始化 RESTART: // keycnt=0; //从当前目录下找歌词文件,有,则标记. //if(FindLrcFile(Cur_Dir_Cluster,FileName->F_ShortName)) //{ // music_info|=0x40; //标记有歌词 // Lrc_Read_Init(); //初始化歌词读取 //} //else music_info&=0xFB; //标记无歌词 key=0; Pen_Point.Key_Sta=Key_Up;//释放按键 //--------------------------- F_Open(FileName);//打开文件 F_Read(FileName,main_buffer);//读出512个字节 p=main_buffer;//指向数据首地址 total_size=load_head(); if(total_size<0xFF) { index++; if(total<2) return 0; if(index>total) index=1; Get_File_Info(Cur_Dir_Cluster,FileName,T_WAV,&index); goto RESTART; } size=total_size; p+=DataOffset; count+=DataOffset; // 进度条显示复位,进度条以按照SD卡读取到的数据计算 bar_value=20; // 进度条数值 bar_flag=0; // 进度条更新标志 bar_cnt=0; // 进度条计数器 bar_coeff=size/512/99; // 进度条系数,用有效数据的扇区数计算 main_buffer[512]=0x80; //解决速度优化模式16位解码溢出 Mus_Info(FileName,total_size,music_info,index,total); // 更新歌曲信息 FifoCnt=0; //复位FIFO队列 FifoWait=0; FifoRead=0; //读取有效数据数量 for(i=0;i<200;i++) { if(SampleBit==16) //如果采样是16位,则抛弃低8位 { p++; count++; size--; if(FifoWait==512) FifoWait=0; Buff[FifoWait++]=*p-0x80; FifoCnt++; p++; count++; size--; } else { if(FifoWait==512) FifoWait=0; Buff[FifoWait++]= *p; FifoCnt++; p++; count++; size--; } } TIM2->CR1 |= 0x01; //使能定时器2 TIM3->CR1 |= 0x01; //使能定时器3 if(!pause_flag) { TIM3->DIER |= 0x01; // 使能更新中断 //TIM2->CCER |= 0x0011; //OC1/2 使能输出 } while(1) //播放音乐的主循环 { //处理音频数据 if((size>2)&&(FifoCnt!=512)&&(count<512)) //如果队列满则等待 { //利用等待获取数据这段时间进行调整写入队列位置 if(SampleBit==16) // 16bit //如果采样是16位,则抛弃低8位 { //优化速度,不考虑特殊情况 p++; count++; size--; // 优化速度时可去掉 if(count>511) //读取一扇区 { if(!F_Read(FileName,main_buffer)) break;//读出512个字节,读数失败时自动退出 p=main_buffer;//指向数据首地址 count=0; if (bar_value<218) bar_cnt++; if(bar_cnt==bar_coeff) { bar_flag=1; bar_cnt=0; } } //--------------------------- if(FifoWait==512) FifoWait=0; Buff[FifoWait++]=*p-0x80; FifoCnt++; p++; count++; size--; } else { if(FifoWait==512) FifoWait=0; Buff[FifoWait++]= *p; FifoCnt++; p++; count++; size--; } } else if(count>511) //读取一扇区 { if(!F_Read(FileName,main_buffer)) break;//读出512个字节,读数失败时自动退出 p=main_buffer;//指向数据首地址 count=0; if (bar_value<218) bar_cnt++; if(bar_cnt==bar_coeff) { bar_flag=1; bar_cnt=0; } } else if((size<3)&&(FifoCnt!=512)) break; // 播放完毕 // 进度条处理,为了优化速度,直接嵌入 if((bar_coeff>5)&&(bar_flag==1)) { bar_flag=0; TFT_WR_REG(0x20); TFT_WR_DATA(bar_value); TFT_WR_REG(0x21); TFT_WR_DATA(98); TFT_WR_REG(0x50); TFT_WR_DATA(bar_value); TFT_WR_REG(0x51); TFT_WR_DATA(bar_value+1); TFT_WR_REG(0x52); TFT_WR_DATA(98); TFT_WR_REG(0x53); TFT_WR_DATA(107); n=20; //TFT_WR_REG(0x22); //while(n--) TFT_WR_DATA(BLUE);//显示所填充的颜色. TFT_WR_DATA_fast_start(); while(n--) TFT_WR_DATA_fast(BLUE); TFT_WR_DATA_fast_finish(); bar_value++; bar_value++; } // void MP3_PROG(u32 pos,u32 lenth) // 更新播放信息 //if(LRC)//歌词模式 //{ // if(mark&&sysfun&(1<<8))LyricDisplayCtrl();//存在歌词,则显示 // else if(mark==0) MP3_PROG(file_pos,FileName->F_Size);//MP3进度显示/播放时间 //} //mark=!mark; //按键处理 if((Pen_Point.Key_Sta==Key_Down||NPEN)&&Pen_Point.Key_LSta) key=Touch_To_Num(5);//得到按键值 if(PEN)//按键松开了,状态改变(状态机) { Pen_Point.Key_LSta=1; Pen_Point.Key_Sta=Key_Up; } if(key)//有按键按下 { Pen_Point.Key_LSta=0; switch(key) { case 1://上一曲 TIM3->DIER &= ~0x01; // 禁止更新中断 index--; if(index==0)index=total; Get_File_Info(Cur_Dir_Cluster,FileName,T_WAV,&index); goto RESTART; case 5://下一曲 TIM3->DIER &= ~0x01; // 禁止更新中断 index++; if(index>total)index=1; Get_File_Info(Cur_Dir_Cluster,FileName,T_WAV,&index); goto RESTART; case 3://播放暂停 if(pause_flag) { pause_flag = 0; BACK_COLOR=GRAY; POINT_COLOR=BLACK; TFT_ShowButton(108,253,2);//播放 TIM3->DIER |= 0x01; // 允许更新中断 TIM2->CCER |= 0x0011; //OC1/2 允许输出 } else { pause_flag = 1; BACK_COLOR=GRAY; POINT_COLOR=BLACK; TFT_ShowButton(108,253,3);//暂停 TIM3->DIER &= ~0x01; // 禁止更新中断 //TIM2->CCER |= 0x0011; //OC1/2 使能输出 } break; case 6: //vol- if(vol>1) { vol--; TIM2->ARR = ((11-vol)<<7)+0x80; Vol_Bar(vol); } break; case 7: //vol+ if(vol<11) { vol++; TIM2->ARR = ((11-vol)<<7)+0x80; Vol_Bar(vol); } break; case 13: // 设置 TIM3->DIER &= ~0x01; // 禁止更新中断 Music_Setting(); Mus_Gui(); Mus_Info(FileName,total_size,music_info,index,total); // 更新歌曲信息 if(bar_coeff>5) //恢复进度条 { TFT_WR_REG(0x20); TFT_WR_DATA(20); TFT_WR_REG(0x21); TFT_WR_DATA(98); TFT_WR_REG(0x50); TFT_WR_DATA(20); TFT_WR_REG(0x51); TFT_WR_DATA(bar_value); TFT_WR_REG(0x52); TFT_WR_DATA(98); TFT_WR_REG(0x53); TFT_WR_DATA(107); n=10*(bar_value-19); //TFT_WR_REG(0x22); //while(n--) TFT_WR_DATA(BLUE);//显示所填充的颜色. TFT_WR_DATA_fast_start(); while(n--) TFT_WR_DATA_fast(BLUE); TFT_WR_DATA_fast_finish(); } if(!pause_flag) TIM3->DIER |= 0x01; // 允许更新中断 break; case 14: return 0; // 返回 default: break; } key=0; } } //TIM2->CCER&=~0x0011; //OC1/2 禁止输出 TIM3->DIER &= ~0x01; // 禁止更新中断 switch(music_info&0x03) { case 0: // 不循环 pause_flag = 1; BACK_COLOR=GRAY; POINT_COLOR=BLACK; TFT_ShowButton(108,253,3);//暂停 //break; case 1: goto RESTART;//重新回到那里播放,单曲循环 case 2: // 全部循环 index++; if(index>total)index=1; Get_File_Info(Cur_Dir_Cluster,FileName,T_WAV,&index); goto RESTART;//重新回到那里播放,全部循环 case 3: // 随机循环 srand(RTC->CNTL);//得到种子 while(1) { index=rand();//得到下一个值 if(index>0&&index<=total) break;//得到了可用的随机数 } Get_File_Info(Cur_Dir_Cluster,FileName,T_WAV,&index); goto RESTART;//重新回到那里播放,随机循环 default: break; } TIM2->CCER&=~0x0011; //OC1/2 禁止输出 TIM2->CR1 &= ~0x01; //关闭定时器2 TIM3->CR1 &= ~0x01; //关闭定时器3 BACK_COLOR=WHITE; return 0; }