SDカードにFT245RL経由でようやく書き込みができました。

ロジアナで見たらFT245RLの信号が汚いのでAVRでやろうかと思ったけれど、これ以上SDカードに時間をかけるのも嫌と思って、FT245RLの制御をもうちょっと頑張ってみた。

まず、クロックが汚いので、ウェイトを入れることにした。time.sleep()の精度は4 msなんでしたっけ。クロックの変化の間に、4 msよりも小さいwaitをかけてみたら、6 msくらいの間が入っていい感じになった。綺麗になったクロックがこちら。

それで次の問題を探したら、どうもリセットコマンドCMD0の後に電源が落ちてるぽい。ので、リセットを二回連続で発行してみた。二回目のリセットの後は電源が落ちなくなったようだが、それでもうまく読み書きができない。根本的に解決をしようと、電源をFT245RLのIOピンからでなく、3V3OUTピンから頂くことにしました。そしたら書き込みできるようになりました。

無事、SEND_STATUS CMD13の返り値が0になった様子。


512
crc: 0x00
< 0
0b11100101 data accepted.
wait for releasing busy state (DO = 1)
cmd13, SEND_STATUS
< 4d
< 0
< 1
  > ff
  > 0
r2: 0
wait 8bit with CS = 1: ff

セクタ0を"Hello, world!"で埋めれたのをwindows 7上でダンプして確認。

pythonコードはこちら


from pylibftdi import BitBangDevice, Bus
import time

class sd(object):
    """
    The FT245R is wired to the SD as follows:
       DB0 to DI
       DB1 to D0
       DB2 to CLK
       DB3 to CS
       DB4 to POW
    """
    DI = Bus(0)
    DO = Bus(1)
    CLK = Bus(2)
    CS = Bus(3)
    POW = Bus(4)

    def __init__(self, device):
        # The Bus descriptor assumes we have a 'device'
        # attribute which provides a port
        self.device = device

    def _trigger(self, n = 1):
        "generate a H->L"
        for _ in range(n):
            #self.CLK = 0
            self.CLK = 1
            self.CLK = 0

    def _getDO(self): #read when clk down -> up mode 0
        #self.CLK = 0
        self.CLK = 1 #up
        time.sleep(0.0001)
        do = self.DO #read
        self.CLK = 0
        return do

    def _putDI(self, di):
        #self.CLK = 0
        self.DI = di #write
        self.CLK = 1 #up
        time.sleep(0.0001)
        self.CLK = 0

    def _write_raw(self, byte, l = 8):
        for i in range(l):
            di = (byte >> (l-1-i)) & 0b1
            self._putDI(di)
        print "< %x" % byte

#        print

    def _read_raw(self, l = 8):
        res = 0
        for i in range(l):
            do = self._getDO()
            #print "%d" % do
            res += do << (l-1-i)

        return res

    def read_res(self, l = 1):
        #print "read_res (timeout >= 8 byte)"
        for i in range(8):
            res = self._read_raw()
            print "  > %x" % res
            if res != 0xFF:
                for _ in range(l-1):
                    res = (res << 8) + self._read_raw()
                break

        return res

    def _wait_sd(self):
        self.CS = 1
        print "wait 8bit with CS = 1: %x" % self._read_raw(8)
        #self._trigger(8*8)
        self.CS = 0

    def write_cmd(self, cmd, addr, crc): #cmd 6bit, addr 8x4bit, crc7bit
        #cmd
        self._write_raw(0b01000000 + cmd)
        #addr
        #for i in range(4):
    #    self._write_raw((addr >> (3-i)*8) & 0xff)
        self._write_raw(addr, 8*4)
        #CRC
        self._write_raw(0b1 + (crc << 1))
        #up
        #self.DI = 1 #stop bit is always 1
        #print "DI after cmd: ", self.DI

    def init_spi(self):
        self.POW = 0
        self.DI = 0
        self.CS = 0
        self.CLK = 0
        time.sleep(0.1)

        print "power on and wait > 1ms"
        self.POW = 1
        self.CS = 0
        #self._trigger(100)#more than 74 times
        time.sleep(0.001)
        print "dummy clock >= 74 with CS and DI high"
        self.CS = 1
        self.DI = 1
        self._trigger(100)#more than 74 times
        #self._wait_sd()
        #time.sleep(0.001)

        print "CMD0 with CS low"
        self.CS = 0
        while 1:
            self.write_cmd(0, 0, 0b1001010)
            if self.read_res() == 0b1:
                print "cmd0 ok (response == 1)"
                #self._wait_sd()
                break


        self._trigger(8)#more than 74 times

        print "CMD0 with CS low"
        self.CS = 0
        while 1:
            self.write_cmd(0, 0, 0b1001010)
            if self.read_res() == 0b1:
                print "cmd0 ok (response == 1)"
                #self._wait_sd()
                break

        #cmd 8?
        #print "cmd 8 (chk SD Ver.2)"
        #self.write_cmd(8, 0x000001aa, 0x43)
        #if self.read_res() == 0x01:
        #    print "SDC V2"
        #    if self.read_res(4) != 0x000001aa:
        #        print "card mismatch"
        #        exit()


        #self._wait_sd()

        type = 1 #if cmd1 is used, host cannot distinguish MMC or SD
        #cmd1
        if type == 0:
            print "cmd1"
            while 1:
                self.write_cmd(1, 0, 0)
                if self.read_res() == 0b0:
                    break
            print "cmd1 ok (res == 0)"
            self._wait_sd()

        #cmd55
        else:
            print "ACMD41"
            while 1:
                #print "CMD55"
                self.write_cmd(55, 0, 0)
                if self.read_res() == 0b1:
                    #print "wait 8bit..: %x" % self._read_raw(8)
        #print "cmd55 ok (response == 1)"
                    #print "CMD41"
                    self.write_cmd(41, 0x40000000, 0)
                    if self.read_res() == 0b0:
                        print "ACMD41 ok (response == 0)"
                        #print "wait 8bit..: %x" % self._read_raw(8)
                        break


    """
    def _wait_DO_0(self, timeout = 8):
        for i in range(timeout*8):
            do = self._getDO()
            print "%d" % do,
            if (do == 0):
                break

            if (i == timeout*8-1):
                print "timeout"
                sys.exit()
                """

    def single_read(self, addr, l = 512):
        #cmd17
        print "single read CMD17, addr: %x" % addr, "len: %d" % l
        while 1:
            self.write_cmd(17, addr, 0)
            if self.read_res() == 0b0:
                print "cmd17 ok, response: 0"
                break

        #start 0xfe
        while (self.read_res() != 0xfe): #_wait_DO_0()
            pass

        print "start data"
        #f = open('sdout.bin', 'wb')

        #for i in range(16):#byte
        #    for j in range(32):#byte
        for i in range(l):
            c = self._read_raw()
            print "%x" % c,
            #f.write(chr(c))
            if i%32==31:
                print

        #f.close()

        print "crc: %x" % self._read_raw(8*2)
        self._wait_sd()
#        print "wait 8bit..: %x" % self._read_raw(8)



    def single_write(self, addr, l = 512):
        print "single write CMD24, addr: %d" % addr, "len: %d" % l
        while 1:
            self.write_cmd(24, addr, 0)
            if self.read_res() == 0b0:
                print "cmd24 ok, response: 0"
                break

        print "wait >= 1 byte"
        self._trigger(8)

        self._write_raw(0xfe) #data token
        print "send data"
        #f = open('sdout.bin', 'wb')
        text = "Hello, world!"
        for i in range(l / len(text)):
            for c in text:
                self._write_raw(ord(c))
                print c, bin(ord(c))
            print i

        #fill 0xff
        #for i in range(512):#range(l % len(text)):

        self._write_raw(0, (l % len(text))*8)
        print (l/len(text))*len(text) + l % len(text)
        #crc
        #print "crc: %x" % self._read_raw(8*2)
        print "crc: 0x00"
        self._write_raw(0, 8*2)
        self.DI = 1
        #self._trigger(8*2)

        #responce
        #self._wait_DO_0()
        res = self._read_raw()
        if (res & 0b00011111) == 0b00101:
            print bin(res), "data accepted."
        else:
            print bin(res), "error."

        print "wait for releasing busy state (DO = 1)"
        while self._read_raw() != 0x0:
            print "  > busy..."

        #print "busy? %x" % res
        #res = self._read_raw()
        #print "busy? %x" % res
        #res = self._read_raw()
        #print "busy? %x" % res
        #        print "do is 1"
        #        break

        #self._wait_sd()
        #self._wait_sd()

        #print "wait 8bit..: %x" % self._read_raw()
        #print "wait 8bit..: %x" % self._read_raw()
        #self.get_status()


    def set_blocklen(self, l):
        #cmd16
        print "set blocklen cmd16 len: %d" % l
        while 1:
            self.write_cmd(16, l, 0)
            if self.read_res() == 0b0:
                break

        print "cmd16 ok, response: 0"


    def multi_read(self, addr):
        #cmd18
        print "cmd18, addr: %d" % addr
        while 1:
            self.write_cmd(18, addr, 0)
            if self.read_res() == 0b0:
                break

        print "cmd18 ok, response: 0"
        f = open('sdout.bin', 'wb')

        for j in range(10):
            self._wait_DO_0()
            print "start data"
            for i in range(512):#byte
                c = self._read_raw(8)
                print "%x" % c,
                f.write(chr(c))
                if i%32==0:
                    print
            #crc
            print "crc:", self._read_raw(8*2)

        f.close()
        self.POW = 0


    def get_status(self):
        print "cmd13, SEND_STATUS"
        self.write_cmd(13, 0, 0)
        #r2
        print "r2: %x" % self.read_res(2) #p.96, 92
        #self._wait_sd()
        self._wait_sd()
        print "wait 8bit..: %x" % self._read_raw(8)

    def get_ocr(self):
        print "cmd58, READ_OCR"
        self.write_cmd(58, 0, 0)
        #r3
        print "r3: %x" % self.read_res(5) #p.37, 92
        print "wait 8bit..: %x" % self._read_raw(8)

    def get_cid(self):
        print "cmd10, SEND_CID"
        #r1, 128/8 byte
        while 1:
            self.write_cmd(10, 0, 0)
            r1 = self.read_res() #p.38, 92
            print "r1: %x" % r1
            if r1 == 0:
                break

        while (self.read_res() != 0xfe):
            pass

        for i in range(16):
            c = self._read_raw()
            print "%x" % c,

        print
        print "crc: %x" % self._read_raw(8*2)
        print "wait 8bit..: %x" % self._read_raw(8)

    def get_csd(self):
        print "cmd9, SEND_CSD"
        self.write_cmd(9, 0, 0)
        #r1, 128/8 byte = 16 byte
        print "r2: %x" % self.read_res(2) #p.39

    def power_off(self):
        self.POW = 0

def display(device_id=None):

    with BitBangDevice(device_id) as bb:
        # the actual baudrate is 16x this in bitbang mode...
        #bb.baudrate = 307200, 153600, 76800, 38400, 19200, 9600, 4800
        bb.baudrate = 9600
        print bb.baudrate
        #bb.ftdi_fn.ftdi_set_latency_timer(1)
        bb.direction = 0b00011101
        #bb.port = 0x00 #output (not pullup)
        sdc = sd(bb)
        sdc.init_spi()
        print "complete init"

        sdc.single_write(0)
        sdc.get_status()
        sdc.single_read(0x0)
        sdc.get_status()

if __name__ == '__main__':
    import sys
    display()