搜索
bottom↓
回复: 3

对“实用公交车语音报站器-WAVE播放器”的程序备注分析

[复制链接]

出0入0汤圆

发表于 2012-10-27 13:41:58 | 显示全部楼层 |阅读模式
正如马老师所说的,WAV播放器的电路是简单的,但其中涉及到的读取SD卡数据的部分却是很复杂的,花了一周时间研究了  “实用公交车语音报站器-WAVE播放器”程序,尤其对其中移植的 那个日本开发者的Petit FatFs - FAT file system module作了一番研究,在网上搜集了很多FAT方面的资料以及SD卡相关物理层规范,在研读马老师程序的时候一一作了相关备注,现将备注后的程序贴出来,供朋友们学习,有不当之处请不吝指教。
主要备注了main.c和mmc.c这两部分。我是在马老师的备注的基础上备注的,注意打开原程序看备注的相关内容即可知道我备注的是哪些内容。

main.c部分:
/*****************************************************
This program was produced by the CodeWizardAVR V2.04.4a Advanced
http://www.hpinfotech.com

Chip type           : ATmega16
Program type        : Application
Clock frequency     : 11.2896 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include "globle.h"

/*---------------------------------------------------------*/
/* Work Area                                               */
/*---------------------------------------------------------*/

#define work_state_no_disk                0
#define work_state_mount_disk        1
#define work_state_scan_disk        2
#define work_state_stand_by                3
#define work_state_play_w                4
#define work_state_play_z                5
#define work_state_play_m                6

#define work_state_opendir                10
#define work_state_play_1                11
#define work_state_play_2                12
#define work_state_err                        15

#define Work_err_mount                        '0'
#define Work_err_opendir                '1'
#define Work_err_readdir                 '2'
#define Work_err_play                        '3'


volatile BYTE FifoRi, FifoWi, FifoCt;        /* FIFO controls */
BYTE Buff[BSIZE];        /* Wave output FIFO */

FATFS Fs;                        /* File system object */
DIR Dir;                        /* Directory object */
FILINFO Fno;                /* File information */
WORD rb;                        /* Return value. Put this here to avoid bugs of avr-gcc */

bit led2_blik;
BYTE play_w_name[13];
BYTE play_m_name[13] = {"M.WAV"};



void main(void)
{
        UCHAR key_value,led1_ct=0,max_z,k;
        UCHAR work_state = work_state_no_disk, pre_key = No_key;
        BYTE * name_ptr;
        BYTE res,i;
        bit play_m_ok,play_ex=FALSE ;
       
        PORTA=0xFF;
        DDRA=0xF0;                        //PA7/PA6输出控制LED;PA0-PA3输入按键口,内部上拉有效

        PORTB=0xF1;                        //
        DDRB=0xBE;                        // PB0/PB6输入,上拉有效;PB4/PB5/PB7输出

        PORTD=0x03;
        DDRD=0x82;                        // PD7(PWM口)/PD1输出;PD0输入,上拉有效

#if _DEBUG == 1
        // USART initialization
        // Communication Parameters: 8 Data, 1 Stop, No Parity
        // USART Receiver: On
        // USART Transmitter: On
        // USART Mode: Asynchronous
        // USART Baud Rate: 9600
        UCSRA=0x00;
        UCSRB=0x18;
        UCSRC=0x86;
        UBRRH=0x00;
        UBRRL=0x47;
#endif       

        // Timer/Counter 0 initialization
        // Clock source: System Clock
        // Clock value: 11.025 kHz
        // Mode: CTC top=OCR0
        // OC0 output: Disconnected
        TCCR0=0x0D; //CTC模式,1024分频率(11289.6/1024=11.025khz) ,OC0 输出脚用作普通I/O脚;
        TCNT0=0x00; //赋初值0;
        OCR0=0xDC;  //fclkio/(N(1+ocr0))=11289.6khz/(1024*(1+220))~=49.886hz; 即对应~=20.045ms中断周期

        // Timer/Counter 1 initialization
        // Clock source: System Clock
        // Clock value: 11289.600 kHz
        // Mode: CTC top=OCR1A
        // OC1A output: Discon.
        // OC1B output: Discon.
        // Noise Canceler: Off
        // Input Capture on Falling Edge
        // Timer1 Overflow Interrupt: Off
        // Input Capture Interrupt: Off
        // Compare A Match Interrupt: On
        // Compare B Match Interrupt: Off
        TCCR1A=0x00;
        TCCR1B=0x09; //分频率为1;
        TCNT1H=0x00;
        TCNT1L=0x00;
        ICR1H=0x00;
        ICR1L=0x00;
        OCR1AH=0x00;
        OCR1AL=0xFE; //由此算出CTC中断频率为22.13647KHZ;应该在后面使用中动态调整其输出频率;
        OCR1BH=0x00;
        OCR1BL=0x00;

        // Timer/Counter 2 initialization
        // Clock source: System Clock
        // Clock value: 11289.600 kHz
        // Mode: Fast PWM top=FFh
        // OC2 output: Non-Inverted PWM
        ASSR=0x00;
        TCCR2=0x69;//快速PWM模式,升序匹配时清零OC0;降序匹配时置位OC0;分频率为1;中断频率为44.1khz;
        TCNT2=0x00;
        OCR2=0x80;//此初值对应的占空比无意义,后面使用时根据需要更改此值;

        // Timer(s)/Counter(s) Interrupt(s) initialization
        TIMSK=0x12;//T/C1输出比较A匹配中断使能,T/C0输出比较匹配中断使能


        // Analog Comparator initialization
        // Analog Comparator: Off
        // Analog Comparator Input Capture by Timer/Counter 1: Off
        ACSR=0x80;
        SFIOR=0x00;

        // Global enable interrupts
        #asm("sei")

        while (1)
    {
                if(key_scan)
                {
                        key_scan = FALSE;
                        key_value = key_read();
                        if(key_value != No_key)
                        {
                                play_ex = TRUE;
                                pre_key = key_value;
                        }

                        if (!sd_in_ok)//如果没有插入SD卡
                        {
                                if (++led1_ct > 25)//大于(25*20ms=500ms);
                                {
                                        led1_ct = 0;
                                        LED_1 = !LED_1;//LED1 1秒亮灭1次;
                                }
                                led2_blik = FALSE;
                                LED_2 = 1;//灭LED2
                                k = 0;//没卡插入的情况下无论是否按键也让计数变量K复位;
                                pre_key = No_key; //没卡插入的情况下无论是否按键也让键值为 无按键;
                                work_state = 0;   //没卡插入的情况下无论是否按键也让工作状态复位到0;
                        }
                        else if(led2_blik)//如果已经插入卡,且需要LED2指示错误状态时;
                        {
                                if (++led1_ct > 25)
                                {
                                        led1_ct = 0;
                                        LED_2 = !LED_2;//LED2 1秒亮灭1次;
                                }
                        }
                }

                switch(work_state)
                {
                        case work_state_no_disk:
                                if(sd_in_ok) //若已插卡
                                {
                                        LED_1 = 0; //点亮LED1;
                                        max_z = 0;
                                        k=0;
                                        work_state = work_state_mount_disk;
                                }
                                break;

                        case work_state_mount_disk:
                                if (pf_mount(&Fs))//加载SD卡卷,若返回值不为0代表出错;
                                {
                                        work_state = work_state_err;
                                }       
                                else//加载SD卡卷 后的返回值为0代表没有出错;
                                {
                                        init_spi_fast();//初始化SPI为5.6448MHZ的主模式;
                                        work_state = work_state_scan_disk;
                                }
                                break;

                        case work_state_scan_disk:
                                if (pf_opendir(&Dir, "") == FR_OK)//打开根目录若返回0代表 成功;
                                {
                                        for(;;)
                                        {
                                                res = pf_readdir(&Dir, &Fno);//反复 读刚创打开的文件目录 类 中国的信息(包括其中的子文件夹也包括其中的文件);读到此根目录最后会给Fno.fname[0]赋值0(即NULL);
                                                if (res || !Fno.fname[0]) // 读刚创建的文件目录 类 的信息 出错;  读完此目录下最后一个项目后会让Fno.fname[0]=0从而进入此条件语句;
                                                {
                                                        if (max_z)    //已经读到有效的文件个数大于1
                                                        {
                                                                LED_2 = 0;      //点亮LED2;
                                                                work_state = work_state_stand_by;
                                                        }       
                                                        else
                                                        {
                                                                work_state = work_state_err;
                                                        }
                                                        break;
                                                }       
                                                else //读刚创建的文件目录 类 的信息 没有出错;
                                                {
                                                        if (Fno.fattrib & (AM_DIR|AM_HID) || !strstr(Fno.fname, "Z.WAV"))//若读到的不是目录也不是隐藏性质的文件,且不是文件名为"Z.WAV"的文件(即Z前面没有XX位数字);
                                                        {
#if _DEBUG == 1
                                                                for (i=0; i<13; i++){putchar(Fno.fname);}
                                                                putchar(0x0d); putchar(0x0a);
#endif                                               
                                                        }
                                                        else  //读到的文件正确(即属于 xxM.wav或xxZ.wav或w.wav)
                                                        {
                                                                max_z++; //对遍历的有效文件个数进行计数;
#if _DEBUG == 1
                                                                for (i=0; i<13; i++){putchar(Fno.fname);}    //显示读出的根目录下的有效文件 的文件名;
                                                                putchar(0x0d); putchar(0x0a);
                                                                putchar(0x30+max_z);
                                                                putchar(0x0d); putchar(0x0a);                                                       
#endif                                       
                                                        }
                                                }
                                        }
                                }
                                else//打开根目录 失败;
                                {
                                        work_state = work_state_err;
                                }
                            break;

                        case work_state_stand_by:  //此步骤的目的是定位要播放的文件的文件名,将其由字符指针name_ptr指向;
                                if (play_ex)
                                {
                                        play_ex = FALSE;
                                        play_m_ok = FALSE;

                                        for(i = 0; i < 13; i++) play_w_name = '';
                               
                                        if(pre_key == play_w)
                                        {
                                                play_w_name[0] = 'W'; play_w_name[1] = '.';
                                                play_w_name[2] = 'W'; play_w_name[3] = 'A';        play_w_name[4] = 'V';
                                                name_ptr = play_w_name;
                                                work_state = work_state_opendir;
                                        }       
                                        else if(pre_key == play_next)
                                        {
                                                if(k < max_z - 1) k++;
                                                play_w_name[0] = 0x30+k; play_w_name[1] = 'Z'; play_w_name[2] = '.';    //由这里看播放的站台文件只能是1Z.WAV~9Z.WAVA而不是马朝老师书上说的0Z.WAV~xxZ.WAV范围;
                                                play_w_name[3] = 'W'; play_w_name[4] = 'A';        play_w_name[5] = 'V';
                                                name_ptr = play_w_name;
                                                work_state = work_state_opendir;
                                        }
                                        else if(pre_key == play_back)
                                        {
                                                if (k) k--;
                                                play_w_name[0] = 0x30+k; play_w_name[1] = 'Z'; play_w_name[2] = '.'; //由这里看播放的站台文件只能是0Z.WAV~9Z.WAVA而不是马朝老师书上说的xxZ.WAV范围;
                                                play_w_name[3] = 'W'; play_w_name[4] = 'A';        play_w_name[5] = 'V';
                                                name_ptr = play_w_name;
                                                work_state = work_state_opendir;
                                        }
                                        else if(pre_key == play_m)
                                        {
                                                name_ptr = play_m_name;
                                                play_m_ok = TRUE;
                                                work_state = work_state_opendir;
                                        }
                                }
                                break;

                        case work_state_opendir:
                                if (pf_opendir(&Dir, "") == FR_OK)//再次打开根目录若返回0代表 成功;
                                {
                                        work_state = work_state_play_w;
                                }
                                else //再次打开根目录若返回非0代表 失败;
                                {
                                        work_state = work_state_err;
                                }       
                                break;

                        case work_state_play_w:   //找到要打开的文件(的位置);
                                res = pf_readdir(&Dir, &Fno);//第2次循环回来重头开始读取根目录里面的内容;
                                if (res || !Fno.fname[0])//若读取出错或此轮已读完;
                                {
                                        if (play_m_ok)
                                                work_state = work_state_opendir;
                                        else
                                        {
                                                work_state = work_state_err;
                                        }       
                                }       
                                else  //读取成功且没到目录末尾;
                                {
                                        if (Fno.fattrib & (AM_DIR|AM_HID) || !strstr(Fno.fname, name_ptr)) //读出的内容为隐藏性质或者为子目录,或者刚打开的文件名不是要找的文件名;//extern char *strstr(char *str1, char *str2)函数的含义为:找出str2字符串在str1字符串中第一次出现的位置(不包括str2的串结束符);返回值:返回该位置的指针,如找不到,返回空指针。
                                        {
#if _DEBUG == 1
                                                for (i=0; i<13; i++){putchar(Fno.fname);}
                                                putchar(0x0d); putchar(0x0a);
#endif                                               
                                        }
                                        else //读出的内容不为隐藏性质且不为子目录,且刚打开的文件名正是要找的文件名;
                                        {
#if _DEBUG == 1
                                                for (i = 0; i<13; i++){putchar(Fno.fname);}
                                                putchar(0x0d); putchar(0x0a);
#endif
                                                if (play_m_ok)
                                                {
                                                        for(i=0; i<13; i++) play_m_name = Fno.fname;
                                                }
                                               
                                                play_state = 0;
                                                work_state = work_state_play_1;
                                        }
                                }       
                            break;

                        case work_state_play_1:
                                if (play(Fno.fname))//进行目标文件的读操作; 动态放入 到前面定义的FIFO中; 不正常的话返回值为1;
                                {
                                        work_state = work_state_err;
                                }
                                else//正常的话返回值为0
                                {
                                        if (play_ex || play_state == 0) //正常的话不会进入到此条件,因为上面 状态”work_state_stand_by“中就已经让play_ex为0了;
                                        {
                                                work_state = work_state_stand_by;
                                                if (play_m_ok)
                                                {
                                                        if (pre_key == play_m || play_state == 0)
                                                        {
                                                                for(i = 0; i < 13; i++) {play_m_name = '';}
                                                                play_m_name[0] = 'M'; play_m_name[1] = '.';
                                play_m_name[2] = 'W'; play_m_name[3] = 'A'; play_m_name[4] = 'V';
                                                                play_ex = FALSE;
                                                                work_state = work_state_play_w;
                                                        }
                                                }
                                        }
                                }
                                break;

                        case work_state_err:
                                led2_blik = TRUE;
#if _DEBUG == 1
//                                putchar(work_err);
//                                putchar(0x0d); putchar(0x0a);
#endif       
                                break;
                }
    }
}
























mmc.c部分:
/*-----------------------------------------------------------------------*/
/* PFF - Low level disk control module for ATmega16 ECNU CMA 2010        */
/*-----------------------------------------------------------------------*/

#include "globle.h"

// SPI initialization -12M
void init_spi_low(void)
{
        SPCR=0x5E;                // SPI Type: Master,Clock Rate: 2*187.5kHz ,Clock Phase: Cycle Start   //SPI主模式,频率11.2896M/=352.8KHZ; 模式3
        SPSR=0x01;                // SPI Clock Polarity: High, Data Order: MSB First
}

// SPI initialization -12M
void init_spi_fast(void)
{
        SPCR=0x5C;                // SPI Type: Master,Clock Rate: 2*3000kHz ,Clock Phase: Cycle Start   //SPI主模式,频率11.2896M/2=5.6448MHZ;模式3;
        SPSR=0x01;                // SPI Clock Polarity: High, Data Order: MSB First
}

BYTE rcv_spi (void)//SPI接收一个字节;
{
        SPDR = 0xff;
        while(!(SPSR&(1<<SPIF))){};
        return SPDR;
}

void xmit_spi (BYTE data)//SPI发送一个字节;
{
        SPDR = data;
        while(!(SPSR&(1<<SPIF))){};       
}

void fmit_spi (void)//SPI发送一个内容为0XFF的字节;
{
        SPDR = 0xff;
        while(!(SPSR&(1<<SPIF))){};       
}

BYTE read_step;
bit read_step_bit;
INT pre_data;
BYTE m_data_0;

void fwd_blk_part(    //读出一个扇区中的特定区域中的数据,注意对读出的音频数据进行的声道与量化位数的转换操作也在这里;
        void *dest,                /* Pointer to the destination object to put data */
        WORD ofs,                /* Byte offset in the sector (0..511) */
        WORD cnt                /* Byte count (1..512), b15:destination flag */
)
{
        WORD rb;
        BYTE* p;
        WORD i;

        rb = 514 - ofs - cnt;        /* 一个扇区中,减掉偏移字节数,减掉要读取的字节数,就剩下要丢弃的字节数(包括两个字节的CRC校验) */
        while(ofs --)rcv_spi();                 /* 丢弃需要偏移的字节。*/
   
        if(dest == 0)                                                                /* 如果指针被赋值为0, 直接处理音频数据 */      
        {
                if (channel_flag == 0x12)                                // 16bit 2ch
                {
                        while(cnt)                                                                /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                --cnt;
                                if (read_step == 1)                                        // 左声道高8位   //左声道低8bit抛弃;
                                {
                                        if(m_data_0 <= 0x80)   //为0或正数的INT;
                                                pre_data = m_data_0 + 0x80; //m_data_0本身为无符号字符类变量,对应16BIT的信号本身用的有符号的INT类型表示的,对于0~+127的正的INT数加上0X80(有符号字符类型变量对应的+0值,结果仍旧为改值本身(因符号位不参与运算)
                                        else  //为负数的INT;
                                                pre_data = m_data_0 + 0x81;//这里为何要对负的字符类型变量加1个-1,原因不明;

                                }
                                else if (read_step == 3)                                        // 右声道高8位  //右声道低8bit抛弃;
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = (m_data_0 + 0x80);
                                        else
                                                m_data_0 = (m_data_0 + 0x81);
                                        m_data_0 = (pre_data + m_data_0) >> 1;        // (左+右)/2
                                       
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                if (++read_step > 3) read_step = 0;                               
                        }
                }
                else if(channel_flag == 0x0A)                                // 8bit 2ch
                {
                        while(cnt)                                                                /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                --cnt;
                                if (read_step_bit)                                        // 右声道高8位
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = (m_data_0 + 0x80);
                                        else
                                                m_data_0 = (m_data_0 + 0x81);
                                        m_data_0 = (pre_data + m_data_0) >> 1;        // (左+右)/2
                                       
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                else                                         // 左声道高8位
                                {
                                        if(m_data_0 <= 0x80)
                                                pre_data = m_data_0 + 0x80;
                                        else
                                                pre_data = m_data_0 + 0x81;
                                }
                                read_step_bit = !read_step_bit;                               
                        }
                }
                else if (channel_flag == 0x11)                        // 16 bit  1ch
                {
                        while(cnt)                                                        /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                --cnt;
                                if (read_step_bit)                                        // 只读取高8位
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = m_data_0 + 0x80;
                                        else
                                                m_data_0 = m_data_0 + 0x81;
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                read_step_bit = !read_step_bit;                               
                        }
        }
                else                                                                // 8bit 1ch
                {
                        for(i = 0;i < cnt;i++)                        /* 接收指定的字节数 */        
                        {
                                m_data_0 = rcv_spi();
                         while(FifoCt == FULL_bsize);/* 如果缓冲区满,则等待 */
                         Buff[FifoWi] = m_data_0;        /* 一旦缓冲区有空位,则接收音频数据填入 */
                         #asm("cli")                                        /* 修改音频数据的个数之前首先关闭中断 */
                         FifoCt ++;                                        /* 修改音频数据 */
                         #asm("sei")                                        /* 修改完成后打开中断 */
                                ++FifoWi;                                       
                      }
                }       
        }  
    else
    {
                p = dest;                                 /* 若指针被赋值为某个缓冲区的地址 */
                do
        {
                *p++ = rcv_spi();                      /* 在指定的缓冲区中放入数据 */
              }while (--cnt);                            /* 接收完指定的字节数 */
        }
        do
    {
            rcv_spi();                                /* 抛弃剩下的字节数 */
        } while (--rb);  
}

/*--------------------------------------------------------------------------
   Module Private Functions
---------------------------------------------------------------------------*/
BYTE CardType;

/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus                                 */
/*-----------------------------------------------------------------------*/
void release_spi(void)
{
        DESELECT();
        fmit_spi();//我估计是通过调用一个发送0XFF字节内容达到延时的目的;
}

/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC                                          */
/*-----------------------------------------------------------------------*/
BYTE send_cmd(BYTE cmd,        DWORD arg)                /* Command byte, Argument */
{
        BYTE n, res;

        if (cmd & 0x80)                /* ACMD<n> is the command sequense of CMD55-CMD<n> */  //这里是PFF原作者使用的一个技巧,对需要先发起CMD55的命令在其宏定义时在其BIT7置位了1作为了此处识别的标志,如ACMD41;
        {
                cmd &= 0x7F;
                res = send_cmd(CMD55, 0);
                if (res > 1) return res;
        }

        /* Select the card and wait for ready */
        DESELECT();
        fmit_spi();
        SELECT();
        fmit_spi();

        /* Send command packet */
        xmit_spi(cmd);                                                /* Start + Command index */
        xmit_spi((BYTE)(arg >> 24));                /* Argument[31..24] */
        xmit_spi((BYTE)(arg >> 16));                /* Argument[23..16] */
        xmit_spi((BYTE)(arg >> 8));                        /* Argument[15..8] */
        xmit_spi((BYTE)arg);                                /* Argument[7..0] */
        n = 0x01;                                                        /* Dummy CRC + Stop */
        if (cmd == CMD0) n = 0x95;                        /* Valid CRC for CMD0(0) */
        if (cmd == CMD8) n = 0x87;                        /* Valid CRC for CMD8(0x1AA) */
        xmit_spi(n);

        /* Receive command response */
        n = 10;                                                                /* Wait for a valid response in timeout of 10 attempts */
        do
        {
                res = rcv_spi();
        }while ((res & 0x80) && --n);//对于R1响应0X01的BIT7为0,故这里只是对0X01值的部分判断;在SD对主机的数据没发出响应之前总线反馈给主机的数据一直保持的是0XFF;
        return res;                        /* Return with the response value */
}

/*--------------------------------------------------------------------------
   Public Functions
---------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (void)
{
        BYTE n, cmd, ty, ocr[4];
        WORD tmr;

        init_spi_low();                /* Initialize USI */

        for (tmr = 100; tmr; tmr--) fmit_spi();        /* Dummy clocks */  //在选中SD卡前发送大于72个小于400KHZ的时钟;
        SELECT();    //选中SD卡;
        for (tmr = 1000; tmr; tmr--) fmit_spi();        /* Dummy clocks */     // 延时一段时间,此时主机到SD的数据总线一直为0XFF;

        ty = 0;
        if (send_cmd(CMD0, 0) == 1)                                /* Enter Idle state */
        {
                if (send_cmd(CMD8, 0x1AA) == 1)                /* SDv2 */    //符合SD2.0标准的卡(即大于4GD卡)会对CMD8返回1;
                {
                        for (n = 0; n < 4; n++) ocr[n] = rcv_spi();                /* Get trailing return value of R7 resp */
                        if (ocr[2] == 0x01 && ocr[3] == 0xAA)                        /* The card can work at vdd range of 2.7-3.6V */
                        {
                                for (tmr = 25000; tmr && send_cmd(ACMD41, 1UL << 30); tmr--) ;        /* Wait for leaving idle state (ACMD41 with HCS bit) */ //从网上查到 UL是unsigned long的缩写;这里发送ACMD41命令中的32bit参数的bit30给出的1即HCS位代表主机支持高容量卡,正常的话FOR循环中用tmr是不会累计减到零的,SD卡对主机发的ACMD41命令的响应为“在初始化完成前返回0X01的R1响应,在初始化完成后返回0x00的R1响应”
                                                                                //故主机一直需要发送ACMD41命令通过返回值进行查询SD是否已经完成了初始化;
                                if (tmr && send_cmd(CMD58, 0) == 0)                        /* Check CCS bit in the OCR */  //正常的话此处的tmr不应为0;使用此处的发送CMD58命令来获得卡是否为高容量SD卡,还是标准容量的SD卡,这会导致后面读扇区的地址参数的不一样; 正常的话“send_cmd(CMD58, 0) == 0”应该为真,也即是CMD58返回的R3(由R1响应+32BIT的OCR寄存器内容)响应中的R1响应值应该为0X00,马朝老师书上说的R1响应正常的话一般为0X01;
                //为何此处就变成了0X00,看了SD卡V2版本规范中对R1响应的解释就明白了,R1响应最后一 bit 的解释是“In idle state: The card is in idle state and running the initializing process”,也就是R1的值为0x01则代表SD卡处于初始化完成前的停止状态,而上面已经用ACMD41命令对卡进行了初始化,且当时对ACMD41返回的R1的值已经为0X00,故凡从(初始化完成)此后的R1响应的值都为0X00;
                                {
                                        for (n = 0; n < 4; n++) ocr[n] = rcv_spi();
                                        ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;        /* SDv2 */   //CT_SD2 | CT_BLOCK代表高容量SD卡,CT_SD2代表标准容量SD卡;
                                }
                        }
                }
                else                                                        /* SDv1 or MMC */     //不符合SD2.0标准的卡不会对CMD8有反应,返回值一直保持为0XFF;
                {
                        if (send_cmd(ACMD41, 0) <= 1)//对SD卡的初始化命令有反应;
                        {
                                ty = CT_SD1; cmd = ACMD41;        /* SDv1 */ //为SD1.0标准的SD卡,用ACMD41命令来初始化;
                        }
                        else  //对SD卡的初始化命令无反应;
                        {
                                ty = CT_MMC; cmd = CMD1;        /* MMCv3 */ //为MMC卡,用CMD1命令来初始化;
                        }
                        for (tmr = 25000; tmr && send_cmd(cmd, 0); tmr--) ;        /* Wait for leaving idle state */ //正常的话此处的CMD1或ACMD41执行后会在 tmr还未减到零时就得到0X00返回值;
                        if (!tmr || send_cmd(CMD16, 512) != 0)                                /* Set R/W block length to 512 */  //这里是运算顺序为先执行逻辑或两端的运算最后执行逻辑或运算; 这里的CMD16的返回值正常的话应该为0X00,因为在已经完成初始化后使用返回R1响应的命令时其返回值不是0X01而是0X00;
                        {
                                ty = 0;
                        }
                }
        }
        CardType = ty;
        release_spi();

        return ty ? 0 : STA_NOINIT; //初始化正常的话返回值为0,否则为代表1的STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Read partial sector                                                   */
/*-----------------------------------------------------------------------*/

DRESULT disk_readp(
        void *dest,                /* Pointer to the destination object to put data */
        DWORD lba,                /* Start sector number (LBA) */
        WORD ofs,                /* Byte offset in the sector (0..511) */
        WORD cnt                /* Byte count (1..512), b15:destination flag */
)
{
        DRESULT res;
        BYTE rc;
        WORD tmr;

        if (!(CardType & CT_BLOCK)) lba *= 512;                /* Convert LBA to BA if needed */  //对应标准容量的卡由逻辑块地址(扇区号)转换为逻辑字节地址;

        res = RES_ERROR;
        if (send_cmd(CMD17, lba) == 0)                                /* READ_SINGLE_BLOCK */
        {
                tmr = 30000;
                do                                                                                /* Wait for data packet in timeout of 100ms */
                {
                        rc = rcv_spi();
                } while (rc == 0xFF && --tmr); //循环直到得到值为0XFE的前导标识响应;

                if (rc == 0xFE) //在SD v2.0规范中的P111~P112的“For Single Block Read, Single Block Write and Multiple Block Read”部分有SD卡给主机发出的数据的格式的详细描述,首先发出的是0XFE前导码(因数据总线空闲时为0XFF,否则无法区别何时是真正的数据);
                {
                        fwd_blk_part(dest, ofs, cnt);
                        res = RES_OK;
                }
        }

        release_spi();

        return res;
}



出0入36汤圆

发表于 2012-10-31 11:42:56 | 显示全部楼层
不传上来,咋下载啊

出0入0汤圆

 楼主| 发表于 2012-10-31 12:30:11 | 显示全部楼层
上面已经把我备注到的2个C文件都粘贴出来了,且标注了对应的文件名,其它代码见马老师第二版书的共享光盘中相关内容。

出0入0汤圆

发表于 2013-12-29 09:06:42 来自手机 | 显示全部楼层
mark                  
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 07:03

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表