0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于ST32F103ZET6設(shè)計(jì)的小說閱讀器詳解

DS小龍哥-嵌入式技術(shù) ? 來源:DS小龍哥-嵌入式技術(shù) ? 作者:DS小龍哥-嵌入式技 ? 2022-02-28 13:37 ? 次閱讀

一、環(huán)境介紹

小車主控MCU: STM32F103ZET6

STM32程序開發(fā)IDE: keil5

STM32程序風(fēng)格: 采用寄存器方式開發(fā),注釋齊全,執(zhí)行效率高,方便移植

硬件包含:一塊STM32F103ZET6系統(tǒng)板、一個(gè)2.8寸TFT電阻觸摸顯示屏、一個(gè)SD卡卡槽(SPI接口)、一張SD卡(存放字庫和小說文件)

二、功能介紹

這是基于ST32F103ZET6設(shè)計(jì)的小說閱讀器,雖然對(duì)于真實(shí)的小說閱讀器產(chǎn)品來講,實(shí)用性和功能方面還差很多,但是對(duì)于剛?cè)腴T的STM32、單片機(jī)開發(fā)工程師來講,這里面設(shè)計(jì)到的技術(shù)才是最有價(jià)值的。

所以這篇文章的小說閱讀器主要是用來作為嵌入式單片機(jī)工程師入門練手項(xiàng)目、大學(xué)生的課程設(shè)計(jì)等。目的不在于小說閱讀器,而是以小說閱讀器為例子,學(xué)習(xí)相關(guān)的技術(shù): SD卡、串口通信、SPI通信、8080時(shí)序、觸摸屏校準(zhǔn)原理、FATFS文件系統(tǒng)使用、語音播報(bào)模塊使用等等。

該閱讀器支持常規(guī)閱小說讀器具備的基本功能:

1. 支持選擇指定的小說進(jìn)行查看閱讀,可以通過觸摸屏上的按鈕進(jìn)行切換。

2. 支持切換字體大小

3. 支持切換字體顏色、背景顏色

4. 標(biāo)題欄顯示當(dāng)前閱讀器查看的小說文件名稱

5. 支持翻頁、上一頁、下一頁

6. 支持語音自動(dòng)閱讀,發(fā)聲接近正常真人發(fā)聲,非常強(qiáng)大。

語音方案可以選擇兩種: (1).宇音SYN6658 (2). 科大訊飛SYN5152。 這兩款芯片都是通過串口通信,編程十分簡(jiǎn)單。

內(nèi)部編程思路介紹:

小說閱讀器的字體是存放在SD卡上的,SD卡采用SPI接口的卡槽與STM32相連接,STM32配合FATFS文件系統(tǒng)對(duì)SD卡上的文件進(jìn)行操作;為了提高訪問效率、在第一次上電的時(shí)候會(huì)將SD卡上的字庫文件拷貝到板載W25Q64芯片內(nèi)。小說文件還是存放在SD卡上,每次翻頁的時(shí)候從SD卡上獲取文本文件,渲染到LCD顯示屏上。

該顯示屏是2.8寸的電阻觸摸顯示屏,驅(qū)動(dòng)芯片是ILI9341(兼容:9325,9328),LCD的引腳接線兼容正點(diǎn)原子的2.8寸LCD顯示屏;電阻屏的驅(qū)動(dòng)芯片是XPT2046,,是很常見的組合,這個(gè)XPT2046就是個(gè)ADC芯片,最終要完成觸摸屏上坐標(biāo)點(diǎn)定位,還需要自己寫校準(zhǔn)算法進(jìn)行換算。 ILI9341驅(qū)動(dòng)芯片支持8080時(shí)序操作,可以采用IO模擬方式驅(qū)動(dòng)、也可以采用STM32的FSMC接口驅(qū)動(dòng)。 STM32增強(qiáng)版支持FSMC功能的,其他沒有FSMC接口的芯片,可以采用模擬8080時(shí)序方式驅(qū)動(dòng),效果一樣,只是效率上差點(diǎn),無法實(shí)現(xiàn)高速刷屏,只要不進(jìn)行高速刷屏,湊合使用是沒什么問題的。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

三、所用到的硬件介紹(都是淘寶買的)

3.1 STM32F103ZET6最小系統(tǒng)板

這是在淘寶上買的硬件詳情,開發(fā)板和LCD用哪一款都可以的,編程思路都是一樣。

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

開發(fā)板的板載資源如下:
CPU:STM32F103ZET6,LQFP144,F(xiàn)LASH:512K,SRAM:64K;
外擴(kuò)SPI FLASH:W25Q32,8M字節(jié);
1個(gè)電源指示燈;
2個(gè)狀態(tài)指示燈;
一個(gè)EEPROM芯片,24C02,容量256字節(jié)(注意:不同產(chǎn)地標(biāo)號(hào)不一,但都是24C02芯片,經(jīng)測(cè)試無誤)
1個(gè)光敏傳感器;
1個(gè)無線模塊接口,可接NRF24L01/RFID/CC01模塊;
1路CAN接口,采用TJA1050芯片;
1路485接口,采用SP485芯片;
1個(gè)標(biāo)準(zhǔn)的2.4/2.8/3.5/4.3/7寸LCD接口,支持觸摸屏;
一個(gè)USB串口,可用于程序下載和代碼調(diào)試(USMART調(diào)試);
1個(gè)USB SLAVE接口,用于USB通信;
1個(gè)復(fù)位按鍵;
2個(gè)獨(dú)立按鍵;
1個(gè)SD卡座,用來接SD卡;
1個(gè)RTC后備電池座;
1個(gè)標(biāo)準(zhǔn)的JTAG/SWD仿真下載調(diào)試接口;
1路5V轉(zhuǎn)3.3V電路;
芯片引腳144個(gè)腳全部引出,方便外接擴(kuò)展實(shí)驗(yàn);
1個(gè)電源開關(guān),用來開關(guān)USB的電源;

3.2 SD卡卡槽

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

3.3 SYN6658語音合成芯片

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

功能特點(diǎn):
? 芯片支持任意中文文本的合成,可以采用GB2312、GBK、BIG5 和Unicode 四種編碼方式;
? 芯片具有文本智能分析處理功能,對(duì)常見的數(shù)值、電話號(hào)碼、時(shí)間日期、度量衡符號(hào)等格式的文本;
? 芯片可以自動(dòng)對(duì)文本進(jìn)行分析,判別文本中多音字的讀法并合成正確的讀音;
? 芯片可實(shí)現(xiàn)10級(jí)數(shù)字音量控制,音量更大,更廣;
? 芯片內(nèi)集成了77首聲音提示音和14首和弦音樂;
? 提供兩男、兩女、一個(gè)效果器和一個(gè)女童聲共6個(gè)中文發(fā)音人;
? 支持多種文本控制標(biāo)記,提升文本處理的正確率;
? 支持多種控制命令,包括:合成、停止、暫停合成、繼續(xù)合成、改變波特率等;
? 支持多種方式查詢芯片的工作狀態(tài);
? 兩種通訊模式:芯片支持UART、SPI兩種通訊方式;
? 芯片支持Power Down 模式。使用控制命令可以使芯片進(jìn)入Power Down 模式;
? 芯片支持的通訊波特率:4800bps,9600bps,57600bps、115200bps;
? 芯片各項(xiàng)指標(biāo)均滿足室外嚴(yán)酷環(huán)境下的應(yīng)用;

應(yīng)用范圍:
? 車載信息終端語音播報(bào),車載調(diào)度,車載導(dǎo)航
? 停車場(chǎng)收費(fèi)系統(tǒng)/誘導(dǎo)系統(tǒng)
? 公交報(bào)站器 ,考勤機(jī)
? 手機(jī),固定電話
? 排隊(duì)叫號(hào)機(jī),收銀收費(fèi)機(jī)
? 自動(dòng)售貨機(jī),信息機(jī), POS 機(jī)
? 智能儀器儀表 ,氣象預(yù)警機(jī),智能變壓器
? 智能玩具,智能手表
? 電動(dòng)自行車
? 語音電子書,彩屏故事書,語音電子詞典,語音電子導(dǎo)游
? 短消息播放 ,新聞播放
? 電子地圖

四、操作說明

4.1 程序下載

開發(fā)板支持Jlink下載、也支持串口下載。

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

4.2屏幕操作說明

目前實(shí)現(xiàn)的功能:
1. 小說翻頁:支持點(diǎn)擊觸摸屏按鈕翻下一頁顯示
2. 換小說:點(diǎn)擊觸摸屏按鈕“下一本”,可以切換小說。
3. 換顏色:點(diǎn)擊觸摸屏按鈕“顏色調(diào)整”,可以切換顏色,支持12種字體顏色切換。
4. 換字體:點(diǎn)擊觸摸屏按鈕“字體調(diào)整”,可以切換字體,目前支持兩種字體(16X16 24X24)。

思路說明:
程序里移植了FATFS文件系統(tǒng),字體文件和小說文件都是存放在SD卡,通過文件系統(tǒng)讀取SD卡里的小說文件進(jìn)行顯示。

操作的過程在串口調(diào)試助手上也會(huì)同步輸出信息。

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

4.3 校準(zhǔn)說明

第一次使用,需要校準(zhǔn)屏幕,否則觸摸屏沒有反應(yīng)。

如果發(fā)現(xiàn)屏幕不靈敏,可以強(qiáng)制進(jìn)行校準(zhǔn),按下按鍵K2再按下復(fù)位鍵即可進(jìn)行強(qiáng)制校準(zhǔn)。

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

依次點(diǎn)擊屏幕上4個(gè)紅圈。

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

4.4 SD卡上存放的文件

SD卡上有兩個(gè)目錄:font目錄和txt目錄。

font目錄:存放字庫文件。有兩個(gè)字庫字體。

txt目錄:存放小說文件,內(nèi)置了3篇小說。

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

五、核心代碼

代碼采用Keil5編寫,下載即可編譯,測(cè)試,學(xué)習(xí)。

工程完整源碼下載地址:基于STM32設(shè)計(jì)的小時(shí)閱讀器完整源碼(不懂可以私信).zip-嵌入式文檔類資源-CSDN下載

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

?

5.1 main.c 主函數(shù)代碼

#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文件系統(tǒng)的頭文件

//更新字庫---從SD卡讀取字庫到W25Q64
void FontUpdate_to_W25Q64();
    
FATFS fatfs; //文件系統(tǒng)注冊(cè)工作區(qū)需要使用

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個(gè)
    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(); //觸摸屏校準(zhǔn)程序
	
    RCC->APB2ENR|=1<<5;
    GPIOD->CRH&=0xFF0FFFFF;
    GPIOD->CRH|=0x00300000;
    while(SDCardDeviceInit()!=0)
    {
        printf("SDCard_DeviceInit 錯(cuò)誤.\r\n");
        PDout(13)=!PDout(13);
        delay_ms(100);
    }
    
	f_mount(&fatfs,"0:",0); //注冊(cè)文件系統(tǒng)的工作區(qū)

    //設(shè)計(jì)界面
    LCD_color_1=RED;
    LCD_color_2=LIGHTBLUE;
	NT35310_DisplayString(16,0,16,"基于STM32的小說閱讀器設(shè)計(jì)");
    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,"字體調(diào)整");
    NT35310_DisplayString(239/2+32,319-30,16,"顏色調(diào)整");
    
     /*1. 打開目錄*/    
    res=f_opendir(&dir,path);
    if(res!=FR_OK)return res;
    
    res=f_readdir(&dir,&fno);
    printf("文件名稱: %s,文件大小: %ld 字節(jié)\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;
    }
    
    //申請(qǐng)存放文件名稱的長(zhǎng)度
    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卷\
第一回 甄士隱夢(mèng)幻識(shí)通靈 賈雨村風(fēng)塵懷閨秀\
此開卷第一回也。作者自云:因曾歷過一番夢(mèng)幻之后,故將真事隱去,\
    而借“通靈”之說,撰此<<石頭記>>一書也。故曰“甄士隱”云云。\
    但書中所記何事何人?自又云:“今風(fēng)塵碌碌,一事無成,忽念及當(dāng)日所有之女子,\
    一一細(xì)考較去,覺其行止見識(shí),皆出于我之上。何我堂堂須眉,誠(chéng)不若彼裙釵哉?");
    
	while(1)
    {
        if(TOUCH_PEN==0) //判斷觸摸屏是否按下
        {
            //判斷是否讀取到XY坐標(biāo)
            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);	
                        }
                      //執(zhí)行代碼                        
                        res=f_read(&text_file,read_text_buf,4096,(UINT*)&br);//讀出4096個(gè)字節(jié)
                        read_text_buf[br]='\0';
                        printf("br=%d\r\n",br);
                        if(br!=4096)
                        {
                            f_close(&text_file);
                        }
                    }
                   
                    //字體大小
                    
                    x=0;     //坐標(biāo)起始位置
                    y=17+16; //坐標(biāo)起始位置
                    p=read_text_buf+read_cnt;
                    while(*p!='\0')
                    {
                            if(*p>0x80) //判斷是否是中文-編碼規(guī)則從 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);//顯示一個(gè)中文
                                    x+=size;
                                    p+=2; //偏移兩個(gè)字節(jié)
                            }
                            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;
                    
                     //關(guān)閉原來的文件
                    f_close(&text_file);
                        
                    //觸發(fā)新的頁
                    read_cnt=0;
                    br=0;
                    //執(zhí)行代碼                        
                    res=f_readdir(&dir,&fno);
                    if(fno.fname[0] == 0 || res!=0)
                    {
                        /*3. 關(guān)閉目錄*/
                        f_closedir(&dir);
                        
                         /*1. 打開目錄*/    
                        res=f_opendir(&dir,path);
                        if(res!=FR_OK)return res;
                        
                        res=f_readdir(&dir,&fno);
                    }
                        
                    printf("文件名稱: %s,文件大小: %ld 字節(jié)\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;
                    }
                    //申請(qǐng)存放文件名稱的長(zhǎng)度
                    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,"字體調(diào)整");
                    
                    //等待觸摸屏松開
                    while(TOUCH_PEN==0){}
                    
                     if(size==16)size=24;
                     else size=16;
                        
                    //執(zhí)行代碼                        
                        
                    //填充顏色
                    NT35310_Fill(0+1,319-40+1,239/2-1,319-1,WHITE);
                    LCD_color_2=WHITE;
                    //顯示字符串
                     NT35310_DisplayString(32,319-30,16,"字體調(diào)整");   
                }
                
                
                 //判斷范圍
                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,"顏色調(diào)整");
                    
                    //等待觸摸屏松開
                    while(TOUCH_PEN==0){}
                    
                    //執(zhí)行代碼  
                    //前景字體顏色切換
                    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,"顏色調(diào)整");   
                }
            }	
        }
    }
}

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. 循環(huán)讀取字庫更新到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. 判斷文件是否結(jié)束*/
		 if(res!=FR_OK||br!=4096)break;
	}
	/*6. 關(guān)閉字庫文件*/
	f_close(&fp);
}

poYBAGDYdXCAWkKMAAAAK8RNs4s030.png

5.2 sdcard.c SD卡驅(qū)動(dòng)代碼

#include "sdcard.h"			   
static u8  SD_Type=0;  //存放SD卡的類型

/*
函數(shù)功能:SD卡底層接口,通過SPI時(shí)序向SD卡讀寫一個(gè)字節(jié)
函數(shù)參數(shù):data是要寫入的數(shù)據(jù)
返 回 值:讀到的數(shù)據(jù)
說明:時(shí)序是第二個(gè)上升沿采集數(shù)據(jù)
*/
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);//第二個(gè)上升沿采集數(shù)據(jù)
		DataRx<<=1;
		if(SDCardInput)DataRx|=0x01;
	}
	return DataRx;
}

/*
函數(shù)功能:底層SD卡接口初始化

本程序SPI接口如下:
PC11  片選 SDCardCS
PC12  時(shí)鐘 SDCardSCLK
PD2   輸出 SPI_MOSI--主機(jī)輸出從機(jī)輸入
PC8   輸入 SPI_MISO--主機(jī)輸入從機(jī)輸出
*/
void SDCardSpiInit(void)
{
 	RCC->APB2ENR|=1<<5;		    //使能PORTD時(shí)鐘
	RCC->APB2ENR|=1<<4;		    //使能PORTC時(shí)鐘
	
	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);
}


/*
函數(shù)功能:取消選擇,釋放SPI總線
*/
void SDCardCancelCS(void)
{
	SDCardCS(1);
 	SDCardReadWriteOneByte(0xff);//提供額外的8個(gè)時(shí)鐘
}

/*
函數(shù) 功 能:選擇sd卡,并且等待卡準(zhǔn)備OK
函數(shù)返回值:0,成功;1,失敗;
*/
u8 SDCardSelectCS(void)
{
	SDCardCS(0);
	if(SDCardWaitBusy()==0)return 0;//等待成功
	SDCardCancelCS();
	return 1;//等待失敗
}


/*
函數(shù) 功 能:等待卡準(zhǔn)備好
函數(shù)返回值:0,準(zhǔn)備好了;其他,錯(cuò)誤代碼
*/
u8 SDCardWaitBusy(void)
{
	u32 t=0;
	do
	{
		if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
		t++;		  
	}while(t<0xFFFFFF);//等待 
	return 1;
}


/*
函數(shù)功能:等待SD卡回應(yīng)
函數(shù)參數(shù):
					Response:要得到的回應(yīng)值
返 回 值:
					0,成功得到了該回應(yīng)值
					其他,得到回應(yīng)值失敗
*/
u8 SDCardGetAck(u8 Response)
{
	u16 Count=0xFFFF;//等待次數(shù)	   						  
	while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到準(zhǔn)確的回應(yīng)  	  
	if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回應(yīng)失敗   
	else return SDCard_RESPONSE_NO_ERROR;//正確回應(yīng)
}


/*
函數(shù)功能:從sd卡讀取一個(gè)數(shù)據(jù)包的內(nèi)容
函數(shù)參數(shù):
				buf:數(shù)據(jù)緩存區(qū)
				len:要讀取的數(shù)據(jù)長(zhǎng)度.
返回值:
			0,成功;其他,失敗;	
*/
u8 SDCardRecvData(u8*buf,u16 len)
{			  	  
	if(SDCardGetAck(0xFE))return 1;//等待SD卡發(fā)回?cái)?shù)據(jù)起始令牌0xFE
    while(len--)//開始接收數(shù)據(jù)
    {
        *buf=SDCardReadWriteOneByte(0xFF);
        buf++;
    }
    //下面是2個(gè)偽CRC(dummy CRC)
    SDCardReadWriteOneByte(0xFF);
    SDCardReadWriteOneByte(0xFF);									  					    
    return 0;//讀取成功
}


/*
函數(shù)功能:向sd卡寫入一個(gè)數(shù)據(jù)包的內(nèi)容 512字節(jié)
函數(shù)參數(shù):
					buf 數(shù)據(jù)緩存區(qū)
					cmd 指令
返 回 值:0表示成功;其他值表示失敗;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{	
	u16 t;		  	  
	if(SDCardWaitBusy())return 1;  //等待準(zhǔn)備失效
	SDCardReadWriteOneByte(cmd);
	if(cmd!=0XFD)//不是結(jié)束指令
	{
		for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,減少函數(shù)傳參時(shí)間
	    SDCardReadWriteOneByte(0xFF); //忽略crc
	    SDCardReadWriteOneByte(0xFF);
		  t=SDCardReadWriteOneByte(0xFF); //接收響應(yīng)
		if((t&0x1F)!=0x05)return 2;   //響應(yīng)錯(cuò)誤									  					    
	}						 									  					    
    return 0;//寫入成功
}



/*
函數(shù)功能:向SD卡發(fā)送一個(gè)命令
函數(shù)參數(shù):
				u8 cmd   命令 
				u32 arg  命令參數(shù)
				u8 crc   crc校驗(yàn)值	
返回值:SD卡返回的響應(yīng)
*/												  
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
	u8 r1;	
	u8 Retry=0; 
		
	SDCardCancelCS();               //取消上次片選
	if(SDCardSelectCS())return 0XFF;//片選失效 
	//發(fā)送數(shù)據(jù)
	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--);	  //等待響應(yīng),或超時(shí)退出
   return r1;	//返回狀態(tài)值
}	


/*
函數(shù)功能:獲取SD卡的CID信息,包括制造商信息
函數(shù)參數(shù):u8 *cid_data(存放CID的內(nèi)存,至少16Byte)	  
返 回 值:
					0:成功,1:錯(cuò)誤				
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
    u8 r1;	   
    //發(fā)SDCard_CMD10命令,讀CID
    r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
    if(r1==0x00)
	  {
			r1=SDCardRecvData(cid_data,16);//接收16個(gè)字節(jié)的數(shù)據(jù)	 
    }
	SDCardCancelCS();//取消片選
	if(r1)return 1;
	else return 0;
}	


/*
函數(shù)說明:
					獲取SD卡的CSD信息,包括容量和速度信息
函數(shù)參數(shù):
					u8 *cid_data(存放CID的內(nèi)存,至少16Byte)	    
返 回 值:
					0:成功,1:錯(cuò)誤	
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
	u8 r1;	 
	r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //發(fā)SDCard_CMD9命令,讀CSD
	if(r1==0)
	{
		r1=SDCardRecvData(csd_data, 16);//接收16個(gè)字節(jié)的數(shù)據(jù) 
	}
	SDCardCancelCS();//取消片選
	if(r1)return 1;
	else return 0;
}  


/*
函數(shù)功能:獲取SD卡的總扇區(qū)數(shù)(扇區(qū)數(shù))   
返 回 值:
				0表示容量檢測(cè)出錯(cuò),其他值表示SD卡的容量(扇區(qū)數(shù)/512字節(jié))
說   明:
				每扇區(qū)的字節(jié)數(shù)必為512字節(jié),如果不是512字節(jié),則初始化不能通過.	
*/
u32 GetSDCardSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u8 n;
	  u16 csize;  					    
    if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;	//取CSD信息,如果期間出錯(cuò),返回0
    if((csd[0]&0xC0)==0x40)	        //V2.00的卡,如果為SDHC卡,按照下面方式計(jì)算
    {	
			csize = csd[9] + ((u16)csd[8] << 8) + 1;
			Capacity = (u32)csize << 10;//得到扇區(qū)數(shù)	 		   
    }
		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);//得到扇區(qū)數(shù)   
    }
    return Capacity;
}

/*
函數(shù)功能: 初始化SD卡
返 回 值: 非0表示初始化失敗!
*/
u8 SDCardDeviceInit(void)
{
  u8 r1;      // 存放SD卡的返回值
  u16 retry;  // 用來進(jìn)行超時(shí)計(jì)數(shù)
  u8 buf[4];  
	u16 i;
	SDCardSpiInit();		//初始化底層IO口
	
 	for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //發(fā)送最少74個(gè)脈沖
	retry=20;
	do
	{
		r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//進(jìn)入IDLE狀態(tài) 閑置
	}while((r1!=0X01) && retry--);
 	SD_Type=0;   //默認(rèn)無卡
	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);	    //發(fā)送SDCard_CMD55
					r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//發(fā)送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);		//發(fā)送SDCard_CMD55
			r1=SendSDCardCmd(SDCard_CMD41,0,0X01);	//發(fā)送SDCard_CMD41
			if(r1<=1)
			{		
				SD_Type=SDCard_TYPE_V1;
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					SendSDCardCmd(SDCard_CMD55,0,0X01);	//發(fā)送SDCard_CMD55
					r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發(fā)送SDCard_CMD41
				}while(r1&&retry--);
			}
			else//MMC卡不支持SDCard_CMD55+SDCard_CMD41識(shí)別
			{
				SD_Type=SDCard_TYPE_MMC;//MMC V3
				retry=0XFFFE;
				do //等待退出IDLE模式
				{											    
					r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//發(fā)送SDCard_CMD1
				}while(r1&&retry--);  
			}
			if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//錯(cuò)誤的卡
		}
	}
	SDCardCancelCS();       //取消片選
	if(SD_Type)return 0;  //初始化成功返回0
	else if(r1)return r1; //返回值錯(cuò)誤值	   
	return 0xaa;          //其他錯(cuò)誤
}


/*
函數(shù)功能:讀SD卡
函數(shù)參數(shù):
				buf:數(shù)據(jù)緩存區(qū)
				sector:扇區(qū)
				cnt:扇區(qū)數(shù)
返回值:
				0,ok;其他,失敗.
說  明:
				SD卡一個(gè)扇區(qū)大小512字節(jié)
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//轉(zhuǎn)換為字節(jié)地址
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//讀命令
		if(r1==0)												  //指令發(fā)送成功
		{
			r1=SDCardRecvData(buf,512);			//接收512個(gè)字節(jié)	   
		}
	}else
	{
		r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//連續(xù)讀命令
		do
		{
			r1=SDCardRecvData(buf,512);//接收512個(gè)字節(jié)	 
			buf+=512;  
		}while(--cnt && r1==0); 	
		SendSDCardCmd(SDCard_CMD12,0,0X01);	//發(fā)送停止命令
	}   
	SDCardCancelCS();//取消片選
	return r1;//
}

/*
函數(shù)功能:向SD卡寫數(shù)據(jù)
函數(shù)參數(shù):
				buf:數(shù)據(jù)緩存區(qū)
				sector:起始扇區(qū)
				cnt:扇區(qū)數(shù)
返回值:
				0,ok;其他,失敗.
說  明:
				SD卡一個(gè)扇區(qū)大小512字節(jié)
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
	u8 r1;
	if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//轉(zhuǎn)換為字節(jié)地址
	if(cnt==1)
	{
		r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//讀命令
		if(r1==0)//指令發(fā)送成功
		{
			r1=SDCardSendData(buf,0xFE);//寫512個(gè)字節(jié)	   
		}
	}
	else
	{
		if(SD_Type!=SDCard_TYPE_MMC)
		{
			SendSDCardCmd(SDCard_CMD55,0,0X01);	
			SendSDCardCmd(SDCard_CMD23,cnt,0X01);//發(fā)送指令	
		}
 		r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//連續(xù)讀命令
		if(r1==0)
		{
			do
			{
				r1=SDCardSendData(buf,0xFC);//接收512個(gè)字節(jié)	 
				buf+=512;  
			}while(--cnt && r1==0);
			r1=SDCardSendData(0,0xFD);//接收512個(gè)字節(jié) 
		}
	}   
	SDCardCancelCS();//取消片選
	return r1;//
}	

工程完整源碼下載地址:https://download.csdn.net/download/xiaolong1126626497/19628524

審核編輯:符乾江

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • STM32
    +關(guān)注

    關(guān)注

    2265

    文章

    10858

    瀏覽量

    354427
  • 閱讀器
    +關(guān)注

    關(guān)注

    0

    文章

    298

    瀏覽量

    27897
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    集成TIRIS射頻模塊TMS3705A低頻閱讀器簡(jiǎn)介

    電子發(fā)燒友網(wǎng)站提供《集成TIRIS射頻模塊TMS3705A低頻閱讀器簡(jiǎn)介.pdf》資料免費(fèi)下載
    發(fā)表于 09-21 11:00 ?0次下載
    集成TIRIS射頻模塊TMS3705A低頻<b class='flag-5'>閱讀器</b>簡(jiǎn)介

    二代身份證識(shí)別儀-身份證閱讀器-讀卡CICR-3X

    閱讀器
    jf_08279201
    發(fā)布于 :2024年09月02日 14:33:47

    STM32F103ZET6產(chǎn)生pwm波脈寬可調(diào)

    32新手,想請(qǐng)教一下各位用STM32F103ZET6產(chǎn)生pwm波可不可以設(shè)計(jì)成脈寬可調(diào),就是將占空比每次可增加百分之一轉(zhuǎn)變成每次可增加幾秒或者幾微秒的脈寬,查了好多資料大家都是占空比可調(diào)。
    發(fā)表于 06-05 10:13

    桌面RFID閱讀器:現(xiàn)代資產(chǎn)管理的革命性工具?

    隨著物聯(lián)網(wǎng)技術(shù)的快速發(fā)展,桌面RFID閱讀器正逐漸成為各行各業(yè)資產(chǎn)管理的重要組成部分。這種小巧而強(qiáng)大的設(shè)備不僅簡(jiǎn)化了數(shù)據(jù)讀取和寫入的過程,而且正在推動(dòng)一場(chǎng)管理效率的革命。桌面RFID閱讀器:小巧機(jī)身
    的頭像 發(fā)表于 06-04 15:53 ?411次閱讀
    桌面RFID<b class='flag-5'>閱讀器</b>:現(xiàn)代資產(chǎn)管理的革命性工具?

    STM32CubeMX中使用F103ZET6怎么配置LCD?

    STM32CubeMX中使用F103ZET6怎么配置LCD
    發(fā)表于 06-04 10:22

    手搓了一個(gè)ESP32墨水屏閱讀器,蠻簡(jiǎn)單的

    工程名稱:3.7寸墨水屏閱讀器時(shí)鐘溫濕度TXT/Epub前言帥小伙手搓ESP32墨水屏閱讀器!可輕松續(xù)航100天!01閱讀器功能/亮點(diǎn)?1.具有閱讀器、時(shí)鐘、溫濕度監(jiān)測(cè)、鬧鐘、番茄鐘功
    的頭像 發(fā)表于 05-27 08:04 ?331次閱讀
    手搓了一個(gè)ESP32墨水屏<b class='flag-5'>閱讀器</b>,蠻簡(jiǎn)單的

    學(xué)STM32為什么首選F103C8T6?

    STM32F103C8T6是STMicroelectronics(ST)推出的一款基于ARMCortex-M3內(nèi)核的32位微控制單片機(jī)(MCU)。它具有一系列優(yōu)勢(shì)和廣泛的應(yīng)用場(chǎng)景。這里就來介紹一下
    的頭像 發(fā)表于 05-18 08:04 ?4466次閱讀
    學(xué)STM32為什么首選<b class='flag-5'>F103C8T6</b>?

    如何用stm32f103zet6控制伺服電機(jī)的加減速運(yùn)動(dòng)?

    想用stm32f103zet6控制伺服電機(jī)的加減速運(yùn)動(dòng),不知該怎么做,請(qǐng)大神指教。具體情況是有個(gè)機(jī)械凸輪,有一根頂桿頂著凸輪邊沿,隨著凸輪轉(zhuǎn)動(dòng)及其外徑的變化,水平頂桿會(huì)沿水平方向作直線運(yùn)動(dòng)?,F(xiàn)在可以
    發(fā)表于 04-30 08:00

    STM32F103ZET6的SPI通訊時(shí),從機(jī)的SPI時(shí)鐘被干擾導(dǎo)致接收的數(shù)據(jù)錯(cuò)位要如何恢復(fù)?

    在使用STM32F103ZET6的SPI通訊時(shí),通訊時(shí)從機(jī)的SPI時(shí)鐘被干擾導(dǎo)致接收的數(shù)據(jù)錯(cuò)位,且無法恢復(fù),只有重新復(fù)位,接收的數(shù)據(jù)才能恢復(fù),程序是基于HAL庫寫的。大家知道有什么辦法可以讓被干擾的時(shí)鐘恢復(fù)正常嗎?
    發(fā)表于 04-25 07:42

    為什么我程序都能下載到stm32f103zet6上,卻不能下載到stm32f103c8上?

    為何我程序都能下載到stm32f103zet6上,卻不能下載到stm32f103c8上?需要改變什么配置嗎?
    發(fā)表于 04-17 06:42

    外接傳感過多,STM32F103ZET6 GPIO太少,如何才能將復(fù)用端口作為普通端口使用?

    比如F103ZET6的{:7:}F13,PF14,PF15是和FSMC_A7,FSMC_A8,FSMC_A9復(fù)用端口,我想當(dāng)作普通端口使用. 像這樣
    發(fā)表于 04-07 08:21

    stm32f103zet6換為stm32f103zct6后編譯不通過是怎么回事?

    芯片類型更換:stm32f103zet6 換為 stm32f103zct6, 編譯不通過
    發(fā)表于 04-02 07:33

    用Cubemx生成的F103ZET6 USB Audio Device Class的代碼,能正常被枚舉但沒有聲音怎么解決?

    用Cubemx 生成的F103ZET6 USB Audio Device Class的代碼,能正常被枚舉,但沒有聲音,用Cubemx 生成的F407ZGT6 USB Audio Device
    發(fā)表于 03-18 06:21

    請(qǐng)問在f103C8T6中外設(shè)的引腳轉(zhuǎn)接到ZET6上的時(shí)候是否需要更換引腳?

    如題,請(qǐng)問在f103C8T6中外設(shè)的引腳轉(zhuǎn)接到ZET6上的時(shí)候是否需要更換引腳,還是已經(jīng)設(shè)置好的可以直接轉(zhuǎn)接上去用
    發(fā)表于 03-14 06:37

    STM32F103ZET6與ADXL362使用SPI通信時(shí)出現(xiàn)了讀取不出數(shù)據(jù)的問題怎么解決?

    工程師們好,我在使用STM32F103ZET6與ADXL362使用SPI通信時(shí)出現(xiàn)了讀取不出數(shù)據(jù)的問題,因此想請(qǐng)教一下。我采用在讀取PART_ID時(shí)可以正常讀出數(shù)據(jù),在讀取XYZ三軸數(shù)據(jù)時(shí)讀取不出
    發(fā)表于 01-01 08:16