主頁 >  其他 > 基于STM32設計的小說閱讀器(翻頁、字體切換、顏色切換、語音播報)

基于STM32設計的小說閱讀器(翻頁、字體切換、顏色切換、語音播報)

2021-06-16 19:22:30 其他

一、環境介紹

小車主控MCU: STM32F103ZET6

STM32程式開發IDE: keil5

STM32程式風格: 采用暫存器方式開發,注釋齊全,執行效率高,方便移植

硬體包含: 一塊STM32F103ZET6系統板、一個2.8寸TFT電阻觸摸顯示屏、一個SD卡卡槽(SPI介面)、一張SD卡(存放字庫和小說檔案)

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

二、功能介紹

這是基于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撰寫,下載即可編譯,測驗,學習,

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

5.1 main.c 主函式代碼

#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include <string.h>
#include <stdio.h>
#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://www.uj5u.com/qita/287834.html

標籤:其他

上一篇:計算機視覺演算法——目標檢測網路總結

下一篇:攻防環境之win2003一鍵部署推薦篇

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more