![]() |
|
:: СОДЕРЖАНИЕ НОМЕРА
:: Газетные рубрики
:: АВТОРЫ
:: Поиск
:: Поддержка проекта
Webmoney:
|
:: №32 (10.10.2009) Просмотров: 21087
![]() Автор: Вячеслав Савенков / 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 |