void Lcd_List(char page) { #ifdef WORD_MODE //文字版 char i; char lcd_char[28]; //+4是为了预留空间的实验编号 select_index_boardtest=0; //重新显示新的一页时重置select_index Lcd_GramScan( 1 ); //设置扫描方向 LCD_Clear(12,86,206,145,BACKGROUND); sprintf(lcd_char,"第 %0d/%0d 页",current_page_boardtest,all_page_boardtest); //显示页码 LCD_DispEnCh(30,235,(const uint8_t *)lcd_char,BLUE); /* 打开playlist,读取音频文件名 */ for(i=0;(i+PER_PAGE*(page-1))<TEST_NUM && i< PER_PAGE;i++) //仅显示第一页 { sprintf(lcd_char,"%0d.%s",PER_PAGE*(page-1)+i+1,test_name[(PER_PAGE*(page-1) + i)]); //拼接出歌曲列表 LCD_DispEnCh(22,87+i*18,(const uint8_t *)lcd_char,BLACK); //显示 } #else //图标版 char i; char lcd_char[28]; //+4是为了预留空间的实验编号 char pic_name[50]; select_index_boardtest=0; //重新显示新的一页时重置select_index Lcd_GramScan( 1 ); //设置扫描方向 LCD_Clear(12,94,212,160,BACKGROUND);//BLUE BACKGROUND sprintf(lcd_char,"第 %0d/%0d 页",current_page_boardtest,all_page_boardtest); //显示页码 LCD_DispEnCh(30,235,(const uint8_t *)lcd_char,BLUE); /* 打开playlist,读取音频文件名 */ for(i=0;(i+PER_PAGE*(page-1))<TEST_NUM && i< PER_PAGE;i++) //仅显示第一页 { sprintf(pic_name,"/boardtest/ISO_MINI/ui_test%d.bmp",PER_PAGE*(page-1)+i+1); if(i<=((PER_PAGE/2)-1)) { Lcd_show_bmp(160, 166-(70*i),pic_name); } else { Lcd_show_bmp(91, 166-(70*(i-(PER_PAGE/2))),pic_name); } } #endif }
/** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { /* 系统定时器 1us 定时初始化 */ SysTick_Init(); LCD_Init(); /* GRAM扫描方向为左下脚->右上角 */ Lcd_GramScan(2); LCD_Clear(0, 0, 320, 240, BACKGROUND); /*------------------------------------------------------------------------------------------------------*/ /* 触摸屏IO和中断初始化 */ Touch_Init(); /* 等待触摸屏校正完毕 */ while(Touch_Calibrate() !=0); /* 触摸取色板初始化 */ Palette_Init(); while( 1 ) { if(touch_flag == 1) /*如果触笔按下了*/ { /*获取点的坐标*/ if(Get_touch_point(&display, Read_2046_2(), &touch_para ) !=DISABLE) { Palette_draw_point(display.x,display.y); } } } }
/* 修复了上下颠倒的BUG,enjoy~ * 要想中文显示成功 * 需把字库拷贝到sd卡上,然后把sd卡插到开发板的卡槽上 */ int main(void) { /* USART1 config */ //USART1_Config(); //printf("\r\n this is a fatfs test demo \r\n"); LCD_Init(); /* 设置Lcd Gram 扫描方向为: 右下角->左上角 */ Lcd_GramScan( 3 ); LCD_Clear(0, 0, 320, 240, BACKGROUND); /* 初始化sd卡文件系统,因为汉字的字库和bmp图片放在了sd卡里面 */ Sd_fs_init(); Screen_shot(0,0,320,240,"/myscreen"); while(1); }
void Lcd_Note(uint8_t test_num) { uint8_t lcd_char[50]; Lcd_GramScan( 1 ); //设置扫描方向 LCD_Clear(12,86,206,165,BACKGROUND); //清除屏幕 // LCD_Clear(12,72,215,152,BACKGROUND); //RED 清除 //LCD_DispEnCh(50,90,test_name[test_num-1],RED); sprintf(lcd_char,"%s程序运行中:",test_name[test_num-1]); LCD_DispEnCh(15,90,lcd_char,BLACK); LCD_DispEnCh(10,230,"软件或硬件复位返回选择界面",BLACK); }
/** * @brief lcd_list 显示歌曲列表 * @param 歌曲列表的页码 * @retval none */ static void lcd_list(char page) { char i; char music_name[MUSIC_NAME_LEN]; char lcd_char[MUSIC_NAME_LEN+4]; //+4是为了预留空间的歌曲编号 FIL name_file; select_index=0; //重新显示新的一页时重置select_index Lcd_GramScan( 1 ); //设置扫描方向 LCD_Clear(12,86,206,145,BACKGROUND); sprintf(lcd_char,"第 %0d/%0d 页",current_page,all_page); //显示页码 LCD_DispEnCh(30,235,(const uint8_t *)lcd_char,BLUE); sprintf(lcd_char,"音量:%2d",voice); //显示音量 LCD_DispEnCh(155,235,(const uint8_t *)lcd_char,BLUE); /* 打开扫描记录到的歌单 */ fres = f_open (&name_file, "0:mp3player/lcdlist.txt", FA_READ); /* 打开playlist,读取音频文件名 */ for(i=0;(i+8*(page-1))<file_num && i< 8;i++) //仅显示第一页 { fres = f_lseek (&name_file, (8*(page-1) + i)*FILE_NAME_LEN); //文件中的歌曲名按FILE_NAME_LEN来偏移 fres = f_read(&name_file, music_name, MUSIC_NAME_LEN, &rw_num); //只读取MUSIC_NAME_LEN长度的字符,太长的话LCD不够空间显示 music_name[MUSIC_NAME_LEN-1]='\0'; //把最后一个元素设置为'\0'防止没有结束符号而溢出 sprintf(lcd_char,"%0d.%s",8*(page-1)+i+1,music_name); //拼接出歌曲列表 LCD_DispEnCh(22,87+i*18,(const uint8_t *)lcd_char,RED); //显示 } fres = f_close (&name_file); }
/* * 显示bmp图片, 24位真彩色 * 图片宽度和高度根据图片大小而定 */ void Lcd_show_bmp(unsigned short int x, unsigned short int y,unsigned char *pic_name) { int i, j, k; int width, height, l_width; BYTE red,green,blue; BITMAPFILEHEADER bitHead; BITMAPINFOHEADER bitInfoHead; WORD fileType; unsigned int read_num; unsigned char tmp_name[20]; sprintf((char*)tmp_name,"0:%s",pic_name); f_mount(0, &bmpfs[0]); BMP_DEBUG_PRINTF("file mount ok \r\n"); bmpres = f_open( &bmpfsrc , (char *)tmp_name, FA_OPEN_EXISTING | FA_READ); /*-------------------------------------------------------------------------------------------------------*/ if(bmpres == FR_OK) { BMP_DEBUG_PRINTF("Open file success\r\n"); /* 读取文件头信息 两个字节*/ f_read(&bmpfsrc,&fileType,sizeof(WORD),&read_num); /* 判断是不是bmp文件 "BM"*/ if(fileType != 0x4d42) { BMP_DEBUG_PRINTF("file is not .bmp file!\r\n"); return; } else { BMP_DEBUG_PRINTF("Ok this is .bmp file\r\n"); } /* 读取BMP文件头信息*/ f_read(&bmpfsrc,&bitHead,sizeof(tagBITMAPFILEHEADER),&read_num); showBmpHead(&bitHead); /* 读取位图信息头信息 */ f_read(&bmpfsrc,&bitInfoHead,sizeof(BITMAPINFOHEADER),&read_num); showBmpInforHead(&bitInfoHead); } else { BMP_DEBUG_PRINTF("open file failed!\r\n"); return; } /*-------------------------------------------------------------------------------------------------------*/ width = bitInfoHead.biWidth; height = bitInfoHead.biHeight; /* 计算位图的实际宽度并确保它为32的倍数 */ l_width = WIDTHBYTES(width* bitInfoHead.biBitCount); if(l_width > 960) { BMP_DEBUG_PRINTF("\n SORRY, PIC IS TOO BIG (<=320)\n"); return; } /* 设置Lcd Gram 扫描方向为: 右下角->左上角 */ Lcd_GramScan( 3 ); /* 开一个图片大小的窗口*/ LCD_OpenWindow(x, y, width, height); /* 判断是否是24bit真彩色图 */ if(bitInfoHead.biBitCount >= 24) { for(i=0;i< height; i++) { /* 读取一行bmp的数据到数组pColorData里面 */ #if 0 for(j=0; j< l_width; j++) { f_read(&bmpfsrc,pColorData+j,1,&read_num); } #elif 1 f_read(&bmpfsrc,pColorData,l_width/2,&read_num); f_read(&bmpfsrc,pColorData+l_width/2,l_width/2,&read_num); #else f_read(&bmpfsrc,pColorData,l_width,&read_num); #endif for(j=0; j<width; j++) //一行有效信息 { k = j*3; //一行中第K个像素的起点 red = pColorData[k+2]; green = pColorData[k+1]; blue = pColorData[k]; LCD_WR_Data(RGB24TORGB16(red,green,blue)); //写入LCD-GRAM } } } else { BMP_DEBUG_PRINTF("SORRY, THIS PIC IS NOT A 24BITS REAL COLOR"); return ; } f_close(&bmpfsrc); }
/** * @brief even_process 根据事件标志进行处理 * @param none * @retval none */ static void even_process(void) { switch(touch_even) { /* 音量加 */ case E_UP: Volume_Add(); touch_even = E_NULL; break; /* 音量减 */ case E_DOWN: Volume_Dec(); touch_even = E_NULL; break; /* 播放、暂停键 */ case E_PLAY_STOP: if(player_state == S_PLAY) //若当前状态为播放 { player_state = S_STOP; //切换为暂停状态 } else //若当前状态为暂停 player_state =S_PLAY; //切换为播放暂停 touch_even = E_NULL; break; /* 上一首 */ case E_PREV: if(play_index <= 0) { play_index = 0; } else { play_index--; #if 0 //放在player_run 函数里统一处理了 /* 检测要切换的歌曲是否在播放列表的上一页 */ if((play_index+8)/8 < current_page) //play_index从零开始计数所以+1,+7是使用进一法。 +1+7 =+8 { current_page--; //更新当前页码 lcd_list(current_page); //刷新LCD列表 } #endif } touch_even = E_NULL; player_state = S_SWITCH; //进入切歌状态 break; /* 下一首 */ case E_NEXT: if(play_index < file_num-1) { play_index++; #if 0 //放在player_run 函数里统一处理了 /* 检测要切换的歌曲是否在播放列表的下一页 */ if((play_index+8)/8 >current_page) //play_index从零开始计数所以+1,+7是使用进一法。 +1+7 =+8 { current_page++; //更新当前页码 lcd_list(current_page); //刷新LCD列表 } #endif } else { play_index = file_num-1; //play_index设置为最后一个歌曲文件 } touch_even = E_NULL; player_state = S_SWITCH; //进入切歌状态 break; /* 向上 */ case E_SELT_UP: if(select_index>0) select_index--; Lcd_GramScan(1); LCD_Clear(12,88,8,145,BACKGROUND); //清除状态栏 Lcd_show_bmp(320-(103+(select_index*18)),240-20,"/mp3player/ui_select.bmp"); //显示“打勾”标签 touch_even = E_NULL; break; /* 向下 */ case E_SELT_DOWN: if(select_index<8-1 && (select_index+8*(current_page-1))<file_num-1) //判断是否溢出,不大于8,不指向无文件名的位置 select_index++; Lcd_GramScan(1); LCD_Clear(12,88,8,145,BACKGROUND); //清除状态栏 Lcd_show_bmp(320-(103+(select_index*18)),240-20,"/mp3player/ui_select.bmp"); //显示“打勾”标签 touch_even = E_NULL; break; /* 直接点选歌曲 */ case E_SELECT: play_index = select_index + ((current_page-1)*8); //根据当前页和select_index确定play_index player_state = S_SWITCH; touch_even = E_NULL; break; /* 下一页 */ case E_SELT_NEXT: if(current_page<all_page) { current_page++; //更新当前页码 lcd_list(current_page); //刷新LCD列表 } else current_page =all_page; touch_even = E_NULL; break; /* 上一页 */ case E_SELT_PREV: if(current_page>1) { current_page--; //更新当前页码 lcd_list(current_page); //刷新LCD列表 } else current_page =1; touch_even = E_NULL; break; /* OK */ case E_SELT_OK: play_index = select_index+8*(current_page-1); //根据当前页和select_index确定play_index touch_even = E_NULL; player_state = S_SWITCH; break; /* 扬声器 */ case E_LOUD_SPEAK: if(speaker_flag == 0) //speaker关状态 { Loud_Speaker_ON(); //开启speaker speaker_flag =1; printf("\r\n loud on"); } else //speaker为开状态 { Loud_Speaker_OFF(); //关闭speaker speaker_flag=0; printf("\r\n loud off"); } touch_even = E_NULL; break; default: //touch_even = E_NULL; break; } }
/** * @brief wav_player 进行wav文件播放 * wav格式存储的就是PCM数据,不需要解码 * @param filename:要播放的文件路径 * @retval none */ static void wav_player(const char *filename) { short *p; WavHead *wav; //打开音频文件 fres = f_open (&file, filename, FA_READ ); //打开失败 if (fres!=FR_OK) { printf("read file %s error ! open another file\r\n",filename); fres = f_close (&file); if (++play_index>=file_num) //索引值加1 { play_index=0; //归0,所以如果所有文件都打开失败会一直循环 } return ; //文件无法打开,终止解码。进入下一次循环,读取下一个文件 } //显示播放图标 Lcd_GramScan(1); LCD_Clear(12,88,8,145,BACKGROUND); Lcd_show_bmp(320-(103+((play_index-((current_page-1)*8))*18)),240-20,"/mp3player/ui_playing.bmp"); fres = f_read(&file,buffer,512,&rw_num); //读取文件头 wav = (WavHead *)buffer; //整理格式 printf("\r\n samprate: %dHz", wav->dwSamplesPerSec); //采样率 if(wav->dwSamplesPerSec >= I2S_AudioFreq_Default) I2S_Freq_Config(wav->dwSamplesPerSec); //设置采样率 //播放循环 while(player_state != S_SWITCH) //循环本过程播放音频,直到切歌 { if (player_state == S_STOP) { even_process(); continue; //暂停的时候跳出本循环 } player_state = S_PLAY; //状态更新为正在播放 //读取wav文件 p = (short *)(buffer+sizeof(buffer)/2*bufflag); fres = f_read(&file, p, sizeof(buffer)/2, &rw_num); if(fres != FR_OK) { printf("读取%s失败! %d\r\n",filename,fres); break; } /* 等待DMA播放完,这段时间我们可以干其他的事,事件处理 */ while((DMA1_Channel5->CCR & DMA_CCR1_EN) && !(DMA1->ISR&DMA1_IT_TC5)) { even_process(); } DMA_ClearFlag(DMA1_FLAG_TC5 | DMA1_FLAG_TE5); DMA_I2S_Configuration((uint32_t)p, rw_num/2); bufflag = 1 -bufflag; //切换buffer if(file.fptr==file.fsize) //如果指针指向了文件尾,表示数据全部读完 { printf("END\r\n"); if(play_index<file_num-1) //自动开始下一首歌曲 { play_index++; player_state = S_SWITCH; } else { play_index = 0; player_state = S_SWITCH; } break; //跳出这首歌的播放状态 while break; } } f_close(&file); //结束播放本歌曲,关闭文件 I2S_Stop(); }
/** * @brief mp3_player 进行mp3文件解码、播放 * @param filename:要播放的文件路径 * @retval none */ static void mp3_player(const char *filename) { int err, i, outputSamps, current_sample_rate = 0; int read_offset = 0; /* 读偏移指针 */ int bytes_left = 0; /* 剩余字节数 */ unsigned long Frames = 0; /* mP3帧计数 */ unsigned char *read_ptr = buffer; /* 缓冲区指针 */ HMP3Decoder Mp3Decoder; /* mp3解码器指针 */ //打开音频文件 fres = f_open (&file, filename, FA_READ ); //打开失败 if (fres!=FR_OK) { printf("read file %s error ! open another file\r\n",filename); fres = f_close (&file); if (++play_index>=file_num) //索引值加1 { play_index=0; //归0,所以如果所有文件都打开失败会一直循环 } return ; //文件无法打开,终止解码。进入下一次循环,读取下一个文件 } //打开成功 //初始化MP3解码器 Mp3Decoder = MP3InitDecoder(); //获取输入数据流,调用helix库解码,输出PCM数据,约20ms完成一次循环 //开始进入播放状态,期间中断会修改touch_even状态 while(player_state != S_SWITCH) //循环1, 如果touch_even不是切歌状态则继续呆在循环体里 { //有时出现解码错误,错误后继续在本循环体内,继续播放 //显示播放图标 Lcd_GramScan(1); LCD_Clear(12,88,8,145,BACKGROUND); Lcd_show_bmp(320-(103+((play_index-((current_page-1)*8))*18)),240-20,"/mp3player/ui_playing.bmp"); //读取mp3文件 fres = f_read(&file, buffer, sizeof(buffer), &rw_num); if(fres != FR_OK) { printf("读取%s失败! %d\r\n",filename,fres); break; //return; } read_ptr = buffer; //指向mp3输入流 bytes_left = rw_num; //实际读到的输入流大小大小 //按帧处理 while(player_state != S_SWITCH) //循环2,循环本过程播放音频,直到按了下一首、上一首 { if (player_state == S_STOP) { even_process(); //检查是否有事件需要处理 continue; //暂停的时候结束本次循环 } player_state = S_PLAY; //状态更新为正在播放 read_offset = MP3FindSyncWord(read_ptr, bytes_left); //寻找帧同步,返回第一个同步字的位置 if(read_offset < 0) //没有找到同步字 { break; //跳出循环2,回到循环1 } read_ptr += read_offset; //偏移至同步字的位置 bytes_left -= read_offset; //同步字之后的数据大小 if(bytes_left < 1024) //补充数据 { /* 注意这个地方因为采用的是DMA读取,所以一定要4字节对齐 */ i=(uint32_t)(bytes_left)&3; //判断多余的字节 if(i) i=4-i; //需要补充的字节 memcpy(buffer+i, read_ptr, bytes_left); //从对齐位置开始复制 read_ptr = buffer+i; //指向数据对齐位置 fres = f_read(&file, buffer+bytes_left+i, sizeof(buffer)-bytes_left-i, &rw_num);//补充数据 bytes_left += rw_num; //有效数据流大小 } err = MP3Decode(Mp3Decoder, &read_ptr, &bytes_left, outBuf[bufflag], 0); //开始解码 参数:mp3解码结构体、输入流指针、输入流大小、输出流指针、数据格式 Frames++; if (err != ERR_MP3_NONE) //错误处理 { switch (err) { case ERR_MP3_INDATA_UNDERFLOW: printf("ERR_MP3_INDATA_UNDERFLOW\r\n"); read_ptr = buffer; fres = f_read(&file, read_ptr, sizeof(buffer), &rw_num); bytes_left = rw_num; break; case ERR_MP3_MAINDATA_UNDERFLOW: /* do nothing - next call to decode will provide more mainData */ printf("ERR_MP3_MAINDATA_UNDERFLOW\r\n"); break; default: printf("UNKNOWN ERROR:%d\r\n", err); // 跳过此帧 if (bytes_left > 0) { bytes_left --; read_ptr ++; } break; } } else //解码无错误,准备把数据输出到PCM { MP3GetLastFrameInfo(Mp3Decoder, &Mp3FrameInfo); //获取解码信息 /* 根据解码信息设置采样率 */ if (Mp3FrameInfo.samprate != current_sample_rate) //采样率 { current_sample_rate = Mp3FrameInfo.samprate; printf(" \r\n Bitrate %dKbps", Mp3FrameInfo.bitrate/1000); printf(" \r\n Samprate %dHz", current_sample_rate); printf(" \r\n BitsPerSample %db", Mp3FrameInfo.bitsPerSample); printf(" \r\n nChans %d", Mp3FrameInfo.nChans); printf(" \r\n Layer %d", Mp3FrameInfo.layer); printf(" \r\n Version %d", Mp3FrameInfo.version); printf(" \r\n OutputSamps %d", Mp3FrameInfo.outputSamps); if(current_sample_rate >= I2S_AudioFreq_Default) //I2S_AudioFreq_Default = 2,正常的帧,每次都要改速率 { I2S_Freq_Config(current_sample_rate); //根据采样率修改iis速率 } } /* 输出到DAC */ outputSamps = Mp3FrameInfo.outputSamps; //PCM数据个数 if (outputSamps > 0) { if (Mp3FrameInfo.nChans == 1) //单声道 { //单声道数据需要复制一份到另一个声道 for (i = outputSamps - 1; i >= 0; i--) { outBuf[bufflag][i * 2] = outBuf[bufflag][i]; outBuf[bufflag][i * 2 + 1] = outBuf[bufflag][i]; } outputSamps *= 2; } //非单声道数据可直接由DMA传输到IIS交给DAC /* 等待DMA播放完,这段时间我们可以干其他的事,扫描事件进行处理 */ while((DMA1_Channel5->CCR&DMA_CCR1_EN) && !(DMA1->ISR&DMA1_IT_TC5)) { even_process(); } /*DMA传输完毕*/ DMA_ClearFlag(DMA1_FLAG_TC5 | DMA1_FLAG_TE5); DMA_I2S_Configuration((uint32_t)outBuf[bufflag], outputSamps); bufflag = 1 -bufflag; //切换buffer }//if (outputSamps > 0) }//else 解码正常 if(file.fptr==file.fsize) //如果指针指向了文件尾,表示数据全部读完 { printf("END\r\n"); if(play_index<file_num-1) //自动开始下一首歌曲 { play_index++; player_state = S_SWITCH; //进入切歌状态,跳出 } else { play_index = 0; player_state = S_SWITCH; } break; //跳出这首歌的播放状态 while break; } }//循环2 内 while(player_state != S_SWITCH) }//循环1 外 while(player_state != S_SWITCH) f_close(&file); //结束播放本歌曲,关闭文件 MP3FreeDecoder(Mp3Decoder); I2S_Stop(); }