AT89S52+SD卡+5110LCD=音乐播放器的问题,一样的程序烧写进去...
时间:10-02
整理:3721RD
点击:
http://www.51hei.com/bbs/dpj-19844-1.html中的资料。改了之后可以显示下排(音阶和计时),但就是没有声音和图片。帮我看一下程序有什么问题,特别是其中的/******* 读取一扇区的点阵图像 *********/uchar sd_read_bmp(uchar data *ad)和 /******* 读取一扇区的声音数据 *********/uchar sd_read_sector(uchar data *ad) 这两个函数真的没有问题吗,有问题要怎么改?
#include <reg51.h>
#include <INTRINS.H>
#include <MATH.H>
#include "LCD_3310.H"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define SD_Disable() CS=1
#define SD_Enable() CS=0
/************ 定义管脚 *************/
sbit DOUT = P3^0; //SD卡数据输出
sbit CLK = P3^1; //SD卡时钟输入
sbit DIN = P3^2; //SD卡数据输入
sbit CS = P3^3; //SD卡片选使能
/************ 全局变量 ************/
uchar pbuf[64]; //数据缓冲区
uchar p; //播放缓冲区指针
uchar px; //频谱显示的X坐标
code ulong Track[17] =
{ //0x15000,0x58000 SD卡中各声音文件的首址,以后打算把这些数据放在SD卡的特定配置文件中再读入。
0xd7800-0x8a00,0x76b800-0x8a00,0xedc000-0x8a00,0x1752800-0x8a00,0x1F08000-0x8a00,
0x2569800-0x8a00,0x2EDB800-0x8a00,0x3480000-0x8a00,0x3BFA800-0x8a00,
0x41EB000-0x8a00,0x48EF000-0x8a00,0x508A000-0x8a00,0x59AE800-0x8a00,
0x60AF000-0x8a00,0x6878000-0x8a00,0x6DBE000-0x8a00,0x7525800-0x8a00,
};
/******* SD访问错误码的定义 *******/
#define INIT_CMD0_ERROR 0X01
#define INIT_CMD1_ERROR 0X02
#define READ_BLOCK_ERROR 0X03
#define WRITE_BLOCK_ERROR 0X04
/********* 通用延时函数 ***********/
void delay(uint i)
{
while(i--);
}
/******** SD写入一个字节 **********/
void spi_write(uchar x)
{ //不采用循环结构是为了提高处理速度
/* CLK=1;
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
DIN=1; */
DIN = x & 0x80;
CLK = 0;
CLK = 1;
DIN = x & 0x40;
CLK = 0;
CLK = 1;
DIN = x & 0x20;
CLK = 0;
CLK = 1;
DIN = x & 0x10;
CLK = 0;
CLK = 1;
DIN = x & 0x08;
CLK = 0;
CLK = 1;
DIN = x & 0x04;
CLK = 0;
CLK = 1;
DIN = x & 0x02;
CLK = 0;
CLK = 1;
DIN = x & 0x01;
CLK = 0;
CLK = 1;
}
/******* SD慢速写入一个字节 ********/
void spi_write_low_speed(uchar x)
{
uchar i;
CLK=1;
for(i=0;i<8;i++)
{
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
}
DIN=1;
/* uchar i;
for(i = 8; i; --i)
{
DIN = x & 0x80;
x <<= 1;
CLK = 0;
delay(1);
CLK = 1;
delay(1);
} */
}
/*********** SD读入一字节 ***********/
uchar spi_read(void)
{ //利用51串口的同步移位功能,以达了最高的读度2MHz CLK
RI = 0;
while(RI == 0);
return SBUF;
/*uchar Byte=0;
uchar i=0;
DIN=1;
for(i=0;i<8;i++)
{
CLK=0;
delay(4);
Byte=Byte<<1;
if(DOUT==1)
Byte|=0x01;
CLK=1;
delay(4);
}
return (Byte);*/
}
/******** SD慢速读入一字节 **********/
uchar spi_read_low_speed(void)
{
/*uchar temp,i;
for(i = 8; i; --i)
{
CLK = 0;
delay(1);
temp <<= 1;
if(DOUT) temp++;
CLK = 1;
delay(1);
}
return temp;*/
uchar Byte=0;
uchar i=0;
DIN=1;
for(i=0;i<8;i++)
{
CLK=0;
delay(4);
Byte=Byte<<1;
if(DOUT==1)
Byte|=0x01;
CLK=1;
delay(4);
}
return (Byte);
}
/******** 发送一组SD命令 ************/
uchar write_cmd(uchar *pcmd)
{
/*uchar temp,time=0,i;
for(i = 0; i<6; i++) //一条命令都是6个字节,形参用指针,
{ //指向6个字节命令,
spi_write(pcmd);
}
do //看看写进去没有,通过so管脚
{
temp = spi_read();
time++;
} //一直到读到的不是0xff或超时,退出去
while(temp==0xff && time<100);
return temp;*/
uchar tmp=0xff;
uint Timeout=0;
uchar a;
SD_Disable();
spi_write(0xff);
SD_Enable();
for(a=0;a<0x06;a++)
spi_write(pcmd[a]);
while(tmp==0xff)
{
tmp=spi_read();
if(Timeout++>500)
break;
}
return(tmp);
}
/****** 慢速发送一组SD命令 **********/
uchar write_cmd_low_speed(uchar *pcmd)
{
/*uchar temp,time=0,i;
for(i=0;i<6;i++) //一条命令都是6个字节,形参用指针,
{ //指向6个字节命令,
spi_write_low_speed(pcmd);
}
do //看看写进去没有,通过so管脚
{
temp = spi_read_low_speed();
time++;
} //一直到读到的不是0xff或超时,退出去
while(temp==0xff && time<100);
return temp;*/
uchar tmp=0xff;
uint Timeout=0;
uchar a;
SD_Disable();
spi_write_low_speed(0xff);
SD_Enable();
for(a=0;a<0x06;a++)
spi_write_low_speed(pcmd[a]);
while(tmp==0xff)
{
tmp=spi_read_low_speed();
if(Timeout++>500)
break;
}
return(tmp);
}
/********* SD卡 激活,复位 *********/
uchar sd_reset(void)
{
uchar time,temp,i;
uchar pcmd[6]={0x40,0x00,0x00,0x00,0x00,0x95};
CS = 1;
for(i = 0; i < 0x0f; i++) //复位时,至少要72个时钟周期,
{ //现在是,15*8=120个clk
spi_write_low_speed(0xff);
}
CS = 0;
time=0;
do
{
temp = write_cmd_low_speed(pcmd);
time++;
if(time > 100) return INIT_CMD0_ERROR;
}
while(temp != 0x01); //校验码是0x01,表示写入成功
CS = 1;
spi_write_low_speed(0xff);//时序上要求补8个clk
return 0; //返回0,写入成功
}
/************ SD卡初始化 ************/
uchar sd_init(void)
{
/*uchar time, temp;
uchar pcmd[6] = {0x41,0x00,0x00,0x00,0x00,0xff};
CS = 0;
time = 0;
do
{
temp = write_cmd_low_speed(pcmd);
time++;
if(time > 100) return INIT_CMD1_ERROR;
}
while(temp != 0x00);
CS = 1;
spi_write_low_speed(0xff);
return 0; */
uchar Timeout=0;
uchar i;
uchar idata CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};
for(i=0;i<0x0f;i++)
write_cmd_low_speed(0xff);
SD_Enable();
while(write_cmd_low_speed(CMD)!=0x01)
{
if(Timeout++>5)
return(1);
}
Timeout=0;
CMD[0]=0x41;
CMD[5]=0xff;
while(write_cmd_low_speed(CMD)!=0)
{
if(Timeout++>100)
return(2);
}
SD_Disable();
return(0);
}
/******* 读取一扇区的点阵图像 *********/
uchar sd_read_bmp(uchar data *ad)
{
uchar temp, time, x, pcmd[6];
uint j = 0;
pcmd[0] = 0x51;
pcmd[1] = *ad;
pcmd[2] = *(++ad);
pcmd[3] = *(++ad);
pcmd[4] = 0;
pcmd[5] = 0xff;
CS = 0;
time = 0;
do
{
temp = write_cmd(pcmd);
if(++time > 100)
{
CS = 1;
return READ_BLOCK_ERROR;
}
}
while(temp != 0);
//等待SD卡回应
while(spi_read() != 0x7f); //0xfe,51的串口移位是LSB优先,所以结果高低位倒置
for (j = 0; j < 504; j++) //3310的分辨率为 84 * 48,总计用504字节
{
LCD3310_write_dat(spi_read());
}
for (x = 0; x < 10; x++) spi_read(); //略过8字节数据和2字节CRC
spi_write(0xff);
CS = 1;
return 0;
}
/******* 读取一扇区的声音数据 *********/
uchar sd_read_sector(uchar data *ad)
{
uchar temp, time, pcmd[6];
uint j = 0;
pcmd[0] = 0x51;
pcmd[1] = *ad;
pcmd[2] = *(++ad);
pcmd[3] = *(++ad);
pcmd[4] = 0;
pcmd[5] = 0xff;
CS = 0;
time = 0;
do
{
temp = write_cmd(pcmd);
if(++time > 100)
{
CS = 1;
return READ_BLOCK_ERROR;
}
}
while(temp != 0);
//等待SD回应的时间有点长,所以在这里插入显示模拟的频谱图
temp = pbuf[16]; //随便挑一个数据显示
LCD3310_set_XY(px,5); //设定显示位置
px += 6;
if (px >= 39) px = 0;
if (temp & 0x80) temp ^= 0x80; //求得声音振幅
else temp = 0x80 - temp;
temp = Level[temp>>4]; //不同幅度对应不同的谱线图案
LCD3310_write_dat(temp);
LCD3310_write_dat(temp);
LCD3310_write_dat(temp);
while(spi_read() != 0x7f);//0xfe,51的串口移位是LSB优先,所以结果高低位倒置
while(1) //读取512字节数据
{
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //为求快速,不用函数调用
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //直接启动串口移入
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //连续读四字节
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF;
if(j >= 512) break;
while((((uchar)j - p) & 63) > 55); //检测播放进度,
} //如果缓冲区接近溢出,先暂停等待
spi_read();//略过 crc
spi_read();//略过 crc
spi_write(0xff);//SD 时序要求补8个脉冲
CS = 1;
return 0;
}
/**************************** 主程序 *******************************/
int main(void)
{
uchar key,n,Count,Min,Sec;
ulong addr; // SD 的扇区地址
P2 = 0x80; // DAC 输出中点电压
RI = 1;
REN = 1;
TMOD = 0x02;
TH0 = 256 - 62.5; //定时器设定约为 32KHz,和WAV文件取样率对应
ET0 = 1;
EA = 1;
px = 0;
n = 64;
do pbuf[--n] = 0x80; while(n); //填充播放缓冲区
delay(65535);
LCD3310_init();
LCD3310_set_XY(0,0);
LCD3310_write_cmd(0x22); //设定LCD扫描顺序
sd_reset();
sd_init();
addr = 0x4f400;
sd_read_bmp((uchar) &addr); //显示欢迎画面
while (D_C == 1) ;
while (D_C == 0) ; //等待按键
delay(65535);
//============== main loop ==================
while(1) //循环播放所有曲目
{
TR0 = 0;
LCD3310_write_cmd(0x22);
LCD3310_set_XY(0,0);
addr = 0x4f600 + ((uint)n<<9);
sd_read_bmp((uchar) &addr); //显示歌名、歌手
LCD3310_write_cmd(0x20);
TR0 = 1;
p = 0xd0;
Min = 0;
Sec = 0;
Count= 0;
for (addr = Track[n]; addr < Track[n+1];)//播放第n曲
{
//============ 按键处理 ===============
key = (key >> 2) | (P3 & 0x30); //仅一句的扫键函数,包括扫描和消抖
if (key == 0x03) //键码为03是播放/暂停键
{
LCD3310_set_XY(78,5);
TCON ^= 0x10; //TR0 取反
if (TR0) LCD3310_print(11); //显示播放符号
else LCD3310_print(12); //显示暂停符号
}
else if (key == 0x2b) //键码为2b是前一曲
{
if ((Min || (Sec & 0xf0))) n--;//10秒后跳本曲开始
else n -= 2; //10秒内跳前一曲
break;
}
else if (key == 0x17) //键码为17是后一曲
break;
//======== 读一扇区数据或暂停 =========
if (TR0 == 0) {delay(2000); continue;}
sd_read_sector((uchar) &addr);
addr += 512;
//=========== 播放时间计数 ============
Count += 2;
if (Count >= 125)
{
Count -= 125;
Sec++;
if ((Sec & 0x0f) > 9)
{
Sec += 6;
if (Sec >= 0x60)
{
Sec = 0;
Min++;
if ((Min & 0x0f) > 9)
{
Min += 6;
if (Min > 0x60) Min = 0;
}
}
}
}
//======= 分时间片显示时间/标志 ========
switch (Count & 14)
{
case 2:
LCD3310_set_XY(44,5);
LCD3310_print(Min>>4);//分钟十位
break;
case 4:
LCD3310_set_XY(50,5);
LCD3310_print(Min&15);//分钟个位
break;
case 6:
LCD3310_set_XY(56,5);
LCD3310_print(10); //分隔符
break;
case 8:
LCD3310_set_XY(62,5);
LCD3310_print(Sec>>4);//秒十位
break;
case 10:
LCD3310_set_XY(68,5);
LCD3310_print(Sec&15);//秒个位
break;
case 12:
LCD3310_set_XY(78,5);
if (Count & 0x40) LCD3310_print(13); //闪动播放符号
else LCD3310_print(11);
}
}
n++; //下一曲
n &=2; //这个SD卡只有16首歌
}//while(1);
}//main()
void timer0 (void) interrupt 1 using 1
{
if (TL0 & 1) _nop_(); //消除中断响应时间不一致,造成的频率抖动
P2= pbuf[++p & 63]; //输出一个声音数据
}
#include <reg51.h>
#include <INTRINS.H>
#include <MATH.H>
#include "LCD_3310.H"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define SD_Disable() CS=1
#define SD_Enable() CS=0
/************ 定义管脚 *************/
sbit DOUT = P3^0; //SD卡数据输出
sbit CLK = P3^1; //SD卡时钟输入
sbit DIN = P3^2; //SD卡数据输入
sbit CS = P3^3; //SD卡片选使能
/************ 全局变量 ************/
uchar pbuf[64]; //数据缓冲区
uchar p; //播放缓冲区指针
uchar px; //频谱显示的X坐标
code ulong Track[17] =
{ //0x15000,0x58000 SD卡中各声音文件的首址,以后打算把这些数据放在SD卡的特定配置文件中再读入。
0xd7800-0x8a00,0x76b800-0x8a00,0xedc000-0x8a00,0x1752800-0x8a00,0x1F08000-0x8a00,
0x2569800-0x8a00,0x2EDB800-0x8a00,0x3480000-0x8a00,0x3BFA800-0x8a00,
0x41EB000-0x8a00,0x48EF000-0x8a00,0x508A000-0x8a00,0x59AE800-0x8a00,
0x60AF000-0x8a00,0x6878000-0x8a00,0x6DBE000-0x8a00,0x7525800-0x8a00,
};
/******* SD访问错误码的定义 *******/
#define INIT_CMD0_ERROR 0X01
#define INIT_CMD1_ERROR 0X02
#define READ_BLOCK_ERROR 0X03
#define WRITE_BLOCK_ERROR 0X04
/********* 通用延时函数 ***********/
void delay(uint i)
{
while(i--);
}
/******** SD写入一个字节 **********/
void spi_write(uchar x)
{ //不采用循环结构是为了提高处理速度
/* CLK=1;
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
DIN=1; */
DIN = x & 0x80;
CLK = 0;
CLK = 1;
DIN = x & 0x40;
CLK = 0;
CLK = 1;
DIN = x & 0x20;
CLK = 0;
CLK = 1;
DIN = x & 0x10;
CLK = 0;
CLK = 1;
DIN = x & 0x08;
CLK = 0;
CLK = 1;
DIN = x & 0x04;
CLK = 0;
CLK = 1;
DIN = x & 0x02;
CLK = 0;
CLK = 1;
DIN = x & 0x01;
CLK = 0;
CLK = 1;
}
/******* SD慢速写入一个字节 ********/
void spi_write_low_speed(uchar x)
{
uchar i;
CLK=1;
for(i=0;i<8;i++)
{
if(x&0x80)
DIN=1;
else
DIN=0;
CLK=0;
delay(4);
x=x<<1;
CLK=1;
delay(4);
}
DIN=1;
/* uchar i;
for(i = 8; i; --i)
{
DIN = x & 0x80;
x <<= 1;
CLK = 0;
delay(1);
CLK = 1;
delay(1);
} */
}
/*********** SD读入一字节 ***********/
uchar spi_read(void)
{ //利用51串口的同步移位功能,以达了最高的读度2MHz CLK
RI = 0;
while(RI == 0);
return SBUF;
/*uchar Byte=0;
uchar i=0;
DIN=1;
for(i=0;i<8;i++)
{
CLK=0;
delay(4);
Byte=Byte<<1;
if(DOUT==1)
Byte|=0x01;
CLK=1;
delay(4);
}
return (Byte);*/
}
/******** SD慢速读入一字节 **********/
uchar spi_read_low_speed(void)
{
/*uchar temp,i;
for(i = 8; i; --i)
{
CLK = 0;
delay(1);
temp <<= 1;
if(DOUT) temp++;
CLK = 1;
delay(1);
}
return temp;*/
uchar Byte=0;
uchar i=0;
DIN=1;
for(i=0;i<8;i++)
{
CLK=0;
delay(4);
Byte=Byte<<1;
if(DOUT==1)
Byte|=0x01;
CLK=1;
delay(4);
}
return (Byte);
}
/******** 发送一组SD命令 ************/
uchar write_cmd(uchar *pcmd)
{
/*uchar temp,time=0,i;
for(i = 0; i<6; i++) //一条命令都是6个字节,形参用指针,
{ //指向6个字节命令,
spi_write(pcmd);
}
do //看看写进去没有,通过so管脚
{
temp = spi_read();
time++;
} //一直到读到的不是0xff或超时,退出去
while(temp==0xff && time<100);
return temp;*/
uchar tmp=0xff;
uint Timeout=0;
uchar a;
SD_Disable();
spi_write(0xff);
SD_Enable();
for(a=0;a<0x06;a++)
spi_write(pcmd[a]);
while(tmp==0xff)
{
tmp=spi_read();
if(Timeout++>500)
break;
}
return(tmp);
}
/****** 慢速发送一组SD命令 **********/
uchar write_cmd_low_speed(uchar *pcmd)
{
/*uchar temp,time=0,i;
for(i=0;i<6;i++) //一条命令都是6个字节,形参用指针,
{ //指向6个字节命令,
spi_write_low_speed(pcmd);
}
do //看看写进去没有,通过so管脚
{
temp = spi_read_low_speed();
time++;
} //一直到读到的不是0xff或超时,退出去
while(temp==0xff && time<100);
return temp;*/
uchar tmp=0xff;
uint Timeout=0;
uchar a;
SD_Disable();
spi_write_low_speed(0xff);
SD_Enable();
for(a=0;a<0x06;a++)
spi_write_low_speed(pcmd[a]);
while(tmp==0xff)
{
tmp=spi_read_low_speed();
if(Timeout++>500)
break;
}
return(tmp);
}
/********* SD卡 激活,复位 *********/
uchar sd_reset(void)
{
uchar time,temp,i;
uchar pcmd[6]={0x40,0x00,0x00,0x00,0x00,0x95};
CS = 1;
for(i = 0; i < 0x0f; i++) //复位时,至少要72个时钟周期,
{ //现在是,15*8=120个clk
spi_write_low_speed(0xff);
}
CS = 0;
time=0;
do
{
temp = write_cmd_low_speed(pcmd);
time++;
if(time > 100) return INIT_CMD0_ERROR;
}
while(temp != 0x01); //校验码是0x01,表示写入成功
CS = 1;
spi_write_low_speed(0xff);//时序上要求补8个clk
return 0; //返回0,写入成功
}
/************ SD卡初始化 ************/
uchar sd_init(void)
{
/*uchar time, temp;
uchar pcmd[6] = {0x41,0x00,0x00,0x00,0x00,0xff};
CS = 0;
time = 0;
do
{
temp = write_cmd_low_speed(pcmd);
time++;
if(time > 100) return INIT_CMD1_ERROR;
}
while(temp != 0x00);
CS = 1;
spi_write_low_speed(0xff);
return 0; */
uchar Timeout=0;
uchar i;
uchar idata CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};
for(i=0;i<0x0f;i++)
write_cmd_low_speed(0xff);
SD_Enable();
while(write_cmd_low_speed(CMD)!=0x01)
{
if(Timeout++>5)
return(1);
}
Timeout=0;
CMD[0]=0x41;
CMD[5]=0xff;
while(write_cmd_low_speed(CMD)!=0)
{
if(Timeout++>100)
return(2);
}
SD_Disable();
return(0);
}
/******* 读取一扇区的点阵图像 *********/
uchar sd_read_bmp(uchar data *ad)
{
uchar temp, time, x, pcmd[6];
uint j = 0;
pcmd[0] = 0x51;
pcmd[1] = *ad;
pcmd[2] = *(++ad);
pcmd[3] = *(++ad);
pcmd[4] = 0;
pcmd[5] = 0xff;
CS = 0;
time = 0;
do
{
temp = write_cmd(pcmd);
if(++time > 100)
{
CS = 1;
return READ_BLOCK_ERROR;
}
}
while(temp != 0);
//等待SD卡回应
while(spi_read() != 0x7f); //0xfe,51的串口移位是LSB优先,所以结果高低位倒置
for (j = 0; j < 504; j++) //3310的分辨率为 84 * 48,总计用504字节
{
LCD3310_write_dat(spi_read());
}
for (x = 0; x < 10; x++) spi_read(); //略过8字节数据和2字节CRC
spi_write(0xff);
CS = 1;
return 0;
}
/******* 读取一扇区的声音数据 *********/
uchar sd_read_sector(uchar data *ad)
{
uchar temp, time, pcmd[6];
uint j = 0;
pcmd[0] = 0x51;
pcmd[1] = *ad;
pcmd[2] = *(++ad);
pcmd[3] = *(++ad);
pcmd[4] = 0;
pcmd[5] = 0xff;
CS = 0;
time = 0;
do
{
temp = write_cmd(pcmd);
if(++time > 100)
{
CS = 1;
return READ_BLOCK_ERROR;
}
}
while(temp != 0);
//等待SD回应的时间有点长,所以在这里插入显示模拟的频谱图
temp = pbuf[16]; //随便挑一个数据显示
LCD3310_set_XY(px,5); //设定显示位置
px += 6;
if (px >= 39) px = 0;
if (temp & 0x80) temp ^= 0x80; //求得声音振幅
else temp = 0x80 - temp;
temp = Level[temp>>4]; //不同幅度对应不同的谱线图案
LCD3310_write_dat(temp);
LCD3310_write_dat(temp);
LCD3310_write_dat(temp);
while(spi_read() != 0x7f);//0xfe,51的串口移位是LSB优先,所以结果高低位倒置
while(1) //读取512字节数据
{
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //为求快速,不用函数调用
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //直接启动串口移入
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF; //连续读四字节
RI = 0;_nop_(); pbuf[j++ & 63] = SBUF;
if(j >= 512) break;
while((((uchar)j - p) & 63) > 55); //检测播放进度,
} //如果缓冲区接近溢出,先暂停等待
spi_read();//略过 crc
spi_read();//略过 crc
spi_write(0xff);//SD 时序要求补8个脉冲
CS = 1;
return 0;
}
/**************************** 主程序 *******************************/
int main(void)
{
uchar key,n,Count,Min,Sec;
ulong addr; // SD 的扇区地址
P2 = 0x80; // DAC 输出中点电压
RI = 1;
REN = 1;
TMOD = 0x02;
TH0 = 256 - 62.5; //定时器设定约为 32KHz,和WAV文件取样率对应
ET0 = 1;
EA = 1;
px = 0;
n = 64;
do pbuf[--n] = 0x80; while(n); //填充播放缓冲区
delay(65535);
LCD3310_init();
LCD3310_set_XY(0,0);
LCD3310_write_cmd(0x22); //设定LCD扫描顺序
sd_reset();
sd_init();
addr = 0x4f400;
sd_read_bmp((uchar) &addr); //显示欢迎画面
while (D_C == 1) ;
while (D_C == 0) ; //等待按键
delay(65535);
//============== main loop ==================
while(1) //循环播放所有曲目
{
TR0 = 0;
LCD3310_write_cmd(0x22);
LCD3310_set_XY(0,0);
addr = 0x4f600 + ((uint)n<<9);
sd_read_bmp((uchar) &addr); //显示歌名、歌手
LCD3310_write_cmd(0x20);
TR0 = 1;
p = 0xd0;
Min = 0;
Sec = 0;
Count= 0;
for (addr = Track[n]; addr < Track[n+1];)//播放第n曲
{
//============ 按键处理 ===============
key = (key >> 2) | (P3 & 0x30); //仅一句的扫键函数,包括扫描和消抖
if (key == 0x03) //键码为03是播放/暂停键
{
LCD3310_set_XY(78,5);
TCON ^= 0x10; //TR0 取反
if (TR0) LCD3310_print(11); //显示播放符号
else LCD3310_print(12); //显示暂停符号
}
else if (key == 0x2b) //键码为2b是前一曲
{
if ((Min || (Sec & 0xf0))) n--;//10秒后跳本曲开始
else n -= 2; //10秒内跳前一曲
break;
}
else if (key == 0x17) //键码为17是后一曲
break;
//======== 读一扇区数据或暂停 =========
if (TR0 == 0) {delay(2000); continue;}
sd_read_sector((uchar) &addr);
addr += 512;
//=========== 播放时间计数 ============
Count += 2;
if (Count >= 125)
{
Count -= 125;
Sec++;
if ((Sec & 0x0f) > 9)
{
Sec += 6;
if (Sec >= 0x60)
{
Sec = 0;
Min++;
if ((Min & 0x0f) > 9)
{
Min += 6;
if (Min > 0x60) Min = 0;
}
}
}
}
//======= 分时间片显示时间/标志 ========
switch (Count & 14)
{
case 2:
LCD3310_set_XY(44,5);
LCD3310_print(Min>>4);//分钟十位
break;
case 4:
LCD3310_set_XY(50,5);
LCD3310_print(Min&15);//分钟个位
break;
case 6:
LCD3310_set_XY(56,5);
LCD3310_print(10); //分隔符
break;
case 8:
LCD3310_set_XY(62,5);
LCD3310_print(Sec>>4);//秒十位
break;
case 10:
LCD3310_set_XY(68,5);
LCD3310_print(Sec&15);//秒个位
break;
case 12:
LCD3310_set_XY(78,5);
if (Count & 0x40) LCD3310_print(13); //闪动播放符号
else LCD3310_print(11);
}
}
n++; //下一曲
n &=2; //这个SD卡只有16首歌
}//while(1);
}//main()
void timer0 (void) interrupt 1 using 1
{
if (TL0 & 1) _nop_(); //消除中断响应时间不一致,造成的频率抖动
P2= pbuf[++p & 63]; //输出一个声音数据
}
写的挺好的,学习了
毕业设计要做这个,请问能加下qq咨询一下吗?212857009