一、環境介紹
STM32程序開發IDE: keil5
STM32程序風格: 采用寄存器方式開發,注釋齊全,執行效率高,方便移植
硬件包含:一塊STM32F103ZET6系統板、一個2.8寸TFT電阻觸摸顯示屏、一個SD卡卡槽(SPI接口)、一張SD卡(存放字庫和小說文件)
二、功能介紹
這是基于ST32F103ZET6設計的小說閱讀器,雖然對于真實的小說閱讀器產品來講,實用性和功能方面還差很多,但是對于剛入門的STM32、單片機開發工程師來講,這里面設計到的技術才是最有價值的。
所以這篇文章的小說閱讀器主要是用來作為嵌入式單片機工程師入門練手項目、大學生的課程設計等。目的不在于小說閱讀器,而是以小說閱讀器為例子,學習相關的技術: SD卡、串口通信、SPI通信、8080時序、觸摸屏校準原理、FATFS文件系統使用、語音播報模塊使用等等。
該閱讀器支持常規閱小說讀器具備的基本功能:
1. 支持選擇指定的小說進行查看閱讀,可以通過觸摸屏上的按鈕進行切換。
2. 支持切換字體大小
3. 支持切換字體顏色、背景顏色
4. 標題欄顯示當前閱讀器查看的小說文件名稱
5. 支持翻頁、上一頁、下一頁
6. 支持語音自動閱讀,發聲接近正常真人發聲,非常強大。
語音方案可以選擇兩種: (1).宇音SYN6658 (2). 科大訊飛SYN5152。 這兩款芯片都是通過串口通信,編程十分簡單。
內部編程思路介紹:
小說閱讀器的字體是存放在SD卡上的,SD卡采用SPI接口的卡槽與STM32相連接,STM32配合FATFS文件系統對SD卡上的文件進行操作;為了提高訪問效率、在第一次上電的時候會將SD卡上的字庫文件拷貝到板載W25Q64芯片內。小說文件還是存放在SD卡上,每次翻頁的時候從SD卡上獲取文本文件,渲染到LCD顯示屏上。
該顯示屏是2.8寸的電阻觸摸顯示屏,驅動芯片是ILI9341(兼容:9325,9328),LCD的引腳接線兼容正點原子的2.8寸LCD顯示屏;電阻屏的驅動芯片是XPT2046,,是很常見的組合,這個XPT2046就是個ADC芯片,最終要完成觸摸屏上坐標點定位,還需要自己寫校準算法進行換算。 ILI9341驅動芯片支持8080時序操作,可以采用IO模擬方式驅動、也可以采用STM32的FSMC接口驅動。 STM32增強版支持FSMC功能的,其他沒有FSMC接口的芯片,可以采用模擬8080時序方式驅動,效果一樣,只是效率上差點,無法實現高速刷屏,只要不進行高速刷屏,湊合使用是沒什么問題的。
?
?
?
?
三、所用到的硬件介紹(都是淘寶買的)
3.1 STM32F103ZET6最小系統板
這是在淘寶上買的硬件詳情,開發板和LCD用哪一款都可以的,編程思路都是一樣。
?
?
開發板的板載資源如下:
CPU:STM32F103ZET6,LQFP144,FLASH:512K,SRAM:64K;
外擴SPI FLASH:W25Q32,8M字節;
1個電源指示燈;
2個狀態指示燈;
一個EEPROM芯片,24C02,容量256字節(注意:不同產地標號不一,但都是24C02芯片,經測試無誤)
1個光敏傳感器;
1個無線模塊接口,可接NRF24L01/RFID/CC01模塊;
1路CAN接口,采用TJA1050芯片;
1路485接口,采用SP485芯片;
1個標準的2.4/2.8/3.5/4.3/7寸LCD接口,支持觸摸屏;
一個USB串口,可用于程序下載和代碼調試(USMART調試);
1個USB SLAVE接口,用于USB通信;
1個復位按鍵;
2個獨立按鍵;
1個SD卡座,用來接SD卡;
1個RTC后備電池座;
1個標準的JTAG/SWD仿真下載調試接口;
1路5V轉3.3V電路;
芯片引腳144個腳全部引出,方便外接擴展實驗;
1個電源開關,用來開關USB的電源;
3.2 SD卡卡槽
?
3.3 SYN6658語音合成芯片
?
功能特點:
? 芯片支持任意中文文本的合成,可以采用GB2312、GBK、BIG5 和Unicode 四種編碼方式;
? 芯片具有文本智能分析處理功能,對常見的數值、電話號碼、時間日期、度量衡符號等格式的文本;
? 芯片可以自動對文本進行分析,判別文本中多音字的讀法并合成正確的讀音;
? 芯片可實現10級數字音量控制,音量更大,更廣;
? 芯片內集成了77首聲音提示音和14首和弦音樂;
? 提供兩男、兩女、一個效果器和一個女童聲共6個中文發音人;
? 支持多種文本控制標記,提升文本處理的正確率;
? 支持多種控制命令,包括:合成、停止、暫停合成、繼續合成、改變波特率等;
? 支持多種方式查詢芯片的工作狀態;
? 兩種通訊模式:芯片支持UART、SPI兩種通訊方式;
? 芯片支持Power Down 模式。使用控制命令可以使芯片進入Power Down 模式;
? 芯片支持的通訊波特率:4800bps,9600bps,57600bps、115200bps;
? 芯片各項指標均滿足室外嚴酷環境下的應用;
應用范圍:
? 車載信息終端語音播報,車載調度,車載導航
? 停車場收費系統/誘導系統
? 公交報站器 ,考勤機
? 手機,固定電話
? 排隊叫號機,收銀收費機
? 自動售貨機,信息機, POS 機
? 智能儀器儀表 ,氣象預警機,智能變壓器
? 智能玩具,智能手表
? 電動自行車
? 語音電子書,彩屏故事書,語音電子詞典,語音電子導游
? 短消息播放 ,新聞播放
? 電子地圖
四、操作說明
4.1 程序下載
開發板支持Jlink下載、也支持串口下載。
?
4.2屏幕操作說明
目前實現的功能:
1. 小說翻頁:支持點擊觸摸屏按鈕翻下一頁顯示
2. 換小說:點擊觸摸屏按鈕“下一本”,可以切換小說。
3. 換顏色:點擊觸摸屏按鈕“顏色調整”,可以切換顏色,支持12種字體顏色切換。
4. 換字體:點擊觸摸屏按鈕“字體調整”,可以切換字體,目前支持兩種字體(16X16 24X24)。
思路說明:
程序里移植了FATFS文件系統,字體文件和小說文件都是存放在SD卡,通過文件系統讀取SD卡里的小說文件進行顯示。
操作的過程在串口調試助手上也會同步輸出信息。
?
4.3 校準說明
第一次使用,需要校準屏幕,否則觸摸屏沒有反應。
如果發現屏幕不靈敏,可以強制進行校準,按下按鍵K2再按下復位鍵即可進行強制校準。
?
依次點擊屏幕上4個紅圈。
?
4.4 SD卡上存放的文件
SD卡上有兩個目錄:font目錄和txt目錄。
font目錄:存放字庫文件。有兩個字庫字體。
txt目錄:存放小說文件,內置了3篇小說。
?
?
五、核心代碼
代碼采用Keil5編寫,下載即可編譯,測試,學習。
工程完整源碼下載地址:基于STM32設計的小時閱讀器完整源碼(不懂可以私信).zip-嵌入式文檔類資源-CSDN下載
?
5.1 main.c 主函數代碼
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include
#include
#include "iic.h"
#include "at24c08.h"
#include "w25q64.h"
#include "nt35310_lcd.h"
#include "xpt2046.h"
#include "sdcard.h"
#include "ff.h" //FATFS文件系統的頭文件
//更新字庫---從SD卡讀取字庫到W25Q64
void FontUpdate_to_W25Q64();
FATFS fatfs; //文件系統注冊工作區需要使用
u16 select_color[]={WHITE,BLACK,BLUE,RED,YELLOW,BROWN,BRRED,GRAY,DARKBLUE,LIGHTBLUE,GRAYBLUE,LIGHTGREEN};
u8 read_text_buf[4096+1];
int main()
{
u32 x;u32 y;u32 size=16;u8 *p;
u8 color_select_cnt=0; //12個
FIL text_file;
u16 br=0;
u8 r_data=10;
u32 read_cnt=0;
DIR dir;
FRESULT res;
FILINFO fno; //存放讀取的文件信息
char *abs_path=NULL;
char path[]="0:/txt";
u32 cnt=0;
USART_X_Init(USART1,72,115200);
NT35310_LcdInit();
NT35310_Clear(WHITE);
IIC_Init(); //IIC總線初始化
W25Q64_Init(); //初始化W25Q64
TOUCH_Init(); //觸摸屏初始化
TOUCH_CheckXY(); //觸摸屏校準程序
RCC->APB2ENR|=1<<5;
GPIOD->CRH&=0xFF0FFFFF;
GPIOD->CRH|=0x00300000;
while(SDCardDeviceInit()!=0)
{
printf("SDCard_DeviceInit 錯誤.\r\n");
PDout(13)=!PDout(13);
delay_ms(100);
}
f_mount(&fatfs,"0:",0); //注冊文件系統的工作區
//設計界面
LCD_color_1=RED;
LCD_color_2=LIGHTBLUE;
NT35310_DisplayString(16,0,16,"基于STM32的小說閱讀器設計");
NT35310_DrawLine(0,16,239,16,DARKBLUE);
//繪制按鍵
NT35310_DrawRectangle(0,319-80,239,319,RED);
NT35310_DrawLine(0,319-40,239,319-40,DARKBLUE);
NT35310_DrawLine(239/2,319-80,239/2,319,DARKBLUE);
LCD_color_2=WHITE;
NT35310_DisplayString(32,319-70,16,"下一頁");
NT35310_DisplayString(239/2+32,319-70,16,"下一本");
NT35310_DisplayString(32,319-30,16,"字體調整");
NT35310_DisplayString(239/2+32,319-30,16,"顏色調整");
/*1. 打開目錄*/
res=f_opendir(&dir,path);
if(res!=FR_OK)return res;
res=f_readdir(&dir,&fno);
printf("文件名稱: %s,文件大小: %ld 字節\r\n",fno.fname,fno.fsize);
LCD_color_1=BLACK;
NT35310_DisplayString(0,17,16,fno.fname);
if(abs_path)
{
free(abs_path);
abs_path=NULL;
}
//申請存放文件名稱的長度
abs_path=malloc(strlen(path)+strlen(fno.fname)+1);
strcpy(abs_path,path);
strcat(abs_path,"/");
strcat(abs_path,fno.fname);
printf("abs_path=%s\n",abs_path);
NT35310_DisplayString(0,17+16,16,"第1卷\
第一回 甄士隱夢幻識通靈 賈雨村風塵懷閨秀\
此開卷第一回也。作者自云:因曾歷過一番夢幻之后,故將真事隱去,\
而借“通靈”之說,撰此<<石頭記>>一書也。故曰“甄士隱”云云。\
但書中所記何事何人?自又云:“今風塵碌碌,一事無成,忽念及當日所有之女子,\
一一細考較去,覺其行止見識,皆出于我之上。何我堂堂須眉,誠不若彼裙釵哉?");
while(1)
{
if(TOUCH_PEN==0) //判斷觸摸屏是否按下
{
//判斷是否讀取到XY坐標
if(TOUCH_ReadXY())
{
// printf("x=%d,y=%d\r\n",touch_info.x,touch_info.y);
//判斷范圍
if((touch_info.x>=0 && touch_info.x<=239/2)&&
(touch_info.y>=319-80 && touch_info.y<=319-40))
{
LCD_color_2=BLUE;
//填充顏色
NT35310_Fill(0+1,319-80+1,239/2-1,319-40-1,BLUE);
//顯示字符串
NT35310_DisplayString(32,319-70,16,"下一頁");
//等待觸摸屏松開
while(TOUCH_PEN==0){}
//填充顏色--清屏
NT35310_Fill(0,18+16,239,319-80-1,WHITE);
LCD_color_2=WHITE;
if(read_cnt>=br)
{
read_cnt=0;
}
if(read_cnt==0)
{
if(br!=4096)
{
res=f_open(&text_file,(const TCHAR*)abs_path,FA_READ);//打開文件
if(res!=0)
{
printf("%s文件打開失敗!\r\n",abs_path);
return 1; //文件打開失敗
}
printf("%s文件打開成功!\n",abs_path);
}
//執行代碼
res=f_read(&text_file,read_text_buf,4096,(UINT*)&br);//讀出4096個字節
read_text_buf[br]='\0';
printf("br=%d\r\n",br);
if(br!=4096)
{
f_close(&text_file);
}
}
//字體大小
x=0; //坐標起始位置
y=17+16; //坐標起始位置
p=read_text_buf+read_cnt;
while(*p!='\0')
{
if(*p>0x80) //判斷是否是中文-編碼規則從 0X8140 開始
{
read_cnt+=2;
if(x+size>239)
{
x=0; //橫坐歸0
y+=size; //換行
if(y+size>=319-80-1)break;
}
NT35310_DisplayGBKData(x,y,size,p);//顯示一個中文
x+=size;
p+=2; //偏移兩個字節
}
else if(*p>=' ' && *p<='~') //常用的ASCII碼
{
read_cnt+=1;
if(x+size/2>239)
{
x=0; //橫坐歸0
y+=size; //換行
if(y+size>=319-80-1)break;
}
if(size==16)
{
//顯示英文字母
NT35310_DisplayData(x,y,size/2,size,(u8*)ASCII_8_16[*p-' ']);
}
else if(size==24)
{
//顯示英文字母
NT35310_DisplayData(x,y,size/2,size,(u8*)asc2_2412[*p-' ']);
}
p+=1;
x+=size/2;
}
else if(*p=='\n')
{
x=0;
y+=size;
p+=1; //偏移指針
read_cnt+=1;
if(y+size>=319-80-1)break;
}
else
{
read_cnt+=1;
p+=1; //偏移指針
}
}
//填充顏色
NT35310_Fill(0+1,319-80+1,239/2-1,319-40-1,WHITE);
LCD_color_2=WHITE;
//顯示字符串
NT35310_DisplayString(32,319-70,16,"下一頁");
}
//判斷范圍
if((touch_info.x>=239/2 && touch_info.x<=239)&&
(touch_info.y>=319-80 && touch_info.y<=319-40))
{
LCD_color_2=BLUE;
//填充顏色
NT35310_Fill(239/2+1,319-80+1,239-1,319-40-1,BLUE);
//顯示字符串
NT35310_DisplayString(239/2+32,319-70,16,"下一本");
//等待觸摸屏松開
while(TOUCH_PEN==0){}
LCD_color_2=WHITE;
//關閉原來的文件
f_close(&text_file);
//觸發新的頁
read_cnt=0;
br=0;
//執行代碼
res=f_readdir(&dir,&fno);
if(fno.fname[0] == 0 || res!=0)
{
/*3. 關閉目錄*/
f_closedir(&dir);
/*1. 打開目錄*/
res=f_opendir(&dir,path);
if(res!=FR_OK)return res;
res=f_readdir(&dir,&fno);
}
printf("文件名稱: %s,文件大小: %ld 字節\r\n",fno.fname,fno.fsize);
LCD_color_1=BLACK;
NT35310_DisplayString(0,17,16,fno.fname);
if(abs_path)
{
free(abs_path);
abs_path=NULL;
}
//申請存放文件名稱的長度
abs_path=malloc(strlen(path)+strlen(fno.fname)+1);
strcpy(abs_path,path);
strcat(abs_path,"/");
strcat(abs_path,fno.fname);
printf("abs_path=%s\n",abs_path);
//填充顏色
NT35310_Fill(239/2+1,319-80+1,239-1,319-40-1,WHITE);
//顯示字符串
NT35310_DisplayString(239/2+32,319-70,16,"下一本");
}
//判斷范圍
if((touch_info.x>=0 && touch_info.x<=239/2)&&
(touch_info.y>=319-40 && touch_info.y<=319))
{
LCD_color_2=BLUE;
//填充顏色
NT35310_Fill(0+1,319-40+1,239/2-1,319-1,BLUE);
//顯示字符串
NT35310_DisplayString(32,319-30,16,"字體調整");
//等待觸摸屏松開
while(TOUCH_PEN==0){}
if(size==16)size=24;
else size=16;
//執行代碼
//填充顏色
NT35310_Fill(0+1,319-40+1,239/2-1,319-1,WHITE);
LCD_color_2=WHITE;
//顯示字符串
NT35310_DisplayString(32,319-30,16,"字體調整");
}
//判斷范圍
if((touch_info.x>=239/2 && touch_info.x<=239)&&
(touch_info.y>=319-40 && touch_info.y<=319))
{
LCD_color_2=BLUE;
//填充顏色
NT35310_Fill(239/2+1,319-40+1,239-1,319-1,BLUE);
//顯示字符串
NT35310_DisplayString(239/2+32,319-30,16,"顏色調整");
//等待觸摸屏松開
while(TOUCH_PEN==0){}
//執行代碼
//前景字體顏色切換
LCD_color_1=select_color[color_select_cnt++];
if(color_select_cnt>=12)
{
color_select_cnt=0;
}
//填充顏色
NT35310_Fill(239/2+1,319-40+1,239-1,319-1,WHITE);
LCD_color_2=WHITE;
//顯示字符串
NT35310_DisplayString(239/2+32,319-30,16,"顏色調整");
}
}
}
}
}
u32 gbk32_32_addr=1024*0;
u8 font_buffer[4096];
//更新字庫---從SD卡讀取字庫到W25Q64
void FontUpdate_to_W25Q64()
{
u32 w_cnt=0;
FILINFO fno;
FIL fp;
UINT br,res;
/*1. 打開字庫*/
f_open(&fp,"0:/font/gbk16.DZK",FA_READ);
/*2. 循環讀取字庫更新到W25Q64*/
f_stat("0:/font/gbk16.DZK",&fno);
printf("文件的大小:%d\r\n",fno.fsize);
while(1)
{
/*3. 讀取字庫文件*/
res=f_read(&fp,font_buffer,4096,&br);
/*4. 寫入到W25Q64里*/
W25Q64_WriteData(gbk32_32_addr,br,font_buffer);
gbk32_32_addr+=br;
w_cnt+=br;
printf("font16:%.f%%\r\n",(w_cnt*1.0/fno.fsize)*100);
/*5. 判斷文件是否結束*/
if(res!=FR_OK||br!=4096)break;
}
/*6. 關閉字庫文件*/
f_close(&fp);
}
5.2 sdcard.c SD卡驅動代碼
#include "sdcard.h"
static u8 SD_Type=0; //存放SD卡的類型
/*
函數功能:SD卡底層接口,通過SPI時序向SD卡讀寫一個字節
函數參數:data是要寫入的數據
返 回 值:讀到的數據
說明:時序是第二個上升沿采集數據
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{
u8 DataRx;
u8 i;
for(i=0;i<8;i++)
{
SDCardSCLK(0);
if(DataTx&0x80){SDCardOut(1);}
else {SDCardOut(0);}
DataTx<<=1;
SDCardSCLK(1);//第二個上升沿采集數據
DataRx<<=1;
if(SDCardInput)DataRx|=0x01;
}
return DataRx;
}
/*
函數功能:底層SD卡接口初始化
本程序SPI接口如下:
PC11 片選 SDCardCS
PC12 時鐘 SDCardSCLK
PD2 輸出 SPI_MOSI--主機輸出從機輸入
PC8 輸入 SPI_MISO--主機輸入從機輸出
*/
void SDCardSpiInit(void)
{
RCC->APB2ENR|=1<<5; //使能PORTD時鐘
RCC->APB2ENR|=1<<4; //使能PORTC時鐘
GPIOD->CRL&=0XFFFFF0FF;
GPIOD->CRL|=0X00000300; //PD2
GPIOD->ODR|=1<<2; //PD2
GPIOC->CRH&=0XFFF00FF0;
GPIOC->CRH|=0X00033008;
GPIOC->ODR|=0X3<<11;
GPIOC->ODR|=1<<8;
SDCardCS(1);
}
/*
函數功能:取消選擇,釋放SPI總線
*/
void SDCardCancelCS(void)
{
SDCardCS(1);
SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘
}
/*
函數 功 能:選擇sd卡,并且等待卡準備OK
函數返回值:0,成功;1,失敗;
*/
u8 SDCardSelectCS(void)
{
SDCardCS(0);
if(SDCardWaitBusy()==0)return 0;//等待成功
SDCardCancelCS();
return 1;//等待失敗
}
/*
函數 功 能:等待卡準備好
函數返回值:0,準備好了;其他,錯誤代碼
*/
u8 SDCardWaitBusy(void)
{
u32 t=0;
do
{
if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
t++;
}while(t<0xFFFFFF);//等待
return 1;
}
/*
函數功能:等待SD卡回應
函數參數:
Response:要得到的回應值
返 回 值:
0,成功得到了該回應值
其他,得到回應值失敗
*/
u8 SDCardGetAck(u8 Response)
{
u16 Count=0xFFFF;//等待次數
while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到準確的回應
if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回應失敗
else return SDCard_RESPONSE_NO_ERROR;//正確回應
}
/*
函數功能:從sd卡讀取一個數據包的內容
函數參數:
buf:數據緩存區
len:要讀取的數據長度.
返回值:
0,成功;其他,失敗;
*/
u8 SDCardRecvData(u8*buf,u16 len)
{
if(SDCardGetAck(0xFE))return 1;//等待SD卡發回數據起始令牌0xFE
while(len--)//開始接收數據
{
*buf=SDCardReadWriteOneByte(0xFF);
buf++;
}
//下面是2個偽CRC(dummy CRC)
SDCardReadWriteOneByte(0xFF);
SDCardReadWriteOneByte(0xFF);
return 0;//讀取成功
}
/*
函數功能:向sd卡寫入一個數據包的內容 512字節
函數參數:
buf 數據緩存區
cmd 指令
返 回 值:0表示成功;其他值表示失敗;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{
u16 t;
if(SDCardWaitBusy())return 1; //等待準備失效
SDCardReadWriteOneByte(cmd);
if(cmd!=0XFD)//不是結束指令
{
for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,減少函數傳參時間
SDCardReadWriteOneByte(0xFF); //忽略crc
SDCardReadWriteOneByte(0xFF);
t=SDCardReadWriteOneByte(0xFF); //接收響應
if((t&0x1F)!=0x05)return 2; //響應錯誤
}
return 0;//寫入成功
}
/*
函數功能:向SD卡發送一個命令
函數參數:
u8 cmd 命令
u32 arg 命令參數
u8 crc crc校驗值
返回值:SD卡返回的響應
*/
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
u8 Retry=0;
SDCardCancelCS(); //取消上次片選
if(SDCardSelectCS())return 0XFF;//片選失效
//發送數據
SDCardReadWriteOneByte(cmd | 0x40);//分別寫入命令
SDCardReadWriteOneByte(arg >> 24);
SDCardReadWriteOneByte(arg >> 16);
SDCardReadWriteOneByte(arg >> 8);
SDCardReadWriteOneByte(arg);
SDCardReadWriteOneByte(crc);
if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
Retry=0X1F;
do
{
r1=SDCardReadWriteOneByte(0xFF);
}while((r1&0X80) && Retry--); //等待響應,或超時退出
return r1; //返回狀態值
}
/*
函數功能:獲取SD卡的CID信息,包括制造商信息
函數參數:u8 *cid_data(存放CID的內存,至少16Byte)
返 回 值:
0:成功,1:錯誤
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
u8 r1;
//發SDCard_CMD10命令,讀CID
r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
if(r1==0x00)
{
r1=SDCardRecvData(cid_data,16);//接收16個字節的數據
}
SDCardCancelCS();//取消片選
if(r1)return 1;
else return 0;
}
/*
函數說明:
獲取SD卡的CSD信息,包括容量和速度信息
函數參數:
u8 *cid_data(存放CID的內存,至少16Byte)
返 回 值:
0:成功,1:錯誤
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
u8 r1;
r1=SendSDCardCmd(SDCard_CMD9,0,0x01); //發SDCard_CMD9命令,讀CSD
if(r1==0)
{
r1=SDCardRecvData(csd_data, 16);//接收16個字節的數據
}
SDCardCancelCS();//取消片選
if(r1)return 1;
else return 0;
}
/*
函數功能:獲取SD卡的總扇區數(扇區數)
返 回 值:
0表示容量檢測出錯,其他值表示SD卡的容量(扇區數/512字節)
說 明:
每扇區的字節數必為512字節,如果不是512字節,則初始化不能通過.
*/
u32 GetSDCardSectorCount(void)
{
u8 csd[16];
u32 Capacity;
u8 n;
u16 csize;
if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0; //取CSD信息,如果期間出錯,返回0
if((csd[0]&0xC0)==0x40) //V2.00的卡,如果為SDHC卡,按照下面方式計算
{
csize = csd[9] + ((u16)csd[8] << 8) + 1;
Capacity = (u32)csize << 10;//得到扇區數
}
else//V1.XX的卡
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
Capacity= (u32)csize << (n - 9);//得到扇區數
}
return Capacity;
}
/*
函數功能: 初始化SD卡
返 回 值: 非0表示初始化失敗!
*/
u8 SDCardDeviceInit(void)
{
u8 r1; // 存放SD卡的返回值
u16 retry; // 用來進行超時計數
u8 buf[4];
u16 i;
SDCardSpiInit(); //初始化底層IO口
for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //發送最少74個脈沖
retry=20;
do
{
r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//進入IDLE狀態 閑置
}while((r1!=0X01) && retry--);
SD_Type=0; //默認無卡
if(r1==0X01)
{
if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1) //SD V2.0
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF); //Get trailing return value of R7 resp
if(buf[2]==0X01&&buf[3]==0XAA) //卡是否支持2.7~3.6V
{
retry=0XFFFE;
do
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //發送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//發送SDCard_CMD41
}while(r1&&retry--);
if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鑒別SD2.0卡版本開始
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC; //檢查CCS
else SD_Type=SDCard_TYPE_V2;
}
}
}
else//SD V1.x/ MMC V3
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //發送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0,0X01); //發送SDCard_CMD41
if(r1<=1)
{
SD_Type=SDCard_TYPE_V1;
retry=0XFFFE;
do //等待退出IDLE模式
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //發送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發送SDCard_CMD41
}while(r1&&retry--);
}
else//MMC卡不支持SDCard_CMD55+SDCard_CMD41識別
{
SD_Type=SDCard_TYPE_MMC;//MMC V3
retry=0XFFFE;
do //等待退出IDLE模式
{
r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//發送SDCard_CMD1
}while(r1&&retry--);
}
if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//錯誤的卡
}
}
SDCardCancelCS(); //取消片選
if(SD_Type)return 0; //初始化成功返回0
else if(r1)return r1; //返回值錯誤值
return 0xaa; //其他錯誤
}
/*
函數功能:讀SD卡
函數參數:
buf:數據緩存區
sector:扇區
cnt:扇區數
返回值:
0,ok;其他,失敗.
說 明:
SD卡一個扇區大小512字節
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//轉換為字節地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//讀命令
if(r1==0) //指令發送成功
{
r1=SDCardRecvData(buf,512); //接收512個字節
}
}else
{
r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//連續讀命令
do
{
r1=SDCardRecvData(buf,512);//接收512個字節
buf+=512;
}while(--cnt && r1==0);
SendSDCardCmd(SDCard_CMD12,0,0X01); //發送停止命令
}
SDCardCancelCS();//取消片選
return r1;//
}
/*
函數功能:向SD卡寫數據
函數參數:
buf:數據緩存區
sector:起始扇區
cnt:扇區數
返回值:
0,ok;其他,失敗.
說 明:
SD卡一個扇區大小512字節
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//轉換為字節地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//讀命令
if(r1==0)//指令發送成功
{
r1=SDCardSendData(buf,0xFE);//寫512個字節
}
}
else
{
if(SD_Type!=SDCard_TYPE_MMC)
{
SendSDCardCmd(SDCard_CMD55,0,0X01);
SendSDCardCmd(SDCard_CMD23,cnt,0X01);//發送指令
}
r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//連續讀命令
if(r1==0)
{
do
{
r1=SDCardSendData(buf,0xFC);//接收512個字節
buf+=512;
}while(--cnt && r1==0);
r1=SDCardSendData(0,0xFD);//接收512個字節
}
}
SDCardCancelCS();//取消片選
return r1;//
}
工程完整源碼下載地址:https://download.csdn.net/download/xiaolong1126626497/19628524
審核編輯:符乾江
-
STM32
+關注
關注
2266文章
10876瀏覽量
354914 -
閱讀器
+關注
關注
0文章
299瀏覽量
27908
發布評論請先 登錄
相關推薦
評論