📄 main.c
字号:
/* 2006.10.15
****************************************
** Copyright (C) W.ch 1999-2006 **
** Web: http://www.wch.cn **
****************************************
** Mp3 Module Test @CH375 **
** TC2.0@PC, KC7.0@MCS51 **
****************************************
*/
#include <reg52.h>
#include <string.h>
#include <stdio.h>
#define MAX_PATH_LEN 64 // 最大路径长度,含所有斜杠分隔符和小数点间隔符以及路径结束符00H,CH375模块支持的最大值是62,最小值是1
#include "CH375HM.H"
/* 该程序查找U盘中的/C51/CH375HFT.MP3文件
1.若找到,则播放,播放完毕,获取U盘中第一个音频文件名,并播放,播放完毕枚举下一曲。
2.找不到,则获取U盘中第一个音频文件名, 并播放,播放完毕枚举下一曲。
*/
/* 电路连接方式,只需要连接3根线,使用串口同步码启动操作
PC 单片机 模块
RXD = TXD = RXD
-- = RXD = TXD
GND = GND = GND
*/
// 说明:
// 1. 在通过PC监视演示过程中,当向PC发送演示信息时,模块也会收到数据,频繁进入串口中断,但会忽略收到的数据。
// 2. 模块必须先上电
// 3. 晶振:24M,波特率:9600bps
// 4. 若启用PC监视,开放宏PC_DEMO
// #define PC_DEMO 1 // 启用PC监视
#define CMD_RootMp3Inf 0xE0 // 枚举U盘中所有音频文件数量
#define CMD_Decode 0xE1 // 播放当前音频文件
#define CMD_Pause 0xE2 // 暂停播放
#define CMD_Stop 0xE3 // 停止播放
#define CMD_VolumeH 0xE4 // 音量增大
#define CMD_VolumeL 0xE5 // 音量减小
#define CMD_EnumUpMp3 0xE6 // 枚举上一个文件
#define CMD_EnumDownMp3 0xE7 // 枚举下一个文件
#define CMD_EnumCurrent 0xE8 // 枚举第一首音频文件
#define CMD_AutoDec 0xEF // 自动播放
CMD_PARAM idata mCmdParam; // 默认情况下该结构将占用60字节的RAM,可以修改MAX_PATH_LEN常量,当修改为32时,只占用32字节的RAM
sbit LED_OUT = P1^4; // P1.4 低电平驱动LED显示,用于监控演示程序的进度
/* 以毫秒为单位延时,适用于24MHz时钟 */
void mDelaymS( unsigned char delay )
{
unsigned char i, j, c;
for ( i = delay; i != 0; i -- )
{
for ( j = 200; j != 0; j -- ) c += 3; /* 在24MHz时钟下延时500uS */
for ( j = 200; j != 0; j -- ) c += 3; /* 在24MHz时钟下延时500uS */
}
}
/* 发送一个字节数据给CH375模块 */
void mSendByte( unsigned char c )
{
TI = 0;
SBUF = c;
while ( TI == 0 );
}
/* 从CH375模块接收一个字节数据 */
unsigned char mRecvByte( )
{
unsigned char c;
while ( RI == 0 );
c = SBUF;
RI = 0;
return c;
}
/* 执行命令 */
unsigned char ExecCommand( unsigned char cmd, unsigned char len )
/* 输入命令码和输入参数长度,返回操作状态码,输入参数和返回参数都在CMD_PARAM结构中 */
{
unsigned char i, j, status;
mSendByte( SER_SYNC_CODE1 ); /* 发送串口同步码通知模块,说明命令码开始发送,请求开始执行命令 */
mSendByte( SER_SYNC_CODE2 ); /* 用两个串口同步码代替STA#的下降沿 */
/* 上面两个串口同步码应该连续发送,如果不连续,那么间隔时间不能超过20mS,否则命令无效 */
RI = 0;
mSendByte( cmd ); /* 写入命令码 */
mSendByte( len ); /* 写入后续参数的长度 */
if ( len ) { /* 有参数 */
for ( i = 0; i != len; i ++ ) mSendByte( mCmdParam.Other.mBuffer[ i ] ); /* 依次写入参数 */
}
while ( 1 ) { /* 处理数据传输,直到操作完成才退出 */
status = mRecvByte( ); /* 等待模块完成操作并返回操作状态 */
if ( status == ERR_SUCCESS ) { /* 操作成功 */
i = mRecvByte( ); /* 返回结果数据的长度 */
if ( i ) { /* 有结果数据 */
j = 0;
do { /* 使用do+while结构是因为其效率高于for */
mCmdParam.Other.mBuffer[ j ] = mRecvByte( ); /* 接收结果数据并保存到参数结构中 */
j ++;
} while ( -- i );
}
break; /* 操作成功返回 */
}
else if ( status == USB_INT_DISK_READ || status == USB_INT_DISK_WRITE || status == USB_INT_DISK_RETRY ) { /* 正在从U盘读数据块,请求数据读出,正在向U盘写数据块,请求数据写入,读写数据块失败重试 */
break; /* 本程序只使用以字节为单位的文件读写子程序,所以正常情况下不会收到该状态码,操作失败返回 */
}
else { /* 操作失败 */
if ( status == ERR_DISK_DISCON || status == ERR_USB_CONNECT )
mDelaymS( 100 ); /* U盘刚刚连接或者断开,应该延时几十毫秒再操作 */
break; /* 操作失败返回 */
}
}
return( status );
}
/* 检查操作状态,如果错误则显示错误代码并停机 */
void mStopIfError( unsigned char iError )
{
unsigned char led;
if ( iError == ERR_SUCCESS ) return; /* 操作成功 */
#ifdef PC_DEMO
printf( "\nPC_DEMO: Error: %02x\n", (unsigned short)iError ); /* 显示错误 */
#endif
led = 0;
while(1)
{
LED_OUT = led & 1; /* LED闪烁 */
mDelaymS( 100 );
led ^= 1;
}
}
void main(void)
{
unsigned char i;
unsigned char mCmdCode; // 命令码
LED_OUT = 0; // 开机后LED亮一下以示工作
mDelaymS(100);
LED_OUT = 1;
// 设置与Mp3模块通讯的串口
SCON = 0x50;
PCON = 0x80;
TMOD = 0x21;
TH1 = 0xF3; // 24MHz晶振, 9600bps
TR1 = 1;
TI = 1;
#ifdef PC_DEMO
printf("\nPC_DEMO: Start Demo\n");
#endif
while( 1 ) // 使用查询方式看U盘是否连接
{
i = ExecCommand( CMD_QueryStatus, 0 ); // 查询当前模块的状态
mStopIfError( i );
if ( mCmdParam.Status.mDiskStatus >= DISK_CONNECT )
break; // U盘已经连接
mDelaymS( 100 ); // 可以在打算读写U盘时再查询,没有必要一直连续不停地查询,可以让单片机做其它事,没事可做就延时等待一会再查询
}
mDelaymS( 200 ); // 延时,可选操作,有的USB存储器需要几十毫秒的延时
LED_OUT = 0;
for( i = 0; i < 5; i ++ ) // 检查U盘是否准备好,大多数U盘不需要这一步,但是某些U盘必须要执行这一步才能工作
{
mDelaymS( 100 );
#ifdef PC_DEMO
printf("\nPC_DEMO: Ready?\n");
#endif
if ( ExecCommand( CMD_DiskReady, 0 ) == ERR_SUCCESS )
{
#ifdef PC_DEMO
printf("\nPC_DEMO: Disk is Ready!\n");
#endif
break; // 查询磁盘是否准备好
}
}
#ifdef PC_DEMO
printf("\nPC_DEMO: Search File:/C51/CH375HFT.MP3\n");
#endif
strcpy(mCmdParam.Open.mPathName, "/C51/CH375HFT.MP3"); // 文件名,该文件在C51子目录下
i = ExecCommand(CMD_FileOpen,MAX_PATH_LEN); // 打开文件,输入参数置为最大值,省得再计算参数长度
if ( i == ERR_MISS_DIR || i == ERR_MISS_FILE ) /* ERR_MISS_DIR说明没有找到C51子目录,ERR_MISS_FILE说明没有找到文件 */
{
#ifdef PC_DEMO
printf("\nPC_DEMO: Can't Find File:/C51/CH375HFT.MP3\n" );
#endif
EnumAllFile:
mCmdCode = CMD_EnumCurrent;
while(1)
{
switch(mCmdCode)
{
case CMD_EnumCurrent:
{
i = ExecCommand(CMD_EnumCurrent,0);
mStopIfError(i);
#ifdef PC_DEMO
printf("\nPC_DEMO: Current File Is:%s\n",mCmdParam.Other.mBuffer);
#endif
mCmdCode = CMD_Decode;
}break;
case CMD_EnumDownMp3:
{
#ifdef PC_DEMO
printf("\nPC_DEMO: Enum Next Files:...\n");
#endif
i = ExecCommand( CMD_EnumDownMp3,0 );
mStopIfError( i );
#ifdef PC_DEMO
printf("\nPC_DEMO: Current File Is:%s\n",mCmdParam.Other.mBuffer);
#endif
mCmdCode = CMD_Decode;
}break;
case CMD_Decode:
{
#ifdef PC_DEMO
printf("\nPC_DEMO: Paly...\n");
#endif
i = ExecCommand(CMD_Decode,0);
mStopIfError(i);
CONTINUE0: i = mRecvByte();
if(i == 0xF2) mCmdCode = CMD_EnumDownMp3;
else goto CONTINUE0;
}break;
}
}
}
else
{
mStopIfError(i);
i = ExecCommand(CMD_Decode,0);
mStopIfError(i);
#ifdef PC_DEMO
printf("\nPC_DEMO: Play:/C51/CH375HFT.MP3\n");
#endif
CONTINUE1:
i = mRecvByte();
if(i == 0xF2) goto EnumAllFile;
else goto CONTINUE1;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -