/** * @brief Reads the CSD. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] csd pointer to the CSD buffer * * @return The operation status. * @retval CH_SUCCESS the operation succeeded. * @retval CH_FAILED the operation failed. * * @notapi */ static bool_t read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) { unsigned i; uint8_t *bp, buf[16]; spiSelect(mmcp->config->spip); send_hdr(mmcp, cmd, 0); if (recvr1(mmcp) != 0x00) { spiUnselect(mmcp->config->spip); return CH_FAILED; } /* Wait for data availability.*/ for (i = 0; i < MMC_WAIT_DATA; i++) { spiReceive(mmcp->config->spip, 1, buf); if (buf[0] == 0xFE) { uint32_t *wp; spiReceive(mmcp->config->spip, 16, buf); bp = buf; for (wp = &cxd[3]; wp >= cxd; wp--) { *wp = ((uint32_t)bp[0] << 24) | ((uint32_t)bp[1] << 16) | ((uint32_t)bp[2] << 8) | (uint32_t)bp[3]; bp += 4; } /* CRC ignored then end of transaction. */ spiIgnore(mmcp->config->spip, 2); spiUnselect(mmcp->config->spip); return CH_SUCCESS; } } return CH_FAILED; }
/** * @brief Reads a block within a sequential read operation. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] buffer pointer to the read buffer * @return The operation status. * @retval FALSE the operation succeeded. * @retval TRUE the operation failed. * * @api */ bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) { int i; chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead"); chSysLock(); if (mmcp->state != MMC_READING) { chSysUnlock(); return TRUE; } chSysUnlock(); for (i = 0; i < MMC_WAIT_DATA; i++) { spiReceive(mmcp->spip, 1, buffer); if (buffer[0] == 0xFE) { spiReceive(mmcp->spip, MMC_SECTOR_SIZE, buffer); /* CRC ignored. */ spiIgnore(mmcp->spip, 2); return FALSE; } } /* Timeout.*/ spiUnselect(mmcp->spip); chSysLock(); if (mmcp->state == MMC_READING) mmcp->state = MMC_READY; chSysUnlock(); return TRUE; }
/** * @brief Writes a block within a sequential write operation. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] buffer pointer to the write buffer * @return The operation status. * @retval FALSE the operation succeeded. * @retval TRUE the operation failed. * * @api */ bool_t mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) { static const uint8_t start[] = {0xFF, 0xFC}; uint8_t b[1]; chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialWrite"); chSysLock(); if (mmcp->state != MMC_WRITING) { chSysUnlock(); return TRUE; } chSysUnlock(); spiSend(mmcp->spip, sizeof(start), start); /* Data prologue. */ spiSend(mmcp->spip, MMC_SECTOR_SIZE, buffer); /* Data. */ spiIgnore(mmcp->spip, 2); /* CRC ignored. */ spiReceive(mmcp->spip, 1, b); if ((b[0] & 0x1F) == 0x05) { wait(mmcp); return FALSE; } /* Error.*/ spiUnselect(mmcp->spip); chSysLock(); if (mmcp->state == MMC_WRITING) mmcp->state = MMC_READY; chSysUnlock(); return TRUE; }
/** * @brief Writes a block within a sequential write operation. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] buffer pointer to the write buffer * * @return The operation status. * @retval HAL_SUCCESS the operation succeeded. * @retval HAL_FAILED the operation failed. * * @api */ bool mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) { static const uint8_t start[] = {0xFF, 0xFC}; uint8_t b[1]; osalDbgCheck((mmcp != NULL) && (buffer != NULL)); if (mmcp->state != BLK_WRITING) { return HAL_FAILED; } spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */ spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */ spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */ spiReceive(mmcp->config->spip, 1, b); if ((b[0] & 0x1FU) == 0x05U) { wait(mmcp); return HAL_SUCCESS; } /* Error.*/ spiUnselect(mmcp->config->spip); spiStop(mmcp->config->spip); mmcp->state = BLK_READY; return HAL_FAILED; }
/** * @brief Reads a block within a sequential read operation. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] buffer pointer to the read buffer * * @return The operation status. * @retval CH_SUCCESS the operation succeeded. * @retval CH_FAILED the operation failed. * * @api */ bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) { int i; chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead"); if (mmcp->state != BLK_READING) return CH_FAILED; for (i = 0; i < MMC_WAIT_DATA; i++) { spiReceive(mmcp->config->spip, 1, buffer); if (buffer[0] == 0xFE) { spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer); /* CRC ignored. */ spiIgnore(mmcp->config->spip, 2); return CH_SUCCESS; } } /* Timeout.*/ spiUnselect(mmcp->config->spip); spiStop(mmcp->config->spip); return CH_FAILED; }
/** * @brief Reads a block within a sequential read operation. * * @param[in] mmcp pointer to the @p MMCDriver object * @param[out] buffer pointer to the read buffer * * @return The operation status. * @retval HAL_SUCCESS the operation succeeded. * @retval HAL_FAILED the operation failed. * * @api */ bool mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) { unsigned i; osalDbgCheck((mmcp != NULL) && (buffer != NULL)); if (mmcp->state != BLK_READING) { return HAL_FAILED; } for (i = 0; i < MMC_WAIT_DATA; i++) { spiReceive(mmcp->config->spip, 1, buffer); if (buffer[0] == 0xFEU) { spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer); /* CRC ignored. */ spiIgnore(mmcp->config->spip, 2); return HAL_SUCCESS; } } /* Timeout.*/ spiUnselect(mmcp->config->spip); spiStop(mmcp->config->spip); mmcp->state = BLK_READY; return HAL_FAILED; }
/* * Application entry point. */ int main(void) { unsigned i; /* * System initializations. * - HAL initialization, this also initializes the configured device drivers * and performs the board-specific initializations. * - Kernel initialization, the main() function becomes a thread and the * RTOS is active. */ halInit(); chSysInit(); /* * Prepare transmit pattern. */ for (i = 0; i < sizeof(txbuf); i++) txbuf[i] = (uint8_t)i; /* Starting driver for test, DSPI_B I/O pins setup.*/ spiStart(&SPID2, &ls_spicfg); SIU.PCR[102].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SCK */ SIU.PCR[103].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SIN */ SIU.PCR[104].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* SOUT */ SIU.PCR[105].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* PCS[0] */ SIU.PCR[106].R = PAL_MODE_OUTPUT_ALTERNATE(1); /* PCS[1] */ /* Testing sending and receiving at the same time.*/ spiExchange(&SPID2, 4, txbuf, rxbuf); spiExchange(&SPID2, 32, txbuf, rxbuf); spiExchange(&SPID2, 512, txbuf, rxbuf); /* Testing clock pulses without data buffering.*/ spiIgnore(&SPID2, 4); spiIgnore(&SPID2, 32); /* Testing sending data ignoring incoming data.*/ spiSend(&SPID2, 4, txbuf); spiSend(&SPID2, 32, txbuf); /* Testing receiving data while sending idle bits (high level).*/ spiReceive(&SPID2, 4, rxbuf); spiReceive(&SPID2, 32, rxbuf); /* Testing stop procedure.*/ spiStop(&SPID2); /* * Starting the transmitter and receiver threads. */ chThdCreateStatic(spi_thread_1_wa, sizeof(spi_thread_1_wa), NORMALPRIO + 1, spi_thread_1, NULL); chThdCreateStatic(spi_thread_2_wa, sizeof(spi_thread_2_wa), NORMALPRIO + 1, spi_thread_2, NULL); /* * Normal main() thread activity, in this demo it does nothing. */ while (TRUE) { chThdSleepMilliseconds(500); palTogglePad(PORT11, P11_LED2); } return 0; }
/** * @brief Performs the initialization procedure on the inserted card. * @details This function should be invoked when a card is inserted and * brings the driver in the @p MMC_READY state where it is possible * to perform read and write operations. * @note It is possible to invoke this function from the insertion event * handler. * * @param[in] mmcp pointer to the @p MMCDriver object * * @return The operation status. * @retval CH_SUCCESS the operation succeeded and the driver is now * in the @p MMC_READY state. * @retval CH_FAILED the operation failed. * * @api */ bool_t mmcConnect(MMCDriver *mmcp) { unsigned i; chDbgCheck(mmcp != NULL, "mmcConnect"); chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY), "mmcConnect(), #1", "invalid state"); /* Connection procedure in progress.*/ mmcp->state = BLK_CONNECTING; /* Slow clock mode and 128 clock pulses.*/ spiStart(mmcp->config->spip, mmcp->config->lscfg); spiIgnore(mmcp->config->spip, 16); /* SPI mode selection.*/ i = 0; while (TRUE) { if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01) break; if (++i >= MMC_CMD0_RETRY) goto failed; chThdSleepMilliseconds(10); } /* Try to detect if this is a high capacity card and switch to block addresses if possible. This method is based on "How to support SDC Ver2 and high capacity cards" by ElmChan.*/ uint8_t r3[4]; if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND, MMCSD_CMD8_PATTERN, r3) != 0x05) { /* Switch to SDHC mode.*/ i = 0; while (TRUE) { if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) == 0x01) && (send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND, 0x400001aa, r3) == 0x00)) break; if (++i >= MMC_ACMD41_RETRY) goto failed; chThdSleepMilliseconds(10); } /* Execute dedicated read on OCR register */ send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3); /* Check if CCS is set in response. Card operates in block mode if set.*/ if (r3[0] & 0x40) mmcp->block_addresses = TRUE; } /* Initialization.*/ i = 0; while (TRUE) { uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0); if (b == 0x00) break; if (b != 0x01) goto failed; if (++i >= MMC_CMD1_RETRY) goto failed; chThdSleepMilliseconds(10); } /* Initialization complete, full speed.*/ spiStart(mmcp->config->spip, mmcp->config->hscfg); /* Setting block size.*/ if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN, MMCSD_BLOCK_SIZE) != 0x00) goto failed; /* Determine capacity.*/ if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd)) goto failed; mmcp->capacity = mmcsdGetCapacity(mmcp->csd); if (mmcp->capacity == 0) goto failed; if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid)) goto failed; mmcp->state = BLK_READY; return CH_SUCCESS; /* Connection failed, state reset to BLK_ACTIVE.*/ failed: spiStop(mmcp->config->spip); mmcp->state = BLK_ACTIVE; return CH_FAILED; }
/** * @brief Performs the initialization procedure on the inserted card. * @details This function should be invoked when a card is inserted and * brings the driver in the @p MMC_READY state where it is possible * to perform read and write operations. * @note It is possible to invoke this function from the insertion event * handler. * * @param[in] mmcp pointer to the @p MMCDriver object * @return The operation status. * @retval FALSE the operation succeeded and the driver is now * in the @p MMC_READY state. * @retval TRUE the operation failed. * * @api */ bool_t mmcConnect(MMCDriver *mmcp) { unsigned i; bool_t result; chDbgCheck(mmcp != NULL, "mmcConnect"); chDbgAssert((mmcp->state != MMC_UNINIT) && (mmcp->state != MMC_STOP), "mmcConnect(), #1", "invalid state"); if (mmcp->state == MMC_INSERTED) { /* Slow clock mode and 128 clock pulses.*/ spiStart(mmcp->spip, mmcp->lscfg); spiIgnore(mmcp->spip, 16); /* SPI mode selection.*/ i = 0; while (TRUE) { if (send_command_R1(mmcp, MMC_CMDGOIDLE, 0) == 0x01) break; if (++i >= MMC_CMD0_RETRY) return TRUE; chThdSleepMilliseconds(10); } /* Try to detect if this is a high capacity card and switch to block * addresses if possible. * * This method is based on "How to support SDC Ver2 and high capacity cards" * by ElmChan. * * */ uint8_t r3[4]; if(send_command_R3(mmcp, MMC_CMDINTERFACE_CONDITION, 0x01AA, r3) != 0x05){ /* Switch to SDHC mode */ i = 0; while (TRUE) { if ((send_command_R1(mmcp, MMC_CMDAPP, 0) == 0x01) && (send_command_R3(mmcp, MMC_ACMDOPCONDITION, 0x400001aa, r3) == 0x00)) break; if (++i >= MMC_ACMD41_RETRY) return TRUE; chThdSleepMilliseconds(10); } /* Execute dedicated read on OCR register */ send_command_R3(mmcp, MMC_CMDREADOCR, 0, r3); /* Check if CCS is set in response. Card operates in block mode if set */ if(r3[0] & 0x40) mmcp->block_addresses = TRUE; } /* Initialization. */ i = 0; while (TRUE) { uint8_t b = send_command_R1(mmcp, MMC_CMDINIT, 0); if (b == 0x00) break; if (b != 0x01) return TRUE; if (++i >= MMC_CMD1_RETRY) return TRUE; chThdSleepMilliseconds(10); } /* Initialization complete, full speed. */ spiStart(mmcp->spip, mmcp->hscfg); /* Setting block size.*/ if (send_command_R1(mmcp, MMC_CMDSETBLOCKLEN, MMC_SECTOR_SIZE) != 0x00) return TRUE; /* Transition to MMC_READY state (if not extracted).*/ chSysLock(); if (mmcp->state == MMC_INSERTED) { mmcp->state = MMC_READY; result = FALSE; } else result = TRUE; chSysUnlock(); return result; } if (mmcp->state == MMC_READY) return FALSE; /* Any other state is invalid.*/ return TRUE; }
/** * @brief Performs the initialization procedure on the inserted card. * @details This function should be invoked when a card is inserted and * brings the driver in the @p MMC_READY state where it is possible * to perform read and write operations. * @note It is possible to invoke this function from the insertion event * handler. * * @param[in] mmcp pointer to the @p MMCDriver object * * @return The operation status. * @retval HAL_SUCCESS the operation succeeded and the driver is now * in the @p MMC_READY state. * @retval HAL_FAILED the operation failed. * * @api */ bool mmcConnect(MMCDriver *mmcp) { unsigned i; uint8_t r3[4]; osalDbgCheck(mmcp != NULL); osalDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY), "invalid state"); /* Connection procedure in progress.*/ mmcp->state = BLK_CONNECTING; mmcp->block_addresses = false; /* Slow clock mode and 128 clock pulses.*/ spiStart(mmcp->config->spip, mmcp->config->lscfg); spiIgnore(mmcp->config->spip, 16); /* SPI mode selection.*/ i = 0; while (true) { if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01U) { break; } if (++i >= MMC_CMD0_RETRY) { goto failed; } osalThreadSleepMilliseconds(10); } /* Try to detect if this is a high capacity card and switch to block addresses if possible. This method is based on "How to support SDC Ver2 and high capacity cards" by ElmChan.*/ if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND, MMCSD_CMD8_PATTERN, r3) != 0x05U) { /* Switch to SDHC mode.*/ i = 0; while (true) { /*lint -save -e9007 [13.5] Side effect unimportant.*/ if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) == 0x01U) && (send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND, 0x400001AAU, r3) == 0x00U)) { /*lint -restore*/ break; } if (++i >= MMC_ACMD41_RETRY) { goto failed; } osalThreadSleepMilliseconds(10); } /* Execute dedicated read on OCR register */ (void) send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3); /* Check if CCS is set in response. Card operates in block mode if set.*/ if ((r3[0] & 0x40U) != 0U) { mmcp->block_addresses = true; } } /* Initialization.*/ i = 0; while (true) { uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0); if (b == 0x00U) { break; } if (b != 0x01U) { goto failed; } if (++i >= MMC_CMD1_RETRY) { goto failed; } osalThreadSleepMilliseconds(10); } /* Initialization complete, full speed.*/ spiStart(mmcp->config->spip, mmcp->config->hscfg); /* Setting block size.*/ if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN, MMCSD_BLOCK_SIZE) != 0x00U) { goto failed; } /* Determine capacity.*/ if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd)) { goto failed; } mmcp->capacity = _mmcsd_get_capacity(mmcp->csd); if (mmcp->capacity == 0U) { goto failed; } if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid)) { goto failed; } mmcp->state = BLK_READY; return HAL_SUCCESS; /* Connection failed, state reset to BLK_ACTIVE.*/ failed: spiStop(mmcp->config->spip); mmcp->state = BLK_ACTIVE; return HAL_FAILED; }
/** * @brief Performs the initialization procedure on the inserted card. * @details This function should be invoked when a card is inserted and * brings the driver in the @p MMC_READY state where it is possible * to perform read and write operations. * @note It is possible to invoke this function from the insertion event * handler. * * @param[in] mmcp pointer to the @p MMCDriver object * @return The operation status. * @retval FALSE the operation was successful and the driver is now * in the @p MMC_READY state. * @retval TRUE the operation failed. */ bool_t mmcConnect(MMCDriver *mmcp) { unsigned i; bool_t result; chDbgCheck(mmcp != NULL, "mmcConnect"); chDbgAssert((mmcp->mmc_state != MMC_UNINIT) && (mmcp->mmc_state != MMC_STOP), "mmcConnect(), #1", "invalid state"); if (mmcp->mmc_state == MMC_INSERTED) { /* Slow clock mode and 128 clock pulses.*/ spiStart(mmcp->mmc_spip, mmcp->mmc_lscfg); spiIgnore(mmcp->mmc_spip, 16); /* SPI mode selection.*/ i = 0; while (TRUE) { if (send_command(mmcp, MMC_CMDGOIDLE, 0) == 0x01) break; if (++i >= MMC_CMD0_RETRY) return TRUE; chThdSleepMilliseconds(10); } /* Initialization. */ i = 0; while (TRUE) { uint8_t b = send_command(mmcp, MMC_CMDINIT, 0); if (b == 0x00) break; if (b != 0x01) return TRUE; if (++i >= MMC_CMD1_RETRY) return TRUE; chThdSleepMilliseconds(10); } /* Initialization complete, full speed. */ spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg); /* Setting block size.*/ if (send_command(mmcp, MMC_CMDSETBLOCKLEN, MMC_SECTOR_SIZE) != 0x00) return TRUE; /* Transition to MMC_READY state (if not extracted).*/ chSysLock(); if (mmcp->mmc_state == MMC_INSERTED) { mmcp->mmc_state = MMC_READY; result = FALSE; } else result = TRUE; chSysUnlock(); return result; } if (mmcp->mmc_state == MMC_READY) return FALSE; /* Any other state is invalid.*/ return TRUE; }