[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();
|
||||||
@ -471,4 +483,4 @@ void COMP_IRQHandler() {
|
|||||||
(LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW),
|
(LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW),
|
||||||
furi_hal_rfid_comp_callback_context);
|
furi_hal_rfid_comp_callback_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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…
Reference in New Issue
Block a user