You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
4.1 KiB
C
223 lines
4.1 KiB
C
/**
|
|
* @file sdcard.c
|
|
* Provides a basic library for accessing an SD card
|
|
*
|
|
* Copyright (C) 2018 Clyne Sullivan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <clock.h>
|
|
#include <gpio.h>
|
|
#include <heap.h>
|
|
#include <sdcard.h>
|
|
#include <string.h>
|
|
|
|
#define SCK GPIO_PORT(A, 5)
|
|
#define SO GPIO_PORT(A, 6)
|
|
#define SI GPIO_PORT(A, 7)
|
|
#define CS GPIO_PORT(B, 6)
|
|
|
|
typedef struct {
|
|
uint8_t cmd;
|
|
uint32_t arg;
|
|
uint8_t crc;
|
|
} __attribute__ ((packed)) sdcmd_t;
|
|
|
|
typedef struct {
|
|
uint8_t zero :1;
|
|
uint8_t param :1;
|
|
uint8_t address :1;
|
|
uint8_t erase :1;
|
|
uint8_t crc :1;
|
|
uint8_t badcmd :1;
|
|
uint8_t ereset :1;
|
|
uint8_t idle :1;
|
|
} __attribute__ ((packed)) r1_t;
|
|
|
|
void flash_out(uint8_t byte)
|
|
{
|
|
for (int i = 0; i < 8; i++) {
|
|
gpio_dout(SI, byte & (1 << (7 - i)));
|
|
gpio_dout(SCK, 1);
|
|
gpio_dout(SCK, 0);
|
|
}
|
|
}
|
|
|
|
uint8_t flash_in(void)
|
|
{
|
|
uint8_t byte = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
if (gpio_din(SO))
|
|
byte |= 1 << (7 - i);
|
|
gpio_dout(SCK, 1);
|
|
gpio_dout(SCK, 0);
|
|
}
|
|
return byte;
|
|
}
|
|
|
|
void sd_delay(void)
|
|
{
|
|
gpio_dout(SI, 1);
|
|
uint8_t so;
|
|
int i = 0;
|
|
do {
|
|
so = flash_in();
|
|
if (i < 8)
|
|
i++;
|
|
} while (i < 8 || so != 0xFF);
|
|
}
|
|
|
|
uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg)
|
|
{
|
|
sdcmd_t cmdp;
|
|
cmdp.cmd = (cmd & 0x3F) | 0x40;
|
|
cmdp.arg = arg;
|
|
|
|
sd_delay();
|
|
flash_out(cmdp.cmd);
|
|
flash_out(cmdp.arg >> 24);
|
|
flash_out(cmdp.arg >> 16);
|
|
flash_out(cmdp.arg >> 8);
|
|
flash_out(cmdp.arg);
|
|
flash_out(cmd == 8 ? 0x87 : 0x95);
|
|
|
|
// wait for a zero
|
|
gpio_dout(SI, 1);
|
|
uint8_t r1;
|
|
do {
|
|
r1 = flash_in();
|
|
} while (r1 & 0x80);
|
|
|
|
return r1;
|
|
}
|
|
|
|
void sd_init(void)
|
|
{
|
|
gpio_mode(SCK, OUTPUT);
|
|
gpio_mode(SI, OUTPUT);
|
|
gpio_mode(CS, OUTPUT);
|
|
gpio_mode(SO, OUTPUT);
|
|
gpio_speed(SCK, VERYHIGH);
|
|
gpio_speed(SI, VERYHIGH);
|
|
gpio_speed(SO, VERYHIGH);
|
|
gpio_speed(CS, VERYHIGH);
|
|
gpio_pupd(SCK, PULLUP);
|
|
gpio_pupd(SI, PULLUP);
|
|
gpio_pupd(SO, PULLUP);
|
|
gpio_pupd(CS, PULLUP);
|
|
gpio_dout(SO, 0);
|
|
gpio_mode(SO, INPUT);
|
|
gpio_dout(CS, 1);
|
|
gpio_dout(SCK, 0);
|
|
gpio_dout(SI, 1);
|
|
|
|
// init? cs and si are high
|
|
delay(10);
|
|
sd_delay();
|
|
|
|
// pull cs low, send cmd0
|
|
gpio_dout(CS, 0);
|
|
uint8_t resp = sd_send_cmd(0, 0);
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
|
|
if (resp != 0x01)
|
|
while (1);
|
|
|
|
// do cmd8
|
|
delay(10);
|
|
gpio_dout(CS, 0);
|
|
resp = sd_send_cmd(8, 0x1AA);
|
|
uint8_t ocr[4];
|
|
for (int i = 0; i < 4; i++)
|
|
ocr[i] = flash_in();
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
(void)ocr;
|
|
|
|
// initialize
|
|
do {
|
|
// cmd55
|
|
gpio_dout(CS, 0);
|
|
resp = sd_send_cmd(55, 0);
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
delay(10);
|
|
|
|
// acmd41
|
|
gpio_dout(CS, 0);
|
|
resp = sd_send_cmd(41, 0x40000000);
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
} while (resp != 0x00);
|
|
|
|
// set block length
|
|
gpio_dout(CS, 0);
|
|
resp = sd_send_cmd(16, 512);
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
}
|
|
|
|
uint8_t *sd_read_block(uint8_t *buf, uint32_t lba)
|
|
{
|
|
if (buf == 0)
|
|
return 0;
|
|
|
|
// send command
|
|
gpio_dout(CS, 0);
|
|
if (sd_send_cmd(17, lba) != 0)
|
|
return 0;
|
|
|
|
// wait for block
|
|
gpio_dout(SI, 1);
|
|
uint8_t byte;
|
|
do byte = flash_in();
|
|
while (byte == 0xFF);
|
|
if (byte != 0xFE)
|
|
return 0;
|
|
|
|
// get block
|
|
uint32_t i = 0;
|
|
for (i = 0; i < 512; i++)
|
|
buf[i] = flash_in();
|
|
|
|
sd_delay();
|
|
gpio_dout(CS, 1);
|
|
return buf;
|
|
}
|
|
|
|
uint8_t *sd_read(uint8_t *buf, uint32_t lba, uint32_t count)
|
|
{
|
|
if (buf == 0)
|
|
return 0;
|
|
|
|
uint8_t *block_buf = malloc(512);
|
|
uint32_t i = 0;
|
|
while (count > 0) {
|
|
sd_read_block(block_buf, lba);
|
|
memcpy(buf + i, block_buf, (count < 512) ? count : 512);
|
|
if (count < 512)
|
|
count = 0;
|
|
else
|
|
count -= 512;
|
|
i += 512;
|
|
lba++;
|
|
}
|
|
|
|
free(block_buf);
|
|
return buf;
|
|
}
|
|
|