Mò mẫm ba ngày ròng rã, cuối cùng con AVR cũng chịu nói chuyện với thằng MMC/SD. Post ở đây vừa chia sẻ kiến thức vừa lưu lại để dành, hồi chừng quên thì quay lại có cái mà mò ;))

        Đầu tiên cần lưu ý là thằng MMC/SD chỉ nói chuyện bằng giao thức SPI, do đó mọi chuyện cần bắt đầu bằng SPI. Toàn bộ code được viết bằng C, trên trình biên dịch CodeVisionAVR Ver 2.03.4 đã được “cờ rắc” :D – Nếu chưa có thì tải ở đây!

Đầu tiên là khởi tạo SPI

void SPI_init()
{
       delay_ms(500);
       DDRB  =  0xB0;                                           // Đặt SCK, MOSI & SS chế độ xuất (outputs )
       SPCR =  0b01010010;                            // Khởi tạo cổng SPI (Mode 0) 
       SPSR  =  0b00000000;
       PORTB  =  0xEF;                                       // Xóa bit MOSI & SCK
}

Gửi / nhận 1 byte bằng SPI

unsigned char SPI_transfer(unsigned char dataS)
{
       char dataR=0;
       SPDR = dataS;
       while (!(SPSR & 0x80));
       dataR=SPDR;
       return (dataR);
}

       Phần khởi tạo giao thức chỉ có bi nhiu đó thôi, gửi thử 1 byte rồi kẹp oscillocope vô, nhìn xung ra đẹp thấy thương. :))

       Với mỗi một thiết bị, nhà sản xuất thường định sẵn các tập lệnh để ghép nối, các tập lênh này với cùng loại thiết bị thì như nhau, đó xem như tiêu chuẩn. Thằng MMC/SD  tuân thủ đúng như vậy, nó cũng có những tập lệnh riêng để giao tiếp, mỗi lệnh có một chức năng khác nhau, cái này muốn hiểu rõ thì phải tham khảo thêm datasheet. Chỉ cần chú ý là sau mỗi lần nói chuyện xong, cần disable em nó một phát, khi nào cần nói tiếp thì enable lại. Vậy nên cần tạo sẵn hai chương trình con bật/tắt để tiện các thao tác về sau.

Enable MMC/SD

void MMC_en()
{
     SPIPORT &= ~CS;
     SPI_transfer(0xFF);
    SPI_transfer(0xFF);
}

Disable MMC/SD

void MMC_dis()
{
     SPIPORT |=CS;
    SPI_transfer(0xFF);
    SPI_transfer(0xFF); 
}

       Vì MMC/SD làm việc với các tập lệnh riêng của nó, nến làm sẵn chương trình con gởi lệnh đến MMC theo đúng cấu trúc lệnh.

Gửi / Nhận giá trị với MMC/SD

char Command(unsigned char B5, unsigned char B4, unsigned char B3, unsigned char B2, unsigned char B1, unsigned char B0 )

    SPI_transfer(0xFF);
    SPI_transfer(B5);
    SPI_transfer(B4);
    SPI_transfer(B3);
    SPI_transfer(B2);
    SPI_transfer(B1);
    SPI_transfer(B0);
    SPI_transfer(0xFF);
    return SPI_transfer(0xFF);

      Các bước chuẩn bị xong, giờ là đến lúc khởi tạo em nó đây.
       Đầu tiên là disable em nó, gửi liên tục 80 xung SCK cho em nó tự khởi động, sau đó enable lên, gửi command 0, nếu kết quả từ MMC/SD trả về là 1 là ok, gửi tiếp command 1, nếu trả về 0 là ok, lại gửi tiếp command 16, kết quả trả về 0 là ok.

Khởi tạo MMC/SD

unsigned char MMC_init()
{
       unsigned int i=0;
       MMC_dis();
       for(i=0;i<10; i++)              //10×8=80 xung SCK cho MMC tu khoi tao
        SPI_transfer(0xFF);
/*———————-Gui CMD0———————-*/
       MMC_en();
       while (Command(0x40,0,0,0,0,0×95)!=1);
       MMC_dis();
/*———————-Gui CMD1———————-*/
       MMC_en();
       while (Command(0x41,0,0,0,0,0xFF)!=0);
       MMC_dis();
/*———————-Gui CMD16———————*/
       MMC_en();
       while (Command(0x50,0,0,0×02,0,0xFF)!=0);
        MMC_dis();
        return 1; 
}

       Sau khi MMC/SD khởi tạo, giờ đọc thử một sector bằng commnad 17, BLOCK_SIZE=512, được định nghĩa ở đầu chương trình : #define BLOCK_SIZE  512 

Đọc một sector

unsigned char  MMC_readblock(unsigned long SECTOR)
{
    unsigned int i=0;
    unsigned long ADDRESS=0;
    unsigned char mmcData=0;

    ADDRESS=(unsigned long)SECTOR*BLOCK_SIZE;
    MMC_en();
   
    while (Command(0x51,ADDRESS>>24,ADDRESS>>16,ADDRESS>>8,ADDRESS>>0,0xFF)!=0);
    while(SPI_transfer(0xFF) != 0xFE);
         
    for(i=0;i<BLOCK_SIZE;i++)
    {
        mmcData=SPI_transfer(0xFF);     
        Sector_buff[i]=mmcData;
    }   
    SPI_transfer(0xFF);
    SPI_transfer(0xFF);
    MMC_dis();
    return 1;
}

       MMC/SD không đặt sector từ đầu không gian lưu trữ, nên cần lấy địa chỉ bắt đầu, dùng command 17

Tìm địa chỉ bảng FAT

unsigned long getPartitionOffset(void)
{
 unsigned int i = 0;
 unsigned long offset = 0;
 unsigned char tmp=0;

    MMC_en();
   
    while (Command(0x51,0,0,0,0,0xFF)!=0);
    while(SPI_transfer(0xFF) != 0xFE);
         
    for(i=0;i<454;i++)
        SPI_transfer(0xFF);
       
    tmp = SPI_transfer(0xFF);
    offset = tmp;
    tmp = SPI_transfer(0xFF);
    offset |= (unsigned long int)tmp << 8;
    tmp = SPI_transfer(0xFF);
    offset |= (unsigned long int)tmp << 16;
    tmp = SPI_transfer(0xFF);
    offset += (unsigned long int)tmp << 24;   
   
    for(i = 458;i<BLOCK_SIZE;i++)
        SPI_transfer(0xFF);

    SPI_transfer(0xFF);
    SPI_transfer(0xFF);
    MMC_dis();
    return offset;
}

các thao tác đọc, ghi xoá sector cũng dựa trên command để làm. Chỉ có nhiêu chương trình đó thôi mà mất 3 ngày 3 đêm :|