[FL-2435] SD over SPI improvements (#2204)
* get rid of BSP layer * sector_cache: init in any case * new sd-spi driver: init * Delete stm32_adafruit_sd.c.old * Delete spi_sd_hal.c.old * Storage: faster api lock primitive * Threads: priority control * Flags: correct error code * Spi: dma mode * SD-card: use DMA for big blocks of SPI data * Fix wrong SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE value * do not memset cache if it is NULL * remove top-level timeouts * sd hal: disable debug * Furi HAL: DMA * Furi HAL RFID: use furi_hal_dma * Furi HAL DMA: tests * Furi HAL DMA: docs * rollback "Furi HAL RFID: use furi_hal_dma" * 4 channels taken from DMA manager for core HAL. * Furi HAL DMA: live fast, die young * RPC tests: increase message buffer * SPI HAL: use second DMA instance * sd hal: new CID getter * SPI hal: use non-DMA version if kernel is not running * IR hal: generalize DMA definition * storage: add CID data to sd info * RFID hal: generalize DMA definition * SUBGHZ hal: generalize DMA definition. Core hal moved to DMA2. * Storage: small optimizations, removal of extra mutex * Storage: redundant macro * SD hal: more timeouts * SPI hal: DMA init * Target: make furi_hal_spi_dma_init symbol private * Update F18 api symbols Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									224d0aefe4
								
							
						
					
					
						commit
						e3d473bf42
					
				| @ -89,7 +89,7 @@ static void test_rpc_setup(void) { | |||||||
|     } |     } | ||||||
|     furi_check(rpc_session[0].session); |     furi_check(rpc_session[0].session); | ||||||
| 
 | 
 | ||||||
|     rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1); |     rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); | ||||||
|     rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); |     rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); | ||||||
|     rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); |     rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); | ||||||
|     rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); |     rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <stm32_adafruit_sd.h> |  | ||||||
| 
 | 
 | ||||||
| #include <cli/cli.h> | #include <cli/cli.h> | ||||||
| #include <lib/toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| @ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) { | |||||||
|         } |         } | ||||||
|     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { |     } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { | ||||||
|         SDInfo sd_info; |         SDInfo sd_info; | ||||||
|         SD_CID sd_cid; |  | ||||||
|         FS_Error error = storage_sd_info(api, &sd_info); |         FS_Error error = storage_sd_info(api, &sd_info); | ||||||
|         BSP_SD_GetCIDRegister(&sd_cid); |  | ||||||
| 
 | 
 | ||||||
|         if(error != FSE_OK) { |         if(error != FSE_OK) { | ||||||
|             storage_cli_print_error(error); |             storage_cli_print_error(error); | ||||||
|         } else { |         } else { | ||||||
|             printf( |             printf( | ||||||
|                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" |                 "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" | ||||||
|                 "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", |                 "%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n", | ||||||
|                 sd_info.label, |                 sd_info.label, | ||||||
|                 sd_api_get_fs_type_text(sd_info.fs_type), |                 sd_api_get_fs_type_text(sd_info.fs_type), | ||||||
|                 sd_info.kb_total, |                 sd_info.kb_total, | ||||||
|                 sd_info.kb_free, |                 sd_info.kb_free, | ||||||
|                 sd_cid.ManufacturerID, |                 sd_info.manufacturer_id, | ||||||
|                 sd_cid.OEM_AppliID, |                 sd_info.oem_id, | ||||||
|                 sd_cid.ProdName, |                 sd_info.product_name, | ||||||
|                 sd_cid.ProdRev >> 4, |                 sd_info.product_revision_major, | ||||||
|                 sd_cid.ProdRev & 0xf, |                 sd_info.product_revision_minor, | ||||||
|                 sd_cid.ProdSN, |                 sd_info.product_serial_number, | ||||||
|                 sd_cid.ManufactMonth, |                 sd_info.manufacturing_month, | ||||||
|                 sd_cid.ManufactYear + 2000); |                 sd_info.manufacturing_year); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         storage_cli_print_usage(); |         storage_cli_print_usage(); | ||||||
|  | |||||||
| @ -12,9 +12,7 @@ | |||||||
| 
 | 
 | ||||||
| #define TAG "StorageAPI" | #define TAG "StorageAPI" | ||||||
| 
 | 
 | ||||||
| #define S_API_PROLOGUE                                     \ | #define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked(); | ||||||
|     FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \ |  | ||||||
|     furi_check(semaphore != NULL); |  | ||||||
| 
 | 
 | ||||||
| #define S_FILE_API_PROLOGUE           \ | #define S_FILE_API_PROLOGUE           \ | ||||||
|     Storage* storage = file->storage; \ |     Storage* storage = file->storage; \ | ||||||
| @ -24,13 +22,12 @@ | |||||||
|     furi_check(                                                                      \ |     furi_check(                                                                      \ | ||||||
|         furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ |         furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ | ||||||
|         FuriStatusOk);                                                               \ |         FuriStatusOk);                                                               \ | ||||||
|     furi_semaphore_acquire(semaphore, FuriWaitForever);                              \ |     api_lock_wait_unlock_and_free(lock) | ||||||
|     furi_semaphore_free(semaphore); |  | ||||||
| 
 | 
 | ||||||
| #define S_API_MESSAGE(_command)      \ | #define S_API_MESSAGE(_command)      \ | ||||||
|     SAReturn return_data;            \ |     SAReturn return_data;            \ | ||||||
|     StorageMessage message = {       \ |     StorageMessage message = {       \ | ||||||
|         .semaphore = semaphore,      \ |         .lock = lock,                \ | ||||||
|         .command = _command,         \ |         .command = _command,         \ | ||||||
|         .data = &data,               \ |         .data = &data,               \ | ||||||
|         .return_data = &return_data, \ |         .return_data = &return_data, \ | ||||||
|  | |||||||
| @ -31,29 +31,13 @@ void storage_file_clear(StorageFile* obj) { | |||||||
| /****************** storage data ******************/ | /****************** storage data ******************/ | ||||||
| 
 | 
 | ||||||
| void storage_data_init(StorageData* storage) { | void storage_data_init(StorageData* storage) { | ||||||
|     storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal); |  | ||||||
|     furi_check(storage->mutex != NULL); |  | ||||||
|     storage->data = NULL; |     storage->data = NULL; | ||||||
|     storage->status = StorageStatusNotReady; |     storage->status = StorageStatusNotReady; | ||||||
|     StorageFileList_init(storage->files); |     StorageFileList_init(storage->files); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool storage_data_lock(StorageData* storage) { |  | ||||||
|     return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool storage_data_unlock(StorageData* storage) { |  | ||||||
|     return (furi_mutex_release(storage->mutex) == FuriStatusOk); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| StorageStatus storage_data_status(StorageData* storage) { | StorageStatus storage_data_status(StorageData* storage) { | ||||||
|     StorageStatus status; |     return storage->status; | ||||||
| 
 |  | ||||||
|     storage_data_lock(storage); |  | ||||||
|     status = storage->status; |  | ||||||
|     storage_data_unlock(storage); |  | ||||||
| 
 |  | ||||||
|     return status; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const char* storage_data_status_text(StorageData* storage) { | const char* storage_data_status_text(StorageData* storage) { | ||||||
|  | |||||||
| @ -38,8 +38,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src); | |||||||
| void storage_file_clear(StorageFile* obj); | void storage_file_clear(StorageFile* obj); | ||||||
| 
 | 
 | ||||||
| void storage_data_init(StorageData* storage); | void storage_data_init(StorageData* storage); | ||||||
| bool storage_data_lock(StorageData* storage); |  | ||||||
| bool storage_data_unlock(StorageData* storage); |  | ||||||
| StorageStatus storage_data_status(StorageData* storage); | StorageStatus storage_data_status(StorageData* storage); | ||||||
| const char* storage_data_status_text(StorageData* storage); | const char* storage_data_status_text(StorageData* storage); | ||||||
| void storage_data_timestamp(StorageData* storage); | void storage_data_timestamp(StorageData* storage); | ||||||
| @ -57,7 +55,6 @@ struct StorageData { | |||||||
|     const FS_Api* fs_api; |     const FS_Api* fs_api; | ||||||
|     StorageApi api; |     StorageApi api; | ||||||
|     void* data; |     void* data; | ||||||
|     FuriMutex* mutex; |  | ||||||
|     StorageStatus status; |     StorageStatus status; | ||||||
|     StorageFileList_t files; |     StorageFileList_t files; | ||||||
|     uint32_t timestamp; |     uint32_t timestamp; | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <toolbox/api_lock.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -130,7 +131,7 @@ typedef enum { | |||||||
| } StorageCommand; | } StorageCommand; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriSemaphore* semaphore; |     FuriApiLock lock; | ||||||
|     StorageCommand command; |     StorageCommand command; | ||||||
|     SAData* data; |     SAData* data; | ||||||
|     SAReturn* return_data; |     SAReturn* return_data; | ||||||
|  | |||||||
| @ -2,15 +2,7 @@ | |||||||
| #include <m-list.h> | #include <m-list.h> | ||||||
| #include <m-dict.h> | #include <m-dict.h> | ||||||
| 
 | 
 | ||||||
| #define FS_CALL(_storage, _fn)   \ | #define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; | ||||||
|     storage_data_lock(_storage); \ |  | ||||||
|     ret = _storage->fs_api->_fn; \ |  | ||||||
|     storage_data_unlock(_storage); |  | ||||||
| 
 |  | ||||||
| #define ST_CALL(_storage, _fn)   \ |  | ||||||
|     storage_data_lock(_storage); \ |  | ||||||
|     ret = _storage->api._fn;     \ |  | ||||||
|     storage_data_unlock(_storage); |  | ||||||
| 
 | 
 | ||||||
| static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { | static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { | ||||||
|     furi_check(type == ST_EXT || type == ST_INT); |     furi_check(type == ST_EXT || type == ST_INT); | ||||||
| @ -44,16 +36,11 @@ static const char* remove_vfs(const char* path) { | |||||||
| 
 | 
 | ||||||
| static StorageType storage_get_type_by_path(Storage* app, const char* path) { | static StorageType storage_get_type_by_path(Storage* app, const char* path) { | ||||||
|     StorageType type = ST_ERROR; |     StorageType type = ST_ERROR; | ||||||
|     if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) && |     if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { | ||||||
|        memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { |  | ||||||
|         type = ST_EXT; |         type = ST_EXT; | ||||||
|     } else if( |     } else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { | ||||||
|         strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) && |  | ||||||
|         memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { |  | ||||||
|         type = ST_INT; |         type = ST_INT; | ||||||
|     } else if( |     } else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { | ||||||
|         strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) && |  | ||||||
|         memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { |  | ||||||
|         type = ST_ANY; |         type = ST_ANY; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -68,21 +55,15 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { | static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { | ||||||
|     if(memcmp( |     if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { | ||||||
|            furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == |  | ||||||
|        0) { |  | ||||||
|         switch(real_storage) { |         switch(real_storage) { | ||||||
|         case ST_EXT: |         case ST_EXT: | ||||||
|             furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); |             furi_string_replace_at( | ||||||
|             furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); |                 path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); | ||||||
|             furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); |  | ||||||
|             furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); |  | ||||||
|             break; |             break; | ||||||
|         case ST_INT: |         case ST_INT: | ||||||
|             furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); |             furi_string_replace_at( | ||||||
|             furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); |                 path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX); | ||||||
|             furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); |  | ||||||
|             furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); |  | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
| @ -604,7 +585,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_semaphore_release(message->semaphore); |     api_lock_unlock(message->lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void storage_process_message(Storage* app, StorageMessage* message) { | void storage_process_message(Storage* app, StorageMessage* message) { | ||||||
|  | |||||||
| @ -23,6 +23,16 @@ typedef struct { | |||||||
|     uint16_t cluster_size; |     uint16_t cluster_size; | ||||||
|     uint16_t sector_size; |     uint16_t sector_size; | ||||||
|     char label[SD_LABEL_LENGTH]; |     char label[SD_LABEL_LENGTH]; | ||||||
|  | 
 | ||||||
|  |     uint8_t manufacturer_id; | ||||||
|  |     char oem_id[3]; | ||||||
|  |     char product_name[6]; | ||||||
|  |     uint8_t product_revision_major; | ||||||
|  |     uint8_t product_revision_minor; | ||||||
|  |     uint32_t product_serial_number; | ||||||
|  |     uint8_t manufacturing_month; | ||||||
|  |     uint16_t manufacturing_year; | ||||||
|  | 
 | ||||||
|     FS_Error error; |     FS_Error error; | ||||||
| } SDInfo; | } SDInfo; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error); | |||||||
| 
 | 
 | ||||||
| static bool sd_mount_card(StorageData* storage, bool notify) { | static bool sd_mount_card(StorageData* storage, bool notify) { | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     uint8_t counter = BSP_SD_MaxMountRetryCount(); |     uint8_t counter = sd_max_mount_retry_count(); | ||||||
|     uint8_t bsp_result; |     uint8_t bsp_result; | ||||||
|     SDData* sd_data = storage->data; |     SDData* sd_data = storage->data; | ||||||
| 
 | 
 | ||||||
|     storage_data_lock(storage); |  | ||||||
| 
 |  | ||||||
|     while(result == false && counter > 0 && hal_sd_detect()) { |     while(result == false && counter > 0 && hal_sd_detect()) { | ||||||
|         if(notify) { |         if(notify) { | ||||||
|             NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); |             NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); | ||||||
| @ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { | |||||||
| 
 | 
 | ||||||
|         if((counter % 2) == 0) { |         if((counter % 2) == 0) { | ||||||
|             // power reset sd card
 |             // power reset sd card
 | ||||||
|             bsp_result = BSP_SD_Init(true); |             bsp_result = sd_init(true); | ||||||
|         } else { |         } else { | ||||||
|             bsp_result = BSP_SD_Init(false); |             bsp_result = sd_init(false); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(bsp_result) { |         if(bsp_result) { | ||||||
| @ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     storage_data_timestamp(storage); |     storage_data_timestamp(storage); | ||||||
|     storage_data_unlock(storage); |  | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| @ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) { | |||||||
|     SDData* sd_data = storage->data; |     SDData* sd_data = storage->data; | ||||||
|     SDError error; |     SDError error; | ||||||
| 
 | 
 | ||||||
|     storage_data_lock(storage); |  | ||||||
|     storage->status = StorageStatusNotReady; |     storage->status = StorageStatusNotReady; | ||||||
|     error = FR_DISK_ERR; |     error = FR_DISK_ERR; | ||||||
| 
 | 
 | ||||||
|     // TODO do i need to close the files?
 |     // TODO do i need to close the files?
 | ||||||
| 
 |  | ||||||
|     f_mount(0, sd_data->path, 0); |     f_mount(0, sd_data->path, 0); | ||||||
|     storage_data_unlock(storage); | 
 | ||||||
|     return storage_ext_parse_error(error); |     return storage_ext_parse_error(error); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) { | |||||||
|     SDData* sd_data = storage->data; |     SDData* sd_data = storage->data; | ||||||
|     SDError error; |     SDError error; | ||||||
| 
 | 
 | ||||||
|     storage_data_lock(storage); |  | ||||||
| 
 |  | ||||||
|     work_area = malloc(_MAX_SS); |     work_area = malloc(_MAX_SS); | ||||||
|     error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS); |     error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS); | ||||||
|     free(work_area); |     free(work_area); | ||||||
| @ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) { | |||||||
|         storage->status = StorageStatusOK; |         storage->status = StorageStatusOK; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     storage_data_unlock(storage); |  | ||||||
| 
 |  | ||||||
|     return storage_ext_parse_error(error); |     return storage_ext_parse_error(error); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| @ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { | |||||||
|     memset(sd_info, 0, sizeof(SDInfo)); |     memset(sd_info, 0, sizeof(SDInfo)); | ||||||
| 
 | 
 | ||||||
|     // get fs info
 |     // get fs info
 | ||||||
|     storage_data_lock(storage); |  | ||||||
|     error = f_getlabel(sd_data->path, sd_info->label, NULL); |     error = f_getlabel(sd_data->path, sd_info->label, NULL); | ||||||
|     if(error == FR_OK) { |     if(error == FR_OK) { | ||||||
| #ifndef FURI_RAM_EXEC | #ifndef FURI_RAM_EXEC | ||||||
|         error = f_getfree(sd_data->path, &free_clusters, &fs); |         error = f_getfree(sd_data->path, &free_clusters, &fs); | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|     storage_data_unlock(storage); |  | ||||||
| 
 | 
 | ||||||
|     if(error == FR_OK) { |     if(error == FR_OK) { | ||||||
|         // calculate size
 |         // calculate size
 | ||||||
| @ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { | |||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     SD_CID cid; | ||||||
|  |     SdSpiStatus status = sd_get_cid(&cid); | ||||||
|  | 
 | ||||||
|  |     if(status == SdSpiStatusOK) { | ||||||
|  |         sd_info->manufacturer_id = cid.ManufacturerID; | ||||||
|  |         memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID)); | ||||||
|  |         memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName)); | ||||||
|  |         sd_info->product_revision_major = cid.ProdRev >> 4; | ||||||
|  |         sd_info->product_revision_minor = cid.ProdRev & 0x0F; | ||||||
|  |         sd_info->product_serial_number = cid.ProdSN; | ||||||
|  |         sd_info->manufacturing_year = 2000 + cid.ManufactYear; | ||||||
|  |         sd_info->manufacturing_month = cid.ManufactMonth; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return storage_ext_parse_error(error); |     return storage_ext_parse_error(error); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| #include "../storage_settings.h" | #include "../storage_settings.h" | ||||||
| #include <stm32_adafruit_sd.h> |  | ||||||
| 
 | 
 | ||||||
| static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { | static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { | ||||||
|     StorageSettings* app = context; |     StorageSettings* app = context; | ||||||
| @ -12,9 +11,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | |||||||
|     DialogEx* dialog_ex = app->dialog_ex; |     DialogEx* dialog_ex = app->dialog_ex; | ||||||
| 
 | 
 | ||||||
|     SDInfo sd_info; |     SDInfo sd_info; | ||||||
|     SD_CID sd_cid; |  | ||||||
|     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); |     FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); | ||||||
|     BSP_SD_GetCIDRegister(&sd_cid); |  | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); |     scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); | ||||||
| 
 | 
 | ||||||
| @ -31,19 +28,19 @@ void storage_settings_scene_sd_info_on_enter(void* context) { | |||||||
|         furi_string_printf( |         furi_string_printf( | ||||||
|             app->text_string, |             app->text_string, | ||||||
|             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" |             "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" | ||||||
|             "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", |             "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", | ||||||
|             sd_info.label, |             sd_info.label, | ||||||
|             sd_api_get_fs_type_text(sd_info.fs_type), |             sd_api_get_fs_type_text(sd_info.fs_type), | ||||||
|             sd_info.kb_total, |             sd_info.kb_total, | ||||||
|             sd_info.kb_free, |             sd_info.kb_free, | ||||||
|             sd_cid.ManufacturerID, |             sd_info.manufacturer_id, | ||||||
|             sd_cid.OEM_AppliID, |             sd_info.oem_id, | ||||||
|             sd_cid.ProdName, |             sd_info.product_name, | ||||||
|             sd_cid.ProdRev >> 4, |             sd_info.product_revision_major, | ||||||
|             sd_cid.ProdRev & 0xf, |             sd_info.product_revision_minor, | ||||||
|             sd_cid.ProdSN, |             sd_info.product_serial_number, | ||||||
|             sd_cid.ManufactMonth, |             sd_info.manufacturing_month, | ||||||
|             sd_cid.ManufactYear + 2000); |             sd_info.manufacturing_year); | ||||||
|         dialog_ex_set_text( |         dialog_ex_set_text( | ||||||
|             dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); |             dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,12.1,, | Version,+,12.2,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -1062,10 +1062,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* | |||||||
| Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* | Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* | ||||||
| Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | ||||||
| Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | ||||||
|  | Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | ||||||
| Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | ||||||
| Function,-,furi_hal_spi_config_deinit_early,void, | Function,-,furi_hal_spi_config_deinit_early,void, | ||||||
| Function,-,furi_hal_spi_config_init,void, | Function,-,furi_hal_spi_config_init,void, | ||||||
| Function,-,furi_hal_spi_config_init_early,void, | Function,-,furi_hal_spi_config_init_early,void, | ||||||
|  | Function,-,furi_hal_spi_dma_init,void, | ||||||
| Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* | Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* | ||||||
| Function,+,furi_hal_switch,void,void* | Function,+,furi_hal_switch,void,void* | ||||||
| Function,+,furi_hal_uart_deinit,void,FuriHalUartId | Function,+,furi_hal_uart_deinit,void,FuriHalUartId | ||||||
| @ -1231,6 +1233,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" | |||||||
| Function,+,furi_thread_free,void,FuriThread* | Function,+,furi_thread_free,void,FuriThread* | ||||||
| Function,+,furi_thread_get_current,FuriThread*, | Function,+,furi_thread_get_current,FuriThread*, | ||||||
| Function,+,furi_thread_get_current_id,FuriThreadId, | Function,+,furi_thread_get_current_id,FuriThreadId, | ||||||
|  | Function,+,furi_thread_get_current_priority,FuriThreadPriority, | ||||||
| Function,+,furi_thread_get_heap_size,size_t,FuriThread* | Function,+,furi_thread_get_heap_size,size_t,FuriThread* | ||||||
| Function,+,furi_thread_get_id,FuriThreadId,FuriThread* | Function,+,furi_thread_get_id,FuriThreadId,FuriThread* | ||||||
| Function,+,furi_thread_get_name,const char*,FuriThreadId | Function,+,furi_thread_get_name,const char*,FuriThreadId | ||||||
| @ -1244,6 +1247,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* | |||||||
| Function,+,furi_thread_resume,void,FuriThreadId | Function,+,furi_thread_resume,void,FuriThreadId | ||||||
| Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" | Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" | ||||||
| Function,+,furi_thread_set_context,void,"FuriThread*, void*" | Function,+,furi_thread_set_context,void,"FuriThread*, void*" | ||||||
|  | Function,+,furi_thread_set_current_priority,void,FuriThreadPriority | ||||||
| Function,+,furi_thread_set_name,void,"FuriThread*, const char*" | Function,+,furi_thread_set_name,void,"FuriThread*, const char*" | ||||||
| Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" | Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" | ||||||
| Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" | Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" | ||||||
|  | |||||||
| 
 | 
| @ -51,6 +51,7 @@ void furi_hal_init() { | |||||||
|     furi_hal_version_init(); |     furi_hal_version_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_spi_config_init(); |     furi_hal_spi_config_init(); | ||||||
|  |     furi_hal_spi_dma_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_speaker_init(); |     furi_hal_speaker_init(); | ||||||
|     FURI_LOG_I(TAG, "Speaker OK"); |     FURI_LOG_I(TAG, "Speaker OK"); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,12.1,, | Version,+,12.2,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -1325,10 +1325,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* | |||||||
| Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* | Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* | ||||||
| Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | ||||||
| Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | ||||||
|  | Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" | ||||||
| Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" | ||||||
| Function,-,furi_hal_spi_config_deinit_early,void, | Function,-,furi_hal_spi_config_deinit_early,void, | ||||||
| Function,-,furi_hal_spi_config_init,void, | Function,-,furi_hal_spi_config_init,void, | ||||||
| Function,-,furi_hal_spi_config_init_early,void, | Function,-,furi_hal_spi_config_init_early,void, | ||||||
|  | Function,-,furi_hal_spi_dma_init,void, | ||||||
| Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* | Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* | ||||||
| Function,-,furi_hal_subghz_dump_state,void, | Function,-,furi_hal_subghz_dump_state,void, | ||||||
| Function,+,furi_hal_subghz_flush_rx,void, | Function,+,furi_hal_subghz_flush_rx,void, | ||||||
| @ -1524,6 +1526,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" | |||||||
| Function,+,furi_thread_free,void,FuriThread* | Function,+,furi_thread_free,void,FuriThread* | ||||||
| Function,+,furi_thread_get_current,FuriThread*, | Function,+,furi_thread_get_current,FuriThread*, | ||||||
| Function,+,furi_thread_get_current_id,FuriThreadId, | Function,+,furi_thread_get_current_id,FuriThreadId, | ||||||
|  | Function,+,furi_thread_get_current_priority,FuriThreadPriority, | ||||||
| Function,+,furi_thread_get_heap_size,size_t,FuriThread* | Function,+,furi_thread_get_heap_size,size_t,FuriThread* | ||||||
| Function,+,furi_thread_get_id,FuriThreadId,FuriThread* | Function,+,furi_thread_get_id,FuriThreadId,FuriThread* | ||||||
| Function,+,furi_thread_get_name,const char*,FuriThreadId | Function,+,furi_thread_get_name,const char*,FuriThreadId | ||||||
| @ -1537,6 +1540,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* | |||||||
| Function,+,furi_thread_resume,void,FuriThreadId | Function,+,furi_thread_resume,void,FuriThreadId | ||||||
| Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" | Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" | ||||||
| Function,+,furi_thread_set_context,void,"FuriThread*, void*" | Function,+,furi_thread_set_context,void,"FuriThread*, void*" | ||||||
|  | Function,+,furi_thread_set_current_priority,void,FuriThreadPriority | ||||||
| Function,+,furi_thread_set_name,void,"FuriThread*, const char*" | Function,+,furi_thread_set_name,void,"FuriThread*, const char*" | ||||||
| Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" | Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" | ||||||
| Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" | Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" | ||||||
|  | |||||||
| 
 | 
							
								
								
									
										877
									
								
								firmware/targets/f7/fatfs/sd_spi_io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										877
									
								
								firmware/targets/f7/fatfs/sd_spi_io.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,877 @@ | |||||||
|  | #include "sd_spi_io.h" | ||||||
|  | #include "sector_cache.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <furi/core/core_defines.h> | ||||||
|  | 
 | ||||||
|  | // #define SD_SPI_DEBUG 1
 | ||||||
|  | #define TAG "SdSpi" | ||||||
|  | 
 | ||||||
|  | #ifdef SD_SPI_DEBUG | ||||||
|  | #define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define sd_spi_debug(...) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define SD_CMD_LENGTH 6 | ||||||
|  | #define SD_DUMMY_BYTE 0xFF | ||||||
|  | #define SD_ANSWER_RETRY_COUNT 8 | ||||||
|  | #define SD_IDLE_RETRY_COUNT 100 | ||||||
|  | #define SD_BLOCK_SIZE 512 | ||||||
|  | 
 | ||||||
|  | #define FLAG_SET(x, y) (((x) & (y)) == (y)) | ||||||
|  | 
 | ||||||
|  | static bool sd_high_capacity = false; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SdSpiDataResponceOK = 0x05, | ||||||
|  |     SdSpiDataResponceCRCError = 0x0B, | ||||||
|  |     SdSpiDataResponceWriteError = 0x0D, | ||||||
|  |     SdSpiDataResponceOtherError = 0xFF, | ||||||
|  | } SdSpiDataResponce; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t r1; | ||||||
|  |     uint8_t r2; | ||||||
|  |     uint8_t r3; | ||||||
|  |     uint8_t r4; | ||||||
|  |     uint8_t r5; | ||||||
|  | } SdSpiCmdAnswer; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SdSpiCmdAnswerTypeR1, | ||||||
|  |     SdSpiCmdAnswerTypeR1B, | ||||||
|  |     SdSpiCmdAnswerTypeR2, | ||||||
|  |     SdSpiCmdAnswerTypeR3, | ||||||
|  |     SdSpiCmdAnswerTypeR4R5, | ||||||
|  |     SdSpiCmdAnswerTypeR7, | ||||||
|  | } SdSpiCmdAnswerType; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |     SdSpiCmd and SdSpiToken use non-standard enum value names convention, | ||||||
|  |     because it is more convenient to look for documentation on a specific command. | ||||||
|  |     For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for | ||||||
|  |     SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". | ||||||
|  | 
 | ||||||
|  |     Do not use that naming convention in other places. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SD_CMD0_GO_IDLE_STATE = 0, | ||||||
|  |     SD_CMD1_SEND_OP_COND = 1, | ||||||
|  |     SD_CMD8_SEND_IF_COND = 8, | ||||||
|  |     SD_CMD9_SEND_CSD = 9, | ||||||
|  |     SD_CMD10_SEND_CID = 10, | ||||||
|  |     SD_CMD12_STOP_TRANSMISSION = 12, | ||||||
|  |     SD_CMD13_SEND_STATUS = 13, | ||||||
|  |     SD_CMD16_SET_BLOCKLEN = 16, | ||||||
|  |     SD_CMD17_READ_SINGLE_BLOCK = 17, | ||||||
|  |     SD_CMD18_READ_MULT_BLOCK = 18, | ||||||
|  |     SD_CMD23_SET_BLOCK_COUNT = 23, | ||||||
|  |     SD_CMD24_WRITE_SINGLE_BLOCK = 24, | ||||||
|  |     SD_CMD25_WRITE_MULT_BLOCK = 25, | ||||||
|  |     SD_CMD27_PROG_CSD = 27, | ||||||
|  |     SD_CMD28_SET_WRITE_PROT = 28, | ||||||
|  |     SD_CMD29_CLR_WRITE_PROT = 29, | ||||||
|  |     SD_CMD30_SEND_WRITE_PROT = 30, | ||||||
|  |     SD_CMD32_SD_ERASE_GRP_START = 32, | ||||||
|  |     SD_CMD33_SD_ERASE_GRP_END = 33, | ||||||
|  |     SD_CMD34_UNTAG_SECTOR = 34, | ||||||
|  |     SD_CMD35_ERASE_GRP_START = 35, | ||||||
|  |     SD_CMD36_ERASE_GRP_END = 36, | ||||||
|  |     SD_CMD37_UNTAG_ERASE_GROUP = 37, | ||||||
|  |     SD_CMD38_ERASE = 38, | ||||||
|  |     SD_CMD41_SD_APP_OP_COND = 41, | ||||||
|  |     SD_CMD55_APP_CMD = 55, | ||||||
|  |     SD_CMD58_READ_OCR = 58, | ||||||
|  | } SdSpiCmd; | ||||||
|  | 
 | ||||||
|  | /** Data tokens */ | ||||||
|  | typedef enum { | ||||||
|  |     SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, | ||||||
|  |     SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, | ||||||
|  |     SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, | ||||||
|  |     SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, | ||||||
|  |     SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, | ||||||
|  | } SdSpiToken; | ||||||
|  | 
 | ||||||
|  | /** R1 answer value */ | ||||||
|  | typedef enum { | ||||||
|  |     SdSpi_R1_NO_ERROR = 0x00, | ||||||
|  |     SdSpi_R1_IN_IDLE_STATE = 0x01, | ||||||
|  |     SdSpi_R1_ERASE_RESET = 0x02, | ||||||
|  |     SdSpi_R1_ILLEGAL_COMMAND = 0x04, | ||||||
|  |     SdSpi_R1_COM_CRC_ERROR = 0x08, | ||||||
|  |     SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, | ||||||
|  |     SdSpi_R1_ADDRESS_ERROR = 0x20, | ||||||
|  |     SdSpi_R1_PARAMETER_ERROR = 0x40, | ||||||
|  | } SdSpiR1; | ||||||
|  | 
 | ||||||
|  | /** R2 answer value */ | ||||||
|  | typedef enum { | ||||||
|  |     /* R2 answer value */ | ||||||
|  |     SdSpi_R2_NO_ERROR = 0x00, | ||||||
|  |     SdSpi_R2_CARD_LOCKED = 0x01, | ||||||
|  |     SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, | ||||||
|  |     SdSpi_R2_ERROR = 0x04, | ||||||
|  |     SdSpi_R2_CC_ERROR = 0x08, | ||||||
|  |     SdSpi_R2_CARD_ECC_FAILED = 0x10, | ||||||
|  |     SdSpi_R2_WP_VIOLATION = 0x20, | ||||||
|  |     SdSpi_R2_ERASE_PARAM = 0x40, | ||||||
|  |     SdSpi_R2_OUTOFRANGE = 0x80, | ||||||
|  | } SdSpiR2; | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_select_card() { | ||||||
|  |     furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); | ||||||
|  |     furi_delay_us(10); // Entry guard time for some SD cards
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_deselect_card() { | ||||||
|  |     furi_delay_us(10); // Exit guard time for some SD cards
 | ||||||
|  |     furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sd_spi_bus_to_ground() { | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->miso, | ||||||
|  |         GpioModeOutputPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFnUnused); | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->mosi, | ||||||
|  |         GpioModeOutputPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFnUnused); | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->sck, | ||||||
|  |         GpioModeOutputPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFnUnused); | ||||||
|  | 
 | ||||||
|  |     sd_spi_select_card(); | ||||||
|  |     furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); | ||||||
|  |     furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); | ||||||
|  |     furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sd_spi_bus_rise_up() { | ||||||
|  |     sd_spi_deselect_card(); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->miso, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->mosi, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  |     furi_hal_gpio_init_ex( | ||||||
|  |         furi_hal_sd_spi_handle->sck, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline uint8_t sd_spi_read_byte(void) { | ||||||
|  |     uint8_t responce; | ||||||
|  |     furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); | ||||||
|  |     return responce; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_write_byte(uint8_t data) { | ||||||
|  |     furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { | ||||||
|  |     uint8_t responce; | ||||||
|  |     furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); | ||||||
|  |     return responce; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { | ||||||
|  |     furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { | ||||||
|  |     furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { | ||||||
|  |     uint32_t timeout_mul = (size / 512) + 1; | ||||||
|  |     furi_check(furi_hal_spi_bus_trx_dma( | ||||||
|  |         furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { | ||||||
|  |     uint32_t timeout_mul = (size / 512) + 1; | ||||||
|  |     furi_check(furi_hal_spi_bus_trx_dma( | ||||||
|  |         furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t sd_spi_wait_for_data_and_read(void) { | ||||||
|  |     uint8_t retry_count = SD_ANSWER_RETRY_COUNT; | ||||||
|  |     uint8_t responce; | ||||||
|  | 
 | ||||||
|  |     // Wait until we get a valid data
 | ||||||
|  |     do { | ||||||
|  |         responce = sd_spi_read_byte(); | ||||||
|  |         retry_count--; | ||||||
|  | 
 | ||||||
|  |     } while((responce == SD_DUMMY_BYTE) && retry_count); | ||||||
|  | 
 | ||||||
|  |     return responce; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { | ||||||
|  |     FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); | ||||||
|  |     uint8_t byte; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         byte = sd_spi_read_byte(); | ||||||
|  |         if(furi_hal_cortex_timer_is_expired(timer)) { | ||||||
|  |             return SdSpiStatusTimeout; | ||||||
|  |         } | ||||||
|  |     } while((byte != data)); | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_deselect_card_and_purge() { | ||||||
|  |     sd_spi_deselect_card(); | ||||||
|  |     sd_spi_read_byte(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_spi_purge_crc() { | ||||||
|  |     sd_spi_read_byte(); | ||||||
|  |     sd_spi_read_byte(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiCmdAnswer | ||||||
|  |     sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { | ||||||
|  |     uint8_t frame[SD_CMD_LENGTH]; | ||||||
|  |     SdSpiCmdAnswer cmd_answer = { | ||||||
|  |         .r1 = SD_DUMMY_BYTE, | ||||||
|  |         .r2 = SD_DUMMY_BYTE, | ||||||
|  |         .r3 = SD_DUMMY_BYTE, | ||||||
|  |         .r4 = SD_DUMMY_BYTE, | ||||||
|  |         .r5 = SD_DUMMY_BYTE, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes
 | ||||||
|  |     // R1b identical to R1 + Busy information
 | ||||||
|  |     // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes
 | ||||||
|  | 
 | ||||||
|  |     frame[0] = ((uint8_t)cmd | 0x40); | ||||||
|  |     frame[1] = (uint8_t)(arg >> 24); | ||||||
|  |     frame[2] = (uint8_t)(arg >> 16); | ||||||
|  |     frame[3] = (uint8_t)(arg >> 8); | ||||||
|  |     frame[4] = (uint8_t)(arg); | ||||||
|  |     frame[5] = (crc | 0x01); | ||||||
|  | 
 | ||||||
|  |     sd_spi_select_card(); | ||||||
|  |     sd_spi_write_bytes(frame, sizeof(frame)); | ||||||
|  | 
 | ||||||
|  |     switch(answer_type) { | ||||||
|  |     case SdSpiCmdAnswerTypeR1: | ||||||
|  |         cmd_answer.r1 = sd_spi_wait_for_data_and_read(); | ||||||
|  |         break; | ||||||
|  |     case SdSpiCmdAnswerTypeR1B: | ||||||
|  |         // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1
 | ||||||
|  |         cmd_answer.r1 = sd_spi_wait_for_data_and_read(); | ||||||
|  | 
 | ||||||
|  |         // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B
 | ||||||
|  |         // reassert card
 | ||||||
|  |         sd_spi_deselect_card(); | ||||||
|  |         furi_delay_us(1000); | ||||||
|  |         sd_spi_deselect_card(); | ||||||
|  | 
 | ||||||
|  |         // and wait for it to be ready
 | ||||||
|  |         while(sd_spi_read_byte() != 0xFF) { | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case SdSpiCmdAnswerTypeR2: | ||||||
|  |         cmd_answer.r1 = sd_spi_wait_for_data_and_read(); | ||||||
|  |         cmd_answer.r2 = sd_spi_read_byte(); | ||||||
|  |         break; | ||||||
|  |     case SdSpiCmdAnswerTypeR3: | ||||||
|  |     case SdSpiCmdAnswerTypeR7: | ||||||
|  |         cmd_answer.r1 = sd_spi_wait_for_data_and_read(); | ||||||
|  |         cmd_answer.r2 = sd_spi_read_byte(); | ||||||
|  |         cmd_answer.r3 = sd_spi_read_byte(); | ||||||
|  |         cmd_answer.r4 = sd_spi_read_byte(); | ||||||
|  |         cmd_answer.r5 = sd_spi_read_byte(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return cmd_answer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { | ||||||
|  |     SdSpiDataResponce responce = sd_spi_read_byte(); | ||||||
|  |     // read busy response byte
 | ||||||
|  |     sd_spi_read_byte(); | ||||||
|  | 
 | ||||||
|  |     switch(responce & 0x1F) { | ||||||
|  |     case SdSpiDataResponceOK: | ||||||
|  |         // TODO: check timings
 | ||||||
|  |         sd_spi_deselect_card(); | ||||||
|  |         sd_spi_select_card(); | ||||||
|  | 
 | ||||||
|  |         // wait for 0xFF
 | ||||||
|  |         if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) { | ||||||
|  |             return SdSpiDataResponceOK; | ||||||
|  |         } else { | ||||||
|  |             return SdSpiDataResponceOtherError; | ||||||
|  |         } | ||||||
|  |     case SdSpiDataResponceCRCError: | ||||||
|  |         return SdSpiDataResponceCRCError; | ||||||
|  |     case SdSpiDataResponceWriteError: | ||||||
|  |         return SdSpiDataResponceWriteError; | ||||||
|  |     default: | ||||||
|  |         return SdSpiDataResponceOtherError; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_init_spi_mode_v1(void) { | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  |     uint8_t retry_count = 0; | ||||||
|  | 
 | ||||||
|  |     sd_spi_debug("Init SD card in SPI mode v1"); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         retry_count++; | ||||||
|  | 
 | ||||||
|  |         // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
 | ||||||
|  |         sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
 | ||||||
|  |         response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         if(retry_count >= SD_IDLE_RETRY_COUNT) { | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  |     } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); | ||||||
|  | 
 | ||||||
|  |     sd_spi_debug("Init SD card in SPI mode v1 done"); | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_init_spi_mode_v2(void) { | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  |     uint8_t retry_count = 0; | ||||||
|  | 
 | ||||||
|  |     sd_spi_debug("Init SD card in SPI mode v2"); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         retry_count++; | ||||||
|  |         // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
 | ||||||
|  |         sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
 | ||||||
|  |         response = | ||||||
|  |             sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         if(retry_count >= SD_IDLE_RETRY_COUNT) { | ||||||
|  |             sd_spi_debug("ACMD41 failed"); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  |     } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); | ||||||
|  | 
 | ||||||
|  |     if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { | ||||||
|  |         sd_spi_debug("ACMD41 is illegal command"); | ||||||
|  |         retry_count = 0; | ||||||
|  |         do { | ||||||
|  |             retry_count++; | ||||||
|  |             // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
 | ||||||
|  |             response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |             sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |             if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { | ||||||
|  |                 sd_spi_debug("CMD55 failed"); | ||||||
|  |                 return SdSpiStatusError; | ||||||
|  |             } | ||||||
|  |             // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
 | ||||||
|  |             response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |             sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |             if(retry_count >= SD_IDLE_RETRY_COUNT) { | ||||||
|  |                 sd_spi_debug("ACMD41 failed"); | ||||||
|  |                 return SdSpiStatusError; | ||||||
|  |             } | ||||||
|  |         } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sd_spi_debug("Init SD card in SPI mode v2 done"); | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_init_spi_mode(void) { | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  |     uint8_t retry_count; | ||||||
|  | 
 | ||||||
|  |     // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and
 | ||||||
|  |     // wait for In Idle State Response (R1 Format) equal to 0x01
 | ||||||
|  |     retry_count = 0; | ||||||
|  |     do { | ||||||
|  |         retry_count++; | ||||||
|  |         response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         if(retry_count >= SD_IDLE_RETRY_COUNT) { | ||||||
|  |             sd_spi_debug("CMD0 failed"); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  |     } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); | ||||||
|  | 
 | ||||||
|  |     // CMD8 (SEND_IF_COND) to check the power supply status
 | ||||||
|  |     // and wait until response (R7 Format) equal to 0xAA and
 | ||||||
|  |     response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { | ||||||
|  |         if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) { | ||||||
|  |             sd_spi_debug("Init mode v1 failed"); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  |         sd_high_capacity = 0; | ||||||
|  |     } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { | ||||||
|  |         if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) { | ||||||
|  |             sd_spi_debug("Init mode v2 failed"); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response
 | ||||||
|  |         response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         if(response.r1 != SdSpi_R1_NO_ERROR) { | ||||||
|  |             sd_spi_debug("CMD58 failed"); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  |         sd_high_capacity = (response.r2 & 0x40) >> 6; | ||||||
|  |     } else { | ||||||
|  |         return SdSpiStatusError; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) { | ||||||
|  |     uint16_t counter = 0; | ||||||
|  |     uint8_t csd_data[16]; | ||||||
|  |     SdSpiStatus ret = SdSpiStatusError; | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  | 
 | ||||||
|  |     // CMD9 (SEND_CSD): R1 format (0x00 is no errors)
 | ||||||
|  |     response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  | 
 | ||||||
|  |     if(response.r1 == SdSpi_R1_NO_ERROR) { | ||||||
|  |         if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == | ||||||
|  |            SdSpiStatusOK) { | ||||||
|  |             // read CSD data
 | ||||||
|  |             for(counter = 0; counter < 16; counter++) { | ||||||
|  |                 csd_data[counter] = sd_spi_read_byte(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             sd_spi_purge_crc(); | ||||||
|  | 
 | ||||||
|  |             /*************************************************************************
 | ||||||
|  |             CSD header decoding  | ||||||
|  |             *************************************************************************/ | ||||||
|  | 
 | ||||||
|  |             csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; | ||||||
|  |             csd->Reserved1 = csd_data[0] & 0x3F; | ||||||
|  |             csd->TAAC = csd_data[1]; | ||||||
|  |             csd->NSAC = csd_data[2]; | ||||||
|  |             csd->MaxBusClkFrec = csd_data[3]; | ||||||
|  |             csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); | ||||||
|  |             csd->RdBlockLen = csd_data[5] & 0x0F; | ||||||
|  |             csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; | ||||||
|  |             csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; | ||||||
|  |             csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; | ||||||
|  |             csd->DSRImpl = (csd_data[6] & 0x10) >> 4; | ||||||
|  | 
 | ||||||
|  |             /*************************************************************************
 | ||||||
|  |             CSD v1/v2 decoding   | ||||||
|  |             *************************************************************************/ | ||||||
|  | 
 | ||||||
|  |             if(sd_high_capacity == 0) { | ||||||
|  |                 csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); | ||||||
|  |                 csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | | ||||||
|  |                                              ((csd_data[8] & 0xC0) >> 6); | ||||||
|  |                 csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; | ||||||
|  |                 csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); | ||||||
|  |                 csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; | ||||||
|  |                 csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; | ||||||
|  |                 csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | | ||||||
|  |                                                 ((csd_data[10] & 0x80) >> 7); | ||||||
|  |             } else { | ||||||
|  |                 csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | | ||||||
|  |                                             ((csd_data[7] & 0xC0) >> 6); | ||||||
|  |                 csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | | ||||||
|  |                                              csd_data[9]; | ||||||
|  |                 csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; | ||||||
|  |             csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); | ||||||
|  |             csd->WrProtectGrSize = (csd_data[11] & 0x7F); | ||||||
|  |             csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; | ||||||
|  |             csd->Reserved2 = (csd_data[12] & 0x60) >> 5; | ||||||
|  |             csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; | ||||||
|  |             csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); | ||||||
|  |             csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; | ||||||
|  |             csd->Reserved3 = (csd_data[13] & 0x1F); | ||||||
|  |             csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; | ||||||
|  |             csd->CopyFlag = (csd_data[14] & 0x40) >> 6; | ||||||
|  |             csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; | ||||||
|  |             csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; | ||||||
|  |             csd->FileFormat = (csd_data[14] & 0x0C) >> 2; | ||||||
|  |             csd->Reserved4 = (csd_data[14] & 0x03); | ||||||
|  |             csd->crc = (csd_data[15] & 0xFE) >> 1; | ||||||
|  |             csd->Reserved5 = (csd_data[15] & 0x01); | ||||||
|  | 
 | ||||||
|  |             ret = SdSpiStatusOK; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { | ||||||
|  |     uint16_t counter = 0; | ||||||
|  |     uint8_t cid_data[16]; | ||||||
|  |     SdSpiStatus ret = SdSpiStatusError; | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  | 
 | ||||||
|  |     // CMD10 (SEND_CID): R1 format (0x00 is no errors)
 | ||||||
|  |     response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  | 
 | ||||||
|  |     if(response.r1 == SdSpi_R1_NO_ERROR) { | ||||||
|  |         if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == | ||||||
|  |            SdSpiStatusOK) { | ||||||
|  |             // read CID data
 | ||||||
|  |             for(counter = 0; counter < 16; counter++) { | ||||||
|  |                 cid_data[counter] = sd_spi_read_byte(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             sd_spi_purge_crc(); | ||||||
|  | 
 | ||||||
|  |             Cid->ManufacturerID = cid_data[0]; | ||||||
|  |             memcpy(Cid->OEM_AppliID, cid_data + 1, 2); | ||||||
|  |             memcpy(Cid->ProdName, cid_data + 3, 5); | ||||||
|  |             Cid->ProdRev = cid_data[8]; | ||||||
|  |             Cid->ProdSN = cid_data[9] << 24; | ||||||
|  |             Cid->ProdSN |= cid_data[10] << 16; | ||||||
|  |             Cid->ProdSN |= cid_data[11] << 8; | ||||||
|  |             Cid->ProdSN |= cid_data[12]; | ||||||
|  |             Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; | ||||||
|  |             Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; | ||||||
|  |             Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; | ||||||
|  |             Cid->Reserved2 = 1; | ||||||
|  | 
 | ||||||
|  |             ret = SdSpiStatusOK; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool sd_cache_get(uint32_t address, uint32_t* data) { | ||||||
|  |     uint8_t* cached_data = sector_cache_get(address); | ||||||
|  |     if(cached_data) { | ||||||
|  |         memcpy(data, cached_data, SD_BLOCK_SIZE); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_cache_put(uint32_t address, uint32_t* data) { | ||||||
|  |     sector_cache_put(address, (uint8_t*)data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { | ||||||
|  |     sector_cache_invalidate_range(start_sector, end_sector); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus | ||||||
|  |     sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { | ||||||
|  |     uint32_t block_address = address; | ||||||
|  |     uint32_t offset = 0; | ||||||
|  | 
 | ||||||
|  |     // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
 | ||||||
|  |     SdSpiCmdAnswer response = | ||||||
|  |         sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     if(response.r1 != SdSpi_R1_NO_ERROR) { | ||||||
|  |         return SdSpiStatusError; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!sd_high_capacity) { | ||||||
|  |         block_address = address * SD_BLOCK_SIZE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(blocks--) { | ||||||
|  |         // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors)
 | ||||||
|  |         response = | ||||||
|  |             sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         if(response.r1 != SdSpi_R1_NO_ERROR) { | ||||||
|  |             sd_spi_deselect_card_and_purge(); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Wait for the data start token
 | ||||||
|  |         if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == | ||||||
|  |            SdSpiStatusOK) { | ||||||
|  |             // Read the data block
 | ||||||
|  |             sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); | ||||||
|  |             sd_spi_purge_crc(); | ||||||
|  | 
 | ||||||
|  |             // increase offset
 | ||||||
|  |             offset += SD_BLOCK_SIZE; | ||||||
|  | 
 | ||||||
|  |             // increase block address
 | ||||||
|  |             if(sd_high_capacity) { | ||||||
|  |                 block_address += 1; | ||||||
|  |             } else { | ||||||
|  |                 block_address += SD_BLOCK_SIZE; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             sd_spi_deselect_card_and_purge(); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SdSpiStatus sd_spi_cmd_write_blocks( | ||||||
|  |     uint32_t* data, | ||||||
|  |     uint32_t address, | ||||||
|  |     uint32_t blocks, | ||||||
|  |     uint32_t timeout_ms) { | ||||||
|  |     uint32_t block_address = address; | ||||||
|  |     uint32_t offset = 0; | ||||||
|  | 
 | ||||||
|  |     // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
 | ||||||
|  |     SdSpiCmdAnswer response = | ||||||
|  |         sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     if(response.r1 != SdSpi_R1_NO_ERROR) { | ||||||
|  |         return SdSpiStatusError; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!sd_high_capacity) { | ||||||
|  |         block_address = address * SD_BLOCK_SIZE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(blocks--) { | ||||||
|  |         // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors)
 | ||||||
|  |         response = sd_spi_send_cmd( | ||||||
|  |             SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); | ||||||
|  |         if(response.r1 != SdSpi_R1_NO_ERROR) { | ||||||
|  |             sd_spi_deselect_card_and_purge(); | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN
 | ||||||
|  |         // TODO: check bytes count
 | ||||||
|  |         sd_spi_write_byte(SD_DUMMY_BYTE); | ||||||
|  |         sd_spi_write_byte(SD_DUMMY_BYTE); | ||||||
|  | 
 | ||||||
|  |         // Send the data start token
 | ||||||
|  |         sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); | ||||||
|  |         sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); | ||||||
|  |         sd_spi_purge_crc(); | ||||||
|  | 
 | ||||||
|  |         // Read data response
 | ||||||
|  |         SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); | ||||||
|  |         sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |         if(data_responce != SdSpiDataResponceOK) { | ||||||
|  |             return SdSpiStatusError; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // increase offset
 | ||||||
|  |         offset += SD_BLOCK_SIZE; | ||||||
|  | 
 | ||||||
|  |         // increase block address
 | ||||||
|  |         if(sd_high_capacity) { | ||||||
|  |             block_address += 1; | ||||||
|  |         } else { | ||||||
|  |             block_address += SD_BLOCK_SIZE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusOK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t sd_max_mount_retry_count() { | ||||||
|  |     return 10; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus sd_init(bool power_reset) { | ||||||
|  |     // Slow speed init
 | ||||||
|  |     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); | ||||||
|  |     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; | ||||||
|  | 
 | ||||||
|  |     // We reset card in spi_lock context, so it is safe to disturb spi bus
 | ||||||
|  |     if(power_reset) { | ||||||
|  |         sd_spi_debug("Power reset"); | ||||||
|  | 
 | ||||||
|  |         // disable power and set low on all bus pins
 | ||||||
|  |         furi_hal_power_disable_external_3_3v(); | ||||||
|  |         sd_spi_bus_to_ground(); | ||||||
|  |         hal_sd_detect_set_low(); | ||||||
|  |         furi_delay_ms(250); | ||||||
|  | 
 | ||||||
|  |         // reinit bus and enable power
 | ||||||
|  |         sd_spi_bus_rise_up(); | ||||||
|  |         hal_sd_detect_init(); | ||||||
|  |         furi_hal_power_enable_external_3_3v(); | ||||||
|  |         furi_delay_ms(100); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SdSpiStatus status = SdSpiStatusError; | ||||||
|  | 
 | ||||||
|  |     // Send 80 dummy clocks with CS high
 | ||||||
|  |     sd_spi_deselect_card(); | ||||||
|  |     for(uint8_t i = 0; i < 80; i++) { | ||||||
|  |         sd_spi_write_byte(SD_DUMMY_BYTE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < 128; i++) { | ||||||
|  |         status = sd_spi_init_spi_mode(); | ||||||
|  |         if(status == SdSpiStatusOK) { | ||||||
|  |             // SD initialized and init to SPI mode properly
 | ||||||
|  |             sd_spi_debug("SD init OK after %d retries", i); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_sd_spi_handle = NULL; | ||||||
|  |     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); | ||||||
|  | 
 | ||||||
|  |     // Init sector cache
 | ||||||
|  |     sector_cache_init(); | ||||||
|  | 
 | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus sd_get_card_state(void) { | ||||||
|  |     SdSpiCmdAnswer response; | ||||||
|  | 
 | ||||||
|  |     // Send CMD13 (SEND_STATUS) to get SD status
 | ||||||
|  |     response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); | ||||||
|  |     sd_spi_deselect_card_and_purge(); | ||||||
|  | 
 | ||||||
|  |     // Return status OK if response is valid
 | ||||||
|  |     if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { | ||||||
|  |         return SdSpiStatusOK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return SdSpiStatusError; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { | ||||||
|  |     SdSpiStatus status; | ||||||
|  | 
 | ||||||
|  |     status = sd_spi_get_csd(&(card_info->Csd)); | ||||||
|  | 
 | ||||||
|  |     if(status != SdSpiStatusOK) { | ||||||
|  |         return status; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     status = sd_spi_get_cid(&(card_info->Cid)); | ||||||
|  | 
 | ||||||
|  |     if(status != SdSpiStatusOK) { | ||||||
|  |         return status; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(sd_high_capacity == 1) { | ||||||
|  |         card_info->LogBlockSize = 512; | ||||||
|  |         card_info->CardBlockSize = 512; | ||||||
|  |         card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL * | ||||||
|  |                                   (uint64_t)card_info->LogBlockSize; | ||||||
|  |         card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); | ||||||
|  |     } else { | ||||||
|  |         card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1); | ||||||
|  |         card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2)); | ||||||
|  |         card_info->LogBlockSize = 512; | ||||||
|  |         card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen); | ||||||
|  |         card_info->CardCapacity *= card_info->CardBlockSize; | ||||||
|  |         card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus | ||||||
|  |     sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { | ||||||
|  |     SdSpiStatus status = SdSpiStatusError; | ||||||
|  | 
 | ||||||
|  |     bool single_sector_read = (blocks == 1); | ||||||
|  | 
 | ||||||
|  |     if(single_sector_read) { | ||||||
|  |         if(sd_cache_get(address, data)) { | ||||||
|  |             return SdSpiStatusOK; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); | ||||||
|  | 
 | ||||||
|  |         if(status == SdSpiStatusOK) { | ||||||
|  |             sd_cache_put(address, data); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus | ||||||
|  |     sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { | ||||||
|  |     sd_cache_invalidate_range(address, address + blocks); | ||||||
|  |     SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SdSpiStatus sd_get_cid(SD_CID* cid) { | ||||||
|  |     SdSpiStatus status; | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); | ||||||
|  |     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; | ||||||
|  | 
 | ||||||
|  |     memset(cid, 0, sizeof(SD_CID)); | ||||||
|  |     status = sd_spi_get_cid(cid); | ||||||
|  | 
 | ||||||
|  |     furi_hal_sd_spi_handle = NULL; | ||||||
|  |     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); | ||||||
|  | 
 | ||||||
|  |     return status; | ||||||
|  | } | ||||||
							
								
								
									
										157
									
								
								firmware/targets/f7/fatfs/sd_spi_io.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								firmware/targets/f7/fatfs/sd_spi_io.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #define __IO volatile | ||||||
|  | 
 | ||||||
|  | #define SD_TIMEOUT_MS (1000) | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SdSpiStatusOK, | ||||||
|  |     SdSpiStatusError, | ||||||
|  |     SdSpiStatusTimeout, | ||||||
|  | } SdSpiStatus; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Card Specific Data: CSD Register | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     /* Header part */ | ||||||
|  |     uint8_t CSDStruct : 2; /* CSD structure */ | ||||||
|  |     uint8_t Reserved1 : 6; /* Reserved */ | ||||||
|  |     uint8_t TAAC : 8; /* Data read access-time 1 */ | ||||||
|  |     uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ | ||||||
|  |     uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ | ||||||
|  |     uint16_t CardComdClasses : 12; /* Card command classes */ | ||||||
|  |     uint8_t RdBlockLen : 4; /* Max. read data block length */ | ||||||
|  |     uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ | ||||||
|  |     uint8_t WrBlockMisalign : 1; /* Write block misalignment */ | ||||||
|  |     uint8_t RdBlockMisalign : 1; /* Read block misalignment */ | ||||||
|  |     uint8_t DSRImpl : 1; /* DSR implemented */ | ||||||
|  | 
 | ||||||
|  |     /* v1 or v2 struct */ | ||||||
|  |     union csd_version { | ||||||
|  |         struct { | ||||||
|  |             uint8_t Reserved1 : 2; /* Reserved */ | ||||||
|  |             uint16_t DeviceSize : 12; /* Device Size */ | ||||||
|  |             uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ | ||||||
|  |             uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ | ||||||
|  |             uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ | ||||||
|  |             uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ | ||||||
|  |             uint8_t DeviceSizeMul : 3; /* Device size multiplier */ | ||||||
|  |         } v1; | ||||||
|  |         struct { | ||||||
|  |             uint8_t Reserved1 : 6; /* Reserved */ | ||||||
|  |             uint32_t DeviceSize : 22; /* Device Size */ | ||||||
|  |             uint8_t Reserved2 : 1; /* Reserved */ | ||||||
|  |         } v2; | ||||||
|  |     } version; | ||||||
|  | 
 | ||||||
|  |     uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ | ||||||
|  |     uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ | ||||||
|  |     uint8_t WrProtectGrSize : 7; /* Write protect group size */ | ||||||
|  |     uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ | ||||||
|  |     uint8_t Reserved2 : 2; /* Reserved */ | ||||||
|  |     uint8_t WrSpeedFact : 3; /* Write speed factor */ | ||||||
|  |     uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ | ||||||
|  |     uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ | ||||||
|  |     uint8_t Reserved3 : 5; /* Reserved */ | ||||||
|  |     uint8_t FileFormatGrouop : 1; /* File format group */ | ||||||
|  |     uint8_t CopyFlag : 1; /* Copy flag (OTP) */ | ||||||
|  |     uint8_t PermWrProtect : 1; /* Permanent write protection */ | ||||||
|  |     uint8_t TempWrProtect : 1; /* Temporary write protection */ | ||||||
|  |     uint8_t FileFormat : 2; /* File Format */ | ||||||
|  |     uint8_t Reserved4 : 2; /* Reserved */ | ||||||
|  |     uint8_t crc : 7; /* Reserved */ | ||||||
|  |     uint8_t Reserved5 : 1; /* always 1*/ | ||||||
|  | 
 | ||||||
|  | } SD_CSD; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Card Identification Data: CID Register | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t ManufacturerID; /* ManufacturerID */ | ||||||
|  |     char OEM_AppliID[2]; /* OEM/Application ID */ | ||||||
|  |     char ProdName[5]; /* Product Name */ | ||||||
|  |     uint8_t ProdRev; /* Product Revision */ | ||||||
|  |     uint32_t ProdSN; /* Product Serial Number */ | ||||||
|  |     uint8_t Reserved1; /* Reserved1 */ | ||||||
|  |     uint8_t ManufactYear; /* Manufacturing Year */ | ||||||
|  |     uint8_t ManufactMonth; /* Manufacturing Month */ | ||||||
|  |     uint8_t CID_CRC; /* CID CRC */ | ||||||
|  |     uint8_t Reserved2; /* always 1 */ | ||||||
|  | } SD_CID; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief SD Card information structure | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     SD_CSD Csd; | ||||||
|  |     SD_CID Cid; | ||||||
|  |     uint64_t CardCapacity; /*!< Card Capacity */ | ||||||
|  |     uint32_t CardBlockSize; /*!< Card Block Size */ | ||||||
|  |     uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks   */ | ||||||
|  |     uint32_t LogBlockSize; /*!< Specifies logical block size in bytes           */ | ||||||
|  | } SD_CardInfo; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief SD card max mount retry count | ||||||
|  |  *  | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t sd_max_mount_retry_count(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Init sd card | ||||||
|  |  *  | ||||||
|  |  * @param power_reset reset card power | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus sd_init(bool power_reset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get card state | ||||||
|  |  *  | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus sd_get_card_state(void); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get card info | ||||||
|  |  *  | ||||||
|  |  * @param card_info  | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus sd_get_card_info(SD_CardInfo* card_info); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Read blocks | ||||||
|  |  *  | ||||||
|  |  * @param data  | ||||||
|  |  * @param address  | ||||||
|  |  * @param blocks  | ||||||
|  |  * @param timeout_ms  | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Write blocks | ||||||
|  |  *  | ||||||
|  |  * @param data  | ||||||
|  |  * @param address  | ||||||
|  |  * @param blocks  | ||||||
|  |  * @param timeout_ms  | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus | ||||||
|  |     sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Get card CSD register | ||||||
|  |  *  | ||||||
|  |  * @param Cid  | ||||||
|  |  * @return SdSpiStatus  | ||||||
|  |  */ | ||||||
|  | SdSpiStatus sd_get_cid(SD_CID* cid); | ||||||
| @ -8,7 +8,6 @@ | |||||||
| 
 | 
 | ||||||
| #define SECTOR_SIZE 512 | #define SECTOR_SIZE 512 | ||||||
| #define N_SECTORS 8 | #define N_SECTORS 8 | ||||||
| #define TAG "SDCache" |  | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint32_t itr; |     uint32_t itr; | ||||||
| @ -20,15 +19,11 @@ static SectorCache* cache = NULL; | |||||||
| 
 | 
 | ||||||
| void sector_cache_init() { | void sector_cache_init() { | ||||||
|     if(cache == NULL) { |     if(cache == NULL) { | ||||||
|         // TODO: tuneup allocation order, to place cache in mem pool (MEM2)
 |  | ||||||
|         cache = memmgr_alloc_from_pool(sizeof(SectorCache)); |         cache = memmgr_alloc_from_pool(sizeof(SectorCache)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(cache != NULL) { |     if(cache != NULL) { | ||||||
|         FURI_LOG_I(TAG, "Init"); |  | ||||||
|         memset(cache, 0, sizeof(SectorCache)); |         memset(cache, 0, sizeof(SectorCache)); | ||||||
|     } else { |  | ||||||
|         FURI_LOG_E(TAG, "Init failed"); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,98 +0,0 @@ | |||||||
| #include <furi_hal.h> |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| #define SD_DUMMY_BYTE 0xFF |  | ||||||
| 
 |  | ||||||
| const uint32_t SpiTimeout = 1000; |  | ||||||
| uint8_t SD_IO_WriteByte(uint8_t Data); |  | ||||||
| 
 |  | ||||||
| /******************************************************************************
 |  | ||||||
|                             BUS OPERATIONS |  | ||||||
|  *******************************************************************************/ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief  SPI Write byte(s) to device |  | ||||||
|  * @param  DataIn: Pointer to data buffer to write |  | ||||||
|  * @param  DataOut: Pointer to data buffer for read data |  | ||||||
|  * @param  DataLength: number of bytes to write |  | ||||||
|  * @retval None |  | ||||||
|  */ |  | ||||||
| static void SPIx_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { |  | ||||||
|     furi_check(furi_hal_spi_bus_trx( |  | ||||||
|         furi_hal_sd_spi_handle, (uint8_t*)DataIn, DataOut, DataLength, SpiTimeout)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief  SPI Write a byte to device |  | ||||||
|  * @param  Value: value to be written |  | ||||||
|  * @retval None |  | ||||||
|  */ |  | ||||||
| __attribute__((unused)) static void SPIx_Write(uint8_t Value) { |  | ||||||
|     furi_check(furi_hal_spi_bus_tx(furi_hal_sd_spi_handle, (uint8_t*)&Value, 1, SpiTimeout)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /******************************************************************************
 |  | ||||||
|                             LINK OPERATIONS |  | ||||||
|  *******************************************************************************/ |  | ||||||
| 
 |  | ||||||
| /********************************* LINK SD ************************************/ |  | ||||||
| /**
 |  | ||||||
|  * @brief  Initialize the SD Card and put it into StandBy State (Ready for |  | ||||||
|  *         data transfer). |  | ||||||
|  * @retval None |  | ||||||
|  */ |  | ||||||
| void SD_IO_Init(void) { |  | ||||||
|     uint8_t counter = 0; |  | ||||||
| 
 |  | ||||||
|     /* SD chip select high */ |  | ||||||
|     furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); |  | ||||||
|     furi_delay_us(10); |  | ||||||
| 
 |  | ||||||
|     /* Send dummy byte 0xFF, 10 times with CS high */ |  | ||||||
|     /* Rise CS and MOSI for 80 clocks cycles */ |  | ||||||
|     for(counter = 0; counter <= 200; counter++) { |  | ||||||
|         /* Send dummy byte 0xFF */ |  | ||||||
|         SD_IO_WriteByte(SD_DUMMY_BYTE); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief  Set SD interface Chip Select state |  | ||||||
|  * @param  val: 0 (low) or 1 (high) state |  | ||||||
|  * @retval None |  | ||||||
|  */ |  | ||||||
| void SD_IO_CSState(uint8_t val) { |  | ||||||
|     /* Some SD Cards are prone to fail if CLK-ed too soon after CS transition. Worst case found: 8us */ |  | ||||||
|     if(val == 1) { |  | ||||||
|         furi_delay_us(10); // Exit guard time for some SD cards
 |  | ||||||
|         furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); |  | ||||||
|     } else { |  | ||||||
|         furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); |  | ||||||
|         furi_delay_us(10); // Entry guard time for some SD cards
 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief  Write byte(s) on the SD |  | ||||||
|  * @param  DataIn: Pointer to data buffer to write |  | ||||||
|  * @param  DataOut: Pointer to data buffer for read data |  | ||||||
|  * @param  DataLength: number of bytes to write |  | ||||||
|  * @retval None |  | ||||||
|  */ |  | ||||||
| void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { |  | ||||||
|     /* Send the byte */ |  | ||||||
|     SPIx_WriteReadData(DataIn, DataOut, DataLength); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief  Write a byte on the SD. |  | ||||||
|  * @param  Data: byte to send. |  | ||||||
|  * @retval Data written |  | ||||||
|  */ |  | ||||||
| uint8_t SD_IO_WriteByte(uint8_t Data) { |  | ||||||
|     uint8_t tmp; |  | ||||||
| 
 |  | ||||||
|     /* Send the byte */ |  | ||||||
|     SPIx_WriteReadData(&Data, &tmp, 1); |  | ||||||
|     return tmp; |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,245 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   ****************************************************************************** |  | ||||||
|   * @file    stm32_adafruit_sd.h |  | ||||||
|   * @author  MCD Application Team |  | ||||||
|   * @version V3.0.0 |  | ||||||
|   * @date    23-December-2016 |  | ||||||
|   * @brief   This file contains the common defines and functions prototypes for |  | ||||||
|   *          the stm32_adafruit_sd.c driver. |  | ||||||
|   ****************************************************************************** |  | ||||||
|   * @attention |  | ||||||
|   * |  | ||||||
|   * <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2> |  | ||||||
|   * |  | ||||||
|   * Redistribution and use in source and binary forms, with or without modification, |  | ||||||
|   * are permitted provided that the following conditions are met: |  | ||||||
|   *   1. Redistributions of source code must retain the above copyright notice, |  | ||||||
|   *      this list of conditions and the following disclaimer. |  | ||||||
|   *   2. Redistributions in binary form must reproduce the above copyright notice, |  | ||||||
|   *      this list of conditions and the following disclaimer in the documentation |  | ||||||
|   *      and/or other materials provided with the distribution. |  | ||||||
|   *   3. Neither the name of STMicroelectronics nor the names of its contributors |  | ||||||
|   *      may be used to endorse or promote products derived from this software |  | ||||||
|   *      without specific prior written permission. |  | ||||||
|   * |  | ||||||
|   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |  | ||||||
|   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |  | ||||||
|   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |  | ||||||
|   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |  | ||||||
|   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |  | ||||||
|   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |  | ||||||
|   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |  | ||||||
|   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |  | ||||||
|   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | ||||||
|   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | ||||||
|   * |  | ||||||
|   ****************************************************************************** |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /* Define to prevent recursive inclusion -------------------------------------*/ |  | ||||||
| #ifndef __STM32_ADAFRUIT_SD_H |  | ||||||
| #define __STM32_ADAFRUIT_SD_H |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /* Includes ------------------------------------------------------------------*/ |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| 
 |  | ||||||
| /** @addtogroup BSP
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| #define __IO volatile |  | ||||||
| 
 |  | ||||||
| /** @addtogroup STM32_ADAFRUIT
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** @defgroup STM32_ADAFRUIT_SD
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** @defgroup STM32_ADAFRUIT_SD_Exported_Types
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|   * @brief  SD status structure definition   |  | ||||||
|   */ |  | ||||||
| enum { BSP_SD_OK = 0x00, MSD_OK = 0x00, BSP_SD_ERROR = 0x01, BSP_SD_TIMEOUT }; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     uint8_t Reserved1 : 2; /* Reserved */ |  | ||||||
|     uint16_t DeviceSize : 12; /* Device Size */ |  | ||||||
|     uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ |  | ||||||
|     uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ |  | ||||||
|     uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ |  | ||||||
|     uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ |  | ||||||
|     uint8_t DeviceSizeMul : 3; /* Device size multiplier */ |  | ||||||
| } struct_v1; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     uint8_t Reserved1 : 6; /* Reserved */ |  | ||||||
|     uint32_t DeviceSize : 22; /* Device Size */ |  | ||||||
|     uint8_t Reserved2 : 1; /* Reserved */ |  | ||||||
| } struct_v2; |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|   * @brief  Card Specific Data: CSD Register |  | ||||||
|   */ |  | ||||||
| typedef struct { |  | ||||||
|     /* Header part */ |  | ||||||
|     uint8_t CSDStruct : 2; /* CSD structure */ |  | ||||||
|     uint8_t Reserved1 : 6; /* Reserved */ |  | ||||||
|     uint8_t TAAC : 8; /* Data read access-time 1 */ |  | ||||||
|     uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ |  | ||||||
|     uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ |  | ||||||
|     uint16_t CardComdClasses : 12; /* Card command classes */ |  | ||||||
|     uint8_t RdBlockLen : 4; /* Max. read data block length */ |  | ||||||
|     uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ |  | ||||||
|     uint8_t WrBlockMisalign : 1; /* Write block misalignment */ |  | ||||||
|     uint8_t RdBlockMisalign : 1; /* Read block misalignment */ |  | ||||||
|     uint8_t DSRImpl : 1; /* DSR implemented */ |  | ||||||
| 
 |  | ||||||
|     /* v1 or v2 struct */ |  | ||||||
|     union csd_version { |  | ||||||
|         struct_v1 v1; |  | ||||||
|         struct_v2 v2; |  | ||||||
|     } version; |  | ||||||
| 
 |  | ||||||
|     uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ |  | ||||||
|     uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ |  | ||||||
|     uint8_t WrProtectGrSize : 7; /* Write protect group size */ |  | ||||||
|     uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ |  | ||||||
|     uint8_t Reserved2 : 2; /* Reserved */ |  | ||||||
|     uint8_t WrSpeedFact : 3; /* Write speed factor */ |  | ||||||
|     uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ |  | ||||||
|     uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ |  | ||||||
|     uint8_t Reserved3 : 5; /* Reserved */ |  | ||||||
|     uint8_t FileFormatGrouop : 1; /* File format group */ |  | ||||||
|     uint8_t CopyFlag : 1; /* Copy flag (OTP) */ |  | ||||||
|     uint8_t PermWrProtect : 1; /* Permanent write protection */ |  | ||||||
|     uint8_t TempWrProtect : 1; /* Temporary write protection */ |  | ||||||
|     uint8_t FileFormat : 2; /* File Format */ |  | ||||||
|     uint8_t Reserved4 : 2; /* Reserved */ |  | ||||||
|     uint8_t crc : 7; /* Reserved */ |  | ||||||
|     uint8_t Reserved5 : 1; /* always 1*/ |  | ||||||
| 
 |  | ||||||
| } SD_CSD; |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|   * @brief  Card Identification Data: CID Register    |  | ||||||
|   */ |  | ||||||
| typedef struct { |  | ||||||
|     uint8_t ManufacturerID; /* ManufacturerID */ |  | ||||||
|     char OEM_AppliID[2]; /* OEM/Application ID */ |  | ||||||
|     char ProdName[5]; /* Product Name */ |  | ||||||
|     uint8_t ProdRev; /* Product Revision */ |  | ||||||
|     uint32_t ProdSN; /* Product Serial Number */ |  | ||||||
|     uint8_t Reserved1; /* Reserved1 */ |  | ||||||
|     uint8_t ManufactYear; /* Manufacturing Year */ |  | ||||||
|     uint8_t ManufactMonth; /* Manufacturing Month */ |  | ||||||
|     uint8_t CID_CRC; /* CID CRC */ |  | ||||||
|     uint8_t Reserved2; /* always 1 */ |  | ||||||
| } SD_CID; |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|   * @brief SD Card information  |  | ||||||
|   */ |  | ||||||
| typedef struct { |  | ||||||
|     SD_CSD Csd; |  | ||||||
|     SD_CID Cid; |  | ||||||
|     uint64_t CardCapacity; /*!< Card Capacity */ |  | ||||||
|     uint32_t CardBlockSize; /*!< Card Block Size */ |  | ||||||
|     uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks   */ |  | ||||||
|     uint32_t LogBlockSize; /*!< Specifies logical block size in bytes           */ |  | ||||||
| } SD_CardInfo; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** @defgroup STM32_ADAFRUIT_SPI_SD_Exported_Constants
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @brief  Block Size |  | ||||||
|   */ |  | ||||||
| #define SD_BLOCK_SIZE 0x200 |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @brief  SD detection on its memory slot |  | ||||||
|   */ |  | ||||||
| #define SD_PRESENT ((uint8_t)0x01) |  | ||||||
| #define SD_NOT_PRESENT ((uint8_t)0x00) |  | ||||||
| 
 |  | ||||||
| #define SD_DATATIMEOUT ((uint32_t)100000000) |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|   * @brief SD Card information structure  |  | ||||||
|   */ |  | ||||||
| #define BSP_SD_CardInfo SD_CardInfo |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** @defgroup STM32_ADAFRUIT_SD_Exported_Macro
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /** @defgroup STM32_ADAFRUIT_SD_Exported_Functions
 |  | ||||||
|   * @{ |  | ||||||
|   */ |  | ||||||
| uint8_t BSP_SD_MaxMountRetryCount(); |  | ||||||
| uint8_t BSP_SD_Init(bool reset_card); |  | ||||||
| uint8_t |  | ||||||
|     BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); |  | ||||||
| uint8_t |  | ||||||
|     BSP_SD_WriteBlocks(uint32_t* pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); |  | ||||||
| uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); |  | ||||||
| uint8_t BSP_SD_GetCardState(void); |  | ||||||
| uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); |  | ||||||
| uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); |  | ||||||
| 
 |  | ||||||
| /* Link functions for SD Card peripheral*/ |  | ||||||
| void SD_SPI_Slow_Init(void); |  | ||||||
| void SD_SPI_Fast_Init(void); |  | ||||||
| void SD_IO_Init(void); |  | ||||||
| void SD_IO_CSState(uint8_t state); |  | ||||||
| void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength); |  | ||||||
| uint8_t SD_IO_WriteByte(uint8_t Data); |  | ||||||
| 
 |  | ||||||
| /* Link function for HAL delay */ |  | ||||||
| void HAL_Delay(__IO uint32_t Delay); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #endif /* __STM32_ADAFRUIT_SD_H */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|   * @} |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |  | ||||||
| @ -46,7 +46,7 @@ static volatile DSTATUS Stat = STA_NOINIT; | |||||||
| static DSTATUS User_CheckStatus(BYTE lun) { | static DSTATUS User_CheckStatus(BYTE lun) { | ||||||
|     UNUSED(lun); |     UNUSED(lun); | ||||||
|     Stat = STA_NOINIT; |     Stat = STA_NOINIT; | ||||||
|     if(BSP_SD_GetCardState() == MSD_OK) { |     if(sd_get_card_state() == SdSpiStatusOK) { | ||||||
|         Stat &= ~STA_NOINIT; |         Stat &= ~STA_NOINIT; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -128,11 +128,18 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { | |||||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); |     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); | ||||||
|     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; |     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; | ||||||
| 
 | 
 | ||||||
|     if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { |     if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == | ||||||
|  |        SdSpiStatusOK) { | ||||||
|  |         FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); | ||||||
|  | 
 | ||||||
|         /* wait until the read operation is finished */ |         /* wait until the read operation is finished */ | ||||||
|         while(BSP_SD_GetCardState() != MSD_OK) { |  | ||||||
|         } |  | ||||||
|         res = RES_OK; |         res = RES_OK; | ||||||
|  |         while(sd_get_card_state() != SdSpiStatusOK) { | ||||||
|  |             if(furi_hal_cortex_timer_is_expired(timer)) { | ||||||
|  |                 res = RES_ERROR; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_hal_sd_spi_handle = NULL; |     furi_hal_sd_spi_handle = NULL; | ||||||
| @ -160,11 +167,18 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { | |||||||
|     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); |     furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); | ||||||
|     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; |     furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; | ||||||
| 
 | 
 | ||||||
|     if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { |     if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == | ||||||
|  |        SdSpiStatusOK) { | ||||||
|  |         FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); | ||||||
|  | 
 | ||||||
|         /* wait until the Write operation is finished */ |         /* wait until the Write operation is finished */ | ||||||
|         while(BSP_SD_GetCardState() != MSD_OK) { |  | ||||||
|         } |  | ||||||
|         res = RES_OK; |         res = RES_OK; | ||||||
|  |         while(sd_get_card_state() != SdSpiStatusOK) { | ||||||
|  |             if(furi_hal_cortex_timer_is_expired(timer)) { | ||||||
|  |                 res = RES_ERROR; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_hal_sd_spi_handle = NULL; |     furi_hal_sd_spi_handle = NULL; | ||||||
| @ -187,7 +201,7 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { | |||||||
|     /* USER CODE BEGIN IOCTL */ |     /* USER CODE BEGIN IOCTL */ | ||||||
|     UNUSED(pdrv); |     UNUSED(pdrv); | ||||||
|     DRESULT res = RES_ERROR; |     DRESULT res = RES_ERROR; | ||||||
|     BSP_SD_CardInfo CardInfo; |     SD_CardInfo CardInfo; | ||||||
| 
 | 
 | ||||||
|     if(Stat & STA_NOINIT) return RES_NOTRDY; |     if(Stat & STA_NOINIT) return RES_NOTRDY; | ||||||
| 
 | 
 | ||||||
| @ -202,21 +216,21 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { | |||||||
| 
 | 
 | ||||||
|     /* Get number of sectors on the disk (DWORD) */ |     /* Get number of sectors on the disk (DWORD) */ | ||||||
|     case GET_SECTOR_COUNT: |     case GET_SECTOR_COUNT: | ||||||
|         BSP_SD_GetCardInfo(&CardInfo); |         sd_get_card_info(&CardInfo); | ||||||
|         *(DWORD*)buff = CardInfo.LogBlockNbr; |         *(DWORD*)buff = CardInfo.LogBlockNbr; | ||||||
|         res = RES_OK; |         res = RES_OK; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     /* Get R/W sector size (WORD) */ |     /* Get R/W sector size (WORD) */ | ||||||
|     case GET_SECTOR_SIZE: |     case GET_SECTOR_SIZE: | ||||||
|         BSP_SD_GetCardInfo(&CardInfo); |         sd_get_card_info(&CardInfo); | ||||||
|         *(WORD*)buff = CardInfo.LogBlockSize; |         *(WORD*)buff = CardInfo.LogBlockSize; | ||||||
|         res = RES_OK; |         res = RES_OK; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     /* Get erase block size in unit of sector (DWORD) */ |     /* Get erase block size in unit of sector (DWORD) */ | ||||||
|     case GET_BLOCK_SIZE: |     case GET_BLOCK_SIZE: | ||||||
|         BSP_SD_GetCardInfo(&CardInfo); |         sd_get_card_info(&CardInfo); | ||||||
|         *(DWORD*)buff = CardInfo.LogBlockSize; |         *(DWORD*)buff = CardInfo.LogBlockSize; | ||||||
|         res = RES_OK; |         res = RES_OK; | ||||||
|         break; |         break; | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ extern "C" { | |||||||
| /* USER CODE BEGIN 0 */ | /* USER CODE BEGIN 0 */ | ||||||
| 
 | 
 | ||||||
| /* Includes ------------------------------------------------------------------*/ | /* Includes ------------------------------------------------------------------*/ | ||||||
| #include "stm32_adafruit_sd.h" | #include "sd_spi_io.h" | ||||||
| #include "fatfs/ff_gen_drv.h" | #include "fatfs/ff_gen_drv.h" | ||||||
| /* Exported types ------------------------------------------------------------*/ | /* Exported types ------------------------------------------------------------*/ | ||||||
| /* Exported constants --------------------------------------------------------*/ | /* Exported constants --------------------------------------------------------*/ | ||||||
|  | |||||||
| @ -53,6 +53,7 @@ void furi_hal_init() { | |||||||
|     furi_hal_region_init(); |     furi_hal_region_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_spi_config_init(); |     furi_hal_spi_config_init(); | ||||||
|  |     furi_hal_spi_dma_init(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_ibutton_init(); |     furi_hal_ibutton_init(); | ||||||
|     FURI_LOG_I(TAG, "iButton OK"); |     FURI_LOG_I(TAG, "iButton OK"); | ||||||
|  | |||||||
| @ -28,6 +28,15 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GPIO_PIN_7}; | |||||||
| #define INFRARED_TX_CCMR_LOW \ | #define INFRARED_TX_CCMR_LOW \ | ||||||
|     (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ |     (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ | ||||||
| 
 | 
 | ||||||
|  | /* DMA Channels definition */ | ||||||
|  | #define IR_DMA DMA2 | ||||||
|  | #define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 | ||||||
|  | #define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 | ||||||
|  | #define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 | ||||||
|  | #define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 | ||||||
|  | #define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL | ||||||
|  | #define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriHalInfraredRxCaptureCallback capture_callback; |     FuriHalInfraredRxCaptureCallback capture_callback; | ||||||
|     void* capture_context; |     void* capture_context; | ||||||
| @ -213,15 +222,15 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_tx_dma_terminate(void) { | static void furi_hal_infrared_tx_dma_terminate(void) { | ||||||
|     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); | ||||||
|     LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); | ||||||
|     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF); | ||||||
| 
 | 
 | ||||||
|     furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); |     furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); | ||||||
| 
 | 
 | ||||||
|     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); | ||||||
|     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_DisableChannel(IR_DMA_CH2_DEF); | ||||||
|     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DisableChannel(IR_DMA_CH1_DEF); | ||||||
|     LL_TIM_DisableCounter(TIM1); |     LL_TIM_DisableCounter(TIM1); | ||||||
|     FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); |     FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); | ||||||
|     furi_check(status == FuriStatusOk); |     furi_check(status == FuriStatusOk); | ||||||
| @ -230,7 +239,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) { | |||||||
| 
 | 
 | ||||||
| static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { | static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { | ||||||
|     uint8_t buf_num = 0; |     uint8_t buf_num = 0; | ||||||
|     uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2); |     uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF); | ||||||
|     if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { |     if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { | ||||||
|         buf_num = 0; |         buf_num = 0; | ||||||
|     } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { |     } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { | ||||||
| @ -242,12 +251,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_tx_dma_polarity_isr() { | static void furi_hal_infrared_tx_dma_polarity_isr() { | ||||||
|     if(LL_DMA_IsActiveFlag_TE1(DMA1)) { | #if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 | ||||||
|         LL_DMA_ClearFlag_TE1(DMA1); |     if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) { | ||||||
|  |         LL_DMA_ClearFlag_TE1(IR_DMA); | ||||||
|         furi_crash(NULL); |         furi_crash(NULL); | ||||||
|     } |     } | ||||||
|     if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { |     if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) { | ||||||
|         LL_DMA_ClearFlag_TC1(DMA1); |         LL_DMA_ClearFlag_TC1(IR_DMA); | ||||||
| 
 | 
 | ||||||
|         furi_check( |         furi_check( | ||||||
|             (furi_hal_infrared_state == InfraredStateAsyncTx) || |             (furi_hal_infrared_state == InfraredStateAsyncTx) || | ||||||
| @ -257,25 +267,29 @@ static void furi_hal_infrared_tx_dma_polarity_isr() { | |||||||
|         uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); |         uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); | ||||||
|         furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); |         furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_tx_dma_isr() { | static void furi_hal_infrared_tx_dma_isr() { | ||||||
|     if(LL_DMA_IsActiveFlag_TE2(DMA1)) { | #if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 | ||||||
|         LL_DMA_ClearFlag_TE2(DMA1); |     if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) { | ||||||
|  |         LL_DMA_ClearFlag_TE2(IR_DMA); | ||||||
|         furi_crash(NULL); |         furi_crash(NULL); | ||||||
|     } |     } | ||||||
|     if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { |     if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) { | ||||||
|         LL_DMA_ClearFlag_HT2(DMA1); |         LL_DMA_ClearFlag_HT2(IR_DMA); | ||||||
|         uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); |         uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); | ||||||
|         uint8_t next_buf_num = !buf_num; |         uint8_t next_buf_num = !buf_num; | ||||||
|         if(infrared_tim_tx.buffer[buf_num].last_packet_end) { |         if(infrared_tim_tx.buffer[buf_num].last_packet_end) { | ||||||
|             LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); |             LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); | ||||||
|         } else if( |         } else if( | ||||||
|             !infrared_tim_tx.buffer[buf_num].packet_end || |             !infrared_tim_tx.buffer[buf_num].packet_end || | ||||||
|             (furi_hal_infrared_state == InfraredStateAsyncTx)) { |             (furi_hal_infrared_state == InfraredStateAsyncTx)) { | ||||||
|             furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); |             furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); | ||||||
|             if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { |             if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { | ||||||
|                 LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); |                 LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); | ||||||
|             } |             } | ||||||
|         } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { |         } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { | ||||||
|             /* fallthrough */ |             /* fallthrough */ | ||||||
| @ -283,8 +297,8 @@ static void furi_hal_infrared_tx_dma_isr() { | |||||||
|             furi_crash(NULL); |             furi_crash(NULL); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { |     if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) { | ||||||
|         LL_DMA_ClearFlag_TC2(DMA1); |         LL_DMA_ClearFlag_TC2(IR_DMA); | ||||||
|         furi_check( |         furi_check( | ||||||
|             (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || |             (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || | ||||||
|             (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || |             (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || | ||||||
| @ -310,6 +324,9 @@ static void furi_hal_infrared_tx_dma_isr() { | |||||||
|             infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); |             infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { | static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { | ||||||
| @ -369,16 +386,19 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { | |||||||
|     dma_config.NbData = 0; |     dma_config.NbData = 0; | ||||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; | ||||||
|     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; |     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; | ||||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); |     LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config); | ||||||
| 
 | 
 | ||||||
|     LL_DMA_ClearFlag_TE1(DMA1); | #if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 | ||||||
|     LL_DMA_ClearFlag_TC1(DMA1); |     LL_DMA_ClearFlag_TE1(IR_DMA); | ||||||
|  |     LL_DMA_ClearFlag_TC1(IR_DMA); | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF); | ||||||
|     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     furi_hal_interrupt_set_isr_ex( |     furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); | ||||||
|         FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { | static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { | ||||||
| @ -394,18 +414,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { | |||||||
|     dma_config.NbData = 0; |     dma_config.NbData = 0; | ||||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; | ||||||
|     dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; |     dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; | ||||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); |     LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config); | ||||||
| 
 | 
 | ||||||
|     LL_DMA_ClearFlag_TC2(DMA1); | #if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 | ||||||
|     LL_DMA_ClearFlag_HT2(DMA1); |     LL_DMA_ClearFlag_TC2(IR_DMA); | ||||||
|     LL_DMA_ClearFlag_TE2(DMA1); |     LL_DMA_ClearFlag_HT2(IR_DMA); | ||||||
|  |     LL_DMA_ClearFlag_TE2(IR_DMA); | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF); | ||||||
|     LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF); | ||||||
|     LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF); | ||||||
| 
 | 
 | ||||||
|     furi_hal_interrupt_set_isr_ex( |     furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); | ||||||
|         FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { | static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { | ||||||
| @ -507,14 +530,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar | |||||||
|     furi_assert(buffer->polarity != NULL); |     furi_assert(buffer->polarity != NULL); | ||||||
| 
 | 
 | ||||||
|     FURI_CRITICAL_ENTER(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); |     bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_DisableChannel(IR_DMA_CH1_DEF); | ||||||
|     } |     } | ||||||
|     LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity); |     LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity); | ||||||
|     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); |     LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_EnableChannel(IR_DMA_CH1_DEF); | ||||||
|     } |     } | ||||||
|     FURI_CRITICAL_EXIT(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| @ -527,14 +550,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { | |||||||
| 
 | 
 | ||||||
|     /* non-circular mode requires disabled channel before setup */ |     /* non-circular mode requires disabled channel before setup */ | ||||||
|     FURI_CRITICAL_ENTER(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); |     bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_DisableChannel(IR_DMA_CH2_DEF); | ||||||
|     } |     } | ||||||
|     LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); |     LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data); | ||||||
|     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); |     LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_EnableChannel(IR_DMA_CH2_DEF); | ||||||
|     } |     } | ||||||
|     FURI_CRITICAL_EXIT(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| @ -545,8 +568,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) { | |||||||
|         (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); |         (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); | ||||||
| 
 | 
 | ||||||
|     furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); |     furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); |     furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch2, NULL, NULL); |     furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); | ||||||
|     LL_TIM_DeInit(TIM1); |     LL_TIM_DeInit(TIM1); | ||||||
| 
 | 
 | ||||||
|     furi_semaphore_free(infrared_tim_tx.stop_semaphore); |     furi_semaphore_free(infrared_tim_tx.stop_semaphore); | ||||||
| @ -597,8 +620,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { | |||||||
|     furi_hal_infrared_state = InfraredStateAsyncTx; |     furi_hal_infrared_state = InfraredStateAsyncTx; | ||||||
| 
 | 
 | ||||||
|     LL_TIM_ClearFlag_UPDATE(TIM1); |     LL_TIM_ClearFlag_UPDATE(TIM1); | ||||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableChannel(IR_DMA_CH1_DEF); | ||||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_EnableChannel(IR_DMA_CH2_DEF); | ||||||
|     furi_delay_us(5); |     furi_delay_us(5); | ||||||
|     LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ |     LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ | ||||||
|     furi_delay_us(5); |     furi_delay_us(5); | ||||||
|  | |||||||
| @ -21,6 +21,14 @@ | |||||||
| #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 | #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 | ||||||
| #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 | #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 | ||||||
| 
 | 
 | ||||||
|  | /* DMA Channels definition */ | ||||||
|  | #define RFID_DMA DMA2 | ||||||
|  | #define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 | ||||||
|  | #define RFID_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 | ||||||
|  | #define RFID_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 | ||||||
|  | #define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL | ||||||
|  | #define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriHalRfidEmulateCallback callback; |     FuriHalRfidEmulateCallback callback; | ||||||
|     FuriHalRfidDMACallback dma_callback; |     FuriHalRfidDMACallback dma_callback; | ||||||
| @ -302,15 +310,19 @@ void furi_hal_rfid_tim_read_capture_stop() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_rfid_dma_isr() { | static void furi_hal_rfid_dma_isr() { | ||||||
|     if(LL_DMA_IsActiveFlag_HT1(DMA1)) { | #if RFID_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 | ||||||
|         LL_DMA_ClearFlag_HT1(DMA1); |     if(LL_DMA_IsActiveFlag_HT1(RFID_DMA)) { | ||||||
|  |         LL_DMA_ClearFlag_HT1(RFID_DMA); | ||||||
|         furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); |         furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(LL_DMA_IsActiveFlag_TC1(DMA1)) { |     if(LL_DMA_IsActiveFlag_TC1(RFID_DMA)) { | ||||||
|         LL_DMA_ClearFlag_TC1(DMA1); |         LL_DMA_ClearFlag_TC1(RFID_DMA); | ||||||
|         furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); |         furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_rfid_tim_emulate_dma_start( | void furi_hal_rfid_tim_emulate_dma_start( | ||||||
| @ -347,8 +359,8 @@ void furi_hal_rfid_tim_emulate_dma_start( | |||||||
|     dma_config.NbData = length; |     dma_config.NbData = length; | ||||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||||
|     dma_config.Priority = LL_DMA_MODE_NORMAL; |     dma_config.Priority = LL_DMA_MODE_NORMAL; | ||||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); |     LL_DMA_Init(RFID_DMA_CH1_DEF, &dma_config); | ||||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     // configure DMA "mem -> CCR3" channel
 |     // configure DMA "mem -> CCR3" channel
 | ||||||
| #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 | #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 | ||||||
| @ -366,13 +378,13 @@ void furi_hal_rfid_tim_emulate_dma_start( | |||||||
|     dma_config.NbData = length; |     dma_config.NbData = length; | ||||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||||
|     dma_config.Priority = LL_DMA_MODE_NORMAL; |     dma_config.Priority = LL_DMA_MODE_NORMAL; | ||||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); |     LL_DMA_Init(RFID_DMA_CH2_DEF, &dma_config); | ||||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); | ||||||
| 
 | 
 | ||||||
|     // attach interrupt to one of DMA channels
 |     // attach interrupt to one of DMA channels
 | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); |     furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, furi_hal_rfid_dma_isr, NULL); | ||||||
|     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_TC(RFID_DMA_CH1_DEF); | ||||||
|     LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_HT(RFID_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     // start
 |     // start
 | ||||||
|     LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); |     LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); | ||||||
| @ -385,14 +397,14 @@ void furi_hal_rfid_tim_emulate_dma_stop() { | |||||||
|     LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); |     LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); | ||||||
|     LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); |     LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); | ||||||
| 
 | 
 | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); |     furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, NULL, NULL); | ||||||
|     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DisableIT_TC(RFID_DMA_CH1_DEF); | ||||||
|     LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DisableIT_HT(RFID_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     FURI_CRITICAL_ENTER(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DeInit(RFID_DMA_CH1_DEF); | ||||||
|     LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); |     LL_DMA_DeInit(RFID_DMA_CH2_DEF); | ||||||
|     LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); |     LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); | ||||||
| 
 | 
 | ||||||
|     FURI_CRITICAL_EXIT(); |     FURI_CRITICAL_EXIT(); | ||||||
|  | |||||||
| @ -1,14 +1,33 @@ | |||||||
|  | #include <furi.h> | ||||||
| #include <furi_hal_spi.h> | #include <furi_hal_spi.h> | ||||||
| #include <furi_hal_resources.h> | #include <furi_hal_resources.h> | ||||||
| #include <furi_hal_power.h> | #include <furi_hal_power.h> | ||||||
|  | #include <furi_hal_interrupt.h> | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stm32wbxx_ll_dma.h> | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| #include <stm32wbxx_ll_spi.h> | #include <stm32wbxx_ll_spi.h> | ||||||
| #include <stm32wbxx_ll_utils.h> | #include <stm32wbxx_ll_utils.h> | ||||||
| #include <stm32wbxx_ll_cortex.h> | #include <stm32wbxx_ll_cortex.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "FuriHalSpi" | ||||||
|  | 
 | ||||||
|  | #define SPI_DMA DMA2 | ||||||
|  | #define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 | ||||||
|  | #define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 | ||||||
|  | #define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 | ||||||
|  | #define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 | ||||||
|  | #define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL | ||||||
|  | #define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL | ||||||
|  | 
 | ||||||
|  | // For simplicity, I assume that only one SPI DMA transaction can occur at a time.
 | ||||||
|  | static FuriSemaphore* spi_dma_lock = NULL; | ||||||
|  | static FuriSemaphore* spi_dma_completed = NULL; | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_dma_init() { | ||||||
|  |     spi_dma_lock = furi_semaphore_alloc(1, 1); | ||||||
|  |     spi_dma_completed = furi_semaphore_alloc(1, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { | void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { | ||||||
|     furi_assert(bus); |     furi_assert(bus); | ||||||
|     bus->callback(bus, FuriHalSpiBusEventInit); |     bus->callback(bus, FuriHalSpiBusEventInit); | ||||||
| @ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx( | |||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static void spi_dma_isr() { | ||||||
|  | #if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 | ||||||
|  |     if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { | ||||||
|  |         LL_DMA_ClearFlag_TC3(SPI_DMA); | ||||||
|  |         furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 | ||||||
|  |     if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { | ||||||
|  |         LL_DMA_ClearFlag_TC4(SPI_DMA); | ||||||
|  |         furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); | ||||||
|  |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_bus_trx_dma( | ||||||
|  |     FuriHalSpiBusHandle* handle, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout_ms) { | ||||||
|  |     furi_assert(handle); | ||||||
|  |     furi_assert(handle->bus->current_handle == handle); | ||||||
|  |     furi_assert(size > 0); | ||||||
|  | 
 | ||||||
|  |     // If scheduler is not running, use blocking mode
 | ||||||
|  |     if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { | ||||||
|  |         return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Lock DMA
 | ||||||
|  |     furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |     const uint32_t dma_dummy_u32 = 0xFFFFFFFF; | ||||||
|  | 
 | ||||||
|  |     bool ret = true; | ||||||
|  |     SPI_TypeDef* spi = handle->bus->spi; | ||||||
|  |     uint32_t dma_rx_req; | ||||||
|  |     uint32_t dma_tx_req; | ||||||
|  | 
 | ||||||
|  |     if(spi == SPI1) { | ||||||
|  |         dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; | ||||||
|  |         dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; | ||||||
|  |     } else if(spi == SPI2) { | ||||||
|  |         dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; | ||||||
|  |         dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(rx_buffer == NULL) { | ||||||
|  |         // Only TX mode, do not use RX channel
 | ||||||
|  | 
 | ||||||
|  |         LL_DMA_InitTypeDef dma_config = {0}; | ||||||
|  |         dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); | ||||||
|  |         dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; | ||||||
|  |         dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||||
|  |         dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||||
|  |         dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |         dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||||
|  |         dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; | ||||||
|  |         dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; | ||||||
|  |         dma_config.NbData = size; | ||||||
|  |         dma_config.PeriphRequest = dma_tx_req; | ||||||
|  |         dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; | ||||||
|  |         LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); | ||||||
|  | 
 | ||||||
|  | #if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 | ||||||
|  |         LL_DMA_ClearFlag_TC4(SPI_DMA); | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |         furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); | ||||||
|  | 
 | ||||||
|  |         bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); | ||||||
|  |         if(!dma_tx_was_enabled) { | ||||||
|  |             LL_SPI_EnableDMAReq_TX(spi); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // acquire semaphore before enabling DMA
 | ||||||
|  |         furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); | ||||||
|  |         LL_DMA_EnableChannel(SPI_DMA_TX_DEF); | ||||||
|  | 
 | ||||||
|  |         // and wait for it to be released (DMA transfer complete)
 | ||||||
|  |         if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { | ||||||
|  |             ret = false; | ||||||
|  |             FURI_LOG_E(TAG, "DMA timeout\r\n"); | ||||||
|  |         } | ||||||
|  |         // release semaphore, because we are using it as a flag
 | ||||||
|  |         furi_semaphore_release(spi_dma_completed); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); | ||||||
|  |         LL_DMA_DisableChannel(SPI_DMA_TX_DEF); | ||||||
|  |         if(!dma_tx_was_enabled) { | ||||||
|  |             LL_SPI_DisableDMAReq_TX(spi); | ||||||
|  |         } | ||||||
|  |         furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_DeInit(SPI_DMA_TX_DEF); | ||||||
|  |     } else { | ||||||
|  |         // TRX or RX mode, use both channels
 | ||||||
|  |         uint32_t tx_mem_increase_mode; | ||||||
|  | 
 | ||||||
|  |         if(tx_buffer == NULL) { | ||||||
|  |             // RX mode, use dummy data instead of TX buffer
 | ||||||
|  |             tx_buffer = (uint8_t*)&dma_dummy_u32; | ||||||
|  |             tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |         } else { | ||||||
|  |             tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         LL_DMA_InitTypeDef dma_config = {0}; | ||||||
|  |         dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); | ||||||
|  |         dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; | ||||||
|  |         dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||||
|  |         dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||||
|  |         dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |         dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; | ||||||
|  |         dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; | ||||||
|  |         dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; | ||||||
|  |         dma_config.NbData = size; | ||||||
|  |         dma_config.PeriphRequest = dma_tx_req; | ||||||
|  |         dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; | ||||||
|  |         LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); | ||||||
|  | 
 | ||||||
|  |         dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); | ||||||
|  |         dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; | ||||||
|  |         dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; | ||||||
|  |         dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||||
|  |         dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |         dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||||
|  |         dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; | ||||||
|  |         dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; | ||||||
|  |         dma_config.NbData = size; | ||||||
|  |         dma_config.PeriphRequest = dma_rx_req; | ||||||
|  |         dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; | ||||||
|  |         LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); | ||||||
|  | 
 | ||||||
|  | #if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 | ||||||
|  |         LL_DMA_ClearFlag_TC3(SPI_DMA); | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |         furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); | ||||||
|  | 
 | ||||||
|  |         bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); | ||||||
|  |         bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); | ||||||
|  | 
 | ||||||
|  |         if(!dma_tx_was_enabled) { | ||||||
|  |             LL_SPI_EnableDMAReq_TX(spi); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!dma_rx_was_enabled) { | ||||||
|  |             LL_SPI_EnableDMAReq_RX(spi); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // acquire semaphore before enabling DMA
 | ||||||
|  |         furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); | ||||||
|  |         LL_DMA_EnableChannel(SPI_DMA_RX_DEF); | ||||||
|  |         LL_DMA_EnableChannel(SPI_DMA_TX_DEF); | ||||||
|  | 
 | ||||||
|  |         // and wait for it to be released (DMA transfer complete)
 | ||||||
|  |         if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { | ||||||
|  |             ret = false; | ||||||
|  |             FURI_LOG_E(TAG, "DMA timeout\r\n"); | ||||||
|  |         } | ||||||
|  |         // release semaphore, because we are using it as a flag
 | ||||||
|  |         furi_semaphore_release(spi_dma_completed); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_DisableChannel(SPI_DMA_TX_DEF); | ||||||
|  |         LL_DMA_DisableChannel(SPI_DMA_RX_DEF); | ||||||
|  | 
 | ||||||
|  |         if(!dma_tx_was_enabled) { | ||||||
|  |             LL_SPI_DisableDMAReq_TX(spi); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!dma_rx_was_enabled) { | ||||||
|  |             LL_SPI_DisableDMAReq_RX(spi); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |         LL_DMA_DeInit(SPI_DMA_TX_DEF); | ||||||
|  |         LL_DMA_DeInit(SPI_DMA_RX_DEF); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_bus_end_txrx(handle, timeout_ms); | ||||||
|  | 
 | ||||||
|  |     furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
| @ -18,6 +18,14 @@ | |||||||
| 
 | 
 | ||||||
| static uint32_t furi_hal_subghz_debug_gpio_buff[2]; | static uint32_t furi_hal_subghz_debug_gpio_buff[2]; | ||||||
| 
 | 
 | ||||||
|  | /* DMA Channels definition */ | ||||||
|  | #define SUBGHZ_DMA DMA2 | ||||||
|  | #define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 | ||||||
|  | #define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 | ||||||
|  | #define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 | ||||||
|  | #define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL | ||||||
|  | #define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     volatile SubGhzState state; |     volatile SubGhzState state; | ||||||
|     volatile SubGhzRegulation regulation; |     volatile SubGhzRegulation regulation; | ||||||
| @ -525,8 +533,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | |||||||
|             *buffer = 0; |             *buffer = 0; | ||||||
|             buffer++; |             buffer++; | ||||||
|             samples--; |             samples--; | ||||||
|             LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); |             LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); | ||||||
|             LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); |             LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); | ||||||
|             LL_TIM_EnableIT_UPDATE(TIM2); |             LL_TIM_EnableIT_UPDATE(TIM2); | ||||||
|             break; |             break; | ||||||
|         } else { |         } else { | ||||||
| @ -567,17 +575,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | |||||||
| 
 | 
 | ||||||
| static void furi_hal_subghz_async_tx_dma_isr() { | static void furi_hal_subghz_async_tx_dma_isr() { | ||||||
|     furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); |     furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); | ||||||
|     if(LL_DMA_IsActiveFlag_HT1(DMA1)) { | 
 | ||||||
|         LL_DMA_ClearFlag_HT1(DMA1); | #if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 | ||||||
|  |     if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { | ||||||
|  |         LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); | ||||||
|         furi_hal_subghz_async_tx_refill( |         furi_hal_subghz_async_tx_refill( | ||||||
|             furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); |             furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); | ||||||
|     } |     } | ||||||
|     if(LL_DMA_IsActiveFlag_TC1(DMA1)) { |     if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { | ||||||
|         LL_DMA_ClearFlag_TC1(DMA1); |         LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); | ||||||
|         furi_hal_subghz_async_tx_refill( |         furi_hal_subghz_async_tx_refill( | ||||||
|             furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, |             furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, | ||||||
|             API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); |             API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); | ||||||
|     } |     } | ||||||
|  | #else | ||||||
|  | #error Update this code. Would you kindly? | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_subghz_async_tx_timer_isr() { | static void furi_hal_subghz_async_tx_timer_isr() { | ||||||
| @ -586,7 +599,7 @@ static void furi_hal_subghz_async_tx_timer_isr() { | |||||||
|         if(LL_TIM_GetAutoReload(TIM2) == 0) { |         if(LL_TIM_GetAutoReload(TIM2) == 0) { | ||||||
|             if(furi_hal_subghz.state == SubGhzStateAsyncTx) { |             if(furi_hal_subghz.state == SubGhzStateAsyncTx) { | ||||||
|                 furi_hal_subghz.state = SubGhzStateAsyncTxLast; |                 furi_hal_subghz.state = SubGhzStateAsyncTxLast; | ||||||
|                 LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); |                 LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); | ||||||
|             } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { |             } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { | ||||||
|                 furi_hal_subghz.state = SubGhzStateAsyncTxEnd; |                 furi_hal_subghz.state = SubGhzStateAsyncTxEnd; | ||||||
|                 //forcibly pulls the pin to the ground so that there is no carrier
 |                 //forcibly pulls the pin to the ground so that there is no carrier
 | ||||||
| @ -634,11 +647,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* | |||||||
|     dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; |     dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; | ||||||
|     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||||
|     dma_config.Priority = LL_DMA_MODE_NORMAL; |     dma_config.Priority = LL_DMA_MODE_NORMAL; | ||||||
|     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); |     LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); |     furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); | ||||||
|     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); | ||||||
|     LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); | ||||||
|     LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     // Configure TIM2
 |     // Configure TIM2
 | ||||||
|     LL_TIM_InitTypeDef TIM_InitStruct = {0}; |     LL_TIM_InitTypeDef TIM_InitStruct = {0}; | ||||||
| @ -696,9 +709,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* | |||||||
|         dma_config.NbData = 2; |         dma_config.NbData = 2; | ||||||
|         dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; |         dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; | ||||||
|         dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; |         dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; | ||||||
|         LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); |         LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); | ||||||
|         LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); |         LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| @ -726,16 +739,16 @@ void furi_hal_subghz_stop_async_tx() { | |||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); |     furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); | ||||||
| 
 | 
 | ||||||
|     // Deinitialize DMA
 |     // Deinitialize DMA
 | ||||||
|     LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); |     LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); | ||||||
| 
 | 
 | ||||||
|     furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); |     furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); | ||||||
| 
 | 
 | ||||||
|     // Deinitialize GPIO
 |     // Deinitialize GPIO
 | ||||||
|     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); |     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
| 
 | 
 | ||||||
|     // Stop debug
 |     // Stop debug
 | ||||||
|     if(furi_hal_subghz_stop_debug()) { |     if(furi_hal_subghz_stop_debug()) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     FURI_CRITICAL_EXIT(); |     FURI_CRITICAL_EXIT(); | ||||||
|  | |||||||
| @ -23,8 +23,8 @@ static FATFS* pfs = NULL; | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| static bool flipper_update_mount_sd() { | static bool flipper_update_mount_sd() { | ||||||
|     for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { |     for(int i = 0; i < sd_max_mount_retry_count(); ++i) { | ||||||
|         if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { |         if(sd_init((i % 2) == 0) != SdSpiStatusOK) { | ||||||
|             /* Next attempt will be without card reset, let it settle */ |             /* Next attempt will be without card reset, let it settle */ | ||||||
|             furi_delay_ms(1000); |             furi_delay_ms(1000); | ||||||
|             continue; |             continue; | ||||||
|  | |||||||
| @ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early(); | |||||||
| /** Initialize SPI HAL */ | /** Initialize SPI HAL */ | ||||||
| void furi_hal_spi_config_init(); | void furi_hal_spi_config_init(); | ||||||
| 
 | 
 | ||||||
|  | /** Initialize SPI DMA HAL */ | ||||||
|  | void furi_hal_spi_dma_init(); | ||||||
|  | 
 | ||||||
| /** Initialize SPI Bus
 | /** Initialize SPI Bus
 | ||||||
|  * |  * | ||||||
|  * @param      handle  pointer to FuriHalSpiBus instance |  * @param      handle  pointer to FuriHalSpiBus instance | ||||||
| @ -103,6 +106,23 @@ bool furi_hal_spi_bus_trx( | |||||||
|     size_t size, |     size_t size, | ||||||
|     uint32_t timeout); |     uint32_t timeout); | ||||||
| 
 | 
 | ||||||
|  | /** SPI Transmit and Receive with DMA
 | ||||||
|  |  * | ||||||
|  |  * @param      handle     pointer to FuriHalSpiBusHandle instance | ||||||
|  |  * @param      tx_buffer  pointer to tx buffer | ||||||
|  |  * @param      rx_buffer  pointer to rx buffer | ||||||
|  |  * @param      size       transaction size (buffer size) | ||||||
|  |  * @param      timeout_ms operation timeout in ms | ||||||
|  |  * | ||||||
|  |  * @return     true on success | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_bus_trx_dma( | ||||||
|  |     FuriHalSpiBusHandle* handle, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout_ms); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { | |||||||
|     if(FURI_IS_IRQ_MODE()) { |     if(FURI_IS_IRQ_MODE()) { | ||||||
|         yield = pdFALSE; |         yield = pdFALSE; | ||||||
|         if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { |         if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { | ||||||
|             rflags = (uint32_t)FuriStatusErrorResource; |             rflags = (uint32_t)FuriFlagErrorResource; | ||||||
|         } else { |         } else { | ||||||
|             rflags = flags; |             rflags = flags; | ||||||
|             portYIELD_FROM_ISR(yield); |             portYIELD_FROM_ISR(yield); | ||||||
|  | |||||||
| @ -195,6 +195,15 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { | |||||||
|     thread->priority = priority; |     thread->priority = priority; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void furi_thread_set_current_priority(FuriThreadPriority priority) { | ||||||
|  |     UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal; | ||||||
|  |     vTaskPrioritySet(NULL, new_priority); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FuriThreadPriority furi_thread_get_current_priority() { | ||||||
|  |     return (FuriThreadPriority)uxTaskPriorityGet(NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { | void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { | ||||||
|     furi_assert(thread); |     furi_assert(thread); | ||||||
|     furi_assert(thread->state == FuriThreadStateStopped); |     furi_assert(thread->state == FuriThreadStateStopped); | ||||||
|  | |||||||
| @ -122,6 +122,18 @@ void furi_thread_set_context(FuriThread* thread, void* context); | |||||||
|  */ |  */ | ||||||
| void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); | void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); | ||||||
| 
 | 
 | ||||||
|  | /** Set current thread priority
 | ||||||
|  |  * | ||||||
|  |  * @param      priority FuriThreadPriority value | ||||||
|  |  */ | ||||||
|  | void furi_thread_set_current_priority(FuriThreadPriority priority); | ||||||
|  | 
 | ||||||
|  | /** Get current thread priority
 | ||||||
|  |  * | ||||||
|  |  * @return     FuriThreadPriority value | ||||||
|  |  */ | ||||||
|  | FuriThreadPriority furi_thread_get_current_priority(); | ||||||
|  | 
 | ||||||
| /** Set FuriThread state change callback
 | /** Set FuriThread state change callback
 | ||||||
|  * |  * | ||||||
|  * @param      thread    FuriThread instance |  * @param      thread    FuriThread instance | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov