:: СОДЕРЖАНИЕ НОМЕРА
:: Газетные рубрики
:: АВТОРЫ
:: Поиск
:: Поддержка проекта
Webmoney:
|
:: №32 (10.10.2009) Просмотров: 20405
Автор: Вячеслав Савенков / savelij. Рубрика: В помощь разработчику. Номер: №32 (10.10.2009). Программирование SD карт на СпектрумеО спецификациях и SD картахКарта памяти SD (Secure Digital) является одной из разновидностей флэш-памяти, такие же как и обычные USB-Flash накопители, но основной ее особенностью является ставка на защиту записанной информации. Разновидностей SD карт на сегодняшний день доступно ровно три вида. Различаются они как по физическому размеру (стандартные, мини и микро), так и по объему. Основным является стандартный размер, для карт размера Mini и Micro существуют переходники до стандартного размера. Различий по объему, согласно спецификации, всего два: до 2 Гб включительно карта называется стандартной и не несет на себе никаких дополнительных указаний, кроме объема (описано спецификацией версия 1.1). Карты начиная с 4 Гб обязательно должны нести на себе гордую надпись SDHC (SD High Capacity) и Class 2 (4 или 6), цифра указывает скорость передачи данных в мегабайтах. Если на карте с объемом 4 Гб и более нет таких надписей, то карта просто неправильно маркирована (описано спецификацией версии 2.0). Так же спецификацией версии 2.0 описаны карты физического размера мини и микро. Набор внутренних команд зависит от спецификации и режима работы. На сегодняшний день последняя версия спецификации имеет номер 2.0 и датирована 25 сентября 2006 годом и существует на английском языке (возможно и на каких-то других языках), перевода на великий и могучий не обнаружено. Согласно этой спецификации объем карты до 2 Гигабайт включительно дает возможность изменять программно размер сектора от 1 до 512 байт, по умолчанию размер сектора равен 512 байтам. Карты объемом от 4 до 32 Гигабайт (32 Гб - ограничение спецификации версии 2.0) имеют фиксированный размер сектора равный 512 байтам, который не может быть изменен. Карты физического размера мини и микро относятся к спецификации 2.0 (хотя могут иметь объем менее 4ГБ) и программируются как SDHC карты, размер сектора также не может быть изменен. Карта может работать в одном из двух режимов: непосредственно SD режим и SPI. Для нас наиболее интересен режим SPI, который и реализован в Z-контроллере. Реализация и работа самого интерфейса может быть описана автором контроллера - Жабиным Алексеем (KOE), я же опишу программирование SD карты в режиме SPI. Количество команд в этом режиме значительно меньше, чем в режиме SD. На текущий момент написан и отлажен драйвер для работы с картами любого объема и размера (по спецификации 2.0). На момент написания статьи появилась новость о скором появлении карт SDXC (объемом до 2 Терабайт) и соответствующих спецификаций, после чего будут внесены соответствующие изменения в драйвер. О файловой системе FAT (File Allocation Table – таблица размещения файлов). Изначально она была создана для гибких дисков размером меньше чем 500K, но со временем развивалась для поддержки дисков всё больших и больших объемов. Сейчас уже существуют три типа FAT: FAT12, FAT16 и FAT32. Основные различия в типах FAT отражены в их названии - это размер (в битах) значений таблицы FAT. 12 бит в FAT12, 16 бит в FAT16, и 32 бит в FAT32. MBR (Master Boot Record – главная загрузочная запись). Это первый физический сектор на винчестере или другом устройстве хранения информации, разбиваемом на логические диски (разделы). MBR содержит таблицу разделов (partition table) и небольшой фрагмент исполняемого кода. SD карты выпускаются отформатированными в файловую систему ФАТ. Разрядность ФАТ зависит от объема карты и может быть изменена переформатированием. После заводского форматирования на карте всегда существует MBR, содержащая только один раздел. Между сектором MBR и началом раздела всегда есть неиспользуемые сектора, количество которых зависит от производителя карточки. Карточки объемом до 16Мб включительно поставлялись с файловой системой ФАТ12, от 32Мб до 2Гб включительно с файловой системой ФАТ16, от 4ГБ и более ФАТ32. Для PC существует программа форматирования карточек (восстановления заводского формата) (http://www.sdcard.org/consumers/formatter/sdfv2000.exe, для скачивания надо принять лицензионное соглашение), или если сказать точнее для возвращения карте исходного заводского форматирования. Эта программа всегда создает MBR и только один раздел. При работе с ФАТом под Windows ХР (далее XP) обнаружилась одна хитрая особенность или возможно ошибка. У ФАТ32 из-за большого количества кластеров в дополнение к описателю раздела добавлен еще один сектор в котором помещается информация о количестве свободных кластеров и номере кластера, с которого надо начинать поиск свободных кластеров для записи. При разрядности 32 бита номер первого свободного кластера записывается как 16-битное число, то есть старшие 16 бит всегда, или почти всегда, равны нулю. При попытке исправить на правильное значение и дальнейшей работе под ХР старшие 16 бит номера первого свободного кластера просто обнуляются. Чтобы правильно работать с ФАТом не только на Спектруме, но и под ХР надо учитывать данную особенность и производить поиск первого свободного кластера или с указанного номера или от начала ФАТ таблицы. Что, конечно, будет занимать длительное время, которое зависит от размера раздела и объема уже записанной информации. Порты Z-контроллера Со стороны Спектрума интерфейс SPI доступен как порт данных #57 и порт конфигурации #77. Оба порта доступны и по чтению и по записи. Порт данных для программирования абсолютно прозрачен и служит для двухсторонней передачи между контроллером и картой. Порт конфигурации по записи: бит 0 – установка этого бита подает питание на карту; бит 1 – установка этого бита снимает выбор карты и делает ее недоступной для программирования, сброс соответственно выбирает карту и позволяет отдавать команды. Остальные биты не используются. Порт конфигурации по чтению: бит 0 – сброшен когда карта памяти вставлена в слот; бит 1 – сброшен если карта памяти не защищена от записи, установлен если карта защищена от записи. Остальные биты не используются. Немного о драйвере При написании драйвера использовалось описание инициализации от создателя Хард-тапер и исходники с сайта wwwArray для работы с MMC картой. Драйвер писался из расчета поддержки только SD карт и не поддерживает MMC карты по причине отсутствия как самих карт для проверки, так и спецификаций на них в свободном доступе. При попытке использования MMC карт драйвер будет зависать - о причинах в тексте драйвера. Текст драйвера снят с рабочего исходника и является полностью рабочим. Со времени написания статьи для журнала «NedoPC №5» прошло достаточно много времени. Выявлены и устранены некоторые неточности и ошибки. Улучшена работа в турбо-режиме за счет дополнительных задержек, предыдущая версия драйвера при частоте процессора 7МГц иногда бессистемно читала с ошибками, что было выявлено в процессе эксплуатации. Данный драйвер поддерживает карты любого объема и любого размера в пределах текущей спецификации версии 2.0, определение типа карты делается во время исполнения команды чтения/записи. Команды и ответы Передача команд/параметров на карту и ответов карты осуществляется от старшего бита до младшего. Размер любой команды для карты равен 6 байтам (48 бит). Формат любой команды: Стартовый Направление Индекс Аргумент CRC7 Стоповый бит передачи команды бит Номер бита 47 46 45-40 39-8 7-1 0 Размер, бит 1 1 6 32 7 1 Значение 0 1 Х Х Х 1 Команды использованные в драйвере: CMD0 (ответ R1) - после подачи питания на карту и инициализации интерфейса переводит карту в режим SPI. CMD8 (ответ R7) - применяется для определения какая версия спецификации поддерживается картой. Для спецификации версии 1.1 выдает ошибку, команда появилась в спецификации 2.0. CMD12 (ответ R1b) - команда применяется для останова многоблочной операции чтения. У SD карт нет счетчика чтения, поэтому после чтения нужного количества секторов должна подаваться команда останова. Для запуска и останова многоблочной записи используются байты-маркеры. CMD16 (ответ R1) - установка размера сектора. Только для карт поддерживающих спецификацию 1.1. Для спецификации 2.0 игнорируется. CMD17 (ответ R1) - команда чтения одного сектора. CMD18 (ответ R1) - команда чтения нескольких секторов. CMD24 (ответ R1) - команда записи одного сектора. CMD25 (ответ R1) - команда записи нескольких секторов. CMD55 (ответ R1) - указывает карте, что следующая команда специальная. CMD58 (ответ R3) - чтение OCR регистра карты. Применяется для определения стандартная или SDHC/мини/микро SD карта. CMD59 (ответ R1) - изменение режима (включить/выключить) подсчета контрольной суммы CRC. ACMD41 (ответ R1) - специальная команда. Применяется для запуска процесса инициализации карты с учетом типа карты. В процессе чтения/записи секторов используются специальные байты-маркеры. Маркер #FE - выдается картой при операциях чтения одного (CMD17) или нескольких (CMD18) секторов. После получения этого маркера можно производить чтение сектора. Выдается перед каждым сектором. При записи одного сектора (CMD24) должен передаваться перед передачей сектора на карту. Маркер #FC - передает карте о начале записи сектора (только при многосекторной записи (CMD25)). После подачи этого маркера можно начинать передачу сектора. Маркер выдается перед каждым сектором. Маркер #FD - передает карте команду об остановке много секторной записи (для завершения команды CMD25). После подачи любой команды, первый считанный байт из SD карты (не равный #FF) является ответом карты и, если не равен нулю (команда будет выполнена), то содержит биты ошибки. В драйвере код ошибки чаще всего игнорируется. По спецификации карта выдает несколько разновидностей ответов различающихся длиной, в драйвере чаще всего используется только первый байт ответа. Ответы карты: R1(R1b) - длина ответа 8 бит. Короткий ответ, так же входит в состав многобайтовых ответов. Коды ошибок по битам. Возможно, я где-то не так перевел, поэтому привожу и оригинальное описание битов ошибок: 7 - всегда 0 6 - parameter error (ошибочный параметр) 5 - address error (неправильный адрес блока) 4 - erase sequence error 3 - com crc error (ошибка CRC команды (при отключенном CRC не должна появляться)) 2 - illegal command (неизвестная команда) 1 - erase reset 0 - in idle state (карта находится в режиме инициализации и недоступна) R7 - длина ответа 40 бит. Старшие 8 бит соответствуют формату ответа R1. Остальные биты ответа в драйвере не используются. Вызов драйвера производится через общую точку входа, код команды помещается сразу после команды вызова. Всего команд 6, нумерация от 0 до 5. Перед вызовом драйвера регистры процессора должны содержать: HL - адрес чтения/записи. DE - младшие 16 бит номера сектора. BC - старшие 16 бит номера сектора. A - количество секторов, только для многосекторных команд (команды 3 и 5). Команды используемые в драйвере: 0 - включение питания и инициализация SD карты 1 - выключение питания карты 2 - чтение одного сектора 3 - чтение нескольких секторов 4 - запись одного сектора 5 - запись нескольких секторов Ошибки, выдаваемые драйвером в регистре «A»: 0 - инициализация прошла успешно 1 - карта не найдена или не ответила 2 - карта защищена от записи 3 - попытка записи в сектор 0 карты ;Пример использования драйвера: CALL COM__SD DB 0 ;включение и инициализация карты памяти ;на выходе А - смотрим коды возвращаемых ошибок LD HL,АДРЕС ЗАГРУЗКИ LD BC,СТАРШИЕ 16 БИТ НОМЕРА СЕТОРА LD DE,МЛАДШИЕ 16 БИТ НОМЕРА СЕКТОРА LD A,КОЛИЧЕСТВО СЕКТОРОВ CALL COM__SD DB 3 ;чтение заданного количества секторов по заданному адресу начиная с заданного сектора ;на выходе не забываем проверять код ошибки в А. CALL COM__SD DB 1 ;выключаем питание карты ;Драйвер SD карты ;LAST UPDATE 14.04.2009 savelij ;Входные параметры общие: ;HL-адрес загрузки в память ;BCDE-32-х битный номер сектора ;A-количество блоков (блок=512 байт) - только для многоблочной записи/чтения ;Ошибки выдаваемые на выходе: ;A=0 - инициализация прошла успешно ;A=1 - карта не найдена или не ответила ;A=2 - карта защищена от записи ;A=3 - попытка записи в сектор 0 карты P_DATA EQU #57 ;порт данных P_CONF EQU #77 ;порт конфигурации CMD_12 EQU #4C ;STOP_TRANSMISSION CMD_17 EQU #51 ;READ_SINGLE_BLOCK CMD_18 EQU #52 ;READ_MULTIPLE_BLOCK CMD_24 EQU #58 ;WRITE_BLOCK CMD_25 EQU #59 ;WRITE_MULTIPLE_BLOCK CMD_55 EQU #77 ;APP_CMD CMD_58 EQU #7A ;READ_OCR CMD_59 EQU #7B ;CRC_ON_OFF ACMD_41 EQU #69 ;SD_SEND_OP_COND ;ОБЩАЯ ТОЧКА ВХОДА ДЛЯ РАБОТЫ С SD COM__SD EXA EX (SP),HL LD A,(HL) INC HL EX (SP),HL ADD A,A PUSH HL LD HL,TABLSDZ ADD A,L LD L,A LD A,H ADC A,0 LD H,A LD A,(HL) INC HL LD H,(HL) LD L,A EXA EX (SP),HL RET ;коды и описания функций TABLSDZ DW SD_INIT ;0-параметров не требует, на выходе A ;смотри коды ошибок, первые 2 значения DW SD__OFF ;1-выключить питание карты DW RDSINGL ;2-читать один сектор DW RDMULTI ;3-читать «А» секторов DW WRSINGL ;4-записать один сектор DW WRMULTI ;5-записать «А» секторов SD_INIT CALL CS_HIGH ;включаем питание карты при снятом выборе LD BC,P_DATA LD DE,#20FF ;бит выбора карты в «1» OUT (C),E ;записываем в порт много единичек DEC D ;количество единичек несколько больше JR NZ,$-3 ;чем надо XOR A ;запускаем счетчик на 256 EXA ;для ожидания инициализации карты ZAW001 LD HL,CMD00 ;даем команду сброса CALL OUTCOM ;этой командой карточка переводится в режим SPI CALL IN_OOUT ;читаем ответ карты EXA DEC A JR Z,ZAW003 ;если карта 256 раз не ответила, то карты нет EXA DEC A JR NZ,ZAW001 ;ответ карты «1», перевод в SPI прошел успешно LD HL,CMD08 ;запрос на поддерживаемые напряжения CALL OUTCOM ;команда поддерживается начиная со спецификации CALL IN_OOUT ;версии 2.0 и только SDHC, мини и микро SD картами IN H,(C) ;в A=код ответа карты NOP ;считываем 4 байта длинного ответа IN H,(C) ;но не используем NOP IN H,(C) NOP IN H,(C) LD HL,0 ;HL=аргумент для команды инициализации BIT 2,A ;если бит 2 установлен, то карта стандартная JR NZ,ZAW006 ;стандартная карта выдаст «ошибка команды» LD H,#40 ;если ошибки не было, то карта SDHC, мини или микро SD ZAW006 LD A,CMD_55 ;запускаем процесс внутренней инициализации CALL OUT_COM ;для карт MMC здесь должна быть другая команда CALL IN_OOUT ;соответственно наличие в слоте MMC-карты LD A,ACMD_41 ;вызовет зависание драйвера, от применения OUT (C),A ;общей команды запуска инициализации я отказался NOP ;бит 6 установлен для инициализации SDHC карты OUT (C),H ;для стандартной сброшен NOP OUT (C),L NOP OUT (C),L NOP OUT (C),L LD A,#FF OUT (C),A CALL IN_OOUT ;ждем перевода карты в режим готовности AND A ;время ожидания примерно 1 секунда JR NZ,ZAW006 ZAW004 LD A,CMD_59 ;принудительно отключаем CRC16 CALL OUT_COM CALL IN_OOUT AND A JR NZ,ZAW004 ZAW005 LD HL,CMD16 ;принудительно задаем размер блока 512 байт CALL OUTCOM CALL IN_OOUT AND A JR NZ,ZAW005 ;включение питания карты при снятом сигнале выбора карты CS_HIGH PUSH AF LD A,3 OUT (P_CONF),A ;включаем питание, снимаем выбор карты XOR A OUT (P_DATA),A ;обнуляем порт данных POP AF ;обнуление порта можно не делать, просто последний RET ;записанный бит всегда 1, а при сбросе через вывод ;данных карты напряжение попадает на вывод питания ;карты и светодиод на питании подсвечивается ;возврат при не ответе карты с кодом ошибки 1 ZAW003 CALL SD__OFF INC A RET SD__OFF XOR A OUT (P_CONF),A ;выключение питания карты OUT (P_DATA),A ;обнуление порта данных RET ;выбираем карту сигналом 0 CS__LOW PUSH AF LD A,1 OUT (P_CONF),A POP AF RET ;запись в карту команды с неизменяемым параметром из памяти ;адрес команды в «HL» OUTCOM CALL CS__LOW PUSH BC LD BC,#600+P_DATA OTIR ;передаем 6 байт команды из памяти POP BC RET ;запись в карту команды с нулевыми аргументами ;А-код команды, аргумент команды равен 0 OUT_COM PUSH BC CALL CS__LOW LD BC,P_DATA OUT (C),A XOR A OUT (C),A NOP OUT (C),A NOP OUT (C),A NOP OUT (C),A DEC A OUT (C),A ;пишем пустой CRC7 и стоповый бит POP BC RET ;запись команды чтения/записи с номером сектора в BCDE для карт стандартного размера ;при изменяемом размере сектора номер сектора нужно умножать на его размер, для карт ;SDHC, мини и микро размер сектора не требует умножения SECM200 PUSH HL PUSH DE PUSH BC PUSH AF PUSH BC LD A,CMD_58 LD BC,P_DATA CALL COM_OUT CALL IN_OOUT IN A,(C) NOP IN H,(C) NOP IN H,(C) NOP IN H,(C) BIT 6,A ;проверяем 30 бит регистра OCR (6 бит в «А») POP HL ;при установленном бите умножение номера сектора JR NZ,SECN200 ;не требуется EX DE,HL ;при сброшенном бите соответственно ADD HL,HL ;умножаем номер сектора на 512 (#200) EX DE,HL ADC HL,HL LD H,L LD L,D LD D,E LD E,0 SECN200 POP AF ;заготовленный номер сектора находится в «HLDE» OUT (C),A ;пишем команду из «А» на SD карту NOP ;записываем 4 байта аргумента OUT (C),H ;пишем номер сектора от старшего NOP OUT (C),L NOP OUT (C),D NOP OUT (C),E ;до младшего байта LD A,#FF OUT (C),A ;пишем пустой CRC7 и стоповый бит POP BC POP DE POP HL RET ;чтение ответа карты до 32 раз, если ответ не #FF - немедленный выход IN_OOUT PUSH DE LD DE,#20FF IN_WAIT IN A,(P_DATA) CP E JR NZ,IN_EXIT IN_NEXT DEC D JR NZ,IN_WAIT IN_EXIT POP DE RET CMD00 DB #40,#00,#00,#00,#00,#95 ;GO_IDLE_STATE ;команда сброса и перевода карты в SPI режим после включения питания CMD08 DB #48,#00,#00,#01,#AA,#87 ;SEND_IF_COND ;запрос поддерживаемых напряжений CMD16 DB #50,#00,#00,#02,#00,#FF ;SET_BLOCKEN ;команда изменения размера блока ;читаем один сектор из карты в память, адрес чтения в «HL» RD_SECT PUSH BC LD BC,P_DATA INIR NOP INIR NOP IN A,(C) NOP IN A,(C) POP BC RET ;записываем один сектор из памяти в карту, адрес записи в «HL» WR_SECT PUSH BC LD BC,P_DATA OUT (C),A NOP OTIR NOP OTIR LD A,#FF OUT (C),A NOP OUT (C),A POP BC RET ;многосекторное чтение RDMULTI EXA ;прячем счетчик секторов LD A,CMD_18 CALL SECM200 ;даем команду многосекторного чтения EXA RDMULT1 EXA CALL IN_OOUT CP #FE JR NZ,$-5 ;ждем маркер готовности #FE для начала чтения CALL RD_SECT ;читаем сектор EXA DEC A JR NZ,RDMULT1 ;продолжаем пока не обнулится счетчик LD A,CMD_12 ;по окончании чтения даем команду карте «СТОП» CALL OUT_COM ;команда мультичтения не имеет счетчика и CALL IN_OOUT ;должна останавливаться здесь командой 12 INC A JR NZ,$-4 ;ждем освобождения карты JP CS_HIGH ;снимаем выбор с карты и выходим с кодом 0 ;чтение одного блока RDSINGL LD A,CMD_17 ;даем команду чтения одного сектора CALL SECM200 CALL IN_OOUT CP #FE JR NZ,$-5 ;ждем маркер готовности #FE для начала чтения CALL RD_SECT ;читаем сектор CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождения карты JP CS_HIGH ;снимаем выбор карты и выходим с кодом 0 ;запись одиночного блока WRSINGL XOR A IN A,(P_CONF) ;проверяем защиту от записи AND 2 ;если защита включена выходим с кодом 2 RET NZ LD A,B ;проверяем на попытку записи в нулевой OR C ;сектор, сделано на всякий пожарный OR D OR E LD A,3 ;если сектор нулевой, выходим с кодом 3 RET Z LD A,CMD_24 ;даем команду записи одного сектора CALL SECM200 CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождения карты LD A,#FE ;пишем стартовый маркер, сам блок и пустое CRC16 CALL WR_SECT CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождение карты JP CS_HIGH ;снимаем выбор карты и выходим с кодом 0 ;многосекторная запись WRMULTI EX AF,AF' ;прячем счетчик секторов XOR A IN A,(P_CONF) ; как и в случае с записью одного сектора AND 2 ;проверяем защиту от записи RET NZ LD A,B ;и попытку записи в MBR OR C OR D OR E LD A,3 ;если сектор нулевой, выходим с кодом 3 RET Z LD A,CMD_25 ;даем команду мультисекторной записи CALL SECM200 CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождения карты EXA WRMULT1 EXA LD A,#FC ;пишем стартовый маркер, сам блок и пустое CRC16 CALL WR_SECT CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождения карты EXA DEC A JR NZ,WRMULT1 ;продолжаем пока счетчик не обнулится LD C,P_DATA LD A,#FD OUT (C),A ;даем команду остановки записи CALL IN_OOUT INC A JR NZ,$-4 ;ждем освобождения карты JP CS_HIGH ;снимаем выбор карты и выходим с кодом 0 |