Передмова
Знадобилось мені в одному проекті вести запис всіляких подій у файл. Просто короткі текстові рядки з датою/часом і що за подія відбулась. Тому максимально просто і не заморочувався з DMA. Бібліотека FATFS є в комплекті з CubeMX, а ось вже як саме буде працювати мікроконтролер з флешкою по шині SPI на рівні залізяччя, треба вже потурбуватись самому. І дописати декілька функцій в заздалегідь призначених для цього місцях. Ідею поцупив звідси. Дуже стисло поділюсь з вами робочим прикладом. Хто захоче розібратись детально, то вже користуйтесь пошуком.
Залізяччя для макету
- Плата "Синя пігулка" з чипом STM32F103C8T6
- Програматор ST-Link v2
- MicroSD SPI Card Adapter
- Флешка MicroSD
- Світлодіод будь якого кольору + резистор на 56 - 220 Ом
Схема макету
Зробіть з'єднання відповідно до схеми:
Макет для тестування |
Клацайте на зображення щоб збільшити.
CubeMX for STM32
Запускаємо CubeMX, обираємо свій чип і робимо налаштування:
Налаштування вкладки pinout |
- Додаємо до проекту бібліотеку FATFS
- Вмикаємо тактування від зовнішнього кварцового резонатору
- Вмикаємо Debug - Serial Wire
- Вмикаємо SPI2
- Та не забуваємо налаштувати PB12 на вихід GPIO_Output і надати ім'я "CS_SD_CARD", а також PB1 на вихід GPIO_Output і надати ім'я "LED_RED"
Переходимо до вкладки "clock configuration" і робимо так як на малюнку:
Налаштування вкладки clock configuration |
Всі інші налаштування за замовчуванням, нічого не міняв.
Надаємо ім'я своєму проекту "STM32F103C8_SD_CARD_DEMO", вказуємо шлях, засіб розробки та генеруємо код.
Код для роботи FATFS по SPI
Відкриваємо в своєму засобі розробки згенерований CubeMX проект "STM32F103C8_SD_CARD_DEMO". Та перш за все навчимо бібліотеку FATFS працювати з нашою SD Card по шині SPI. Відкриваємо файл "user_diskio.h" і в секцію /* USER CODE BEGIN 0 */ такі рядки:
- #define SPI hspi2
- extern SPI_HandleTypeDef SPI;
- void sdcard_systick_timerproc(void);
Надалі, для зручності буду додавати весь код в користувацьких секціях і функціях де необхідно зробити зміни.
Ось так буде виглядати секція "USER CODE 0" у файлі "user_diskio.h" з нашим кодом:
/* USER CODE BEGIN 0 */ #define SPI hspi2 /* Includes ------------------------------------------------------------------*/ /* Exported types ------------------------------------------------------------*/ extern SPI_HandleTypeDef SPI; /* Exported constants --------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ extern Diskio_drvTypeDef USER_Driver; /*-----------------------------------------------------------------------*/ /* Device Timer Interrupt Procedure (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* This function must be called in period of 1ms */ void sdcard_systick_timerproc(void); /* USER CODE END 0 */
Далі відкриваємо файл "user_diskio.c" і до секції "/* USER CODE DECL */" вносимо необхідні визначення, змінні, функції.
Секція "/* USER CODE DECL */" в файлі "user_diskio.c" тепер буде виглядати так:
/* USER CODE BEGIN DECL */ /* Includes ------------------------------------------------------------------*/ #include <string.h> #include "ff_gen_drv.h" #include "user_diskio.h" #include "stdbool.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Definitions for MMC/SDC command */ #define CMD0 (0x40+0) /* GO_IDLE_STATE */ #define CMD1 (0x40+1) /* SEND_OP_COND */ #define CMD8 (0x40+8) /* SEND_IF_COND */ #define CMD9 (0x40+9) /* SEND_CSD */ #define CMD10 (0x40+10) /* SEND_CID */ #define CMD12 (0x40+12) /* STOP_TRANSMISSION */ #define CMD16 (0x40+16) /* SET_BLOCKLEN */ #define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ #define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ #define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ #define CMD24 (0x40+24) /* WRITE_BLOCK */ #define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ #define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ #define CMD55 (0x40+55) /* APP_CMD */ #define CMD58 (0x40+58) /* READ_OCR */ #define SELECT() HAL_GPIO_WritePin(CS_SD_CARD_GPIO_Port, CS_SD_CARD_Pin, GPIO_PIN_RESET) #define DESELECT() HAL_GPIO_WritePin(CS_SD_CARD_GPIO_Port, CS_SD_CARD_Pin, GPIO_PIN_SET) /* Private variables ---------------------------------------------------------*/ /* Disk status */ static volatile DSTATUS Stat = STA_NOINIT; static BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ static BYTE PowerFlag = 0; /* indicates if "power" is on */ static volatile BYTE Timer1, Timer2; /* 100Hz decrement timer */ static void xmit_spi(BYTE Data) { while (HAL_SPI_GetState(&SPI) != HAL_SPI_STATE_READY); HAL_SPI_Transmit(&SPI, &Data, 1, 5000); } static BYTE rcvr_spi(void) { unsigned char Dummy, Data; Dummy = 0xFF; Data = 0; while ((HAL_SPI_GetState(&SPI) != HAL_SPI_STATE_READY)); HAL_SPI_TransmitReceive(&SPI, &Dummy, &Data, 1, 5000); return Data; } static void rcvr_spi_m(BYTE *dst) { *dst = rcvr_spi(); } /*-----------------------------------------------------------------------*/ /* Wait for card ready */ /*-----------------------------z------------------------------------------*/ static BYTE wait_ready(void) { BYTE res; Timer2 = 50; rcvr_spi(); do res = rcvr_spi(); while ((res != 0xFF) && Timer2); return res; } static bool rcvr_datablock(BYTE *buff, /* Data buffer to store received data */ UINT btr /* Byte count (must be even number) */ ) { BYTE token; Timer1 = 10; do { /* Wait for data packet in timeout of 100ms */ token = rcvr_spi(); } while ((token == 0xFF) && Timer1); if (token != 0xFE) return false; /* If not valid data token, retutn with error */ do { /* Receive the data block into buffer */ rcvr_spi_m(buff++); rcvr_spi_m(buff++); } while (btr -= 2); rcvr_spi(); /* Discard CRC */ rcvr_spi(); return true; /* Return with success */ } /*-----------------------------------------------------------------------*/ /* Send a data packet to MMC */ /*-----------------------------------------------------------------------*/ #if _READONLY == 0 static bool xmit_datablock(const BYTE *buff, /* 512 byte data block to be transmitted */ BYTE token /* Data/Stop token */ ) { BYTE resp, wc; uint32_t i = 0; if (wait_ready() != 0xFF) return false; xmit_spi(token); /* Xmit data token */ if (token != 0xFD) { /* Is data token */ wc = 0; do { /* Xmit the 512 byte data block to MMC */ xmit_spi(*buff++); xmit_spi(*buff++); } while (--wc); rcvr_spi(); rcvr_spi(); while (i <= 64) { resp = rcvr_spi(); /* Reveive data response */ if ((resp & 0x1F) == 0x05) /* If not accepted, return with error */ break; i++; } while (rcvr_spi() == 0) ; } if ((resp & 0x1F) == 0x05) return true; else return false; } #endif /* _READONLY */ static void power_on(void) { unsigned char i, cmd_arg[6]; unsigned int Count = 0x1FFF; DESELECT(); for (i = 0; i < 10; i++) xmit_spi(0xFF); SELECT(); cmd_arg[0] = (CMD0 | 0x40); cmd_arg[1] = 0; cmd_arg[2] = 0; cmd_arg[3] = 0; cmd_arg[4] = 0; cmd_arg[5] = 0x95; for (i = 0; i < 6; i++) xmit_spi(cmd_arg[i]); while ((rcvr_spi() != 0x01) && Count) Count--; DESELECT(); xmit_spi(0XFF); PowerFlag = 1; } static void power_off(void) { PowerFlag = 0; } /*-----------------------------------------------------------------------*/ /* Send a command packet to MMC */ /*-----------------------------------------------------------------------*/ static BYTE send_cmd(BYTE cmd, /* Command byte */ DWORD arg /* Argument */ ) { BYTE n, res; if (wait_ready() != 0xFF) return 0xFF; /* Send command packet */ xmit_spi(cmd); /* Command */ xmit_spi((BYTE) (arg >> 24)); /* Argument[31..24] */ xmit_spi((BYTE) (arg >> 16)); /* Argument[23..16] */ xmit_spi((BYTE) (arg >> 8)); /* Argument[15..8] */ xmit_spi((BYTE) arg); /* Argument[7..0] */ n = 0; if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ xmit_spi(n); /* Receive command response */ if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ n = 10; /* Wait for a valid response in timeout of 10 attempts */ do res = rcvr_spi(); while ((res & 0x80) && --n); return res; /* Return with the response value */ } static int chk_power(void) /* Socket power state: 0=off, 1=on */ { return PowerFlag; } /*-----------------------------------------------------------------------*/ /* Device Timer Interrupt Procedure (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* This function must be called in period of 1ms */ void disk_timerproc(void) { uint8_t n; n = Timer1; /* 100Hz decrement timer */ if (n) Timer1 = --n; n = Timer2; if (n) Timer2 = --n; } volatile unsigned short int sdcard_timer; void inline sdcard_systick_timerproc(void) { ++sdcard_timer; if (sdcard_timer >= 10) { sdcard_timer = 0; disk_timerproc(); } } /* USER CODE END DECL */
А далі вносимо зміни до функцій в файлі user_diskio.c:
- DSTATUS USER_initialize (BYTE pdrv);
- DSTATUS USER_status (BYTE pdrv);
- DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
- DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
- DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
Функція USER_initialize:
/** * @brief Initializes a Drive * @param pdrv: Physical drive number (0..) * @retval DSTATUS: Operation status */ DSTATUS USER_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { /* USER CODE BEGIN INIT */ BYTE n, ty, ocr[4]; if (pdrv) return STA_NOINIT; /* Supports only single drive */ if (Stat & STA_NODISK) return Stat; /* No card in the socket */ power_on(); /* Force socket power on */ //send_initial_clock_train(); SELECT(); /* CS = L */ ty = 0; if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ Timer1 = 100; /* Initialization timeout of 1000 msec */ if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ do { if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ } while (Timer1); if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); ty = (ocr[0] & 0x40) ? 6 : 2; } } } else { /* SDC Ver1 or MMC */ ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ do { if (ty == 2) { if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ } else { if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ } } while (Timer1); if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ ty = 0; } } CardType = ty; DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ if (ty) /* Initialization succeded */ Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ else /* Initialization failed */ power_off(); return Stat; /* USER CODE END INIT */ }
Функція USER_status:
/** * @brief Gets Disk Status * @param pdrv: Physical drive number (0..) * @retval DSTATUS: Operation status */ DSTATUS USER_status ( BYTE pdrv /* Physical drive number to identify the drive */ ) { /* USER CODE BEGIN STATUS */ if (pdrv) return STA_NOINIT; /* Supports only single drive */ return Stat; /* USER CODE END STATUS */ }
Функція USER_read:
/** * @brief Reads Sector(s) * @param pdrv: Physical drive number (0..) * @param *buff: Data buffer to store read data * @param sector: Sector address (LBA) * @param count: Number of sectors to read (1..128) * @retval DRESULT: Operation result */ DRESULT USER_read ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { /* USER CODE BEGIN READ */ if (pdrv || !count) return RES_PARERR; if (Stat & STA_NOINIT) return RES_NOTRDY; if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ SELECT(); /* CS = L */ if (count == 1) { /* Single block read */ if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ && rcvr_datablock(buff, 512)) count = 0; } else { /* Multiple block read */ if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ do { if (!rcvr_datablock(buff, 512)) break; buff += 512; } while (--count); send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ } } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ return count ? RES_ERROR : RES_OK; /* USER CODE END READ */ }
Функція USER_write:
/** * @brief Writes Sector(s) * @param pdrv: Physical drive number (0..) * @param *buff: Data to be written * @param sector: Sector address (LBA) * @param count: Number of sectors to write (1..128) * @retval DRESULT: Operation result */ #if _USE_WRITE == 1 DRESULT USER_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { /* USER CODE BEGIN WRITE */ /* USER CODE HERE */ if (pdrv || !count) return RES_PARERR; if (Stat & STA_NOINIT) return RES_NOTRDY; if (Stat & STA_PROTECT) return RES_WRPRT; if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ SELECT(); /* CS = L */ if (count == 1) { /* Single block write */ if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ && xmit_datablock(buff, 0xFE)) count = 0; } else { /* Multiple block write */ if (CardType & 2) { send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ } if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ do { if (!xmit_datablock(buff, 0xFC)) break; buff += 512; } while (--count); if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ count = 1; } } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ return count ? RES_ERROR : RES_OK; /* USER CODE END WRITE */ } #endif /* _USE_WRITE == 1 */
Функція USER_ioctl:
/** * @brief I/O control operation * @param pdrv: Physical drive number (0..) * @param cmd: Control code * @param *buff: Buffer to send/receive control data * @retval DRESULT: Operation result */ #if _USE_IOCTL == 1 DRESULT USER_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { /* USER CODE BEGIN IOCTL */ DRESULT res; BYTE n, csd[16], *ptr = buff; WORD csize; if (pdrv) return RES_PARERR; res = RES_ERROR; if (cmd == CTRL_POWER) { switch (*ptr) { case 0: /* Sub control code == 0 (POWER_OFF) */ if (chk_power()) power_off(); /* Power off */ res = RES_OK; break; case 1: /* Sub control code == 1 (POWER_ON) */ power_on(); /* Power on */ res = RES_OK; break; case 2: /* Sub control code == 2 (POWER_GET) */ *(ptr + 1) = (BYTE) chk_power(); res = RES_OK; break; default: res = RES_PARERR; } } else { if (Stat & STA_NOINIT) return RES_NOTRDY; SELECT(); /* CS = L */ switch (cmd) { case GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */ if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ csize = csd[9] + ((WORD) csd[8] << 8) + 1; *(DWORD*) buff = (DWORD) csize << 10; } else { /* MMC or SDC ver 1.XX */ n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; csize = (csd[8] >> 6) + ((WORD) csd[7] << 2) + ((WORD) (csd[6] & 3) << 10) + 1; *(DWORD*) buff = (DWORD) csize << (n - 9); } res = RES_OK; } break; case GET_SECTOR_SIZE: /* Get sectors on the disk (WORD) */ *(WORD*) buff = 512; res = RES_OK; break; case CTRL_SYNC: /* Make sure that data has been written */ if (wait_ready() == 0xFF) res = RES_OK; break; case MMC_GET_CSD: /* Receive CSD as a data block (16 bytes) */ if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ && rcvr_datablock(ptr, 16)) res = RES_OK; break; case MMC_GET_CID: /* Receive CID as a data block (16 bytes) */ if (send_cmd(CMD10, 0) == 0 /* READ_CID */ && rcvr_datablock(ptr, 16)) res = RES_OK; break; case MMC_GET_OCR: /* Receive OCR as an R3 resp (4 bytes) */ if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ for (n = 0; n < 4; n++) *ptr++ = rcvr_spi(); res = RES_OK; } default: res = RES_PARERR; } DESELECT(); /* CS = H */ rcvr_spi(); /* Idle (Release DO) */ } return res; /* USER CODE END IOCTL */ } #endif /* _USE_IOCTL == 1 */
Демо код
Відкриваємо файл stm32f1xx_it.c і до секції /* USER CODE 0 */ додаємо рядок:
/* USER CODE BEGIN 0 */ extern void sdcard_systick_timerproc(void); /* USER CODE END 0 */
А до функції "SysTick_Handler" додаємо виклик функції "sdcard_systick_timerproc":
void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ sdcard_systick_timerproc(); /* USER CODE END SysTick_IRQn 1 */ }
І нарешті відкриваємо файл main.c нашого проекту і в секцію /* USER CODE 2 */ додаємо такий демо-код:
/* USER CODE BEGIN 2 */ //-------------SD DEMO START----------------- char buffer[512]; static FATFS g_sFatFs; FRESULT fresult; FIL file; int len; UINT bytes_written; HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_SET); fresult = f_mount(&g_sFatFs, "0:", 0); //mount SD card fresult = f_open(&file, "testfile.txt", FA_OPEN_ALWAYS | FA_WRITE); //open file on SD card fresult = f_lseek(&file, file.fsize); //go to the end of the file len = sprintf(buffer, "Hello, World!\r\nThis is demonstration write on SD Card\r\n"); //generate some string fresult = f_write(&file, buffer, len, &bytes_written); //write data to the file fresult = f_close(&file); HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_RESET); //-------------SD DEMO END----------------- /* USER CODE END 2 */
Компілюємо проект, заливаємо до мікроконтролеру. Стартуємо мікроконтролер. Можна декілька разів перезавантажити. При цьому світлодіод має блимнути. Тепер можна витягти флешку з SD Card Adapter і вставити її до ПК та пересвідчитись що файл "testfile.txt" з декількома текстовими рядками "Hello, World!" та "This is demonstration write on SD Card" існує.
I can't make it work in every SD card. Do you have any idea why? I've tested many cards and, until now, there's only one generating the file and writing on it (I've even tested with a similar one).
ВідповістиВидалитиGreeting.
ВидалитиYes, I also do not work on every SD-CARD. I do not know the reason. Not taken care of If you find a reason, please let me know, I will correct it and update the library.
I ended up using a different code from yours, but it is actually pretty similar. I'm not sure if this is going to help you, but I just had to change the Vcc from 3.3V to 5V (remember that SPI2 ports on STM32f103C8 are 5V tolerant).
Видалитиit didn't help in my case
Видалитиsincerely thank you for the information. I will test it in practice.
ВідповістиВидалитинапишіть, будь-ласка, приклад, як читати построково файл з gcode, такого виду:
ВідповістиВидалитиG1 X166.895 Y40.157 E10.07675
G1 X167.512 Y41.109 E10.20410
G1 X168.271 Y42.549 E10.38682
G1 X168.899 Y44.196 E10.58469
G1 X169.319 Y45.894 E10.78104
G1 X169.532 Y47.597 E10.97369
G1 X169.550 Y49.208 E11.15454
Вітання.
ВидалитиВаше питання ближче до документації бібліотеки FatFS, чим до теми цієї статті.
Ось вам ланка на потрібну вам функцію бібліотеки FatFS: http://microsin.net/programming/file-systems/fatfs-gets-read-string.html
дякую за відповідь, я на f_gets реалізував читання построково, а потім sscanf розпарсив строчки. Ніби все працює, але таке "рагульство" вийшло, я думав є кращий і більш елегантний спосіб
ВідповістиВидалитиДякую за чудовий приклад роботи з SD картою! Запустилося без проблем!
ВідповістиВидалитиБудь ласка. Sd-card живили від 5 чи 3.3 вольт?
ВідповістиВидалитидесь читав, що не можна живити SD картки на 5V.
ВидалитиТут читав
https://eax.me/stm32-sdcard/
Хоча читав десь і ось це.
ВидалитиIf getting FR_NOT_READY error, use some external 5V power source. Power from board seems to be not enough (it could be current required for the module)
на SD адаптері такий як на першому рисунку є регулятор напруги. Тому можна не боятись підключати 5В пін. Ще й треба, бо в мене не працювало через 3.3В. Це інформація для таких же довбнів, як і я, який 3 дні не розумів, що відбувається. Плюс, якщо щось не виходить і є впевненість у коді, треба обов'язково переконатись мультиметром, що СД картка живиться саме від 5В
ВидалитиЧи можна за цієї конфігурації, що у вас показана користуватись дисплеєм ST7735 та SDcard на одному SPI2? Які нюанси? DMA?
ВідповістиВидалитиТак, можна. Кожному slave пристрою свій chip select і немає проблем.
Видалитидякую
ВидалитиСвітлодіод не блимнув. А точніше він горить постійно. Запис на SD карту не здійснюється. У чому може бути причина? SD карта самсунг
ВідповістиВидалитиfresult зміна в яку кладеться результат кожної дії з прикладу, що пов'язане з роботою сдкард. Що там? Плюс, як світлодіод не згасає, то значить десь зависло. Треба дебагом покроково пройтись щоб зрозуміти на якому кроці висне.
Видалитиfresult = f_open(&file, "testfile.txt", FA_OPEN_ALWAYS | FA_WRITE); //open file on SD card
ВидалитиНе хоче проходити далі цього рядка :(. І файл вже створював окремо на карті, і змінював параметр FA_OPEN_ALWAYS, на FA_CREATE_ALWAYS і FA_CREATE_NEW - не допомогло
Рекомендую ще раз все перевірити і звіритись зі статею. Бо зависати не має, навіть коли сд картки немає в слоті.
ВидалитиЗі світлодіодом було наступне: RESET навпаки вмикає світлодіод, я використовував PC13. І тому виникло так, що демо код ввімкнув світлодіод і залишив його у такому стані
Видалитизрозумів. тому рекомендую робити макроси типу:
Видалити#define LED_ON() HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_RESET)
#define LED_OFF() HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_SET)
і далі в коді вже маніпулювати макросами LED_ON(), LED_OFF() для увімкнення і вимкнення світлодіодів не задумуючись як діод під'єднано чи з загальним катодом, чи з загальним анодом.
Воно насправді не зависає. Воно там фейлиться і йде далі по програмі.
ВідповістиВидалитиfresult = f_mount(&g_sFatFs, "0:", 0); -------> fresult дорівнює FR_OK, тобто чудово.
fresult = f_open(&file, "testfile.txt", FA_OPEN_ALWAYS | FA_WRITE); -----> FR_NOT_READY
fresult = f_lseek(&file, file.fsize); ----> FR_INVALID_OBJECT
Ось таке виводиться :(
Я намагався сам проект створити, потім завантажив ваш. І мій, і ваш у мене поки не працює. Мабуть якась біда з СД картою, хоча туди все норм записується (з ноута). Відформатована у FAT
спробуй живити cd-card від 5 вольт. FR_NOT_READY - це тому що файла такого немає і він має створитись. FR_INVALID_OBJECT - це значить файл не створився і такого об'єкта не існує.
Видалити5В живиться, файл дійсно не створюється :( Адаптер точно такий же як у вас на схемці
Видалитизмінив SD картку на іншу все одно не працює... Робив все точно, як у вас. На жаль, ніяк. Може за три роки якісь функції змінили чи переробили у FATFS, бо якщо зараз генерувати кубом проект то файли бібліотеки розташовуються інакше, ніж у Вашому проекті, який ви залишили для завантаження
ВідповістиВидалитиВсе, проблема зникла. Дякую Вам велике за такий чудовий матеріал. Завдяки Вам і дисплей під'єднав, і SD карту. З SD картою я виходить дуже затупив, бо живив bluepil через SWD, а не через microUSB, тобто пін 5В насправді видавав 3.3В. Ще раз дякую!!!
ВідповістиВидалитиВітаю! Щиро радий, що вирішилось.
ВидалитиДобрый день! Спасибо за прекрасную статью, всё заработало!
ВідповістиВидалитиВопрос. Если файла нет на флешке, то первый раз работа с ней длится дольше, а потом быстрее. Вот цифры. У нас 5 функций - монтируем, открываем, ищем конец, пишем, закрываем.
Если файла нет: 4мкс, 65мс, 5мкс, 750мс, 150мс. Не вынимая флешку, жмем ресет и еще раз пишем. Теперь : 4мкс, 19мс, 5мс, 25мкс, 130мс. Последующие разы также пишется быстро. От флешки не зависит (у меня на 256М и 2Г). Почему такая разница?
Доброго дня.
ВидалитиРадію що допомагаю.
Ці питання скоріше до драйверу FATFS. Я таких досліджень не робив, але маю думку, що на перевірку чи є такий файл, на пошук місця де розташувати новий файл, та його формування потрібно більше часу чим коли такий файл вже існує.
Насчет отсутствия файла я был неправ, это не влияет. Если делаю на пк пустой файл, то его наличие никак не влияет на задержку. Влияние оказывает лишь редактирование, создание или удаление файла на пк. Даже если вынимать/вставлять из модуля - задержка уже маленькая. Что отличается - время создания файла. Фатфс ставит 01.01.1980. Но связи не вижу.
ВидалитиДата создания файла в системах без RTC задается в настройках FATFS.
ВидалитиЗдравствуйте! Я читал, что инициализация SD карты после включения питания должна происходить на скорости от 100 kHz до 400 kHz, после чего можно переходить на высокую скорость. Я нашел в вашей программе функцию, которая выполняет инициализацию - это power_on() в файле user_diskio.c . Но переключения скорости SPI в тексте программы я вообще не нашел, как и вообще настройки интерфейса SPI. Скажите, вы делаете переключение скорости SPI или нет? Если да, то в какой момент можно включать высокую скорость?
ВідповістиВидалитиСам спросил, сам отвечаю. Моя SD карта (Class 4) работает и без переключения скорости. Инициализация и дальнейшая работа проводились при скорости SPI 12 MHz. Использовал участки кода из вашей статьи, всё сразу заработало.
ВідповістиВидалитиАвтор видалив цей коментар.
ВідповістиВидалитиАвтор видалив цей коментар.
ВідповістиВидалитиРаньше использовал этот драйвер SD карты вместе с контроллером stm32f1xx, всё работало. Недавно попробовал запустить этот драйвер на stm32L4XX, и срезу повылазили ошибки. В некоторых местах пришлось добавить ожидание завершения передачи spi. Но это не ошибка автора, причина в том, что у L4 есть буфер FIFO. ошибка обнаружилась в функции USER_initialize().
ВідповістиВидалити...
if (send_cmd(CMD8, 0x1AA) == 1){
for(n = 0; n < 4; n++)
{ocr[n] = rcvr_spi();}
if (ocr[2] == 0x01 && ocr[3] == 0xAA){ //Здесь ошибка!
...
Команда send_cmd(CMD8, 0x1AA) (согласно документации на SD карты) возвращает 4 байта. Первый байт результата забирает функция send_cmd, а цикл for(n = 0; n < 4; n++) пытается забрать ещё 4 байта. В то время, как нужно забрать только 3. Условие проверки результата CMD8 пришлось исправить на if (ocr[1] == 0x01 && ocr[2] == 0xAA), после чего всё заработало.
Доброго дня! Було б добре якби просто писати/читати інформацію блоками з картки - робота з файловою системою займає багато ресурсів і часу , особливо якщо писати аудіо данні (до 3кГц) на непотужному stm32f103. А на комп'ютері написати програму для читання і запису.
ВідповістиВидалитиОбнаружил ошибку в функции "USER_write". Когда аргумент "count" больше 1, функция
ВідповістиВидалитивсегда завершается с ошибкой.
Фрагмент када с ошибкой:
if (send_cmd(CMD25, sector) == 0)
{ /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC))
break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1; //Здесь ошибка!
}
Должно быть: "count = 0;" так как count равный 0 является признаком успешного
завершения команды.