/* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013-2019 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "py/runtime.h" #include "py/mphal.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" #include "sdcard.h" //#include "propeller.h" #include "platform.h" //RJA #define drvl_(x) pinlow(x) #define drvh_(x) pinhigh(x) //RJA moving this to platform.h //#define PIN_SS 60 //#define PIN_MOSI 59 //#define PIN_MISO 58 //#define PIN_CLK 61 //#define PIN_SD_TEST1 (58) /* if high, assume card present */ //#define PIN_SD_TEST2 (60) /* if pullup on this pin, assume card present */ #if MICROPY_HW_ENABLE_SDCARD #define PYB_SDMMC_FLAG_SD (0x01) #define PYB_SDMMC_FLAG_SDHC (0x02) #define PYB_SDMMC_FLAG_ACTIVE (0x04) static uint8_t pyb_sdmmc_flags; static uint8_t isSDHC; /* SPI support code for P2 */ static uint64_t getcycles() { uint32_t hi, lo, h2; again: h2 = getcnth(); lo = getcnt(); hi = getcnth(); if (hi != h2) goto again; return ((uint64_t)hi) << 32 | lo; } #define spi_short_delay() waitcnt(getcnt() + 10) static int spi_read() { int i, r; r = 0; for (i = 0; i < 8; i++) { drvl_(PIN_CLK); drvh_(PIN_CLK); spi_short_delay(); r = (r << 1) | getpin(PIN_MISO); } return r; } // timeout is 100ms if we use a 160 MHz clock #define TIMEOUT 16000000 static int spi_chktimeout(uint64_t endtime) { uint64_t now = getcycles(); if (now > endtime) { return 1; } return 0; } static int spi_readresp(void) { int r; uint64_t endtime = getcycles() + TIMEOUT; while (1) { r = spi_read(); if (r != 0xff) break; if (spi_chktimeout(endtime)) { return -1; } } return r; } static void spi_send(int outv) { int i; for (i = 0; i < 8; i++) { drvl_(PIN_CLK); if (outv & 0x80) { drvh_(PIN_MOSI); } else { drvl_(PIN_MOSI); } outv <<= 1; drvh_(PIN_CLK); } drvh_(PIN_MOSI); } // // Wait until card stops returning busy // int spi_busy(void) { int r; uint64_t endtime = getcycles() + 10*TIMEOUT; while (1) { r = spi_read(); if (r) break; if (spi_chktimeout(endtime)) { return -1; } } return r; } // calculate crc for commands static int crc7(int crc, int val) { int i; for (i = 0; i < 8; i++) { crc = crc << 1; if ( (crc^val) & 0x80 ) { crc ^= 0x09; } val = val<<1; } return crc & 0x7f; } static int crc_send(int crc, int byte) { crc = crc7(crc, byte); spi_send(byte); return crc; } static int spi_cmd(int index, int arg) { int crc = 0; drvl_(PIN_SS); (void)spi_read(); crc = crc_send(crc, 0x40+index); crc = crc_send(crc, (arg >> 24) & 0xff); crc = crc_send(crc, (arg >> 16) & 0xff); crc = crc_send(crc, (arg >> 8) & 0xff); crc = crc_send(crc, arg & 0xff); // send the CRC crc = (crc<<1) | 1; spi_send(crc); return spi_readresp(); } static void spi_endcmd(void) { drvh_(PIN_SS); } static int sdcard_wait_for_status(void) { int tries = 0; int i; while (tries++ < 100) { spi_cmd(13, 0); i = spi_readresp(); spi_endcmd(); if (i == 0) { return 0; } } return -1; } /* main code */ void sdcard_init(void) { // Set SD/MMC to no mode and inactive pyb_sdmmc_flags = 0; // configure SD GPIO _dirl(PIN_MISO); _dirh(PIN_CLK); _pinh(PIN_SS); } bool sdcard_is_present(void) { int i; if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE) { return true; } i = _pinr(PIN_SD_TEST1); if (i == 0) return 1; // check for a pull-up on pin 60 _pinl(PIN_SD_TEST2); _waitx(200); // wait > 1us _pinf(PIN_SD_TEST2); // does a fltl _waitx(1000); // wait > 5 us i = _pinr(PIN_SD_TEST2); return i; } bool sdcard_power_on(void) { int i; int x; if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE) { return true; } _pinh(PIN_MOSI); for (i = 0; i < 600; i++) { (void) spi_read(); } i = spi_cmd(0, 0); spi_endcmd(); if (i < 0) { return false; } i = spi_cmd(8, 0x1aa); spi_endcmd(); if (i < 0) { return false; } while (1) { spi_cmd(55, 0); i = spi_cmd(41, 0x40000000); if (i == 0) break; if (i < 0) { return false; } } i = spi_cmd(58, 0); x = spi_read(); spi_endcmd(); isSDHC = (x >> 6) & 1; if (i) { // printf("could not init card\n"); return false; } else { // printf("cmd58 returned 0x%x\n", x); } i = sdcard_wait_for_status(); if (i < 0) { //printf("timeout waiting for card\n"); return false; } pyb_sdmmc_flags |= PYB_SDMMC_FLAG_ACTIVE; return true; } void sdcard_power_off(void) { pyb_sdmmc_flags &= ~PYB_SDMMC_FLAG_ACTIVE; _pinl(PIN_MOSI); _dirl(PIN_MOSI); } uint64_t sdcard_get_capacity_in_bytes(void) { uint64_t c_size; uint8_t csd[16]; // 16 bytes int i; if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { return 0; } i = spi_cmd(9, 0); i = spi_readresp(); if (i < 0) { //iprintf("csd read timed out\n"); spi_endcmd(); return 0; } for (i = 0; i < 16; i++) { csd[i] = spi_read(); } (void)spi_read(); (void)spi_read(); spi_endcmd(); c_size = (((uint32_t)csd[7] & 0x3f) << 16) | ((uint32_t)csd[8] << 8) | csd[9]; c_size = (c_size+1) * (512ULL * 1024ULL); return c_size; } // read one block static int sdcard_read_one_block(uint8_t *dest, uint32_t offset) { int r, i; if (!isSDHC) { offset = offset << 9; // multiply by 512 for non SDHC cards } spi_cmd(17, offset); r = spi_readresp(); if (r < 0) return r; for (i = 0; i < 512; i++) { *dest++ = spi_read(); } spi_read(); spi_read(); spi_endcmd(); return 0; } // read one block static int sdcard_write_one_block(const uint8_t *src, uint32_t offset) { int r, i; if (!isSDHC) { offset = offset << 9; // multiply by 512 for non SDHC cards } spi_cmd(24, offset); spi_send(0xfe); for (i = 0; i < 512; i++) { spi_send(*src++); } spi_read(); spi_read(); r = spi_readresp(); if ((r & 0x1f) != 5) { return -42; } r = spi_busy(); spi_endcmd(); if (r < 0) return r; return 0; } mp_uint_t sdcard_read_blocks(uint8_t *dest, uint32_t start_block, uint32_t num_blocks) { uint32_t i; int r; // check that SD card is initialised if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { return -1; } //printf("sdcard_read_blocks: start=%lu num=%lu\n", start_block, num_blocks); for (i = 0; i < num_blocks; i++) { r = sdcard_read_one_block(dest, start_block); if (r) return r; dest += 512; start_block++; } return 0; } mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t start_block, uint32_t num_blocks) { uint32_t i; int r; // check that SD card is initialised if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { return -1; } for (i = 0; i < num_blocks; i++) { r = sdcard_write_one_block(src, start_block); if (r) return r; src += 512; start_block++; } return 0; } /******************************************************************************/ // MicroPython bindings // // Expose the SD card or MMC as an object with the block protocol. // There are singleton SDCard/MMCard objects #if MICROPY_HW_ENABLE_SDCARD const mp_obj_base_t pyb_sdcard_obj = {&pyb_sdcard_type}; #endif #if MICROPY_HW_ENABLE_MMCARD const mp_obj_base_t pyb_mmcard_obj = {&pyb_mmcard_type}; #endif #if MICROPY_HW_ENABLE_SDCARD STATIC mp_obj_t pyb_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); #if MICROPY_HW_ENABLE_MMCARD if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_MMC) { mp_raise_ValueError("peripheral used by MMCard"); } #endif pyb_sdmmc_flags |= PYB_SDMMC_FLAG_SD; // return singleton object return MP_OBJ_FROM_PTR(&pyb_sdcard_obj); } #endif #if MICROPY_HW_ENABLE_MMCARD STATIC mp_obj_t pyb_mmcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); #if MICROPY_HW_ENABLE_SDCARD if (pyb_sdmmc_flags & PYB_SDMMC_FLAG_SD) { mp_raise_ValueError("peripheral used by SDCard"); } #endif pyb_sdmmc_flags |= PYB_SDMMC_FLAG_MMC; // return singleton object return MP_OBJ_FROM_PTR(&pyb_mmcard_obj); } #endif STATIC mp_obj_t sd_present(mp_obj_t self) { return mp_obj_new_bool(sdcard_is_present()); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(sd_present_obj, sd_present); STATIC mp_obj_t sd_power(mp_obj_t self, mp_obj_t state) { bool result; if (mp_obj_is_true(state)) { result = sdcard_power_on(); } else { sdcard_power_off(); result = true; } return mp_obj_new_bool(result); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(sd_power_obj, sd_power); STATIC mp_obj_t sd_info(mp_obj_t self) { if (!(pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE)) { return mp_const_none; } #ifdef FIXME uint32_t card_type; uint32_t log_block_nbr; uint32_t log_block_size; { card_type = sdmmc_handle.sd.SdCard.CardType; log_block_nbr = sdmmc_handle.sd.SdCard.LogBlockNbr; log_block_size = sdmmc_handle.sd.SdCard.LogBlockSize; } // cardinfo.SD_csd and cardinfo.SD_cid have lots of info but we don't use them mp_obj_t tuple[3] = { mp_obj_new_int_from_ull((uint64_t)log_block_nbr * (uint64_t)log_block_size), mp_obj_new_int_from_uint(log_block_size), mp_obj_new_int(card_type), }; return mp_obj_new_tuple(3, tuple); #else printf("*** warning: sd_info ignored\n"); return mp_const_none; #endif } STATIC MP_DEFINE_CONST_FUN_OBJ_1(sd_info_obj, sd_info); // now obsolete, kept for backwards compatibility STATIC mp_obj_t sd_read(mp_obj_t self, mp_obj_t block_num) { uint8_t *dest = m_new(uint8_t, SDCARD_BLOCK_SIZE); mp_uint_t ret = sdcard_read_blocks(dest, mp_obj_get_int(block_num), 1); if (ret != 0) { m_del(uint8_t, dest, SDCARD_BLOCK_SIZE); nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "sdcard_read_blocks failed [%u]", ret)); } return mp_obj_new_bytearray_by_ref(SDCARD_BLOCK_SIZE, dest); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(sd_read_obj, sd_read); // now obsolete, kept for backwards compatibility STATIC mp_obj_t sd_write(mp_obj_t self, mp_obj_t block_num, mp_obj_t data) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); if (bufinfo.len % SDCARD_BLOCK_SIZE != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "writes must be a multiple of %d bytes", SDCARD_BLOCK_SIZE)); } mp_uint_t ret = sdcard_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_BLOCK_SIZE); if (ret != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "sdcard_write_blocks failed [%u]", ret)); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(sd_write_obj, sd_write); STATIC mp_obj_t pyb_sdcard_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); mp_uint_t ret = sdcard_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_BLOCK_SIZE); return mp_obj_new_bool(ret == 0); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_sdcard_readblocks_obj, pyb_sdcard_readblocks); STATIC mp_obj_t pyb_sdcard_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); mp_uint_t ret = sdcard_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_BLOCK_SIZE); return mp_obj_new_bool(ret == 0); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_sdcard_writeblocks_obj, pyb_sdcard_writeblocks); STATIC mp_obj_t pyb_sdcard_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { mp_int_t cmd = mp_obj_get_int(cmd_in); switch (cmd) { case MP_BLOCKDEV_IOCTL_INIT: if (!sdcard_power_on()) { return MP_OBJ_NEW_SMALL_INT(-1); // error } return MP_OBJ_NEW_SMALL_INT(0); // success case MP_BLOCKDEV_IOCTL_DEINIT: sdcard_power_off(); return MP_OBJ_NEW_SMALL_INT(0); // success case MP_BLOCKDEV_IOCTL_SYNC: // nothing to do return MP_OBJ_NEW_SMALL_INT(0); // success case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: return MP_OBJ_NEW_SMALL_INT(sdcard_get_capacity_in_bytes() / SDCARD_BLOCK_SIZE); case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: return MP_OBJ_NEW_SMALL_INT(SDCARD_BLOCK_SIZE); default: // unknown command return MP_OBJ_NEW_SMALL_INT(-1); // error } } STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_sdcard_ioctl_obj, pyb_sdcard_ioctl); STATIC const mp_rom_map_elem_t pyb_sdcard_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_present), MP_ROM_PTR(&sd_present_obj) }, { MP_ROM_QSTR(MP_QSTR_power), MP_ROM_PTR(&sd_power_obj) }, { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&sd_info_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&sd_read_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&sd_write_obj) }, // block device protocol { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&pyb_sdcard_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&pyb_sdcard_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&pyb_sdcard_ioctl_obj) }, }; STATIC MP_DEFINE_CONST_DICT(pyb_sdcard_locals_dict, pyb_sdcard_locals_dict_table); #if MICROPY_HW_ENABLE_SDCARD const mp_obj_type_t pyb_sdcard_type = { { &mp_type_type }, .name = MP_QSTR_SDCard, .make_new = pyb_sdcard_make_new, .locals_dict = (mp_obj_dict_t*)&pyb_sdcard_locals_dict, }; #endif #if MICROPY_HW_ENABLE_MMCARD const mp_obj_type_t pyb_mmcard_type = { { &mp_type_type }, .name = MP_QSTR_MMCard, .make_new = pyb_mmcard_make_new, .locals_dict = (mp_obj_dict_t*)&pyb_sdcard_locals_dict, }; #endif void sdcard_init_vfs(fs_user_mount_t *vfs, int part) { pyb_sdmmc_flags = (pyb_sdmmc_flags & PYB_SDMMC_FLAG_ACTIVE) | PYB_SDMMC_FLAG_SD; // force SD mode vfs->base.type = &mp_fat_vfs_type; vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL; vfs->fatfs.drv = vfs; vfs->fatfs.part = part; vfs->blockdev.readblocks[0] = MP_OBJ_FROM_PTR(&pyb_sdcard_readblocks_obj); vfs->blockdev.readblocks[1] = MP_OBJ_FROM_PTR(&pyb_sdcard_obj); vfs->blockdev.readblocks[2] = MP_OBJ_FROM_PTR(sdcard_read_blocks); // native version vfs->blockdev.writeblocks[0] = MP_OBJ_FROM_PTR(&pyb_sdcard_writeblocks_obj); vfs->blockdev.writeblocks[1] = MP_OBJ_FROM_PTR(&pyb_sdcard_obj); vfs->blockdev.writeblocks[2] = MP_OBJ_FROM_PTR(sdcard_write_blocks); // native version vfs->blockdev.u.ioctl[0] = MP_OBJ_FROM_PTR(&pyb_sdcard_ioctl_obj); vfs->blockdev.u.ioctl[1] = MP_OBJ_FROM_PTR(&pyb_sdcard_obj); } // disk utilities uint32_t get_fattime(void) { uint64_t seconds = getcycles() / 160000000; uint32_t dosdate; uint32_t month, day, year; uint32_t hh, mm, ss; ss = (uint32_t)seconds; year = 2019; month = 6; day = ss / 86400; if (day > 30) day = 31; ss = ss % 86400; hh = ss / 3600; ss = ss % 3600; mm = ss / 60; ss = ss % 60; dosdate = ((year-1980) << 25) | ((month+1)<<31) | ((day+1)<<16); dosdate |= (hh << 11) | (mm << 5) | (ss >> 1); return dosdate; } #endif // MICROPY_HW_ENABLE_SDCARD || MICROPY_HW_ENABLE_MMCARD