| 我用的是at89s52,改了之后可以顯示下排(音階和計時),但就是沒有聲音和圖片。幫我看一下程序有什么問題,特別是其中的/******* 讀取一扇區的點陣圖像 *********/uchar sd_read_bmp(uchar data *ad)和 /******* 讀取一扇區的聲音數據 *********/uchar sd_read_sector(uchar data *ad) 這兩個函數真的沒有問題嗎,有問題要怎么改?程序怎么改?應該就是讀不出SD卡的圖片和wav文件 #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 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; 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) {CS=1; 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]; //輸出一個聲音數據 } 下面是5110的程序 #include <reg51.h> #include <INTRINS.H> #define X_Col_Addr 0x80 //定位到第0列指令(列起始地址)(0 - 83) #define Y_Page_Addr 0x40 //定位到第0頁指令(頁起始地址)(0 - 5) sbit SDIN = P3^2; sbit SCLK = P3^4; sbit D_C = P3^5; sbit 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_dat(unsigned char dat) { unsigned char i; //D_C = 1; SCLK = 0; SCE = 0; i = 2; while(--i); //延時一會使SCLK穩定 i = 8; ACC = dat << 1; //直接訪問ACC和CY加快執行速度 do { SDIN = CY; SCLK = 0; ACC += ACC; SCLK = 1; } while(--i); D_C = 1; SDIN = 1; SCE = 1; } void LCD3310_write_cmd(unsigned char cmd) { D_C = 0; LCD3310_write_dat(cmd); } void LCD3310_clear_screen(void) { unsigned int i; LCD3310_write_cmd(X_Col_Addr); LCD3310_write_cmd(Y_Page_Addr); for(i = 504; i; i--) LCD3310_write_dat(0x00); } void LCD3310_init(void) { //LCD_reset_hard; //硬件復位 // LCD_reset_soft; //軟件復位 LCD3310_write_cmd(0x21); //工作模式, 水平尋址, 擴展指令 LCD3310_write_cmd(0x06); //VLCD溫度系數2 LCD3310_write_cmd(0x13); //設置偏置系統(BSx) 1:48 LCD3310_write_cmd(0xc8); //設置電壓VLCD = 3.06 + 0.06*Vop, 對比度調整 LCD3310_write_cmd(0x20); //工作模式, 水平尋址, 常規指令 LCD3310_write_cmd(0x0c); //普通模式 LCD3310_write_cmd(Y_Page_Addr); //起始頁地址0 LCD3310_write_cmd(X_Col_Addr); //起始列地址0 LCD3310_clear_screen(); //清全屏 } 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]); } |
| 歡迎光臨 (http://m.raoushi.com/bbs/) | Powered by Discuz! X3.1 |