在各种基于ARM的作品中,图像显示功能的实现已经十分普遍,但实现音频播放功能的却不多。这里就以NUCLEO_L073为控制核心,介绍一种带中文菜单的MP3点播器,其组成结构如图1所示。 在整体结构中,NUCLEO_L073主要承担的任务有:按键判别处理、菜单显示、控制信号输出、串行指令发送等。 Mini MP3播放模块承担的任务有:接收控制信号、读取SDHC文件、音频变换处理及驱动处理等。该模块共有16个引脚,其外观如图3所示。 (注:在VCC和GND接L073开发板的+5V和GND引脚,ADKEY_1接L073的PC13即B1键的情况下,可简单测试歌曲的播放。在RX和TX分别与L073开发板的TX和RX 的情况下,也测试指令方式播放歌曲 。) MP3播放模块在使用过程中有2种模式,即简单的独立操控模式和基于通讯的指令操控模式。 对于独立操控模式,只需配置几个小按键就可对它进行控制,所这些的操作有播放第一首、下一首、上一首、增加音量、减小音量等。在配置相应电阻的情况下,还可通过电位的变化来实现指定顺序播放。 对于指令操控模式,其操控方式要相对灵活,但需要有串行通讯来配合工作,其通讯的波特率为9600bps。 该模块各引脚的功能如下: 菜单显示是采用0.96’双色OLED显示屏,是一种无需背光的自发光器件。该模块尽管体积小很小,但分辨率却达128*64。对于采用IIC接口方式的OLED,其引脚只有4个,所以很节省GPIO资源。对于双色的OLED显示屏,其上部的1/3为黄色,余下的2/3则为蓝色,其显示效果如图5所示。 为了实现中文菜单的显示,是采用构建小字库的方式完成的,所用的工具为PCtoLCD2002,其界面如图6所示。 在控制信号的发送过程中,即支持GPIO输出开关信号,也支持串口输出的指令信号。为了便于统一管理,在MP3播放模块上并没有直接连接按键,而是通过NUCLEO_L073按使用者的操作来发送信号。 除了硬件方面的设计,要实现相应的功能目标,软件的设计也是不可或缺的。 在显示方面,涉及的功能函数有:OLED 初始化函数、清屏函数、汉字显示函数、字符串显示函数及菜单显示函数等。 对于IIC接口的OLED其初始化函数如下: - // GND GND
- // VCC 5V/3.3v
- // SCL PA5
- // SDA PA7
- #define OLED_MODE 0
- #define SIZE 8
- #define XLevelL 0x00
- #define XLevelH 0x10
- #define Max_Column 128
- #define Max_Row 64
- #define Brightness 0xFF
- #define X_WIDTH 128
- #define Y_WIDTH 64
- #define OLED_SCLK_Clr() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)
- #define OLED_SCLK_Set() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET)
- void OLED_Init(void)
- {
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
- GPIO_InitTypeDef GPIO_InitStruct;
- __HAL_RCC_GPIOA_CLK_ENABLE();
- GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- OLED_SCLK_Set();
- OLED_SDIN_Set();
- Delay_1ms(800); //delay_ms(800);
- OLED_WR_Byte(0xAE,OLED_CMD);//--display off
- OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
- OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
- OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
- OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
- OLED_WR_Byte(0x81,OLED_CMD); // contract control
- OLED_WR_Byte(0xFF,OLED_CMD);//--128
- OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
- OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
- OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
- OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
- OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
- OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
- OLED_WR_Byte(0x00,OLED_CMD);//
- OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
- OLED_WR_Byte(0x80,OLED_CMD);//
- OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
- OLED_WR_Byte(0x05,OLED_CMD);//
- OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
- OLED_WR_Byte(0xF1,OLED_CMD);//
- OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
- OLED_WR_Byte(0x12,OLED_CMD);//
- OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
- OLED_WR_Byte(0x30,OLED_CMD);//
- OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
- OLED_WR_Byte(0x14,OLED_CMD);//
- OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
- }
复制代码清屏函数如下: - void OLED_Clear(void)
- {
- unsigned char i,n;
- for(i=0;i<8;i++)
- {
- OLED_WR_Byte (0xb0+i,OLED_CMD);
- OLED_WR_Byte (0x00,OLED_CMD);
- OLED_WR_Byte (0x10,OLED_CMD);
- for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
- }
- }
复制代码在字符显示方面,是使用程序数组中所存储的字模,并分为不同显示大小的规格,字符串显示函数如下: - void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *chr,unsigned char Char_Size)
- {
- unsigned char j=0;
- while (chr[j]!='\0')
- { OLED_ShowChar(x,y,chr[j],Char_Size);
- x+=8;
- if(x>120){x=0;y+=2;}
- j++;
- }
- }
复制代码在汉字显示方面,是采用自行构建的小字库,因此在使用是按排列顺序来建立映射关系,具体的汉字显示函数如下: - void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no)
- {
- unsigned char t,adder=0;
- OLED_Set_Pos(x,y);
- for(t=0;t<16;t++)
- {
- OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
- adder+=1;
- }
- OLED_Set_Pos(x,y+1);
- for(t=0;t<16;t++)
- {
- OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
- adder+=1;
- }
- }
复制代码为了便于快捷地构建乐曲菜单,是将各首歌名分别建立一个函数来实现,以“莫斯科郊外的晚上”为例,其显示函数为: - void cd2()
- { // 莫斯科郊外的晚上
- OLED_Clearp(); // 局部清除
- OLED_ShowCHinese(18,2,3);
- OLED_ShowCHinese(36,2,4);
- OLED_ShowCHinese(54,2,5);
- OLED_ShowCHinese(72,2,6);
- OLED_ShowCHinese(90,2,7);
- OLED_ShowCHinese(108,2,8);
- OLED_ShowCHinese(18,4,9);
- OLED_ShowCHinese(36,4,10);
- }
复制代码MP3点播器的处理流程为: 选择歌曲播放的方向(前2项)->选择歌曲->选择播放中的控制功能(后4项)->轮回处理 由于在NUCLEO_L073开发板上只配置了一个供控制所用的按键,为了省去另加按键的麻烦,这里是以单按键的方式来执行选择处理,即短按为选择,长按为确认。 在点播过程中,主要分为两类操作,即歌曲的选择(可选上一首或下一首)和播放控制(暂停、退出、加大音量、减小音量)。 受OLED屏显示信息量的限制,在歌曲选择时,是随着选择来更换歌曲名称;而在播放控制时,是通过控制工具栏的符号闪动来提示当前供选择的功能项,其处理效果如图7所示。 菜单选择的程序代码如下: - f=1; // 进入菜单选择
- while(f)
- {
- if(i==0) cd1(); // 雪绒花
- if(i==1) cd2(); // 莫斯科郊外的晚上
- if(i==2) cd3(); // 等待
- if(i==3) cd4(); // 鸿雁
- if(i==4) cd5(); // 贝加尔湖畔
- if(i==5) cd6(); // 春暖花开
- if(i==6) cd7(); // 传奇
- if(i==7) cd8(); // 她
- if(i==8) cd9(); // 味道
- if(i==9) cd10(); // 放心去飞
- cdm(); // 功能键提示栏显示
- while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)!=0);
- HAL_Delay(6000); // 长短按区分延时
- if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)==0) f=0;
- else
- {
- i++;
- if(i>9) i=0; // 轮回处理
- }
- }
复制代码播放控制的处理代码如下: - cdm(); // 显示功能选择栏
- f=1;
- while(f)
- {
- while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)!=0)
- {
- // 产生当前功能选择项闪动效果
- OLED_ShowCHinese(i*18,6,46);
- HAL_Delay(200);
- cdm();
- HAL_Delay(200);
- }
- HAL_Delay(700);
- // 长短按区分处理
- if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)==0) f=0;
- i++;
- if(i>5) i=0; // 功能选择轮回处理
- HAL_Delay(500);
- }
复制代码在指令控制的实现上,主要涉及串行通讯的初始化、指令生成与输出等函数。测试过程中,可通过串口助手来验证相应的指令功能。 常用的指令有: 播放_7E FF 06 0D 00 00 00 FE EE EF 暂停_7E FF 06 0E 00 00 00 FE ED EF 下一首_7E FF 06 01 00 00 00 FE FA EF 上一首_7E FF 06 02 00 00 00 FE F9 EF 播放的代码如下: - void play()
- {
- // 定义指令内容
- unsigned cha play_cmd [10] =
- { 0X7E, 0xFF, 0x06, 0X0D, 00, 00, 00, 0xFE, 0xee, 0XEF};
- sendCmd (play_cmd);
- }
复制代码未来的改进方向,是采用TFT触摸屏来取代OLED屏显示,从而使菜单所列出的选项更丰富,操作也更便捷。此外,采用硬字库替代小字库也使菜单的更新更灵活。当然你要有承担造价会较大提升的心理准备哟!
|
unsigned cha play_cmd [10] =
{ 0X7E, 0xFF, 0x06, 0X0D, 00, 00, 00, 0xFE, 0xee, 0XEF};
sendCmd (play_cmd);
}
以数组的方式按字节发送指令内容,至于指令是按参考资料分析出来的。
楼主能不能加个qq啊 我想详细咨询一下这个到底该怎么去做1658348073
太具体的也难帮上你了,前一段电脑完蛋了,多年的积累毁于一旦。此外,本来的双核大赛前三,也因工程文件的丢失而名落孙山,唉参透了!
多谢了!
谢了!
板子上少了flash作为字库,否则用串口传一下就可以。
谢了!
不妨一试