Разработка электроники,

Систем автоматики,

Программного обеспечения

ООО "Антех ПСБ",
Санкт-Петербург

+79811865082

anteh@bk.ru

ООО Антех ПСБ примеры

Подключение Transcend microSDHC UHS-I 32GB Class 10 к 8bit AVR микроконтроллеру через SPI шину в параллель с несколькими устройствами. Разработка SPI драйвера SDHC AVR Assembler

17.07.2020 Сайт https://anteh.ru

Часть разрабатываемого, на 8bit AVR микроконтроллере, устройства считывает случайно приходящие данные с двух микросхем CAN шины максимум 1 Mbit, подключённых по SPI и циклически записывает считанные данные на microSDHC подключённую по тому же SPI. На SPI висит FRAM, RTC, есть температурный датчик на I2C, и реализован USB через CP2104 USART0 для подключения к компьютеру -это второстепенно и не рассматривается. Речь о 3х устройствах на SPI одно из которых SDHC карта, а 2 других генерируют записываемые данные со случайной скоростью. Рассматривается реализация драйвера обмена с SDHC, как его лучше/проще реализовать исходя из текущей задачи, готовых библиотек не будет по шкурным соображениям, будет исчерпывающее описание программной реализации, сама программная реализация AVR Assembler, будет несколько рабочих принципиальных схем для SPI подключения к 8bit AVR микроконтроллеру, SCLK=10MHz. Не планируется использование карт других типов, или сильно отличающихся от используемой, универсальность не нужна, нужна максимальная надежность устройства в разных климатических условиях для круглосуточной работы. Важна гарантрованная запись абсолютно всех сгенерированных данных на карту, без потери данных.

Максимальный для CAN шины 250kHz, теоретический объем генерируемых данных 2мя SPI устройствами 62.5 килобайта в секунду или 500 килобит в секунду -это когда одна посылка длится около 500микросекунгд и каждая посылка преобразуется в 16 байтную структуру данных. Предельный теоретически аппаратно возможный поток данных 2 мегабита в секунду с 2х SPI устройств -2 CAN шины реализованные на внешних микросхемах. Речь о уже обработанном потоке полученном с 2x CAN шин с добавлением дополнительных данных 2 мегабита. Реальный поток предположительно будет 10 - 100 килобит. Интригой будет предельная скорость записи на SDHC потоковых данных по SPI и определение загруженности SPI при обслуживании 3х SPI устройств, четвёртое FRAM в расчет пока не принимается. Имеется в виду несколько разнотипных устройств на одной SPI шине. Интересует отсутствие пропусков входных данных из-за черезмерной загрузки SPI. Частота тактирования микроконтроллера 20MHz частота SPI SCLK 10MHz. Забегая сильно вперед: на SDHC карту нужно писать поток 250 килобайта в секунду при частоте SPI 10 MHz. Это предельный теоретический никогда не достижимый объём данных генерируемый двумя CAN шинами на частоте 1 мегабит в секунду. Причем данные уже обработанные, каждой CAN посылке сопоставляется 16 байтный блок данных. Также, в рассматриваемом случае 512 байтные блоки данныхиз микроконтроллера в SDHC карту будут передаваться исключительно в одиночном режиме. И в общем передача одного 512 байтного блока данных в SDHC при SPI 10MHz занимает 800 микросекунд + около 1 миллисекунды на выполнение операции записи картой -это и есть около 250 килобайт в секунду, в притык, задача реализуема. Речь о записи на предварительно очищенный блок в SDHC. Запись на очищенный блок происходит быстрее. Есть проблема: если 512 байтный блок в карту передавать весь целиком за 800 микросекунд, то есть вероятность пропуска входних CAN посылок, на частоте CAN шины 1 мегабит длительность посылки будет около 125 микросекунд, а приемных буферов у микросхемы реализующей CAN интерфейс только 2, это максимум 250 микросекунд на реакцию управляющего AVR микроконтроллера. Обязательно нужно разбивать передачу 512 байтного блка данных на несколько частей для перерыва на считывание CAN приемных буферов. Т.е. 512 байтный блок данных по SPI передаём не целиком с буфера, а произвольными частями, это увеличивает время реакции микроконтроллера, и в силу особенностей SPI интерфейса SDHC карт здесь важна аппаратная поддержка подобной функции. Схема согласования уровней помимо согласования уровней превращает SDHC карту в обыкновенное SPI устройство которое можно подключать и отключать от SPI шины по перебросу какого-нибудь пина микроконтроллера. У SDHC карт DO не переходит в третье состояние, если её CS в высоком и карта занята внутренниими операциями, карта через DO индицирует свою занятость. Схема согласования уровней. Функция записи 512 байтного блока произвольными частями работает не для всех SDHC карт, после нескольких дней поиска проблемы выяснилось, что 32G Samsung microSDHC UHS-I Card EVO plus экзестанциально не переавривает подобный способ записи данных, после работы в течении какого-то времени карта зависала, помогало только переподключение питания. С Transcend картами всё было в порядке. Каждый производитель немного по разному реализует SPI интерфейс и если кто захочет универсальности то граблей наловите много.

Найдена официальная документация на Transcend microSDHC UHS-I 16GB Class 10 по тегу "SDHC10 Card series 4~32GB High Capacity Secure Digital Card".

Документация подтвердила или уточнила: питание SDHC стандартно 3.3V, микроконтроллер питается от 5V, уровни будем согласовывать через логические буферы с открытым стоком open drain. В реальности, аппаратно, уровни согласовывались сначала резисторами(скорость получилась максимум до 2 мегабит SPI), потом логическими буферами с открытым стоком(слишком много нужно тока на подтяжку для высоких скоростей, реализующие CAN шины микросхемы не тянули), затем самым оптимальным образом -через логические буферы поддерживающие перенапряжения логических уровней на входах. Блок/сектор чтения или записи стандартно 512 байт, после подачи питания и перед процедурой инициализации нужно подождать минимум 250ms будем ждать 300ms, токопотребление до подачи первой команды <=15mA, в течении инициализации <=100mA, работа в режиме по умолчанию <=100mA, работа в высокоскоростном режиме <=200mA, работа с другими функциями <=500mA, что это за другие функции такие непонятно, надеюсь с ними столкнуться не придется, но к сведению 500mA принимаем -линейный LDO в sot223 на ампер и керамические банки нам в помощь, скорее всего в 10MHz SPI режиме нужно расчитывать на максимум 100mA токопотребления, резисторы подтяжки рекомендуют от 10k до 100k, в режиме инициализации частота SPI должна быть 100kHz-400kHz, в режиме передачи данных для SPI максимум 25MHz будет 10MHz, в режиме высокоскоростной передачи данных 50MHz не актуально, рабочая температура -25+85 надеюсь хватит, хранения -40, влажность в текущем случае неактуальна будет заливка компаундом + герметичный корпус, важно: в регистре CSD:46 бит именуемый ERASE_BLK_EN зафиксирован в 1 и означает, что контроллер может стирать один или более блоков/секторов по 512 байт -это важно с точки зрения быстродействия, записывать блок/сектор в 512 байт оптимальнее/быстрее на пустой или стертый блок, операция записи на занятый блок выполняется дольше, чем запись на пустой. Будет реализована циклическая запись данных, блоки записывабтся на предварительно стертые блоки, для этого производится стирание кластера из нескольких блоков, стирать по 1024 блока оказалось оптимально по времени затрачиваемому на стирание по измерению осцилографом для Transcend 16G. Бывает, что стирание возможно только кластерами от единиц килобайт до мегабайт. У используемой карты размер кластера 64 килобайта, но эта информация не пригодится и вроде вообще ник чему. Для резистивного сопряжения 3.3V логики скарты с 5V логикой микроконтроллера нужен параметр напряжение минимального уровня логической единицы = 0.625*Vdd = 2.1V, для правильного подбора резисторов делителей и подтяжки. Прараметр CL: максимальная нагрузочная ёмкость на выводах хоста + емкость шины SPI не должна превышать 30pF. Для интересу сначала использовался резистивный делитель, на SPI SCLK = 10MHz обмена не было, работало на 1MHz. На 10MHz при резистивном согласовании по осцилографу фронты SCLK после делителя треугольные амплотуда 1V + постоянная составляющая 1V, не работало, стояли резисторы на единицы килоом, нужны согласующие резисторы на сотни ом, по току будет тяжеловато для AVR. На делитель SCLK приходит правильный прямаугольный. На резисторах можно мегагерца 2 SCLK получить. Cогласование логических уровней делаем через 2 логических буфера 74LVC2G07G, как на второй схеме. На 10MHz работает отлично, при необходимости можно до 25MHz реализовать но уже не на AVR. На AVR можно SPI SCLK максимум до 14-16MHz сделать желательно с кварцевым генератором и для стендов, не для штатных устройств. Предельная частота работы Flash памяти AVR микроконтроллеров 33MHz. В последствии от согласования на 74LVC2G07G пришлось отказаться, слишком низкоомной должна быть подтяжка для работы на 10MHz SPI и не все микросхемы висящие на SPI могли выдавать достаточное количество тока.

На карту данные записываются напрямую, без использования каких либо файловых систем, если на карте будут какая либо файловая система или данные, они будут потеряны. Записываются данные на карту прямо блоками по 512 байт начиная с адреса 0x00000000 и до упора -1, "упор" у каждой карты свой смотрим регистр CSD 22бита [69:48] и вычислякм размер карты в килобайтах: (CSD[69:48] +1)*512k, если *1024 получим размер в байтах, в реальности нужен размер в 512 байтных блоках. Форматирование подготовка и пр. не требуется, разумеется операционные системы читать такие карты не будут. Все данные будут перезатерты. К слову, для бытового применения, форматировать карты желательно только специализированными программами предоставляемыми или производителем карты или ассоциацией "SD Card Association" https://www.sdcard.org/ -здесь же можно разжиться официальной спецификацией на SD карты, форматирование из операционной системы приводит к замедлению работы, связано с необходимости знать индивидуальные характеристики карты и правильно подбирать размеры секторов кластеров и т.п. Из-за этой же некоммерческой ассоциации пришлось отказаться от использования 4х байтного SD режима, уже разведённого на плате и переделывать всё на работу по SPI. Хотя софтовый SD режим будет сушественно медленнее паппаратного SPI. Нюанс: если ваш хост/микроконтроллер аппаратно не поддерживает SDIO интерфейс, за который производитель микроконтроллера уже заплатил лицензионный сбор в "SD Card Association", то использовать SD режим реализованный программно официально нельзя или нужно платить лицензионный сбор. По спецификации все SD карты поддерживают SPI режим, он по понятным причинам медленнее 4х битного SD режима. Некоторые "левые" нонейм карты, не поддерживают SPI. Режим spi не поддерживается картами SDUC - и больно нужно, ёмкость от 2 до 128 террабайт. Карту нужно запитывать 1.8 вольт при высокоскоростном подключении в SD режиме. Забегая вперёд, если нужно полноценно использовать карту с возможностью работы на больших скоростях обмена, используйте контроллер с аппаратной поддержкой 4х проводного SDIO интерфейса, а согласование уровней при помощи двунаправленных преобразователей уровней, на худой конец неинвертирующих буферов с открытым стоком. У неинвертирующих буферов с открытиым стоком конец худой по причине необходимости использования резисторов подтяжки на пару сотен ом а то и сотню, а это приличная нагрузка на SPI устройства висящие на шине совместно с картой, не каждое может несколько десятков миллиампер выжать, придётся их тоже через буферы цеплять, а это очень большой паровоз по площади занимаемой печатной платы, увеличение вероятности брака при пайке, номенклатуры закупки, у SMD установщика лишние слоты отжирать будет, гемор если речь о серийном устройстве.

В сети информации о подключении SD карт к микроконтроллеру по SPI предостаточно местами она противоречива, рекомендуемая многими статья на английском http://elm-chan.org/docs/mmc/mmc_e.html и её перевод http://microsin.net/programming/file-systems/howto-use-mmc-sdc.html, самая подкрученная инфа здесь microsin.net, также смотрим http://4a4ik.blogspot.com/2015/08/sd-mmc-spi.html http://4a4ik.blogspot.com/2015/08/spi.html http://microsin.net/programming/file-systems/sd-specifications-part-1-physical-layer-simplified-specification-ver-200-spi-mode.html и http://microsin.net/programming/file-systems/sd-specifications-part-1-physical-layer-simplified-specification-ver-200.html. По статьям не получилось с начальными нулевыми знаниями понять ряд моментов. Например описания ответов на команды. На CMD0 по статьям не понять, ответ R1 то ли он одно байтный то ли шестибайтный. По факту это 1 байтный ответ с текущим статусом после предыдущей команды. Также для CMD8 утверждается что ответ будет 48 битный, по факту 40 битный как и указано в спецификации с SD Association сайта, если например CRC у CMD8 неправильная то ответ будет не R7, а однобайтный R1 = 0b00001001 = 0x09 флаги означают idle и CRC error для предыдущей команды точнее ответ при неправильной CRC будет 0x09ffffffff потому что ожидаем 5 байт. Если карта приняла команду CMD8 =0x48000001aa87, то она ответит 0x01000001aa -это правильный ответ и формат ответа R7: первый байт это R1 + ещё 32 бита, биты 0x1AA означают, версия карты V2 и может работать в диапазоне напряжения от 2.7 до 3.6 вольт. Подробности в спецификации Part1_Physical_Layer_Simplified_Specification_Ver7.10.pdf скачиваемой с сайта SD Association, раздел 7. SPI Mode

Пока не инициализируем карту всегда используем правильную CRC7 для каждой команды, есть карты которые в SPI режиме при инициализации независимо ни от чего используют CRC7. CRC7 старшие 7 бит вычисляется по первым 5 байтам команды. Полином G(x) = x^7 + x^3 + 1 (10001001), младший 0 стоп бит CRC всегда =1. Можно использовать сервис http://www.ghsi.de/pages/subpages/Online%20CRC%20Calculation/ там нужно указать полином 10001001 и 5 байт команды, полученное значение умножить на 2 прибавить 1 и перевести в hex это будет CRC шестой байт команды. Для читаемых/записываемых блоков данных 512 байт, если включена, используется CRC16. Если CRC отключена, то байты CRC16 всё равно должны быть. По спецификации SD Association для формирования CRC16 используется стандартный CCITT (CRC-16/XMODEM) полином x^16+x^12+x^5+1, 0x1021 init=0x0000. У конфигурационных регистров как минмум для Transcebd используется CRC-16/XMODEM для 128 64 512 битных регистров, описание ниже. Подозреваю, спецификация SDA(SA Association) носит рекомендательный характер, некоторые моменты, у разных производителей карт, могут быть реализованы по разному.

Name       Polynom     Init val   Reverse byte   Reverse result   Swap result
CCITT         1021         ffff             no               no            no
XModem        1021         0000             no               no            no
Kermit        1021         0000            yes              yes           yes
CCITT 1D0F    1021         1d0f             no               no            no
IBM           8005         0000            yes              yes            no

Если на SPI шине несколько устройств, то первым/первыми инициализируем карту/карты. Неинициализированная карта при CS = 1 высокий уровень, может воспринимать активность на SPI шине с разными последствиями - зависание, возможно порча данных. При подаче питания, все карты на одной и той же SPI шине находятся в SD режиме, в SD режиме адресация карт выполняется не через физический CS вывод карты а через 16 бит RCA регистр адреса конкретной карты. При подаче питания RCA сбрасывается в 0x0000, т.е. все карты на одной и той же SPI шине будут одинаково воспринимать активность SPI шины. Полагаю, подача 80 единиц на карту минимум включает CS вывод и CMD0 уже подаём последовательно на каждую карту. С SD режимом не работал, проверяйте так оно или нет.

Коротко про ответы: R1 однобайтный старший бит всегда 0 -для реализации алгоритма полезный факт. R1b это R1 + индикация заняторсти карты, занятость индицируется через установку DO карты в низкий уровень, если во время занятости непрерывно слать 0xFF в карту то она будет отвечать 0x00 и когда освободится станет отвечать 0xFF -это можно использовать для определения готовности карты к дальнейшей работе, но это непрактично, особенно когда на SPI висит ещё несколько устройств требующих внимания. Поэтому занятость карты контролируем по её DO выходу, каждый раз при подключении SPI к карте проверяем вход микроконтроллера подключенного к DO карты, если вход в низком, карта занята, отключаемся от неё для работы с другими SPI устройствами и возвращаемся позже -И ВНИМАНИЕ это было досадным заблуждением. Как оказалось DO карты после окончания внутренней операции остается в 0 низком уровне неограниченное количество времени до подачи в неё 0xFF. В высокий DO переходит только после окончания внутренней операции и после подачи 0xFF байта в карту. После подачи долго обрабатываемой команды, например записи или стирания блока/блоков данных, можно отключаться от карты для работы с другими SPI устройствами. DO карты после установки CS карты в высокий уровень перейдёт в третье состояние -это действительно так, проверено, на всякий пожарный, после установки CS в высокий подаём 0xFF в карту. Отсюда универсальный способ подключения к карте при работе с несколькими устройствами на SPI шине: после подключения к карте -установке CS в 0, подаём в карту 0xFF, и если в ответ приняли не 0x00 значит карта свободна, можно подавать команды. Это справедливо при работе с любыми командами требующими времени для обработки. R2 двухбайтный ответ со статусом карты, первый байт также R1 + 8 бит со статусом. R3 ответ на команду CMD58 с данными OCR регистра, первый байт как обычо R1 далее 32 бита OCR. Ответов R4 и R5 в SPI нет, есть только в SD режиме обмена данными. R7 -первый байт R1 далее 32 бита версия карты и диапазон рабочих напряжений. Для реализации алгоритма удобно думать что ответ на любую команду это либо только R1 -один байт либо R1 + какое-то количество байт определяемое командой, а также R1b это R1 + индикация занятости карты через установку её DO в низкий уровень.

Правильный алгоритм должен анализировать первый байт любого ответа R1 и предпринимать действия согласно ситуации, учитывать все ситуации сложно, поэтому контролируются только важные флаги соответствующие смыслу команды. Будем считать что карта безотказная, не будет ошибок обмена, респонсы всегда правильные, предварительно карта будет полностью протестирована.

Принципиальная схема подключения 3.3V SDHC карты к 5V 8bit AVR микроконтрорллеру через резистивный делитель:

Согласование 3.3В SDHC с 5В логикой микроконтроллера через резистивный делитель

1, 8 выводы microSDHC не используются и подтянуты через 12k к +3.3V. 7 DO и 5 SCLK через делителдь заведены на MISO и SCK микроконтроллера соответственно и после подачи питания, гарантированно будет низкий уровень на SCLK и высокий на DO -высокий уровень даст сама карта. При подаче питания на микроконтроллер его выводы до инициализации находятся в третьем состоянии. Есть вероятность что карту может "заглючить", если её выводы какое-то время будут в третьем состоянии. DI карты зваодится на MOSI микроконтроллера через делитель, также CS карты заводится на PB2 контроллера через делитель. Помимо всего MOSI и PB2 подтянуты к +5V через 1.8k. Это важный момент здесь как раз используем информацию из документации на карту минимального уровня логической единицы = 0.625*Vdd = 2.1V. Таким образом на DI и CS карты после подачи питания гарантированно 2.39V т.е. логическая единица.

Принципиальная схема подключения 3.3V SDHC карты к 5V 8bit AVR микроконтрорллеру через неинвертирующий буфер с открытым стоком 74LVC2G07G:

Согласование 3.3В SDHC с 5В логикой микроконтроллера через 74LVC2G07GV

Схему подключения 3.3V SDHC карты к 5V 8bit AVR микроконтрорллеру через четырехканальный двунаправленный преобразователь уровней можно найти в сети, но она не решает проблему DO выхода SDHC, если на SPI шине "висит" несколько устройств. Привожу разработанную финальную схему подключения SDHC к микроконтроллеру решающую все проблемы:

Согласование 3.3В SDHC с 5В логикой микроконтроллера через SN74AHC125PW

Общая частичная схема устройства:

Общая частичная схема устройства с несколькими устройствами на SPI шине помимо SDHC карты

Прошивка пишется на Assembler. Приводятся только фрагменты кода, которые позволят разобраться как начать работать с SDHC по SPI. Инициализация microSDHC:

Параметры настройки SPI AVR 8bit для подключения microSDHC: MSB first, Master, SPI Mode 0 CPHA=0 CPOL=0, SCLK = 20MHz/64 = 312.5kHz. Порты ввода вывода для AtMega88pa настраиваем соответствующим образом:

;Микроконтроллер AtMega88PA
                ;PB2    OUT High CS microSDHC
                ;PB3    MOSI out HIGH
                ;PB4    MISO in
                ;PB5    SCK      out LOW
                ldi     r16, 0b00101111
                out     DDRB, r16
                ldi     r16, 0b00011110
                out     PORTB, r16
                ;PC0    OUT high ~CS_CAN1
                ;PC1    OUT high для CAN2 SECOND канал выключен
                ;PC2    OUT high ~CS_EEP
                ;PC3    OUT HIGH Настройка PC3 для WDI TPS3823
                ;PC4    ThreeState LOGGER TWI
                ;PC5    ThreeState LOGGER TWI
                ldi     r16, 0b00001111
                out     DDRC, r16
                ldi     r16, 0b00001111
                out     PORTC, r16
                ;PD0    RX USART
                ;PD1    TX USART
                ;PD2    RESET MCP
                ;PD3    OUT High Tx/Rx ERR CAN1
                ;PD4    OUT High Tx/Rx ERR CAN2
                ;PD5    IN N.U.
                ;PD6    IN N.U.
                ;PD7    IN N.U.
                ldi     r16, 0b00011110
                out     DDRD, r16
                ldi     r16, 0b11111011
                out     PORTD, r16

Длина команд от контроллера к карте всегда 6 байт, ответ разной длины от 1 байта. После каждой команды всегда ожидаем ответа от карты, ждать от 0 до 8 байт. Т.е. передали команду и за командой передаём 0xFF до тех пор пока в ответ по DO карты не получим байт !=0xFF. И байт !=0xFF и будет ответом или началом ответных байт следующих за первым байтом !=0xFF, длина ответа зависит от команды. Это основная логика работы. Также в алгоритме работы задействуется watchdog микроконтроллера. Словами описать алгоритм точно и однозначно сложно, поэтому смотрим рабочий код там все по полкам.

;Микроконтроллер AtMega88PA
.dseg
.org 0x0100
        ;Формат SDHCMessageBUF
        ;байт [7-5 тип сообщения : xxxxx] биты xxxxx и последующие байты зависят от типа сообщения
        ;[001 : 4 номер CAN канала : 3-0 DLC][T2][T1][T0][EID3][EID2][EID1][EID0][D7][D6][D5][D4][D3][D2][D1][D0]
        ;[010 : 00000][T2][T1][T0][t5][t4][t3][t2][t1][t0][0xYY]    [0xYY]   [0xYY][0xYY][0xYY][0xYY] Индикатор подачи питания, позже будет добавлена реальная дата и время из RTC
        ;[011 : 00000][T2][T1][T0][t5][t4][t3][t2][t1][t0][TEMP10-3][TEMP2-0][SupplyVolt_H][SupplyVolt_L][0x33][0x33] Метка RTC времени добавляемая каждую минуту
        ;       [T2][T1][T0] -текущее мгновенное время
        ;       [t5][t4][t3][t2][t1][t0] -дата время RTC
        ;       [TEMP10-3][TEMP2-0] -два байта температуры LM75A
        ;       [SupplyVolt_H][SupplyVolt_L] -напряжение питания бортовой сети
        ;=== Местами не менять ============================================
        CNT_SDHCMessageBUF:     .BYTE 1         ;Технологический счетчик количества байт в SDHCMessageBUF
        SDHCMessageBUF: .BYTE 1+3+4+8   ;[ID][time][EID][DATA]
        SDHCMessageBUF_end:
        ;==================================================================
        SDHCMessageBUF_DataTime: .BYTE SDHCMessageBUF_end - SDHCMessageBUF

        .equ SDHCBufLen = 32    ;##########32 раза по 16 байт = 512байт
        SDHCBuf: .BYTE (SDHCMessageBUF_end - SDHCMessageBUF) * SDHCBufLen + 2 ;Буфер обмена данными с SDHC + 2 байта CRC

        CurWritePointer:        .BYTE 1                 ;указатель на свободную ячейку для записи 16 байтной структуры в буфер SDHCBuf
        ReadPointer:            .BYTE 1                 ;указатель на читаемую ячейку для записи в SDHC
        Lost16byteStructureCNTR: .BYTE 1        ;счетчик количества потеряных 16 байтных структур из-за переполнения буфера
        Partial_Flag: .BYTE 1   ;Флаг инкриментный счетчик указатель шагов выполнения Partial_Writer_SDHCBuf
        SDHCBuf_16bStructureCount: .BYTE 1      ;Количество записанных в SDHC 16 байтных блоков. Для отсчета 512 байт

        ResponseBUF: .BYTE 16 ;Максимальный размер буфера приема ответных сообщений от microSDHC карты. Ответы на команды
        ResponseBUF_end:

        ;=== МЕСТАМИ НЕ МЕНЯТЬ ==============================================
        ;GEN_old_delay: .BYTE 3 ;задержка перед передачей следующего сообщения генератором
        ;Cntr100u_3b [защитный][2][1][0] трехбайтный инкримнтный циклический счетчик
        Cntr100u_3b: .BYTE 4 ;счетчик 50u интервалов. Старший байт защитный от переполнения
        urb_timeout_cntr: .BYTE 1 ;по окончанию работы таймера запускается анализ содержимого urb буфера USART
        dd_delay_100usUSART: .BYTE 1 ;задержка в 50us интервалах перед передачей следующего блока по USRT при чтении данных c SDHC карты
        ;====================================================================
        TXBnCTRLa: .BYTE 1 ;вспомогательное хранение адреса TXBnCTRL для поиска свободного буфера при передаче

        ;=== МЕСТАМИ НЕ МЕНЯТЬ ==============================================
        dseg_LastFree512BlockNum:                       .BYTE 4 ;Адрес крайнего свободного 512б буфера к записи
        dseg_LastNOTFreeClear512BlockNum:       .BYTE 4 ;Адрес первого буфера кластера. Стирание производится кластерами по ClearClusterLen блоков
        ;====================================================================
        SAFE_LastFree512BlockNum_BUF: .BYTE 8 ;Промежуточный буфер для сохранения dseg_LastFree512BlockNum и dseg_LastNOTFreeClear512BlockNum

        ;===== 2байта ClearClusterLen количество блоков к стиранию ==================
        .equ    ClearClusterLen = 1024 ;1024 количество блоков к стиранию
        ;===============

        RAMBUFCommand: .BYTE 8 ;буфер для команды
        ;===== МЕСТАМИ НЕ МЕНЯТЬ =================================================
        R512BB: .BYTE 12        ;R512BB = [0xAA][0x01][a3][a2][a1][a0][b3][b2][b1][b0][dd][0x55]
        urbCNT: .BYTE 1         ;Количество принятых байт в urb
        urb:    .BYTE 16        ;[urbCNT][0xAA][0x01][a3][a2][a1][a0][b3][b2][b1][b0][dd][0x55][][][][]
        urb_end:                        ;urb приёмный USART буфер команд с защитой от переполнения
        ;=========================================================================

        ;===== МЕСТАМИ НЕ МЕНЯТЬ =================================================
        ADCcnt: .BYTE 1 ;счетчик количества считанных из АЦП значений
        ADCval: .BYTE 2 ;накапливаемое значение АЦП
        ;=========================================================================
        LFr512BlNum_2byteOV: .BYTE 1    ;предыдущее значение второго байта dseg_LastFree512BlockNum [3][2][1][0]. Для сохранения dseg_LastFree512BlockNum каждые 32 мегабайта в EEPROM
                                                                        ;Один разряд -32 мегабайта или 65536 блоков



.cseg


;Формат: [Len][Command][d3][d2][d1][d0][CRC][Response][Conf][выравнивающий]
;[Len]          -длина передаваемой команды. Всегда 6 байт -[Command][d3][d2][d1][d0][CRC]. Там где не 6, это специальные команды
;[Command]      -команда SDHC карте
;[d3][d2][d1][d0] -данные команды
;[CRC]          -CRC7 + младший бит всегда =1. полином для CRC7=10001001
;[Response]     -количество байт читаемое из SDHC карты после отправки команды
;[Conf]         -конфигурационный байт для специфической обработки некоторых команд
;                       [Conf]:0 -бит флаг ожидания токена 0xFE перед началом приёма данных из SDHC, нужен для CMD17 и для чтения некоторых сервисных регистров
;                       [Conf]:1 -бит флаг 1= запрет передачи 0xFF после передачи команды в SDHC
;                       [Conf]:2 -бит флаг 1= вывод response байт сразу на USART, в буфер response не записывается
;[выравнивающий] -любой байт
                        ;Инициализация
                SCLK_80:        .DB  10,                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0b00000000,            0x00
                   CMD0:        .DB   6, 0x40+0,  0x00, 0x00, 0x00, 0x00, 0x95, 1, 0b00000000,          0x00    ;0x40    R1 8 бит
                   CMD8:        .DB   6, 0x40+8,  0x00, 0x00, 0x01, 0xaa, 0x87, 5, 0b00000000,          0x00    ;0x48    R1 + 32 бит
                  CMD55:        .DB   6, 0x40+55, 0x00, 0x00, 0x00, 0x00, 0x65, 1, 0b00000000,          0x00    ;0x77    R1 8 бит
                 ACMD41:        .DB   6, 0x40+41, 0x40, 0x00, 0x00, 0x00, 0x77, 1, 0b00000000,          0x00    ;0x69    R1 8 бит. [31 Reserved][30 HCS][29:0 Reserved][CRC]
                  CMD58:        .DB   6, 0x40+58, 0x00, 0x00, 0x00, 0x00, 0xfd, 5, 0b00000000,          0x00    ;0x7a    R1 + 32 бит. Запрос OCR ответ R3
                  CMD59:        .DB   6, 0x40+59, 0x00, 0x00, 0x00, 0x00, 0x91, 1, 0b00000000,          0x00    ;0x7b    R1 8 бит. Отключение CRC CMD59:0=0 CRC отключена, CMD59:0=1 CRC включена
                        ;Стирание запись
                  CMD32:        .DB       6, 0x40+32, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 1, 0b00000000,              0x00    ;Адрес первого стираемого блока
                  CMD33:        .DB       6, 0x40+33, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 1, 0b00000000,              0x00    ;Адрес последнего стираемого блока
                  CMD38:        .DB       6, 0x40+38, 0x00, 0x00, 0x00, 0x00, 0x00, 1, 0b00000000,              0x00    ;Стирание
                  CMD24:        .DB       6, 0x40+24, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 1, 0b00000010,              0x00    ;Запись 512 байт блока
    ;CRC16_512Tx:       .DB       2, 0xAA, 0x55, 1      ;CRC передаваемая после передачи блока 512 байт по SPI в SDHC. В ответ ждем data_response байт 0xE5 и 0x00 bysy один байт, остальные bysy байты не принимаются
         Resp_512Tx:    .DB       0, 1, 0b00000000,             0x00    ;для чтения 0xE5 response после записи блока данных в карту 
                        ;Чтение 512 байтного блока + 2байта CRC
                  CMD17:        .DB       6, 0x40+17, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0, 0b00000011,              0x00    ;Чтение 512 байт блока
                  CMD18:        .DB       6, 0x40+18, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0, 0b00000011,              0x00    ;Чтение 512 байтных блоков до приёма команды CMD12
                  CMD12:    .DB   6, 0x40+12, 0x00, 0x00, 0x00, 0x00, 0x00, 1, 0b00000000,              0x00    ;0x4C   R1b 8 бит
                  ;CMD12:    .DB   6, 0x40+12, 0x00, 0x00, 0x00, 0x00, 0x00,                                    0x00    ;0x4C   R1b 8 бит

;       #if defined(debug_Response_to_USART)
;                 CMD10:        .DB   6, 0x40+10, 0x00, 0x00, 0x00, 0x00, 0x1b,   30, 0b00000000,       0x00    ;20 0x4a R1 + задержка + 128 + CRC16 бит. Запрос CID ответ R2 136 бит
;                  CMD9:        .DB   6, 0x40+9,  0x00, 0x00, 0x00, 0x00, 0xaf,   30, 0b00000000,       0x00    ;20 0x49 R1 + задержка + 128 + CRC16 бит. Запрос CSD ответ R2 136 бит
;                ACMD51:        .DB   6, 0x40+51, 0x00, 0x00, 0x00, 0x00, 0xc7,   70, 0b00000000,       0x00    ;9  0x73 R1 + задержка + 64 + CRC16 бит. Запрос SCR
;                ACMD13:        .DB   6, 0x40+13, 0x00, 0x00, 0x00, 0x00, 0x0d,  128, 0b00000000,       0x00    ;0x4d    R1 + задержка + 512 + CRC16 бит. Запрос SSR
;                 CMD13:        .DB   6, 0x40+13, 0x00, 0x00, 0x80, 0x00, 0x0d,    2, 0b00000000,       0x00    ;0x4d    R1 + 8 бит. Запрос CSR регистра статуса. Ответ 2 байта
                CMD58_2:        .DB   6, 0x40+58, 0x00, 0x00, 0x00, 0x00, 0xfd,    5, 0b00000100,       0x00    ;0x7a    R1 + 32 бит. Запрос OCR ответ R3
                CMD10_2:        .DB   6, 0x40+10, 0x00, 0x00, 0x00, 0x00, 0x1b, 16+2, 0b00000101,       0x00    ;20 0x4a R1 + задержка + 128 + CRC16 бит. Запрос CID ответ R2 136 бит
                 CMD9_2:        .DB   6, 0x40+9,  0x00, 0x00, 0x00, 0x00, 0xaf, 16+2, 0b00000101,       0x00    ;20 0x49 R1 + задержка + 128 + CRC16 бит. Запрос CSD ответ R2 136 бит
           ACMD51_2:    .DB   6, 0x40+51, 0x00, 0x00, 0x00, 0x00, 0xc7,  8+2, 0b00000101,       0x00    ;9  0x73 R1 + задержка + 64 + CRC16 бит. Запрос SCR
           ACMD13_2:    .DB   6, 0x40+13, 0x00, 0x00, 0x00, 0x00, 0x0d, 64+2, 0b00000101,       0x00    ;0x4d    R1 + задержка + 512 + CRC16 бит. Запрос SSR
;       #endif
                        ;Данные для отладки
;         CRC_DD_AB:    .DB       16, 0x40, 0x0E, 0x00, 0x32, 0x5B, 0x59, 0x00, 0x00, 0x76, 0xED, 0x7F, 0x80, 0x0A, 0x40, 0x00, 0xD5,           0x00
;         CRC_84_16:    .DB        8, 0x02, 0x35, 0x80, 0x43, 0x00, 0x00, 0x00, 0x00,                                                                                                           0x00
;         CRC_2F_28:    .DB       16, 0x74, 0x4A, 0x60, 0x55, 0x53, 0x44, 0x55, 0x31, 0x20, 0x42, 0x8C, 0xB9, 0x14, 0x01, 0x22, 0xAD,           0x00
;  USART_RXCdbg:        .DB   11, 0xaa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x55





                ;Инициализация microSDHC: SPI Mode 0 CPHA=0 CPOL=0, Master, fck/64 = 312500 bit/s
                ;PB2    OUT High CS microSDHC
                ;PB3    MOSI out HIGH
                ;PB4    MISO in
                ;PB5    SCK      out LOW
                ;=== SPI инициализация 100kHz - 400kHz на кварце=20Mhz ================
                ldi     r17, 0b01010010 ;Enable SPI, Master, set clock rate fck/64
                out     SPCR, r17
                ;===============================================================
                ;ldi    r31, high(2*CMD10_2)  ;R1 + 128 бит. Запрос CID, ответ от Transcend microSDHC UHS-I 16G class10 карты
                ;ldi    r30, low(2*CMD10_2)     ;00(R1) FE 74 4A 60 55 53 44 55 31 20 42 8C B9 14 01 22 AD 2F 28 ответ Transcend microSDHC UHS-I 16G class10 карты
                ;rcall  FlashToSPI_Response

                ;Передаём 80 SCLK тактов в карту для перевода в режим native
                ;DI и CS уже в высоком
                ;sbi    PORTB, 2        ;CS = 1
                ldi     r31, high(2*SCLK_80) ;Native
                ldi     r30, low(2*SCLK_80)
                rcall   initFlashToSPI

                rcall   SDHC_CS_Connect ;cbi    PORTB, 2        ;CS = 0

                ldi     r31, high(2*CMD0) ;CMD0
                ldi     r30, low(2*CMD0)
                rcall   FlashToSPI_Response
                lds             r17, ResponseBUF
                cpi             r17, 0x01       ;Проверка нахождения карты в idle
                breq    IdleOK
        SoftReset:
                rjmp    SoftReset       ;Уход на перезапуск контроллера, карту не увидели
        IdleOK:

                ldi     r31, high(2*CMD8) ;CMD8 проверка диапазона напряжения питания
                ldi     r30, low(2*CMD8)
                rcall   FlashToSPI_Response

        WaitCardInit:
                ldi     r31, high(2*CMD55)
                ldi     r30, low(2*CMD55)
                rcall   FlashToSPI_Response
                ldi     r31, high(2*ACMD41)
                ldi     r30, low(2*ACMD41)
                rcall   FlashToSPI_Response             
                lds             r17, ResponseBUF
                cpi             r17, 0x00                       ;Ожидание R1=0. Проверка окончания инициализации
                brne    WaitCardInit

                        ;На всякий проверяем OCR:31 если != 1 ждём 1 или будет перезапуск по watchdog
                wdr
        WaitOCR31:
                ldi     r31, high(2*CMD58)
                ldi     r30, low(2*CMD58)
                rcall   FlashToSPI_Response
                lds             r17, ResponseBUF + 1
                sbrs    r17, 7                          ;Ожидание OCR:31 = 1
                rjmp    WaitOCR31

                        ;На всякий отключение проверки CRC. По умолчанию, при переходе в SPI, CRC автоматически должна отключаться
                ldi     r31, high(2*CMD59)
                ldi     r30, low(2*CMD59)
                rcall   FlashToSPI_Response

                rcall   SDHC_CS_Disconnect      ;sbi    PORTB, 2        ;CS = 1 Отключаем SDHC от SPI шины



;Передача байт из Флеш памяти в SPI
;r31:r30 адрес передаваемой последовательности
;количество передаваемых байт в старшем байте
initFlashToSPI:
                ;Проверяем наличие высокого уровня на MISO -проверка занятости карты
                ;sbis   PORTB, 4        ;DO MISO
                ;ret    ;Если карта занята

                ;Передача команды
                lpm     r16, Z+ ;Количество передаваемых байт по SPI
                or              r16, r16 ;Если передаваемых байт 0 то выход, используется для ожидания rasponse байт в функции записи блока на карту
                breq    ExitIfZerosswe4d

        NXbtrn:
                lpm     r17, Z+
                out             SPDR, r17
        Wait_Transmit2:
                in              r17, SPSR
                sbrs    r17, SPIF
                rjmp    Wait_Transmit2
                in              r17, SPDR       ;Очистка SPI регистра данных
                dec             r16
                brne    NXbtrn
        ExitIfZerosswe4d:
ret

SDHC_CS_Connect:                        ;Подача 0xFF после CS=0 обязательна, проверено. Без подачи 0xFF DO карты останется в низком после выполнения длительной операции, минимум при записи/стирании данных
                cbi             PORTB, 2        ;CS = 0 подключение SDHC к SPI шине
                ldi     r17, 0xff       ;передача 0xFF, нужно для перевода DO в высокий, если карта освободилась от внутренних операций
                out             SPDR, r17
        Wait_Transmit7:
                in              r17, SPSR
                sbrs    r17, SPIF
                rjmp    Wait_Transmit7
                in              r17, SPDR       ;очистка флага
ret

SDHC_CS_Disconnect:             ;Необходимость подачи 0xFF не проверена
                sbi             PORTB, 2        ;CS = 1 отключение SDHC от SPI шины
;       ldi     r17, 0xff       ;передача 0xFF, пока на всякий случай. Вроде DO отпускат после CS=0 и подачи 0xFF
;       out             SPDR, r17
;Wait_Transmit8:
;       in              r17, SPSR
;       sbrs    r17, SPIF
;       rjmp    Wait_Transmit8
;       in              r17, SPDR       ;очистка флага
ret


;Z передаваемое сообщение из FLASH в карту
FlashToSPI_Response:
                rcall   initFlashToSPI  ;Передача статической команды из FLASH
        ReadAnswerrr:
                ldi     r17, 0xff               ;ожидание response байта
        WaitAnswer:
                out             SPDR, r17
        Wait_Transmit3:
                in              r17, SPSR
                sbrs    r17, SPIF
                rjmp    Wait_Transmit3
                in              r17, SPDR               ;Проверка на начало приёма ответного байта
                cpi             r17, 0xff
                breq    WaitAnswer              ;Если ответа не будет то перезапуск через watchdog

                ldi     r27, high(ResponseBUF) ;Запись respons ответа в буфер ResponseBUF
                ldi     r26, low(ResponseBUF)

                lpm             r18, Z+ ;Загрузка количества response ответных байт из Flash конфигурационной строки
                lpm             r16, Z  ;чтение конфигурационного регистра

                        ;Если [Conf]:0 =1 то ожидание ответного токена 0xFE байта от SDHC
                sbrs    r16, 0
                rjmp    NoWait0xFEStarter
                        ;Ожидание 0xFE от карты
        r17Checkksw:
                cpi             r17, 0xFE
                breq    NoWait0xFEStarter
                ldi     r17, 0xff               ;ожидание ответного 0xFE байта от SDHC
                out             SPDR, r17
        Wait_Transmit12:
                in              r17, SPSR
                sbrs    r17, SPIF
                rjmp    Wait_Transmit12
                in              r17, SPDR               ;чтение ответного байта
                rjmp    r17Checkksw
        NoWait0xFEStarter:

                        ;Если количество ответных байт к чтению =0
                cpi             r18, 0
                breq    RespFinish
                sbrc    r16, 0          ;Переход для пропуска записи ответного 0xFE токена в буфер, если [Conf]:0 =1
                rjmp    Skip0xFEWritetobuf
        SafeRespByte:
                sbrs    r16, 2          ;Флаг прямой передачи полученного байта на USART
                rjmp    HHAASSDe        
                cli
                USRe5:                                          ;Ожидаем освобождение UDRE0
                        lds             r19, UCSR0A
                        sbrs    r19, UDRE0
                        rjmp    USRe5
                        sts             UDR0, r17               ;UDR0
                USART_Transmitww:
                        lds     r17, UCSR0A     ;UCSR0A
                        sbrs    r17, TXC0               ;Ожидаем окончания передачи байта
                        rjmp    USART_Transmitww
                        sts             UCSR0A, r17             ;сброс флага окончания передачи
                sei
        HHAASSDe:
                sbrs    r16, 2  ;Пропуск, если байт передается на USART
                st              X+, r17 ;response байт
                dec             r18
                breq    RespFinish
                Skip0xFEWritetobuf:
                        ldi     r17, 0xff               ;ожидание следующего response байта
                        out             SPDR, r17
                Wait_Transmit4:
                        in              r17, SPSR
                        sbrs    r17, SPIF
                        rjmp    Wait_Transmit4
                        in              r17, SPDR
                rjmp    SafeRespByte
        RespFinish:

                        ;Если [Conf]:1 =1 то запрет передачи 0xFF после передачи команды в SDHC
                ;lpm    r16, Z  ;чтение конфигурационного регистра
                sbrc    r16, 1          
                rjmp    Skip0XFFs
                        ;передача 0xFF после каждой команды перед передачей следующей
                ldi     r17, 0xff
                out             SPDR, r17
        Wait_Transmit5:
                in              r17, SPSR
                sbrs    r17, SPIF
                rjmp    Wait_Transmit5
                in              r17, SPDR       ;Очистка SPI дата регистра
        Skip0XFFs:
ret

Алгоритм учитывает, что после получения response на команду и перед передачей следующей команды нужно передать 8 SCLK тактов, а также необходимость передачи 8 SCLK тактов после установке CS в 0 или 1 при подключении отключении карты от SPI. Нужно отключать передачу 0xff после получения response для команд CMD24 CMD17 и включить функцию ожидания токена 0xfe для CMD17, это реализованно в приведённом выше алгоритме. Включать функцию ожидания токена 0xFE от контроллера карты нужно и для некоторых команд чтения сервисных регистров карты, длина которых отличается от стандартных response. Если карта занята внутренними операциями, то она устанавливает свой DO в низкий уровень, например во время стирания/записи блока, и можно отключать карту от SPI шины и работать с другим SPI устройством, при каждом подключении к SPI шине карты после CS = 0 всегда нужно подавать 0xff и проверять её занятость по состоянию её DO, если DO в низком карта занята и либо ждем либо подключаемся позже. Унгиверсальней будет проверять занятость карты перед подачей любой команды, мало ли какая карта как устроена. Проверка CRC в режиме SPI по умолчанию отключается, при необходимости можно включить, придется вычислять CRC. Если CRC отключена, то карта все равно вычисляет и присылает CRC, а в карту нужно передавать 1 или 2 байта CRC с любым содержимым. После подачи питания карта по умолчанию в SD режиме, CRC по умолчанию включена, команды подаём с правильной CRC. Переводим карту в SPI режим на скорости 20MHz/64 = 312.5kHz. В SPI, CRC по спецификации должна по умолчанию отключается и правильная CRC нужна для первых двух CMD0 CMD8, так может быть не всегда, на всякий, до инициализации карты используем правильную CRC для каждой команды.

После подачи питания настраиваем потры контроллера/хоста управляющего картой, watchdog, ждём от 300ms на установку питающего напряжений на карте, настраиваем USART на 2.5 мегабита работаем через cp2104 у неё по документации максимум 2 мегабита, но и 2.5 работают нормально, USART используем для отладки, на него response от карты передаём, по экрану осциллографа respons ответы неудобно контролировать, настраиваем SPI 100-400kHz как описано выше, DI и CS карты в высоком, подаём 80 единиц или 10шт 0xFF точнее тактов на SCLK карты, устанавливаем CS в низкий, передаём CMD0 =0x400000000095 команда сброса с правильной CRC, затем передаём в бесконечном цикле 0xFF пока не получим от карты первый и единственный response байт !=0xFF, бесконечный цикл будет длится 0-8 байт или пока не сработает watchdog, при отладке response засылаем в USART для контроля, проверяем если байт ответа =0x01 то всё в порядке idle режим, иначе бесконечное зацикливание для ухода на перезапуск через watchdog.

watchdog перезапускает микроконтроллер, если от карты не будет получен правильный ответ после CMD0 и в ряде других случаев. Разрабатываемое устройство подразумевает безусловную работу sdhc карты, поэтому если карта не работает, то устройство не работает.

Дальше передаём CMD8 =0x48000001aa87 с правильной CRC, это проверка поддерживаемого картой напряжения питания, для UHS-I обязательная команда, в ответ должны получить 0x01000001aa, проверяем, чтобы последние 2 байта = 0x01aa иначе уход на перезапуск по watchdog. При штатной работе, для разрабатываемого устройства со впаянной microSDHC, можно response не проверять, достаточно проверки ответа CMD0. Уточнение: устройство затачивается для работы с конкретным типом/классом microSDHC по SPI, универсальность не нужна.

Передаем CMD55(0x770000000065) + ACMD41(0x694000000077) с установленным 30:HCS битом для запуска процесса инициализации. От CMD55 ответом будет один response байт R1 = 0x01 idle режим, от ACMD41 по началу ответ R1=0x01, после окончания инициализации R1=0x00. Чтобы увидеть R1=0x00 нужно зациклиить передачу команд CMD55 + ACMD41 с выходом из цикла при ACMD41:R1=0x00. Это занало буквально одну итерацию цикла, т.е. после повторной подачи CMD55 + ACMD41 уже ACMD41:R1=0x00. Подробнгости в листинге кода. Реальные Response ответы принятые по USART при инициализации:

0x01 после CMD0
0x01 0x00 0x00 0x01 0xAA после СМD8
0x01 0x01 0x01 0x00 = CMD55 ACMD41 CMD55 ACMD41 после бесконечного цикла подачи CMD55 + ACMD41 с условием выхода ACMD41:R1=0x00

Повторюсь CRC у всех команд правильные, несмотря на то что после CMD0 CMD8 при переходе в SPI можно CRC не расчитывать. Минимум у Transcend по по этому вопросу есть пунктик или так показалось. На всякий проверяем, чтобы OCR:31 =1 через подачу CMD58 и также на всякий отключаем отключенную по умолчанию CRC командой CMD59.

Поскольку затачиваемся на работу с конкретными типами карт, то с инициализацией заканчиваем, всё остальное итак известно, и оно по дефолту такое какое нужно. Именно размер читаемого, записываемого блока/сектора = 512 байт, минимальный размер стираемого блока/сектора также 512 байт. Чтение запись исключительно поблочная по 512 байт, одновременной записи нескольких блоков не бует, такой алгоритм, нужно успевать работать с другими SPI устройствами. Чтение сервисных регистров в штатном режиме работы не понадобится всю информацию берем из документации на описываемую карту, или из регистров читаемых однократно в отладочном режиме и занесением нужной информации во флеш память AVR контрроллера. Реально понадобится размер карты в блоках. Далее увеличиваем частоту SPI SCLK до 10MHz и реализуем циклический поблочный алгоритм записи данных на карту из буфера.

Вывод содержимого всех доступных сервисных/информационных регистров для Transcend microSDHC UHS-I 16GB Class 10

По документации для Transcend microSDHC UHS-I 16GB Class 10 предусмотрены регистры OCR, CID, CSD, RCA, DSR и SCR про SSR, CSR ничего не сказано по спецификации SD Association должны быть. RCA в режиме SPI не используется, DSR опционален, у Transcend DSR есть, но не используется в SPI скорость обмена низкая 10MHz SCLK -это максимальная по документации частота выдаваемая микроконтроллером на частоте кварца 20MHz. Если для тестов или для стенда, то можно поставить кварцевый генератор 30MHz на AVR и получим 15MHz SCLK на SPI. Предельная неофициальная частота работы флеш памяти AVR микроконтроллера 33MHz, на 40 уже не работает. Максимальная SPI частота карты 25MHz смотрим документацию

Имя Ответных бит Описание
CID R1+"чего-то 0xff разной длины"+128+CRC-16/XMODEM(CCITT) Ответ на CMD10. Индивидуальный идентификационный номер карты. С чтением регистра та же история что и для SCR SSR и CSD
RCA 16* В SPI режиме не используется. Содержит адрес карты, используется при подключении нескольких карт к одной SDIO шине. При подаче питания адрес каждой карты RCA=0x0000
DSR 16* Регистр конфигурирования выходных драйверов выводов карты, чтобы на высоких скоростях передачи с фронтами сигналов было все впорядке, и вероятность ошибки была меньше. Регистр опционален, у Transcend есть. При работе по SPI не торогаем. По умолчанию у Transcend =0x0404. Для записи в DSR используется CMD4, по спецификации в SPI команда не используется
CSD R1+"чего-то разной длины"+128+CRC-16/XMODEM Ответ на CMD9. Данные, специфические для конкретной карты, размеры всевозможных блоков времена доступа, разрешения и пр. С чтением регистра та же история что и для SCR и SSR
SCR R1+"чего-то разной длины"+64+CRC-16/XMODEM Ответ на ACMD51. Конфигурационный регистр карты, информация о специальных функциях возможностях карты. С чтением регистра такая же история как и с SSR
OCR R1+32 Ответ на CMD58 R3. Индицирует напряжение питания карты, 31 бит индицирует факт окончания инициализации питающего напряжения. Если 30 бит =0 то карта SDSC, если =1 то SDHC или SDXC
SSR R1+"чего-то разной длины"+512+CRC-16/XMODEM Ответ на ACMD13. SD Status, информация о функциях, размерах тех или иных информационных единиц, особенностях карты. Алгоритм чтения нигде не описан, а он может оказаться с нюансом, описание ниже. "чего-то разной длины" зависит от частоты SPI чем выше, тем этого "чего-то" 0xFF больше, судя по всему есть фиксированная временная задержка после которой выдается SSR
CSR R1+8 Ответ на CMD13 R2. Статус карты. В ответ приходит 2 байта, первый R1 второй разная статусная информация относящаяся к предыдущей команде, если не сказано другое. Команду можно слать в любой момент, например в цикле чтобы определить занятость карты. Есть противоречие, в официальной SD Association документации ответ на CMD13 это R1+4 байта, возможно это ответ в SD режиме

* -с регистрами RCA DSR не работал, информацию проверяйте

CSR

По SD Association документации есть 3 типа статуса состояния SD карты Card Status Task Status и SD Status. R1 -это Card Status первый ответный байт на любую команду. Если в аргументе CMD13 [15] байт =0 то будет возвращен Card Status, если =1 то Task Status -но это вроде если используется режим многопоточной обработки команд CQ mode. Третий статус состояний это SD Status поле из 512 бит содержащее индивидуальные особенности карты это вроде и есть регистр SSR. Первый протокол/режим SD, представленный в 2000, позволял выполнение одной команды за раз. В 2016 SDA (SD Association) представила поддержку Command Queuing (CQ) для интерфейса SD, это позволяет выполнять до 32 команд. У нас речь про SPI, аргумент CMD13 =0x00000000 и речь только о Card Status, будет только 2 ответных байта.

Все нюансы с чтением регистров пришлось досканально выяснять чтобы убедиться в правильности работы разрабатываемых алгоритмов обмена с картой по SPI.

SSR

Transcend microSDHC UHS-I 16GB Class 10 выдаёт следующее в зависимости от частоты SPI:

ldi  r31, high(2*CMD55)      ;R1 + 512 байт SSR
ldi r30, low(2*CMD55)
rcall FlashToSPI_Response ldi r31, high(2*ACMD13)
ldi r30, low(2*ACMD13)
rcall FlashToSPI_Response Разные запуски, разные чатоты SPI, пустые символы(табуляции пробелы) добавлены в ручную для выравнивания: ;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5 ;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5
;00(CMD55) 00(R1) 00 FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5
;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5
;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5
;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5
;00(CMD55) 00(R1) 00 FF FF FF FF FF FF FF FF FF FF FE 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C9 A5 Algorithm Result Check Polynom Init RefIn RefOut XorOut
CRC-16/XMODEM 0xC9A5 0x31C3 0x1021 0x0000 false false 0x0000

Выдается разное количество байт 0xFF в начале, это не ошибки чтения. Связано с ожиданием чего-то, чем больше SPI частота, тем больше 0xFF перед маркером/токеном 0xFE начала 512 битного блока данных. Учитывая что длина SSR 512 бит = 64 байта, а последние 2 байта допустим CRC16, и также допустим что 0xFE это некий маркер начала SSR регистра к нему не относящийся. Далее через онлайн сервис вычисления разнообразных CRC16 сумм выясняем что если 0xC9A5 это CRC16 и CRC16 вычисляется по алгоритму CRC-16/XMODEM, то SSR = 00 00 00 00 04 00 00 00 04 00 90 00 08 11 19 0A 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00. Пока это очень правдоподобная фантазия. Сопоставим содержимое регистра с описанием. От 0 до 312 бит = 39 байт зарезервированно там 0x00, доходим до "00 18" -это 383:378 зарезервированные + 377:368 10 бит VSV_AU_SIZE = 0x18 = 24 мегабайта, цифра реалистичная, смотрим дальше, 391:384 байт VIDEO_SPEED_CLASS = 0x0A = 10 цифра в точку, Class 10 на карте написано. Соответствий много, полученные данные и есть SSR, но подобное "пребайтие" при чтении регистра нигде не описано. Информации достаточно для написания алгоритма извлечения SSR, видно что 0xFE некий стартовый байт скорее всего токен, после него 64 байта SSR + CRC16. Возможно у других карт по другому, или какой-нибудь где-нибудь "секурный" бит стоит. Если информация из регистров будет прочитана неправильно то неправильные данные могут повлиять на работу хоста с картой, вплоть до повреждения данных, поэтому если нужно статичное содержимое сервисных регистров карты, читайте их на низкой скорости при инициализации карты, проверяйте CRC.

OCR

Ответ на CMD58. Значения бит актуальны только после того как OCR:31 бит установится картой в 1 после инициализации питания:

OCR бит Описание
31 1 индицирует факт окончания инициализации питающего напряжения
30 Если 30 бит =0 то карта SDSC, если =1 то SDHC или SDXC
29..24 Зарезервировано
23 3.5V..3.6V если 1 то поддерживается
22 3.4V..3.5V
21 3.3V..3.4V
20 3.2V..3.3V
19 3.1V..3.2V
18 3.0V..3.1V
17 2.9V..3.0V
16 2.8V..2.9V
15 2.7V..2.8V
14..8 Зарезервировано
7 Зарезервировано для диапазона низких напряжений питания
6..0 Зарезервировано

Ответ от Transcend microSDHC UHS-I 16GB Class 10: "00(R1) C0 FF 80 00" означает поддержку всех питающих напряжений 2.7-3.6V + инициализация питания окончена + карта SDHC или SDXC всё бьется. OCR = C0 FF 80 00

SCR

Ответ на команду ACMD51. Конфигурационный регистр карты, информация о специальных функциях возможностях карты. С чтением регистра такая же история как и с SSR повторять её не будем:

SPI_SCLK = 312.5kHz 01[CMD0] 01 00 00 01 AA[CMD8] 01[CMD55] 01[ACMD41] 01[CMD55] 00[ACMD41] SPI_SCLK = 312.5kHz всё что дальше ответ на ACMD51 с буфером 70 байт 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE 02 35 80 43 00 00 00 00 84 16 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
SPI_SCLK = 312.5kHz 01[CMD0] 01 00 00 01 AA[CMD8] 01[CMD55] 01[ACMD41] 01[CMD55] 00[ACMD41] SPI_SCLK = 1.250MHz всё что дальше ответ на ACMD51 с буфером 70 байт 00 00 FF FF FF FF FF FF FF FF FF FE 02 35 80 43 00 00 00 00 84 16 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Тоже что и для SSR после ожидания и байта 0xFE идёт 64 бита SCR + 2 байта CRC-16/XMODEM. В таблице выше 2 строки это полный вывод response от команд при инициализации карты и запроса SCR. В верхней строке инициализация и ACMD51 на SCLK SPI = 312.5kHz в нижней инициализация на 312.5kHz, а ACMD51 подавалась на 1.25MHz соответственно видим одинаковую задержку перед выдачей SCR. Количество читаемых байт из карты, после подачи ACMD51 установлено в 70. Алгорим чтения специализированных регистров для Transcend microSDHC UHS-I 16GB Class 10 будет одинаковым. SCR = 02 35 80 43 00 00 00 00

CSD

Ответ на CMD9. CMD9 подавалась на 1.25MHz. Ответ на CMD9 начинается после байта 0xFE, после CMD9 на всякий избыточно читается 30 байт:

01 01 00 00 01 AA 01 01 01 00 00 FE 40 0E 00 32 5B 59 00 00 76 ED 7F 80 0A 40 00 D5 DD AB FF FF FF FF FF FF FF FF FF FF

CSD = 40 0E 00 32 5B 59 00 00 76 ED 7F 80 0A 40 00 D5

Объём SDHC карты: в кластерах = CSD:69:48bit = 22 бита = C_SIZE = 0x0076ED = 30445 кластеров. memory capacity = (C_SIZE+1) * 512K byte = 15588352k * 1024 = 15962472448 байт, виндовс при FAT32 разметке показывает размер в 15954083840 байт. Размер SDHC в 512 байтных блоках = 15962472448 / 512 = 31176704 блоков -эту цифру используем для работы с картой при циклической непрерывной записи данных.

memory capacity = (C_SIZE+1) * 512K byte формула взята из документа Transcend SDHC10 Card series 4~32GB High Capacity Secure Digital Card

CID

Ответ на CMD10. CMD10 подавалась на 1.25MHz. Ответ на CMD10 начинается после байта 0xFE, после CMD10 избыточно читается 30 байт из карты:

01 01 00 00 01 AA 01 01 01 00 00 FE 74 4A 60 55 53 44 55 31 20 42 8C B9 14 01 22 AD 2F 28 FF FF FF FF FF FF FF FF FF FF 

CID = 74 4A 60 55 53 44 55 31 20 42 8C B9 14 01 22 AD

Увеличиваем частоту SPI до 10MHz:

             ;=== SPI инициализация 10MHz на кварце=20Mhz ================
                ldi     r17,(1<<SPE)|(1<<MSTR) ;Enable SPI, Master, set clock rate fck/2, mode 00
                out     SPCR, r17
                in              r16, SPSR
                ori     r16, 0b00000001 ;Double spi
                out             SPSR, r16               
                ;=====================================================
                

Для быстрой записи 512 байтного блока в карту сначала нужно стереть блок на карте, используем CMD32 CMD33 CMD38, для рассматриваемой карты стирать можно от одного блока, но практичнее стирать сразу несколько блоков, например 128 одновременно, причина во времени требующемся карте на операцию стирания. Для рассматриваемой карты стирание одного 512 байтного блока занимает 3.4ms -это реальное измеренное осциллографом значение. Кластер в 128 блоков 64k стирается за 2.5ms и 3.2ms первый раз после подачи питания, в 512 блоков 256k за 2.5ms, 513 блоков за 4.1ms, 1024 блока за 2.6ms(3.2ms при первом стирании после подачи питания), 1025 блоков за 4.3ms, 2048 блоков за 2.8ms, 2049 блока 4.4ms и при первом стирании кластера после подачи питания 5ms, 4096 за 3.1ms, 4097 за 4.7ms 5.4ms при первом стирании после подачи питания, 16384 за 5.3ms, 16385 блоков за 6.9ms, 32768 за 8.1ms, 32769 блоков за 9.9ms, 65536 за 6.4ms, 65537 блоков или 32мегабайта за 8.5ms, 1048576 за 30.03ms, 1048577 блоков 512 мегабайт за 31ms, 15728640 за 360ms, 15728641 блоков 7680 мегабайт за 370ms(триста семьдесят). 64 блока 2.6ms, 128 блоков 2.5ms, 32 блока 2.6ms, 256 блоков 2.5ms, 512 блоков 2.5ms. Обращаем внимание на 64 килобайта и 32 мегабайта -"волшебные". 131072 блока 64 мегабайта 10.5ms. Команды на стирание CMD32 CMD33 CMD38 по времени занимают ничего, выше речь идёт о времени выполнеия самой картой операции стирания, т.е. подали команды стирания кластера и отключаем карту от SPI, при подключении карты к SPI проверяем её занятость как описано несколько раз выше.

Будем стирать кластерами по 1024 блока по 512килобайта 2.6ms. Удобно написать алгоритм определения оптимального размера кластера для минимизации времени стирания. Также нужен алгоритм проверки времени стирания каждого кластера и времени записи каждого блока. С операциями стирания записи на карту проблем почти не было. С чтением были, ситуация такая же, как и с чтением сервисных регистров, сначала идёт неопределённое количество 0xFF зависящее от рабочиз частот, затем, судя по всему, некий токен 0xFE, за ним 512 байт данных + CRC16. Пришлось в очередной раз дорабатывать алгоритм для реализации опции ожидания 0xFE перед чтением данных из карты, также пригодится для чтения некоторых сервисных регистров. Добавлен конфигурационный регистр к каждой команде с соответствующим флагом. Получается всё не относящееся к response ответам из карты будет передаваться как: некоторое количество 0xFF, затем маркер начала блока данных токен 0xFE за ним сами данные, это относится и к половине сервисных регистров.

Запись 512 байт блока в карту: после подачи CMD24 с адресом блока и получения response R1 байта, подаём 0xFE дата токен начала 512 байтного блока данных, затем подаём 512 байт БЕЗ CRC, и ожидаем response data token, должен быть 0xE5 -данные приняты.

Чтение 512 байт блока из карты: после подачи CMD17 с адресом блока и получения response R1 байта, выбираем байты из карты 0xFF, пока не получим 0xFE, читаем следующие 512 байт данных из карты + 2 байта CRC.

Стирание кластера. Именно кластер стираем чтобы уменьшить время записи данных на карту и уменьшить общее время взаимодействия с картой, для увеличения времени работы с другими SPI устройствами на одной шине. Выбор размера кластера в 1024 байта описан выше и обусловлен оптимальным временем стирания. Подаём CMD32 CMD33 CMD38 и отключаем карту от SPI или ждём пока DO карты не установится в 1 после подачи 0xFF в карту. Данные на карте после стирания будут нулями или единицами, в зависимости от производителя карты. Регистр SCR, бит DATA_STAT_AFTER_ERASE, номер бита 55 определяет что это будут за данные после стирания - либо 0, либо 1. У Transcend это "0".

Всё что касается взаимодействия с картой описано. Дальше реализация самого алгоритма циклической записи данных с контролем отсутствия пропусков при максимальной нагрузке. Разумеется реализация алгорима частичной передачи 512 байтного блока в карту, для реализации окон выбора приёмных буферов двух внешних CAN устройств на той же SPI шине.

По итогу всё получилось максимально чётко, даже с существеным запасом по быстродействию микроконтроллера.

CS чип селект, работает как обычно, в разных источниках советуют до или после передавать 0xFF -ерунда нужно сделать нормальное согласование уровней.

Некоторые карты могут требовать большого и не предсказуемого времени для записи блока данных. После приема блока данных и завершения проверки CRC, карта начнет запись и удерживать сигнал DAT0 в лог. 0, если её буфер записи будет заполнен, и новые данные не будут доступны от новой команды WRITE_BLOCK. Хост может опрашивать состояние карты командой SEND_STATUS (CMD13) в любой момент времени, и карта ответит информацией о своем статусе. Бит статуса READY_FOR_DATA показывает, готова ли карта принять новые данные, или процесс записи все еще продолжается. Хост может снять выбор карты выдачей команды CMD7 (чтобы выбрать другую карту) что поместит эту карту в отключенное состояние (Disconnect State), и освободит линию DAT без обрыва операции записи. Когда карта будет выбрана снова, будет повторно активирована индикация занятости переводом DAT в лог. 0, если программирование все еще продолжается, и буфер записи пока недоступен. В действительности хост может выполнить одновременно операцию записи в несколько карт, если проводить запись с чередованием между картами. Чередование может быть осуществлено путем доступа к каждой карте по отдельности - пока другие карты находятся в занятом состоянии, манипуляции происходят со свободными картами. Этот процесс может быть осуществлен правильной манипуляцией CMD и линией DAT0-DAT3 (отключение занятых карт).

Ну как бы вот, если нужно надёжное устройство с использованием SDHC карт, обращайтесь разработаю, при необходимости будет организовано производство устройств на своих производственных мощностях.

Copyright ©Новиков Алексей Александрович,

2023 Санкт-Петербург, 197372, ООО "Антех ПСБ",

anteh собака bk.ru