转自网络:
这个小玩意,采用 ATMEL 的传统51MCU作主控制芯片,加上SD卡和显示屏,就可以作简单的音乐播放器了,虽然音质不怎么样,不过作为DIY还是蛮有乐趣,希望大家喜欢。
没有采用FAT文件系统,只是按扇区读取SD卡,由于2051资源有限,改为4051有望可以操作FAT,但目前程序还在不断完善中。
128byte怎样读取512byte的扇区数据?可以采用边读边播放的方式,就能解决。音乐文件是32KHz取样率的WAV文件,所以和HIFI就沾不上边了。
- #include
- #include
- #include
- #include "LCD_3310.H"
- #define uchar unsigned char
- #define uint unsigned int
- #define ulong unsigned long
- /************ 定义管脚 *************/
- ***it DOUT = P3^0; //SD卡数据输出
- ***it CLK = P3^1; //SD卡时钟输入
- ***it DIN = P3^2; //SD卡数据输入
- ***it 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)
- { //不采用循环结构是为了提高处理速度
- 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;
- 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;
- }
- /******** 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;
- }
- /******** 发送一组SD命令 ************/
- uchar write_cmd(uchar data *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;
- }
- /****** 慢速发送一组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;
- }
- /********* 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 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 的扇区地址
- P1 = 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 &= 15; //这个SD卡只有16首歌
- }//while(1);
- }//main()
- void timer0 (void) interrupt 1 using 1
- {
- if (TL0 & 1) _nop_(); //消除中断响应时间不一致,造成的频率抖动
- P1 = pbuf[++p & 63]; //输出一个声音数据
- }
复制代码
- #include
- #include
- ***it SDIN = P3^2; //P3^2
- ***it SCLK = P3^4;
- ***it D_C = P3^5;
- ***it SCE = P3^7;
- code unsigned char Font[70] =
- {
- 0x3E, 0x51, 0x49, 0x45, 0x3E , // 0
- 0x00, 0x42, 0x7F, 0x40, 0x00 , // 1
- 0x42, 0x61, 0x51, 0x49, 0x46 , // 2
- 0x21, 0x41, 0x45, 0x4B, 0x31 , // 3
- 0x18, 0x14, 0x12, 0x7F, 0x10 , // 4
- 0x27, 0x45, 0x45, 0x45, 0x39 , // 5
- 0x3C, 0x4A, 0x49, 0x49, 0x30 , // 6
- 0x01, 0x71, 0x09, 0x05, 0x03 , // 7
- 0x36, 0x49, 0x49, 0x49, 0x36 , // 8
- 0x06, 0x49, 0x49, 0x29, 0x1E , // 9
- 0x00, 0x00, 0x36, 0x36, 0x00 , // :
- 0x7f, 0x3e, 0x1c, 0x08, 0x00 , // >
- 0x3e, 0x3e, 0x00, 0x3e, 0x3e , // ||
- 0x00, 0x00, 0x00, 0x00, 0x00 , //" "
- };
- code unsigned char Level[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff,};
- extern void delay(unsigned int i);
- void LCD3310_write_cmd(unsigned char cmd)
- {
- D_C = 0;
- SCLK = 0;
- SCE = 0;
- delay(3);
- SDIN = cmd & 0x80;
- SCLK = 1;
- SDIN = cmd & 0x40;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x20;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x10;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x08;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x04;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x02;
- SCLK = 0;
- SCLK = 1;
- SDIN = cmd & 0x01;
- SCLK = 0;
- SCLK = 1;
- D_C = 1;
- SDIN = 1;
- SCE = 1;
- }
- void LCD3310_write_dat(unsigned char dat)
- {
- // D_C = 1;
- SCLK = 0;
- SCE = 0;
- delay(3);
- SDIN = dat & 0x80;
- SCLK = 1;
- SDIN = dat & 0x40;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x20;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x10;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x08;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x04;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x02;
- SCLK = 0;
- SCLK = 1;
- SDIN = dat & 0x01;
- SCLK = 0;
- SCLK = 1;
- D_C = 1;
- SDIN = 1;
- SCE = 1;
- }
- void LCD3310_init(void)
- {
- LCD3310_write_cmd(0x21);
- LCD3310_write_cmd(0xd7);
- LCD3310_write_cmd(0x06);
- LCD3310_write_cmd(0x20);
- LCD3310_write_cmd(0x0c);
- }
- void LCD3310_set_XY(unsigned char x,unsigned char y)
- {
- if (x >= 84) return;
- if (y >= 6) return;
- LCD3310_write_cmd(0x80 | x);
- LCD3310_write_cmd(0x40 | y);
- }
- void LCD3310_print(unsigned char n)
- {
- n = (n << 2) + n;
- LCD3310_write_dat(Font[n]);
- LCD3310_write_dat(Font[++n]);
- LCD3310_write_dat(Font[++n]);
- LCD3310_write_dat(Font[++n]);
- LCD3310_write_dat(Font[++n]);
- }
复制代码
|