41а. SD-карта по SDIO: четыре линии данных, команды, DMA и FatFS
В разделе 41 карта подключена по SPI — четыре провода, любой микроконтроллер, скорость около 3 МБ/с в лучшем случае. У F4 (и у F103 высокой плотности) есть собственный блок SDIO: он говорит с картой на её родном протоколе, гонит данные по четырём линиям сразу, считает контрольные суммы аппаратно и кормит DMA. Тот же FatFS поверх него работает быстрее в несколько раз. Цена — три лишних провода, подтяжки и протокол запуска, который нужно понимать, когда карта «не определяется».
Правила протокола, формула делителя, устройство FIFO и порядок работы с DMA
взяты из справочного руководства на F4 (из пакета Keil); последовательность
команд при запуске — из исходников HAL 2.17.1. Времена, CRC и делители пересчитаны
программой _verify/sdio41a/sdio_calc.c, собранной gcc на компьютере. Сверяет
_verify/sdio41a_check.py.
Как это называется в документации
Оригинал Перевод SDIO, SDMMC блок для SD/MMC-карт (в новых семействах — SDMMC) SDIO_CK, SDIO_CMD, SDIO_D0…D3 такт, линия команд, линии данных CPSM, DPSM автоматы команд и данных внутри блока command, response (R1…R7) команда и ответ карты ACMD «прикладная» команда — идёт после CMD55 OCR, CID, CSD, RCA, SCR регистры карты: питание, паспорт, параметры, адрес, возможности SDSC, SDHC/SDXC карта стандартной и высокой ёмкости block, DBLOCKSIZE блок данных и код его размера wide bus четырёхбитный режим
41а.1 Чем SDIO отличается от карты по SPI
У карты два лица. В режиме SPI она притворяется обычным ведомым устройством:
одна линия туда, одна обратно, выбор кристалла. В родном режиме у неё отдельная
линия команд CMD, такт CLK и до четырёх линий данных D0…D3; по всем
четырём биты идут одновременно. Блок SDIO знает только родной режим — режима
SPI у него нет, это прямо сказано в руководстве.
Что это даёт на 24 МГц (рабочий такт по умолчанию в HAL):
| Режим | Один блок 512 байт | Предел фазы данных |
|---|---|---|
| SDIO, 1 линия | 4114 тактов — 171,4 мкс | 3,0 МБ/с |
| SDIO, 4 линии | 1042 такта — 43,4 мкс | 11,8 МБ/с |
| SPI на 25 Мбит/с | ≈ 4100 тактов | ≈ 3 МБ/с |
Блок — это стартовый бит, 4096 бит данных, по 16 бит CRC на каждой линии и стоповый бит: на четырёх линиях данные делятся на четыре, а контрольные суммы считаются параллельно. Отсюда почти четырёхкратный выигрыш.
Это предел самой передачи данных. В настоящий поток входят ещё команда
и ответ по 48 бит, время доступа карты до первого блока, а при записи — время,
пока карта держит D0 «занято»: у дешёвых карт это миллисекунды на блок.
Близко к расчёту подходит только чтение многими блоками подряд.
Ещё два отличия: контрольные суммы всегда включены — CRC7 у команд и ответов,
CRC16 у каждой линии данных, и считает их блок сам; и линии двунаправленные,
им нужны подтяжки к питанию на всех D0…D3 и CMD — руководство требует внешних
резисторов на линиях данных. Без них карта молчит или работает только в
однобитном режиме.
У F411 блок SDIO есть (в даташите — «SD/MMC/eMMC»), у F407 и F429 тоже; у BluePill на F103C8 его нет — там только SPI (раздел 41).
41а.2 Такт: делитель и два режима частоты
Тактовая частота карты получается из SDIOCLK делителем в регистре CLKCR:
SDIO_CK = SDIOCLK / (CLKDIV + 2)
SDIOCLK — те же 48 МГц, что для USB (руководство: до 50 МГц, 48 при совместной
работе с USB), поэтому ФАПЧ должна выдавать честные 48 МГц, иначе карта получит
не ту частоту. Есть и режим обхода делителя — тогда карта тактируется прямо от
SDIOCLK.
HAL держит два делителя:
#define SDIO_INIT_CLK_DIV ((uint8_t)0x76) /* 48MHz / (SDMMC_INIT_CLK_DIV + 2) < 400KHz */
#define SDIO_TRANSFER_CLK_DIV ((uint8_t)0x0) /* 48MHz / (SDMMC_TRANSFER_CLK_DIV + 2) < 25MHz */
0x76 — это 118: 48 МГц / 120 = ровно 400 кГц, предел для опроса карты при
запуске. 0 — 48 / 2 = 24 МГц для передачи данных. Запуск на низкой частоте —
не прихоть HAL, а требование карты: на 24 МГц она не ответит на первые команды,
и симптом будет «карта не определяется», причём разные карты ведут себя по-разному.
41а.3 Команда и ответ
Команда — всегда 48 бит на линии CMD: стартовый бит, бит направления, 6 бит
номера команды, 32 бита аргумента, CRC7 и стоповый бит. Ответы бывают короткие —
тоже 48 бит — и длинные, 136 бит: так карта отдаёт регистры CID и CSD. Аппарат
(автомат команд, CPSM) сам ставит CRC7 и сам проверяет его в ответе; исключение —
ответ на запрос питания R3, где поле CRC заполнено единицами и не проверяется.
Кадры первых команд совпадают с теми, что считались вручную в 18.10 для режима
SPI: CMD0 — CRC 0x95, CMD8 с аргументом 0x1AA — 0x87. Только здесь их
считает блок, а не программа.
Ответы, которые встречаются при запуске:
| Ответ | Длина | Что несёт |
|---|---|---|
| R1 | 48 | номер команды и состояние карты |
| R2 | 136 | CID (паспорт) или CSD (параметры) |
| R3 | 48 | OCR: окно питания, готовность, признак SDHC |
| R6 | 48 | опубликованный адрес RCA |
| R7 | 48 | ответ на CMD8: напряжение и эхо шаблона |
Часть команд — прикладные, ACMD: номер у них может совпадать с обычной
командой (ACMD41, ACMD6, ACMD51), различает их карта только по тому, что
перед ними пришла CMD55 с её адресом. Без CMD55 тот же номер она выполнит как
обычную команду — или ответит ошибкой.
41а.4 Запуск карты: что делает HAL_SD_Init
HAL_SD_Init вызывает HAL_SD_InitCard, и там весь протокол запуска, в том же
порядке, что и по SPI в разделе 41, но с другими ответами:
- Блок настраивается на 1 бит и
SDIO_INIT_CLK_DIV, включается питание выводов, такт, паузаHAL_Delay(2)— карте нужно время после подачи такта. CMD0— карта в состоянии ожидания.CMD8с шаблоном0x1AA. Ответила — карта версии 2, не ответила — версии 1, иCMD0повторяется.CMD55+ACMD41в цикле — до0xFFFFпопыток: аргумент несёт окно питания и бит0x40000000«хост умеет SDHC». В ответе бит 31 — «готова»; пока он нулевой, цикл продолжается. Бит 30 в ответе готовой карты —SDHC/SDXC, иначеSDSC.CMD2— карта отдаёт CID (136 бит),CMD3— публикует адрес RCA.CMD9с RCA — CSD: из него HAL вычисляет число блоков и ёмкость.CMD7— выбрать карту: она переходит в состояние передачи.CMD16— длина блока 512.
После этого HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) читает
регистр SCR (ACMD51, блок 8 байт), убеждается, что карта умеет 4 бита, шлёт
ACMD6 и перенастраивает блок на четыре линии и рабочий делитель. Перепутать
порядок нельзя: 4 бита включаются только после выбора карты, а частота
поднимается только после запуска.
if (HAL_SD_Init(&hsd) != HAL_OK) /* 1 бит, 400 кГц, весь протокол */
return -1;
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
return -2; /* 4 линии, 24 МГц */
В самой hsd.Init.BusWide при этом оставляют SDIO_BUS_WIDE_1B: сразу после
CMD7 HAL перенастраивает блок по hsd.Init — и делитель, и ширину шины.
С SDIO_BUS_WIDE_4B там блок перешёл бы на четыре линии раньше карты (41а.7).
Ёмкость и адресация: у SDSC адрес в командах чтения — байтовый, у SDHC/SDXC —
номер блока; HAL прячет это внутри HAL_SD_ReadBlocks, и наружу всегда торчит
номер блока по 512 байт.
41а.5 Блок данных, FIFO и DMA
Данные ходят блоками по 512 байт — ровно сектор FAT. В регистре управления
данными размер кодируется степенью двойки: DBLOCKSIZE = 0x9 — это 2⁹ = 512.
Внутри блока FIFO на 32 слова по 32 бита; флаг «половина» — 8 слов — служит
запросом DMA. Читать FIFO процессором можно (HAL_SD_ReadBlocks так и делает),
но на 24 МГц по четырём линиям слово прилетает каждые 333 нс, и процессор
занят только этим.
Правильный путь — DMA, и у него в руководстве точный рецепт: DMA2, поток 3 или
поток 6, канал 4; слова по 32 бита; управление потоком — периферией
(PFCTRL: длину знает SDIO, а не DMA); пакеты по 4 слова хотя бы со стороны
периферии. Порядок при записи блока: запустить DMA, послать CMD24, дождаться
CMDREND, включить автомат данных с DMAEN, дождаться DBCKEND.
HAL_SD_WriteBlocks_DMA и HAL_SD_ReadBlocks_DMA делают это сами; от программы
нужны два обработчика — SDIO_IRQHandler и прерывание потока DMA — и буфер,
выровненный на 4 байта.
Контрольные суммы данных — CRC16 с полиномом 0x1021, по одной на каждую линию.
Блок из 512 байт 0xFF по одной линии даёт 0x7FA1 (в четырёхбитном режиме
каждая линия несёт 128 байт 0xFF, и сумма на линии — 0xEDA9) — число, по которому проверяют свою
реализацию все, кто писал драйвер карты руками; здесь его считает аппарат, а
ошибка приходит флагом DCRCFAIL. Второй сторож — таймер данных DTIMER: если карта
не ответила на чтение или держит линию «занято» дольше заданного, блок поднимает
DTIMEOUT вместо вечного ожидания.
41а.6 FatFS поверх SDIO
Слой диска для FatFS (раздел 42) — те же пять функций, что и для SPI, только внутри них HAL:
static volatile int rx_done;
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *h) { (void)h; rx_done = 1; }
DRESULT disk_read(BYTE pdrv, BYTE *buf, LBA_t sector, UINT count)
{
(void)pdrv;
rx_done = 0;
if (HAL_SD_ReadBlocks_DMA(&hsd, buf, (uint32_t)sector, count) != HAL_OK)
return RES_ERROR;
uint32_t t0 = HAL_GetTick();
while (!rx_done) /* DMA перенёс всё */
if (HAL_GetTick() - t0 > 1000u) return RES_ERROR;
while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) /* карта снова готова */
if (HAL_GetTick() - t0 > 1000u) return RES_ERROR;
return RES_OK;
}
HAL_SD_ReadBlocks_DMA только запускает обмен и сразу возвращается. Конец
переноса отмечает обратный вызов HAL_SD_RxCpltCallback: его зовёт прерывание
DMA, когда последнее слово легло в память, а при чтении нескольких блоков HAL
перед ним сам посылает CMD12. Поэтому сначала ждут его и лишь потом —
состояния TRANSFER: оно говорит о карте, а не о памяти, — DMA мог ещё не
выгрузить хвост FIFO, а опрос CMD13 посреди обмена сталкивается с CMD12
из прерывания. Оба ожидания — с тайм-аутом, иначе вынутая карта повесит
программу навсегда.
disk_write — так же, с HAL_SD_TxCpltCallback, и там ожидание TRANSFER
обязательно вдвойне: карта отвечает «принято» раньше, чем физически записала
блок, и следующая команда без этой паузы получит ошибку «занято». Буфер от
FatFS бывает невыровненным, а DMA нужен адрес, кратный 4 (41а.5): проверяют
((uintptr_t)buf & 3) != 0 и в этом случае гонят секторы по одному через свой
выровненный буфер. Размер сектора — 512, число секторов — из HAL_SD_GetCardInfo,
LogBlockNbr. Всё остальное в FatFS от шины не зависит.
41а.7 Грабли
| Симптом | Причина |
|---|---|
| карта не отвечает вовсе | нет подтяжек на CMD и D0…D3 — линии двунаправленные |
| работает в 1 бит, в 4 бита ошибки CRC | подтяжки только на D0; или D1…D3 заняты другой периферией |
HAL_SD_Init прошёл, а первое же чтение — ошибка |
в hsd.Init.BusWide стоит SDIO_BUS_WIDE_4B: после CMD7 HAL перенастроил блок на четыре линии, а карта без ACMD6 осталась на одной |
| «не определяется» на одних картах, на других работает | запуск сразу на рабочей частоте, а не на 400 кГц |
| частота такта не та, что рассчитана | SDIOCLK — от 48-мегагерцового выхода ФАПЧ, он не настроен |
ACMD41 крутится до таймаута |
перед прикладной командой нет CMD55; или карта версии 1 без ответа на CMD8 |
| DMA переносит мусор или виснет | не поток 3/6 DMA2, нет PFCTRL, буфер не выровнен на 4 байта |
| запись возвращает «занято» через раз | следующая команда до перехода карты в TRANSFER |
disk_read вернул успех, а в буфере не всё |
ждали только TRANSFER, а не обратного вызова конца DMA |
| вынули карту — программа висит | ожидание обратного вызова или TRANSFER без тайм-аута |
| после записи данные читаются старыми | обработчики SDIO_IRQHandler и потока DMA не подключены — колбэки не приходят |
| ошибки CRC на длинных проводах | 24 МГц по шлейфу: снизить делителем или укоротить |
DCRCFAIL при записи на коротких проводах |
у F4 включено аппаратное управление потоком (HWFC_EN, бит 14 SDIO_CLKCR) или NEGEDGE: по перечню ошибок оба портят данные — не включать |
| перезапуск при обращении к карте | пик тока до 100 мА, нет конденсатора у держателя (раздел 41) |
41а.8 Итоги
- SDIO — родной протокол карты:
CMD,CLK, до четырёх линий данных; режима SPI у блока нет; подтяжки наCMDи всехDобязательны. - Такт:
SDIO_CK = SDIOCLK / (CLKDIV + 2)от 48 МГц; запуск на 400 кГц (CLKDIV = 0x76), работа на 24 МГц (CLKDIV = 0). - Команда — 48 бит с CRC7; ответы 48 или 136 бит;
ACMDидут послеCMD55. - Запуск:
CMD0 → CMD8 → ACMD41 (бит 31 готовность, бит 30 SDHC) → CMD2 → CMD3 → CMD9 → CMD7 → CMD16; 4 бита — после выбора карты (ACMD6). - Блок 512 байт (
DBLOCKSIZE 0x9), FIFO 32 слова, DMA2 поток 3/6 канал 4 с управлением от периферии и пакетами по 4; на четырёх линиях блок — 43,4 мкс, 11,8 МБ/с против 3 по SPI. - CRC16
0x1021на каждой линии,0x7FA1для 512 байт0xFF; таймер данных вместо вечного ожидания. - FatFS:
disk_read/writeчерезHAL_SD_*Blocks_DMA: ждать обратного вызова конца переноса, потомTRANSFER, оба раза с тайм-аутом; невыровненный буфер FatFS — через свой.
Дальше: раздел 41б «FSMC и FMC: дисплей 8080, внешняя SRAM и SDRAM» — параллельная шина, на которой внешняя память и дисплей становятся областью адресов.