Работа с MMC (или SD?)

Печатные платы, программируемая логика, разработка и изготовление аппаратуры

Moderator: Shaos

Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Работа с MMC (или SD?)

Post by Romanich »

в общем купил в магазине я устройство под названием MMC plus от производителя Kingston. В инструкции на девайс написано что отвечает стандарту MMC 4.0, но совместима с более древними стандартами MMC.

Очень удивило расположение и количество ножек - их не 7 (как в классических MMC), и не 9 (как в SD) - а больше(см. нижний рисунок)Image
Нумерацию выводов расставил я - в предположении что указанная группа контактов совместима с 9-ми контактами SD. (рисунок ниже)
Image

Далее подрубил её к контроллеру и по SPI завязал обмен.
Два исходника пересмотрел (русский и немецкий) - как с MMC работать в режиме SPI (интересует именно этот режим).
Пробовал команды CMD0(reset),CMD1(init) и CMD17 - block read...
При использовании команды CMD17 - возвращается лажа!!! :(
или иногда вообще повисает - при опросе байтов в цикле

В чем может быть дело?:
1) неверно определены выводы
2) карта поддерживает другие команды
3) не верен перевод в SPI-режим
4) ?

Помогите разобраться плиз :)
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Post by Romanich »

в кард-ридере/райтере на разъёме дополнительные контакты не импользуются (посмотрел)

MMC отформатирована и на неё записан файл (в WinXP)

зарезервированные пины (8/9) подтянул к питанию +3.3V через резисторы на 4,7кОм

питание контроллера и MMCplus тоже 3.3V

на MMCplus указано напряжение 3,3V
User avatar
Shaos
Admin
Posts: 24014
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

У меня на новом пальме телефоне SD/MMC-карточка с лишними контактами SD-читалкой на моём ноуте НЕ читается, зато читается универсальной читалкой карточек что к компу по USB цепляется. Видимо не всё там стандартно...
Я тут за главного - если что шлите мыло на me собака shaos точка net
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Post by Romanich »

На моём рисунке выводы красного цвета для кард ридера/райтера не используются (это точно -смотрел разъём)!

к-р/р - читает и пишет всё! и MMC,SD,CF,и другие редко встречаемые.
USB-шный

можно конечно питание прозвонить с землёй при включенном кард-ридере - чтоб быть уверенным в совпадении распиновки. Но боюсь, что там ПЛИС - тоесть выводы могут переназначаться для каждой MMC/SD

P.S. и всё-таки непонятный у меня девайс-мутант. 9-ножек говорят об SD, а написано что MMC+ 128MB

другого дешёвого к сожалению нет - 128MB(которые счас никто не берет) и сразу 2GB (которые дорого - и не каждый возьмет)

а такой "классики" как ATmel или SanDisk - и подавно нет - антиквариат...
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Post by Romanich »

Выдержка из статьи по Flash-носителям:
Несколько удивляет отсутствие прямой совместимости между этими двумя видами карт (т.е. то, что SD неспособна работать по протоколу MMC). Если внимательно рассматривать спецификации обоих типов карт и не обращать внимания на то, что SD может быть толще MMC, то отсутствие такой совместимости даже удивляет, поскольку реализовать её было несложно, да и выглядело бы это очень естественно. Что наводит на мысль о том, что, хотя подобную совместимость можно было реализовать без особых трудностей, SD намеренно разработана не как расширение спецификации MMC, а как отдельный конкурирующий стандарт

А вот выдержка из статьи счастливого юзера, у которого SD-карточка якобы оказалась совместимой с MMC (на которую может я и повёлся):
P.S.: Как выяснилось, SD карты полностью совместимы с карточками MMC, в т.ч. в режиме SPI. "Лишние" выводы в этом режиме не используются. Единственно что стоит сделать - поставить подтягивающие сопротивления на выв. 8 и 9 (в р-не неск. деятков кОм).

ХТО ПРАВЕЕ???
User avatar
Shaos
Admin
Posts: 24014
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Post by Shaos »

SD-карточки у нас уже обсуждались в прошлом году:

viewtopic.php?t=8353
Я тут за главного - если что шлите мыло на me собака shaos точка net
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Post by Romanich »

Нашёл!!!

Как раз мой случай на стр.19 сего даташита :rotate:
Буду разбираца... вначале хотя бы по SPI

Shaos убрал даташит из аплоада...
jdigreze
God
Posts: 1388
Joined: 02 Jan 2006 02:28
Location: Abakan

эээх... видимо все наступают на одни и те же грабли...

Post by jdigreze »

... как и я сам когда-то ;)
Вот рабочий код для AT89S8252:

Code: Select all

;==============================================================================
;--- описатели портов устройства ---
_CS				EQU	P1.4
MOSI				EQU	P1.5
MISO				EQU	P1.6
SCK				EQU	P1.7

;==============================================================================
;--- описатели для работы SPI ---
CMD0				EQU	0		;reset the sd-card
CMD1				EQU	1		;activates the card's initialization process
CMD9				EQU	9		;read card specific data
CMD10				EQU	10		;read card identification (CID register)
CMD17				EQU	17		;read single block from SD-card
CMD24				EQU	24		;write single block to SD-card
CMD55				EQU	55		;next command to be application

ACMD13				EQU	13		;read status block

SPI_REG_LEN			EQU	010h		;длина регистров CID И CSD

spcr				data	0d5h		;SPI control register
spsr				data	0aah		;SPI status register
spif				equ	10000000b	;interrupt flag
spdr				data	086h		;SPI data register

WMCON				DATA	096h
PS2				EQU	10000000b
PS1				EQU	01000000b
PS0				EQU	00100000b
EEMWE				EQU	00010000b
EEMEN				EQU	00001000b
DPS				EQU	00000100b
WDTRST				EQU	00000010b
WDTEN				EQU	00000001b


;==============================================================================
ccitt_crc:
				mov	b,#8			; 8 Bits In A Byte
				xrl	CRC_H,a			; HI ^= Data
l_p1:
				clr	c			; 0 Into Low Bit
				mov	a,CRC_L			; D_CRC << 1
				rlc	a			; Shift Left
				mov	CRC_L,a			; Store Back
				mov	a,CRC_H			; Get High Byte
				rlc	a			; Shift Left
				mov	CRC_H,a			; Store Back
				jnc	l_p2			; Skip If Bit 15 Wasn't Set
				xrl	CRC_H,#10h		; XOR In Polynomial High
				xrl	CRC_L,#21h		; XOR In Polynomial Low
l_p2:
				djnz	b,l_p1			; Repeat R0 More Times
				ret				; Return To Caller

;========================================================================
; работа с SPI
;========================================================================
spi_init:
				setb	_cs
				setb	mosi
				setb	miso
				setb	sck
				mov	spcr,	#01010001b

				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte

				clr	a
				mov	param1,	a
				mov	param2,	a
				mov	param3,	a
				mov	param4,	a
				call	spi_send_command

				call	spi_read_response
				cjne	a,	#001h,	_no_response

_wait_for_init:
				mov	a,	#CMD1
				call	spi_send_command

				call	spi_read_response
				jnz	_wait_for_init

				setb	cy
				ret
_no_response:
				mov	last_error,	#err_sd_init
				clr	cy
				ret

;--- чтение регистров CID и CSD SD-карты в ОЗУ ---
spi_read_csd:
				mov	b,	#CMD9		;номер команды чтения CSD в "В"
				sjmp	spi_read_reg	;переход на обработку команды
spi_read_cid:
				mov	b,	#CMD10	;номер команды чтения CID в "В"
spi_read_reg:
				mov	dptr,	#temp_buffer
				clr	a			;обнулим аккумулятор
				mov	param1,	a	;обнулим параметр 1
				mov	param2,	a	;обнулим параметр 2
				mov	param3,	a	;обнулим параметр 3
				mov	param4,	a	;обнулим параметр 4

				mov	crc_h,	a
				mov	crc_l,	a

				mov	a,	b		;получим команду из "В"
				call	spi_send_command	;отправить команду

				call	spi_wait_data
				jnc	_spi_read_cid_err

				mov	b,	#spi_reg_len	;длина буфера
_spi_read_cid_l1:
				push	b
				call	spi_read_byte	;получить данные
				call	mem_write_rep	;запишем полученный байт в ОЗУ
				call	ccitt_crc
				inc	dptr			;увеличим указатель ОЗУ на 1
				pop	b
				djnz	b,	_spi_read_cid_l1	;если не все получено, то повторить

				mov	last_error,	#err_crc
				call	spi_read_byte	;получим байт из SD-карты	MSB_CRC16
				cjne	a,	crc_h,	_spi_read_cid_err
				call	spi_read_byte	;получим байт из SD-карты	LSB_CRC16
				cjne	a,	crc_l,	_spi_read_cid_err

				setb	cy			;данные получены
				ret				;вернемся

;--- чтение статуса SD-карты (ACMD13) ---
; всегда читаем во внешнее ОЗУ с адреса 0!!!
spi_read_status:
				mov	dptr,	#00000h

				clr	a			;обнулим аккумулятор
				mov	param1,	a	;обнулим параметр 1
				mov	param2,	a	;обнулим параметр 2
				mov	param3,	a	;обнулим параметр 3
				mov	param4,	a	;обнулим параметр 4
				mov	a,	#CMD55	;получим команду расширения
				call	spi_send_command	;отправить команду

				call	spi_read_response	;ждем ответа
				jnz	_spi_read_cid_err	;ошибка выполнения команды

				mov	a,	#ACMD13	;чтение статуса
				call	spi_send_command	;отправить команду

				call	spi_wait_data_r2
				jnc	_spi_read_cid_err

				mov	b,	#042h		;для получения 66 байт
				sjmp	_spi_read_data_l1	;если все нормально, то читаем данные

;--- была ошибка ---
_spi_read_cid_err:
				clr	cy			;ошибка выполнения команды
				ret				;вернемся

;--- чтение блока данных из SD карты ---
; на входе должен быть установлен DPTR
spi_read_data:
				mov	last_error,	#err_read
				mov	a,	#CMD17	;команда чтения одного блока данных
				call	spi_send_command	;отправить команду

				mov	crc_h,	#000h
				mov	crc_l,	#000h

				call	spi_wait_data
				jnc	_spi_read_cid_err

				call	_spi_read_data_d1	;выполним прием первых 256 байт
				call	_spi_read_data_d1	;а после еще 256 байт

				call	spi_read_byte	;получим байт из SD-карты	MSB_CRC16
				cjne	a,	crc_h,	_spi_read_cid_err
				call	spi_read_byte	;получим байт из SD-карты	LSB_CRC16
				cjne	a,	crc_l,	_spi_read_cid_err

				setb	cy
				ret				;вернемся
;---
_spi_read_data_d1:
				mov	b,	#000h		;счетчик 256 байт
_spi_read_data_l1:
				push	b			;сохраним, т.к. точно потеряем
				call	spi_read_byte	;получим байт из SD-карты
				call	mem_write_rep	;запишем полученный байт в ОЗУ
				call	ccitt_crc
				inc	dptr			;увеличим указатель ОЗУ на 1
				pop	b			;восстановим счетчик принятых байт
				djnz	b,	_spi_read_data_l1	;если не все считано, то повторим
				ret

;--- запись блока данных в SD карту ---
spi_write_data:
				mov	last_error,	#err_write
				mov	a,	#CMD24
				call	spi_send_command

				call	spi_read_response
				jnz	_spi_write_err

				mov	crc_h,	a
				mov	crc_l,	a

				call	spi_sync_byte
				call	spi_sync_byte
				call	spi_sync_byte

				mov	a,	#0FEh
				call	spi_send_byte

				call	_spi_write_data
				call	_spi_write_data

				mov	a,	crc_h
				call	spi_send_byte
				mov	a,	crc_l
				call	spi_send_byte

				call	spi_read_response
				anl	a,	#01Fh
				cjne	a,	#005h,	_spi_write_err
_spi_write_busy:
				call	spi_read_byte
				jz	_spi_write_busy	;wait for busy

				setb	cy
				ret
_spi_write_err:
				clr	cy
				ret
;--- send 256 bytes block from dptr and accumulate ccitt crc16 ---
_spi_write_data:
				mov	b,	#000h
_spi_write_data_l1:
				push	b
				movx	a,	@dptr
				call	spi_send_byte
				movx	a,	@dptr
				call	ccitt_crc
				pop	b
				inc	dptr
				djnz	b,	_spi_write_data_l1
				ret
;--- wait for data block start ---
spi_wait_data_r2:
				call	spi_read_response	;ждем ответа R1
				jnz	_spi_wait_data	;ошибка выполнения команды
spi_wait_data:
				call	spi_read_response	;ждем ответа R1 или R2
				jnz	_spi_wait_data	;ошибка выполнения команды

				call	spi_read_response	;ждем готовности карты
				cjne	a,	#0FEh,	_spi_wait_data	;получен не заголовок блока данных
				setb	cy
				ret
_spi_wait_data:
				clr	cy
				ret

;--- read the response from sd-card ---
spi_read_response:
				call	spi_read_byte		;читаем шину
				cpl	a			;инверсия для проверки на #0FFh
				jz	spi_read_response	;если #000h (а фактически #0FFh), то ждем
				cpl	a			;инверсия, для восстановления фактического значения
				ret				;вернемся

;--- in acc command to be send, and param[1..4] - parameters to be send ---
spi_send_command:
				setb	_cs			;отключимся от карты
				push	acc			;запомним номер команды
				call	spi_sync_byte		;межоперационное пространство
				clr	_cs			;подключимся к карте
				call	spi_sync_byte		;синхронизируемся
				pop	acc			;восстановим номер команды
				orl	a,	#040h		;установим префикс
				call	spi_send_byte		;отошлем команду
				mov	a,	param1		;получим параметр #1
				call	spi_send_byte		;отошлем параметр #1
				mov	a,	param2		;получим параметр #2
				call	spi_send_byte		;отошлем параметр #2
				mov	a,	param3		;получим параметр #3
				call	spi_send_byte		;отошлем параметр #3
				mov	a,	param4		;получим параметр #4
				call	spi_send_byte		;отошлем параметр #4
				mov	a,	#095h		;CRC7 для команды "0" (idle), остальные команды без проверки
				sjmp	spi_send_byte		;отошлем CRC7

;--- прием и синхронизация ведется при высоком уровне MOSI
spi_sync_byte:
spi_read_byte:
				mov	a,	#0FFh		;во время приема идет передача "1"

;--- прием и передача идут одновременно, в аккумуляторе данные для передачи и результат приема ---
spi_send_byte:
				mov	spdr,	a		;загрузим передатчик
_spi_send_byte:
				mov	a,	spsr		;регистр флагов SPI
				anl	a,	#spif		;проверим флаг готовности
				jz	_spi_send_byte	;если передача не завершена, то проверяем снова
				mov	a,	spdr		;отсылка прошла успешно, в регистре данных результат приема
				ret				;вернемся
В общем, перед CMD0 надо 10 штук FF кинуть(!!!), по документации это что-то типа синхронизации. Потом CMD0 и ждем response. Потом CMD1 и опять response. Теперь карта должна войти в нормальный режим работы по SPI, т.е. можно читать, писать, идентифицировать и т.п.
И еще, на МК надо корректно настроить фазы синхронизации относительно данных, в доке это описано. Для S8252 это через SPCR (SPI control register) делается.

Romanich, пиши о результатах ;)
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Re: эээх... видимо все наступают на одни и те же грабли...

Post by Romanich »

jdigreze wrote: Romanich, пиши о результатах ;)
Как только разбирусь и попробую - обязательно напишу!
Спасибо за код!
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Оказалась совместимой!

Post by Romanich »

Ура!!! Получилось!!!
В процессе написания программы было несколько ошибок:

1) Команда 17 - это не 0x57, а 0x51 !!! (по инерции забыл что 17=0x11 и прибавил к 0x40+0x17, забыв что в десятичной записано)
2) Неверно была написаны инициализация и сброс MMC
3) Ясного понимания раньше небыло в алгоритмах перехвата данных и статуса от MMC+
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Оказалась совместимой!

Post by Romanich »

Вот мой рабочий код:

Code: Select all

/*
Фрагмент кода, демонстрирующий работу с 128MB MMCplus Memory Card Kingston technology в режиме SPI
Карта отвечает спецификации MMC 4.0 при обратной совместимости с прежними моделями MMC

Используются самые КРАЙНИЕ контакты:

1 xCS Выбор кристалла
2 DI Входные данные
3 GND Земля
4 VDD Питание +3.3V
5 SCLK Синхронизация
6 GND Земля
7 DO Выходные данные
8 Reserv Подтянуть к +3.3V резистором 4.7кОм
9 Reserv Подтянуть к +3.3V резистором 4.7кОм

Остальные контакты(10,11,12,13 Reserv) оставить свободными
*/

unsigned char Buffer[512]; //Буфер для сектора

void MMCCS(unsigned long cs) //Выбор чипа MMC+
{
 <используем софтварное управление ножкой выбора кристалла, так удобнее :)>
}

unsigned char SPI(unsigned char b) //Передача байта по SPI и тут же его приём
{
 <ждем пока буфер передачи не освободится>
 <передаём по SPI байт b>
 <ждём пока буфер приёма не заполнится>
 <читаем байт из SPI - и возвращаем его значение return>
}

void InSector(unsigned long Address) //Чтение сектора 512 байт
{
 unsigned char b;
 unsigned int i;
 MMCCS(0);
 SPI(0x51); //Код команды чтения одиночного сектора
 SPI(Address>>24);
 SPI(Address>>16);
 SPI(Address>> 8);
 SPI(Address    );
 b=SPI(0xFF);
 while(b==0xFF) b=SPI(0xFF); //Опрашиваем пока не встретится что-нибудь отличное от 0xFF
 while(b!=0xFF) b=SPI(0xFF); //Опрашиваем пока не встретится 0xFF
 while(b==0xFF) b=SPI(0xFF); //Опрашиваем пока не встретим что-нибудь отличное от 0xFF - ожидаем что 0xFE :)
 for(i=0;i<512;i++) Buffer[i]=SPI(0xFF); //Теперь MMC+ готова передать 512 байт ЧИСТЫХ данных
 while(SPI(0xFF)!=0xFF); //Опрашиваем пока не встретится 0xFF (тут и два байта CRC замаятся - фиг с ними :)
 MMCCS(1);
}

void MMCSpeedSlow(void) //медленная скорость по SPI
{
 <настраиваем клок SPI на малую частоту (желательно 0.4МГц)>
}

void MMCSpeedFast(void) //быстрая скорость по SPI
{
 <настраиваем клок SPI на высокую частоту (до 20МГц?)>
}

int main(void)
{
 unsigned char b;
 unsigned long i;
 <энаблим SPI>
 <настраиваем его как мастер,запрещаем прерывания от SPI>
 <для MMC+ главное чтоб запись битов происходила по возрастанию клока, тоесть POL=0 & PHA=1 или POL=1 & PHA=0>
 //Начинаем работать с MMC+
 MMCSpeedSlow(); //Устанавливаем медленную скорость обмена с MMC+
 MMCCS(0);
 for(i=0;i<10;i++) SPI(0xFF); //10 байтов 0xFF
 MMCCS(1);
 for(i=0;i<2;i++) SPI(0xFF); //2 байта 0xFF
 //Даём команду сброса (CMD0)
 MMCCS(0);
 SPI(0x40); //Код команды сброса
 SPI(0);
 SPI(0);
 SPI(0);
 SPI(0);
 b=SPI(0x95); //CRC здесь обязателен!
 while(b==0xFF) b=SPI(0xFF); //Опрашиваем пока не встретится что-нибудь отличное от 0xFF
 while(b!=0xFF) b=SPI(0xFF); //Опрашиваем пока не встретится 0xFF
 MMCCS(1);
 //А теперь инициализируем карту пока из спячки не выйдет (CMD1)
 MMCCS(0);
 Again:
 SPI(0x41); //Код команды инициализации
 SPI(0);
 SPI(0);
 SPI(0);
 SPI(0);
 b=SPI(0xFF);
 while(b==0xFF) b=SPI(0xFF); //Опрашиваем пока не встретится что-нибудь отличное от 0xFF
 while(SPI(0xFF)!=0xFF); //Опрашиваем пока не встретится 0xFF
 if(b!=0) goto Again; //Если карта из спячки не проснулась то заново посылаем команду
 MMCCS(1);
 MMCSpeedFast(); //Устанавливаем быструю скорость обмена с MMC+
 /*
 Далее ради примера читаем ВСЕ сектора MMC+ карточки (128МБ = 262144 сектора)
 Аргумент в функции InSector - адрес, должен быть кратен 512,
 т.к. по умолчанию длина считываемых байт равна размеру сектора, а пересекать сектора при чтении нельзя!
 */
 for(i=0;i<(128*1024*1024/512);i++) InSector(i<<9);
}
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

функция записи для MMC+

Post by Romanich »

Разобрался и с записью. Ничего сложного :)
Вот рабочий код:

Code: Select all

void OutSector(unsigned long Address) //Запись сектора 512 байт
{
 unsigned char b;
 unsigned int i;
 MMCCS(0);
 SPI(0x58); //Код команды записи одиночного сектора
 SPI(Address>>24);
 SPI(Address>>16);
 SPI(Address>> 8);
 SPI(Address    );
 b=SPI(0xFF);
 while(b==0xFF) b=SPI(0xFF); //Опрашиваем пока 0xFF
 while(b!=0xFF) b=SPI(0xFF); //Опрашиваем пока не 0xFF
 SPI(0xFE); //Сообщаем MMC+ что счас будем записывать
 for(i=0;i<512;i++) SPI(Buffer[i]); //Записываем в MMC+ 512 байт данных
 SPI(0xFF); //Без этой фигни не работает! CRC?
 SPI(0xFF);
 SPI(0xFF);
 while(SPI(0xFF)!=0xFF); //Опрашиваем не 0xFF
 MMCCS(1);
}
На самом деле моя MMC+ не ровно 128МБ, а 122,5МБ с точностью до байта! Выяснилось когда хотел записать в самый последний сектор(чтоб FAT не разрушить) - возникла ошибка записи и чтения - стало ясно что сектор с номером 262143 НЕ существует! Последний доступный сектор ¹250879

А вообще от себя добавлю, что MMC+ карточки - КРУТО! :)
Маленькие, удобные, быстрые, много-Erse/Write'абельные :)
Очень полезны для хранения больших объёмов информации (в основном мультимедийной)
jdigreze
God
Posts: 1388
Joined: 02 Jan 2006 02:28
Location: Abakan

Post by jdigreze »

Без этой фигни не работает! CRC?
Именно CRC. Посмотри как в моем исходнике считается. ;)
Кстати, там полином CCITT, но инициализируется 0x0000.
User avatar
Shaos
Admin
Posts: 24014
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: функция записи для MMC+

Post by Shaos »

Romanich wrote: На самом деле моя MMC+ не ровно 128МБ, а 122,5МБ с точностью до байта! Выяснилось когда хотел записать в самый последний сектор(чтоб FAT не разрушить) - возникла ошибка записи и чтения - стало ясно что сектор с номером 262143 НЕ существует! Последний доступный сектор ¹250879
Ну они (впрочем как и все производители винчестеров) считают мегабайтом не то в чём 1024 килобайта, а где есть 1000000 байт - умножь 250880 на 512 и получишь число 128 450 560 (те самые 128M что значатся на карточке).
Я тут за главного - если что шлите мыло на me собака shaos точка net
Romanich
Banned
Posts: 608
Joined: 12 Oct 2006 16:44

Post by Romanich »

jdigreze wrote: Именно CRC
Насколько я знаю, что в режиме SPI правильный CRC нужен только в CMD0, а он уже всеми кому не лень подсчитан(0x95)