refactor: SD card SPI HAL (#3957)
* refactor: SD card SPI driver * Removed SPI and DMA StdPeriph drivers * Allow for disabling SPI DMA support * Disable SPI sdcard CRC support * Fix 2MHz timer on NV14 * Shared VS1053b driver * Fix: SPI2 uses PCLK1 * feat: new generic diskio API * More generic SD_CARD_PRESENT() * Fixes * Flush incoming block on token mismatch * Scratch buffer test * Incredible hack to re-sync sd card * fix: SDIO driver
This commit is contained in:
@@ -84,12 +84,6 @@ set(GVARS_VARIANT 1)
|
||||
set(FRSKY_VARIANT 2)
|
||||
set(3POS_VARIANT 4)
|
||||
|
||||
set(FATFS_SRC
|
||||
${FATFS_DIR}/ff.c
|
||||
${FATFS_DIR}/ffunicode.c
|
||||
${FATFS_DIR}/fattime.c
|
||||
)
|
||||
|
||||
if(PCB STREQUAL X12S OR PCB STREQUAL X10)
|
||||
include(targets/horus/CMakeLists.txt)
|
||||
elseif(PCB STREQUAL NV14)
|
||||
@@ -252,9 +246,8 @@ endif()
|
||||
|
||||
if(SDCARD)
|
||||
add_definitions(-DSDCARD)
|
||||
include_directories(${FATFS_DIR} ${FATFS_DIR}/option)
|
||||
set(SRC ${SRC} sdcard.cpp rtc.cpp logs.cpp thirdparty/libopenui/src/libopenui_file.cpp)
|
||||
set(FIRMWARE_SRC ${FIRMWARE_SRC} ${FATFS_SRC})
|
||||
set(FIRMWARE_SRC ${FIRMWARE_SRC})
|
||||
endif()
|
||||
|
||||
if(SHUTDOWN_CONFIRMATION)
|
||||
|
||||
+11
-7
@@ -23,7 +23,6 @@
|
||||
#include <FreeRTOS/include/stream_buffer.h>
|
||||
|
||||
#include "opentx.h"
|
||||
#include "diskio.h"
|
||||
#include "timers_driver.h"
|
||||
#include "watchdog_driver.h"
|
||||
|
||||
@@ -33,6 +32,8 @@
|
||||
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/module_port.h"
|
||||
#include "hal/fatfs_diskio.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#include "tasks.h"
|
||||
#include "tasks/mixer_task.h"
|
||||
@@ -347,8 +348,9 @@ int cliReadSD(const char ** argv)
|
||||
uint32_t bytesRead = numberOfSectors * 512;
|
||||
tmr10ms_t start = get_tmr10ms();
|
||||
|
||||
auto drv = storageGetDefaultDriver();
|
||||
while (numberOfSectors > 0) {
|
||||
DRESULT res = __disk_read(0, buffer, startSector, bufferSectors);
|
||||
DRESULT res = drv->read(0, buffer, startSector, bufferSectors);
|
||||
if (res != RES_OK) {
|
||||
cliSerialPrint("disk_read error: %d, sector: %d(%d)", res, startSector, numberOfSectors);
|
||||
}
|
||||
@@ -385,10 +387,11 @@ int cliReadSD(const char ** argv)
|
||||
int cliTestSD(const char ** argv)
|
||||
{
|
||||
// Do the read test on the SD card and report back the result
|
||||
auto drv = storageGetDefaultDriver();
|
||||
|
||||
// get sector count
|
||||
uint32_t sectorCount;
|
||||
if (disk_ioctl(0, GET_SECTOR_COUNT, §orCount) != RES_OK) {
|
||||
if (drv->ioctl(0, GET_SECTOR_COUNT, §orCount) != RES_OK) {
|
||||
cliSerialPrint("Error: can't read sector count");
|
||||
return 0;
|
||||
}
|
||||
@@ -401,8 +404,9 @@ int cliTestSD(const char ** argv)
|
||||
cliSerialPrint("Not enough memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint32_t s = sectorCount - 16; s<sectorCount; ++s) {
|
||||
DRESULT res = __disk_read(0, buffer, s, 1);
|
||||
DRESULT res = drv->read(0, buffer, s, 1);
|
||||
if (res != RES_OK) {
|
||||
cliSerialPrint("sector %d read FAILED, err: %d", s, res);
|
||||
}
|
||||
@@ -421,8 +425,8 @@ int cliTestSD(const char ** argv)
|
||||
}
|
||||
|
||||
cliSerialPrint("Starting multiple sector read test, reading two sectors at the time");
|
||||
for (uint32_t s = sectorCount - 16; s<sectorCount; s+=2) {
|
||||
DRESULT res = __disk_read(0, buffer, s, 2);
|
||||
for (uint32_t s = sectorCount - 16; s < sectorCount; s += 2) {
|
||||
DRESULT res = drv->read(0, buffer, s, 2);
|
||||
if (res != RES_OK) {
|
||||
cliSerialPrint("sector %d-%d read FAILED, err: %d", s, s+1, res);
|
||||
}
|
||||
@@ -441,7 +445,7 @@ int cliTestSD(const char ** argv)
|
||||
}
|
||||
|
||||
cliSerialPrint("Starting multiple sector read test, reading 16 sectors at the time");
|
||||
DRESULT res = __disk_read(0, buffer, sectorCount-16, 16);
|
||||
DRESULT res = drv->read(0, buffer, sectorCount - 16, 16);
|
||||
if (res != RES_OK) {
|
||||
cliSerialPrint("sector %d-%d read FAILED, err: %d", sectorCount-16, sectorCount-1, res);
|
||||
}
|
||||
|
||||
+2
-2
@@ -101,8 +101,8 @@ uint16_t crc16(uint8_t index, const uint8_t * buf, uint32_t len, uint16_t start)
|
||||
{
|
||||
uint16_t crc = start;
|
||||
const unsigned short * tab = crc16tab[index];
|
||||
for (uint32_t i=0; i<len; i++) {
|
||||
crc = (crc<<8) ^ tab[((crc>>8) ^ *buf++) & 0x00FF];
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
crc = (crc << 8) ^ tab[((crc >> 8) ^ *buf++) & 0x00FF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
+37
-32
@@ -24,11 +24,6 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if defined(SIMU) && !defined(SIMU_DISKIO)
|
||||
#define __disk_read(...) (RES_OK)
|
||||
#define __disk_write(...) (RES_OK)
|
||||
#endif
|
||||
|
||||
#if 0 // set to 1 to enable traces
|
||||
#include "debug.h"
|
||||
#define TRACE_DISK_CACHE(...) TRACE(__VA_ARGS__)
|
||||
@@ -47,15 +42,16 @@ DiskCache diskCache;
|
||||
|
||||
class DiskCacheBlock
|
||||
{
|
||||
public:
|
||||
public:
|
||||
DiskCacheBlock();
|
||||
bool read(BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT fill(BYTE drv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT fill(const diskio_driver_t* drv, BYTE lun, BYTE* buff, DWORD sector,
|
||||
UINT count);
|
||||
void free(DWORD sector, UINT count);
|
||||
void free();
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
uint8_t data[DISK_CACHE_BLOCK_SIZE];
|
||||
DWORD startSector;
|
||||
DWORD endSector;
|
||||
@@ -77,16 +73,17 @@ bool DiskCacheBlock::read(BYTE * buff, DWORD sector, UINT count)
|
||||
return false;
|
||||
}
|
||||
|
||||
DRESULT DiskCacheBlock::fill(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
DRESULT DiskCacheBlock::fill(const diskio_driver_t* drv, BYTE lun, BYTE * buff,
|
||||
DWORD sector, UINT count)
|
||||
{
|
||||
DRESULT res = __disk_read(drv, data, sector, DISK_CACHE_BLOCK_SECTORS);
|
||||
DRESULT res = drv->read(lun, data, sector, DISK_CACHE_BLOCK_SECTORS);
|
||||
if (res != RES_OK) {
|
||||
return res;
|
||||
}
|
||||
startSector = sector;
|
||||
endSector = sector + DISK_CACHE_BLOCK_SECTORS;
|
||||
memcpy(buff, data, count * BLOCK_SIZE);
|
||||
TRACE_DISK_CACHE("\tcache %p FILLED from read(%u, %u)", this, (uint32_t)sector, (uint32_t)count);
|
||||
TRACE_DISK_CACHE("cache %p FILLED from read(%u, %u)", this, (uint32_t)sector, (uint32_t)count);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
@@ -108,13 +105,17 @@ bool DiskCacheBlock::empty() const
|
||||
return (endSector == 0);
|
||||
}
|
||||
|
||||
DiskCache::DiskCache():
|
||||
lastBlock(0)
|
||||
DiskCache::DiskCache() : lastBlock(0), blocks(nullptr), diskDrv(nullptr)
|
||||
{
|
||||
stats.noHits = 0;
|
||||
stats.noMisses = 0;
|
||||
stats.noWrites = 0;
|
||||
}
|
||||
|
||||
void DiskCache::initialize(const diskio_driver_t* drv)
|
||||
{
|
||||
blocks = new DiskCacheBlock[DISK_CACHE_BLOCKS_NUM];
|
||||
diskDrv = drv;
|
||||
}
|
||||
|
||||
void DiskCache::clear()
|
||||
@@ -128,24 +129,28 @@ void DiskCache::clear()
|
||||
}
|
||||
}
|
||||
|
||||
DRESULT DiskCache::read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
uint32_t DiskCache::getSectors(uint8_t lun)
|
||||
{
|
||||
// TODO: check if not caching first sectors would improve anything
|
||||
// if (sector < 1000) {
|
||||
// ++stats.noMisses;
|
||||
// return __disk_read(drv, buff, sector, count);
|
||||
// }
|
||||
if (sectors == 0) {
|
||||
diskDrv->ioctl(lun, GET_SECTOR_COUNT, §ors);
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
DRESULT DiskCache::read(BYTE lun, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
// if read is bigger than cache block, then read it directly without using cache
|
||||
if (count > DISK_CACHE_BLOCK_SECTORS) {
|
||||
TRACE_DISK_CACHE("\t\t big read(%u, %u)", (uint32_t)sector, (uint32_t)count);
|
||||
return __disk_read(drv, buff, sector, count);
|
||||
TRACE_DISK_CACHE("big read(%u, %u)", (uint32_t)sector, (uint32_t)count);
|
||||
return diskDrv->read(lun, buff, sector, count);
|
||||
}
|
||||
|
||||
// if block + cache block size is beyond the end of the disk, then read it directly without using cache
|
||||
if (sector + DISK_CACHE_BLOCK_SECTORS >= sdGetNoSectors()) {
|
||||
TRACE_DISK_CACHE("\t\t cache would be beyond end of disk %u (%u)", (uint32_t)sector, sdGetNoSectors());
|
||||
return __disk_read(drv, buff, sector, count);
|
||||
// if block + cache block size is beyond the end of the disk,
|
||||
// then read it directly without using cache
|
||||
if (sector + DISK_CACHE_BLOCK_SECTORS >= getSectors(lun)) {
|
||||
TRACE_DISK_CACHE("cache would be beyond end of disk %u (%u)",
|
||||
(uint32_t)sector, getSectors(lun));
|
||||
return diskDrv->read(lun, buff, sector, count);
|
||||
}
|
||||
|
||||
for (int n = 0; n < DISK_CACHE_BLOCKS_NUM; ++n) {
|
||||
@@ -161,7 +166,7 @@ DRESULT DiskCache::read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
for (int n = 0; n < DISK_CACHE_BLOCKS_NUM; ++n) {
|
||||
if (blocks[n].empty()) {
|
||||
TRACE_DISK_CACHE("\t\t using free block");
|
||||
return blocks[n].fill(drv, buff, sector, count);
|
||||
return blocks[n].fill(diskDrv, lun, buff, sector, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,16 +176,16 @@ DRESULT DiskCache::read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
lastBlock = 0;
|
||||
}
|
||||
|
||||
return blocks[lastBlock].fill(drv, buff, sector, count);
|
||||
return blocks[lastBlock].fill(diskDrv, lun, buff, sector, count);
|
||||
}
|
||||
|
||||
DRESULT DiskCache::write(BYTE drv, const BYTE* buff, DWORD sector, UINT count)
|
||||
DRESULT DiskCache::write(BYTE lun, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
++stats.noWrites;
|
||||
for(int n = 0; n < DISK_CACHE_BLOCKS_NUM; ++n) {
|
||||
blocks[n].free(sector, count);
|
||||
}
|
||||
return __disk_write(drv, buff, sector, count);
|
||||
return diskDrv->write(lun, buff, sector, count);
|
||||
}
|
||||
|
||||
const DiskCacheStats & DiskCache::getStats() const
|
||||
@@ -195,13 +200,13 @@ int DiskCache::getHitRate() const
|
||||
return (stats.noHits * 1000) / all;
|
||||
}
|
||||
|
||||
DRESULT disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
DRESULT disk_cache_read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
return diskCache.read(drv, buff, sector, count);
|
||||
}
|
||||
|
||||
|
||||
DRESULT disk_write(BYTE drv, const BYTE * buff, DWORD sector, UINT count)
|
||||
DRESULT disk_cache_write(BYTE drv, const BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
return diskCache.write(drv, buff, sector, count);
|
||||
}
|
||||
|
||||
|
||||
+22
-15
@@ -19,10 +19,9 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _DISK_CACHE_H_
|
||||
#define _DISK_CACHE_H_
|
||||
#pragma once
|
||||
|
||||
#include "FatFs/diskio.h"
|
||||
#include "hal/fatfs_diskio.h"
|
||||
|
||||
// tunable parameters
|
||||
#define DISK_CACHE_BLOCKS_NUM 32 // no cache blocks
|
||||
@@ -39,23 +38,31 @@ class DiskCacheBlock;
|
||||
|
||||
class DiskCache
|
||||
{
|
||||
public:
|
||||
DiskCache();
|
||||
public:
|
||||
DiskCache();
|
||||
|
||||
void clear();
|
||||
void initialize(const diskio_driver_t* drv);
|
||||
void clear();
|
||||
|
||||
DRESULT read(BYTE drv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT write(BYTE drv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT read(BYTE drv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT write(BYTE drv, const BYTE* buff, DWORD sector, UINT count);
|
||||
|
||||
const DiskCacheStats & getStats() const;
|
||||
int getHitRate() const;
|
||||
const DiskCacheStats& getStats() const;
|
||||
int getHitRate() const;
|
||||
|
||||
private:
|
||||
DiskCacheStats stats;
|
||||
uint32_t lastBlock;
|
||||
DiskCacheBlock * blocks;
|
||||
private:
|
||||
DiskCacheStats stats;
|
||||
uint32_t lastBlock;
|
||||
DiskCacheBlock* blocks;
|
||||
const diskio_driver_t* diskDrv;
|
||||
uint32_t sectors;
|
||||
|
||||
uint32_t getSectors(uint8_t lun);
|
||||
};
|
||||
|
||||
extern DiskCache diskCache;
|
||||
|
||||
#endif // _DISK_CACHE_H_
|
||||
DRESULT disk_cache_read(BYTE drv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_cache_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count);
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class FileNameEditWindow : public Page
|
||||
};
|
||||
|
||||
RadioSdManagerPage::RadioSdManagerPage() :
|
||||
PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER)
|
||||
PageTab(STR_SD_CARD, ICON_RADIO_SD_MANAGER)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "io/multi_firmware_update.h"
|
||||
#include "io/bootloader_flash.h"
|
||||
#include "libopenui/src/libopenui_file.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1]
|
||||
#define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname)))
|
||||
@@ -39,26 +40,15 @@ void menuRadioSdManagerInfo(event_t event)
|
||||
{
|
||||
SIMPLE_SUBMENU(STR_SD_INFO_TITLE, 1);
|
||||
|
||||
lcdDrawTextAlignedLeft(2*FH, STR_SD_TYPE);
|
||||
lcdDrawText(10*FW, 2*FH, SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD);
|
||||
|
||||
lcdDrawTextAlignedLeft(3*FH, STR_SD_SIZE);
|
||||
lcdDrawNumber(10*FW, 3*FH, sdGetSize(), LEFT);
|
||||
lcdDrawTextAlignedLeft(2*FH, STR_SD_SIZE);
|
||||
lcdDrawNumber(10*FW, 2*FH, sdGetSize(), LEFT);
|
||||
lcdDrawChar(lcdLastRightPos, 3*FH, 'M');
|
||||
|
||||
lcdDrawTextAlignedLeft(4*FH, STR_SD_SECTORS);
|
||||
#if defined(SD_GET_FREE_BLOCKNR)
|
||||
lcdDrawNumber(10*FW, 4*FH, SD_GET_FREE_BLOCKNR()/1000, LEFT);
|
||||
lcdDrawChar(lcdLastRightPos, 4*FH, '/');
|
||||
lcdDrawNumber(lcdLastRightPos+FW, 4*FH, sdGetNoSectors()/1000, LEFT);
|
||||
#else
|
||||
lcdDrawNumber(10*FW, 4*FH, sdGetNoSectors()/1000, LEFT);
|
||||
#endif
|
||||
lcdDrawChar(lcdLastRightPos, 4*FH, 'k');
|
||||
|
||||
lcdDrawTextAlignedLeft(5*FH, STR_SD_SPEED);
|
||||
lcdDrawNumber(10*FW, 5*FH, SD_GET_SPEED()/1000, LEFT);
|
||||
lcdDrawText(lcdLastRightPos, 5*FH, "kb/s");
|
||||
lcdDrawTextAlignedLeft(3*FH, STR_SD_SECTORS);
|
||||
lcdDrawNumber(10*FW, 3*FH, sdGetFreeSectors()/1000, LEFT);
|
||||
lcdDrawChar(lcdLastRightPos, 3*FH, '/');
|
||||
lcdDrawNumber(lcdLastRightPos+FW, 3*FH, sdGetNoSectors()/1000, LEFT);
|
||||
lcdDrawChar(lcdLastRightPos, 3*FH, 'k');
|
||||
}
|
||||
|
||||
inline bool isFilenameGreater(bool isfile, const char * fn, const char * line)
|
||||
@@ -298,7 +288,7 @@ void menuRadioSdManager(event_t _event)
|
||||
|
||||
event_t event = (EVT_KEY_MASK(_event) == KEY_ENTER ? 0 : _event);
|
||||
uint8_t old_editMode = s_editMode;
|
||||
SIMPLE_MENU(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, menuTabGeneral, MENU_RADIO_SD_MANAGER, HEADER_LINE + reusableBuffer.sdManager.count);
|
||||
SIMPLE_MENU(STR_SD_CARD, menuTabGeneral, MENU_RADIO_SD_MANAGER, HEADER_LINE + reusableBuffer.sdManager.count);
|
||||
|
||||
switch (_event) {
|
||||
case EVT_ENTRY:
|
||||
|
||||
@@ -8,3 +8,24 @@ set(SRC ${SRC}
|
||||
hal/adc_driver.cpp
|
||||
hal/key_driver.cpp
|
||||
)
|
||||
|
||||
include_directories(${FATFS_DIR} ${FATFS_DIR}/option)
|
||||
|
||||
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hal/key_driver.cpp
|
||||
)
|
||||
|
||||
if (NOT NATIVE_BUILD)
|
||||
set(SRC ${SRC}
|
||||
hal/storage.cpp
|
||||
hal/fatfs_diskio.cpp
|
||||
${FATFS_DIR}/ff.c
|
||||
${FATFS_DIR}/ffunicode.c
|
||||
)
|
||||
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hal/storage.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hal/fatfs_diskio.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${FATFS_DIR}/ff.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${FATFS_DIR}/ffunicode.c
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "fatfs_diskio.h"
|
||||
#include "FatFs/diskio.h"
|
||||
|
||||
#if FF_FS_REENTRANT != 0
|
||||
#include "rtos.h"
|
||||
#endif
|
||||
|
||||
struct fatfs_drive_t {
|
||||
const diskio_driver_t* drv;
|
||||
uint8_t lun;
|
||||
bool initialized;
|
||||
#if FF_FS_REENTRANT != 0
|
||||
RTOS_MUTEX_HANDLE mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
static fatfs_drive_t _fatfs_drives[FF_VOLUMES];
|
||||
static uint8_t _fatfs_n_drives = 0;
|
||||
|
||||
int fatfsRegisterDriver(const diskio_driver_t* drv, uint8_t lun)
|
||||
{
|
||||
if (_fatfs_n_drives >= FF_VOLUMES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t vol = _fatfs_n_drives++;
|
||||
auto& drive = _fatfs_drives[vol];
|
||||
drive.drv = drv;
|
||||
drive.lun = lun;
|
||||
drive.initialized = false;
|
||||
|
||||
#if FF_FS_REENTRANT != 0
|
||||
// init IO mutex only once
|
||||
RTOS_CREATE_MUTEX(drive.mutex);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const diskio_driver_t* fatfsGetDriver(uint8_t pdrv)
|
||||
{
|
||||
if (pdrv >= _fatfs_n_drives) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _fatfs_drives[pdrv].drv;
|
||||
}
|
||||
|
||||
uint8_t fatfsGetLun(uint8_t pdrv)
|
||||
{
|
||||
if (pdrv >= _fatfs_n_drives) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _fatfs_drives[pdrv].lun;
|
||||
}
|
||||
|
||||
#if FF_FS_REENTRANT != 0
|
||||
|
||||
int ff_cre_syncobj(BYTE vol, FF_SYNC_t* mutex)
|
||||
{
|
||||
*mutex = _fatfs_drives[vol].mutex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ff_req_grant(FF_SYNC_t mutex)
|
||||
{
|
||||
RTOS_LOCK_MUTEX(mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ff_rel_grant(FF_SYNC_t mutex) { RTOS_UNLOCK_MUTEX(mutex); }
|
||||
|
||||
int ff_del_syncobj(FF_SYNC_t mutex) { return 1; }
|
||||
|
||||
#endif
|
||||
|
||||
DSTATUS disk_initialize(BYTE pdrv)
|
||||
{
|
||||
if (pdrv >= _fatfs_n_drives) {
|
||||
return STA_NODISK;
|
||||
}
|
||||
|
||||
DSTATUS stat = RES_OK;
|
||||
auto drive = &_fatfs_drives[pdrv];
|
||||
if (!drive->initialized) {
|
||||
stat = drive->drv->initialize(drive->lun);
|
||||
if (stat == RES_OK) {
|
||||
drive->initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
DSTATUS disk_status(BYTE pdrv)
|
||||
{
|
||||
auto drv = _fatfs_drives[pdrv].drv;
|
||||
uint8_t lun = _fatfs_drives[pdrv].lun;
|
||||
return drv->status(lun);
|
||||
}
|
||||
|
||||
DRESULT disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
auto drv = _fatfs_drives[pdrv].drv;
|
||||
uint8_t lun = _fatfs_drives[pdrv].lun;
|
||||
return drv->read(lun, buff, sector, count);
|
||||
}
|
||||
|
||||
DRESULT disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
auto drv = _fatfs_drives[pdrv].drv;
|
||||
uint8_t lun = _fatfs_drives[pdrv].lun;
|
||||
return drv->write(lun, buff, sector, count);
|
||||
}
|
||||
|
||||
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
auto drv = _fatfs_drives[pdrv].drv;
|
||||
uint8_t lun = _fatfs_drives[pdrv].lun;
|
||||
return drv->ioctl(lun, cmd, buff);
|
||||
}
|
||||
|
||||
#if FF_FS_NORTC == 0
|
||||
|
||||
#include "rtc.h"
|
||||
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
struct gtm t;
|
||||
|
||||
gettime(&t);
|
||||
|
||||
/* Pack date and time into a DWORD variable */
|
||||
return ((DWORD)(t.tm_year - 80) << 25)
|
||||
| ((uint32_t)(t.tm_mon+1) << 21)
|
||||
| ((uint32_t)t.tm_mday << 16)
|
||||
| ((uint32_t)t.tm_hour << 11)
|
||||
| ((uint32_t)t.tm_min << 5)
|
||||
| ((uint32_t)t.tm_sec >> 1);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FatFs/diskio.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct diskio_driver_t
|
||||
{
|
||||
DSTATUS (*initialize)(BYTE pdrv);
|
||||
|
||||
DSTATUS (*status)(BYTE pdrv);
|
||||
|
||||
DRESULT (*read)(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
|
||||
DRESULT (*write)(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
|
||||
DRESULT (*ioctl)(BYTE pdrv, BYTE cmd, void* buff);
|
||||
};
|
||||
|
||||
// returns 1 if successful, 0 otherwise
|
||||
int fatfsRegisterDriver(const diskio_driver_t* drv, uint8_t lun);
|
||||
|
||||
// returns a pyhsical disk driver or NULL
|
||||
const diskio_driver_t* fatfsGetDriver(uint8_t pdrv);
|
||||
|
||||
// returns a physical LUN or 0
|
||||
uint8_t fatfsGetLun(uint8_t pdrv);
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "hal/storage.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#if defined(STORAGE_USE_SDIO)
|
||||
#include "diskio_sdio.h"
|
||||
#define _STORAGE_DRIVER sdio_diskio_driver
|
||||
#elif defined(STORAGE_USE_SDCARD_SPI)
|
||||
#include "diskio_spi.h"
|
||||
#define _STORAGE_DRIVER sdcard_spi_driver
|
||||
#else
|
||||
#error "No supported storage driver configured"
|
||||
#endif
|
||||
|
||||
#if defined(DISK_CACHE)
|
||||
#include "disk_cache.h"
|
||||
const diskio_driver_t disk_cache_shim = {
|
||||
.initialize = _STORAGE_DRIVER.initialize,
|
||||
.status = _STORAGE_DRIVER.status,
|
||||
.read = disk_cache_read,
|
||||
.write = disk_cache_write,
|
||||
.ioctl = _STORAGE_DRIVER.ioctl,
|
||||
};
|
||||
#endif
|
||||
|
||||
void storageInit()
|
||||
{
|
||||
if (fatfsGetDriver(0) != nullptr)
|
||||
return;
|
||||
|
||||
const diskio_driver_t* drv = &_STORAGE_DRIVER;
|
||||
|
||||
#if defined(DISK_CACHE)
|
||||
diskCache.initialize(drv);
|
||||
drv = &disk_cache_shim;
|
||||
#endif
|
||||
|
||||
if (!fatfsRegisterDriver(drv, 0)) {
|
||||
TRACE("fatfsRegisterDriver: [FAILED]");
|
||||
}
|
||||
}
|
||||
|
||||
void storagePreMountHook()
|
||||
{
|
||||
#if defined(DISK_CACHE)
|
||||
diskCache.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool storageIsPresent()
|
||||
{
|
||||
return (_STORAGE_DRIVER.status(0) & STA_NODISK) == 0;
|
||||
}
|
||||
|
||||
const diskio_driver_t* storageGetDefaultDriver()
|
||||
{
|
||||
return &_STORAGE_DRIVER;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Called when the storage is initialized
|
||||
//
|
||||
// Note: can be used to attach the pyhsical volume driver
|
||||
// to the generic FatFs driver
|
||||
void storageInit();
|
||||
|
||||
// Called before the storage is mounted
|
||||
void storagePreMountHook();
|
||||
|
||||
bool storageIsPresent();
|
||||
|
||||
#define SD_CARD_PRESENT() storageIsPresent()
|
||||
|
||||
struct diskio_driver_t;
|
||||
|
||||
// returns the default storage's IO driver
|
||||
const diskio_driver_t* storageGetDefaultDriver();
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "opentx.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "io/frsky_firmware_update.h"
|
||||
#include "hal/adc_driver.h"
|
||||
#include "hal/switch_driver.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#include "timers_driver.h"
|
||||
#include "watchdog_driver.h"
|
||||
|
||||
@@ -198,10 +200,6 @@ void per10ms()
|
||||
if (mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) AUDIO_MIX_WARNING(3);
|
||||
#endif
|
||||
|
||||
#if defined(SDCARD) && defined(PCBTARANIS)
|
||||
sdPoll10ms();
|
||||
#endif
|
||||
|
||||
outputTelemetryBuffer.per10ms();
|
||||
|
||||
heartbeat |= HEART_TIMER_10MS;
|
||||
|
||||
+9
-6
@@ -22,7 +22,8 @@
|
||||
#ifndef _RTOS_H_
|
||||
#define _RTOS_H_
|
||||
|
||||
#include "definitions.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C++" {
|
||||
@@ -114,15 +115,17 @@ extern "C++" {
|
||||
#endif
|
||||
}
|
||||
|
||||
template<int SIZE>
|
||||
inline void RTOS_CREATE_TASK(pthread_t &taskId, void * (*task)(void *), const char * name, TaskStack<SIZE> &, unsigned size = 0, unsigned priority = 0)
|
||||
template <int SIZE>
|
||||
inline void RTOS_CREATE_TASK(pthread_t &taskId, void *(*task)(void *),
|
||||
const char *name, TaskStack<SIZE> &,
|
||||
unsigned size = 0, unsigned priority = 0)
|
||||
{
|
||||
UNUSED(size);
|
||||
UNUSED(priority);
|
||||
(void)size;
|
||||
(void)priority;
|
||||
RTOS_CREATE_TASK(taskId, task, name);
|
||||
}
|
||||
|
||||
#define TASK_RETURN() return nullptr
|
||||
#define TASK_RETURN() return nullptr
|
||||
|
||||
constexpr uint32_t mainStackAvailable()
|
||||
{
|
||||
|
||||
+7
-51
@@ -22,8 +22,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal/fatfs_diskio.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#include "opentx.h"
|
||||
#include "diskio.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
@@ -479,7 +481,8 @@ uint32_t sdGetSize()
|
||||
|
||||
uint32_t sdGetFreeSectors()
|
||||
{
|
||||
return ((SDCARD_MIN_FREE_SPACE_MB*1024*1024)/BLOCK_SIZE)+1; // SIMU SD card is always above threshold
|
||||
// SIMU SD card is always above threshold
|
||||
return ((SDCARD_MIN_FREE_SPACE_MB*1024*1024)/BLOCK_SIZE)+1;
|
||||
}
|
||||
|
||||
uint32_t sdGetFreeKB() { return SDCARD_MIN_FREE_SPACE_MB * 1024 + 1; }
|
||||
@@ -501,41 +504,11 @@ FIL g_bluetoothFile = {};
|
||||
|
||||
#include "audio.h"
|
||||
#include "sdcard.h"
|
||||
#include "disk_cache.h"
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Lock / unlock functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static RTOS_MUTEX_HANDLE ioMutex;
|
||||
uint32_t ioMutexReq = 0, ioMutexRel = 0;
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t * mutex)
|
||||
{
|
||||
*mutex = ioMutex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ff_req_grant (FF_SYNC_t mutex)
|
||||
{
|
||||
ioMutexReq += 1;
|
||||
RTOS_LOCK_MUTEX(mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ff_rel_grant (FF_SYNC_t mutex)
|
||||
{
|
||||
ioMutexRel += 1;
|
||||
RTOS_UNLOCK_MUTEX(mutex);
|
||||
}
|
||||
|
||||
int ff_del_syncobj (FF_SYNC_t mutex)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void sdInit()
|
||||
{
|
||||
TRACE("sdInit");
|
||||
RTOS_CREATE_MUTEX(ioMutex);
|
||||
storageInit();
|
||||
sdMount();
|
||||
}
|
||||
|
||||
@@ -543,9 +516,7 @@ void sdMount()
|
||||
{
|
||||
TRACE("sdMount");
|
||||
|
||||
#if defined(DISK_CACHE)
|
||||
diskCache.clear();
|
||||
#endif
|
||||
storagePreMountHook();
|
||||
|
||||
if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) {
|
||||
// call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called
|
||||
@@ -598,18 +569,3 @@ uint32_t sdMounted()
|
||||
return _g_FATFS_init && (g_FATFS_Obj.fs_type != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint32_t sdIsHC()
|
||||
{
|
||||
// defined in diskio
|
||||
#define CT_BLOCK 0x08
|
||||
extern uint32_t SD_GetCardType();
|
||||
|
||||
return SD_GetCardType() & CT_BLOCK;
|
||||
}
|
||||
|
||||
uint32_t sdGetSpeed()
|
||||
{
|
||||
return 330000;
|
||||
}
|
||||
|
||||
@@ -135,21 +135,6 @@ uint32_t sdGetFreeSectors();
|
||||
uint32_t sdGetFreeKB();
|
||||
bool sdIsFull();
|
||||
|
||||
#if defined(PCBTARANIS)
|
||||
void sdPoll10ms();
|
||||
#endif
|
||||
|
||||
#if !defined(SIMU) || defined(SIMU_DISKIO)
|
||||
uint32_t sdIsHC();
|
||||
uint32_t sdGetSpeed();
|
||||
#define SD_IS_HC() (sdIsHC())
|
||||
#define SD_GET_SPEED() (sdGetSpeed())
|
||||
#define SD_GET_FREE_BLOCKNR() (sdGetFreeSectors())
|
||||
#else
|
||||
#define SD_IS_HC() (0)
|
||||
#define SD_GET_SPEED() (0)
|
||||
#endif
|
||||
|
||||
const char * sdCheckAndCreateDirectory(const char * path);
|
||||
|
||||
#if !defined(BOOT)
|
||||
@@ -162,7 +147,6 @@ inline const char * SDCARD_ERROR(FRESULT result)
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE: 'size' must = 0 or be a valid character position within 'filename' array -- it is NOT validated
|
||||
const char * getBasename(const char * path);
|
||||
|
||||
bool isFileAvailable(const char * filename, bool exclDir = false);
|
||||
|
||||
@@ -83,16 +83,13 @@ extern "C" void dbgSerialPrintf(const char * format, ...)
|
||||
va_list arglist;
|
||||
char tmp[PRINTF_BUFFER_SIZE+1];
|
||||
|
||||
// no need to do anything if we don't have an output
|
||||
if (!dbg_serial_putc) return;
|
||||
|
||||
va_start(arglist, format);
|
||||
vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist);
|
||||
tmp[PRINTF_BUFFER_SIZE] = '\0';
|
||||
va_end(arglist);
|
||||
|
||||
const char *t = tmp;
|
||||
while (*t && dbg_serial_putc) {
|
||||
while (*t) {
|
||||
SEGGER_RTT_Write(0, (const void *)t++, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,69 +30,43 @@
|
||||
#define MANUAL_MODE 0x1000 // manual mode channel 0
|
||||
#define MANUAL_MODE_CHANNEL(x) (MANUAL_MODE | ((x) << 7))
|
||||
|
||||
static uint16_t ads79xx_rw(SPI_TypeDef* SPIx, uint16_t value)
|
||||
{
|
||||
while (!LL_SPI_IsActiveFlag_TXE(SPIx));
|
||||
LL_SPI_TransmitData16(SPIx, value);
|
||||
#define ADS79XX_MAX_FREQ (20000000UL)
|
||||
|
||||
while (!LL_SPI_IsActiveFlag_RXNE(SPIx));
|
||||
return LL_SPI_ReceiveData16(SPIx);
|
||||
static inline uint16_t ads79xx_rw(const stm32_spi_t* spi, uint16_t value)
|
||||
{
|
||||
return stm32_spi_transfer_word(spi, value);
|
||||
}
|
||||
|
||||
void ads79xx_init(const stm32_spi_adc_t* adc)
|
||||
{
|
||||
stm32_gpio_enable_clock(adc->GPIOx);
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
const auto* spi = &adc->spi;
|
||||
|
||||
pinInit.Pin = adc->GPIO_Pins;
|
||||
pinInit.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
pinInit.Alternate = adc->GPIO_AF;
|
||||
LL_GPIO_Init(adc->GPIOx, &pinInit);
|
||||
|
||||
pinInit.Pin = adc->GPIO_CS;
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Alternate = LL_GPIO_AF_0;
|
||||
LL_GPIO_Init(adc->GPIOx, &pinInit);
|
||||
|
||||
auto SPIx = adc->SPIx;
|
||||
stm32_spi_enable_clock(SPIx);
|
||||
LL_SPI_DeInit(SPIx);
|
||||
|
||||
LL_SPI_InitTypeDef spiInit;
|
||||
LL_SPI_StructInit(&spiInit);
|
||||
|
||||
spiInit.TransferDirection = LL_SPI_FULL_DUPLEX;
|
||||
spiInit.Mode = LL_SPI_MODE_MASTER;
|
||||
spiInit.DataWidth = LL_SPI_DATAWIDTH_16BIT;
|
||||
spiInit.NSS = LL_SPI_NSS_SOFT;
|
||||
spiInit.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
|
||||
LL_SPI_Init(SPIx, &spiInit);
|
||||
|
||||
LL_SPI_Enable(SPIx);
|
||||
|
||||
LL_SPI_DisableIT_TXE(SPIx);
|
||||
LL_SPI_DisableIT_RXNE(SPIx);
|
||||
|
||||
_SPI_ADC_CS_HIGH(adc);
|
||||
stm32_spi_init(spi);
|
||||
stm32_spi_set_max_baudrate(spi, ADS79XX_MAX_FREQ);
|
||||
LL_SPI_SetDataWidth(spi->SPIx, LL_SPI_DATAWIDTH_16BIT);
|
||||
|
||||
stm32_spi_unselect(spi);
|
||||
delay_01us(1);
|
||||
_SPI_ADC_CS_LOW(adc);
|
||||
ads79xx_rw(SPIx, RESETCMD);
|
||||
_SPI_ADC_CS_HIGH(adc);
|
||||
|
||||
stm32_spi_select(spi);
|
||||
ads79xx_rw(spi, RESETCMD);
|
||||
stm32_spi_unselect(spi);
|
||||
delay_01us(1);
|
||||
_SPI_ADC_CS_LOW(adc);
|
||||
ads79xx_rw(SPIx, MANUAL_MODE);
|
||||
_SPI_ADC_CS_HIGH(adc);
|
||||
|
||||
stm32_spi_select(spi);
|
||||
ads79xx_rw(spi, MANUAL_MODE);
|
||||
stm32_spi_unselect(spi);
|
||||
}
|
||||
|
||||
static void ads79xx_dummy_read(const stm32_spi_adc_t* adc, uint16_t start_channel)
|
||||
{
|
||||
// A dummy command to get things started
|
||||
// (because the sampled data is lagging behind for two command cycles)
|
||||
_SPI_ADC_CS_LOW(adc);
|
||||
const auto* spi = &adc->spi;
|
||||
stm32_spi_select(spi);
|
||||
delay_01us(1);
|
||||
ads79xx_rw(adc->SPIx, MANUAL_MODE_CHANNEL(start_channel));
|
||||
_SPI_ADC_CS_HIGH(adc);
|
||||
ads79xx_rw(spi, MANUAL_MODE_CHANNEL(start_channel));
|
||||
stm32_spi_unselect(spi);
|
||||
delay_01us(1);
|
||||
}
|
||||
|
||||
@@ -115,8 +89,9 @@ static uint32_t ads79xx_read_next_channel(const stm32_spi_adc_t* adc,
|
||||
// 62 0 0.225ms - 0.243ms
|
||||
delay_01us(40);
|
||||
|
||||
const auto* spi = &adc->spi;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
_SPI_ADC_CS_LOW(adc);
|
||||
stm32_spi_select(spi);
|
||||
delay_01us(1);
|
||||
|
||||
// command is changed to the next index for the last two readings
|
||||
@@ -126,7 +101,7 @@ static uint32_t ads79xx_read_next_channel(const stm32_spi_adc_t* adc,
|
||||
auto input_idx = chan[chan_idx];
|
||||
auto spi_chan = inputs[input_idx].ADC_Channel;
|
||||
|
||||
uint16_t val = (0x0fff & ads79xx_rw(adc->SPIx, MANUAL_MODE_CHANNEL(spi_chan)));
|
||||
uint16_t val = (0x0fff & ads79xx_rw(spi, MANUAL_MODE_CHANNEL(spi_chan)));
|
||||
|
||||
#if defined(JITTER_MEASURE)
|
||||
if (JITTER_MEASURE_ACTIVE()) {
|
||||
@@ -134,7 +109,7 @@ static uint32_t ads79xx_read_next_channel(const stm32_spi_adc_t* adc,
|
||||
}
|
||||
#endif
|
||||
|
||||
_SPI_ADC_CS_HIGH(adc);
|
||||
stm32_spi_unselect(spi);
|
||||
delay_01us(1);
|
||||
result += val;
|
||||
}
|
||||
|
||||
@@ -21,14 +21,11 @@ set(BOOTLOADER_SRC
|
||||
../../../../../${STM32USB_DIR}/STM32_USB_Device_Library/Class/msc/src/usbd_msc_scsi.c
|
||||
../../../../../${STM32USB_DIR}/STM32_USB_Device_Library/Class/msc/src/usbd_msc_bot.c
|
||||
../../../../../${STM32USB_DIR}/STM32_USB_Device_Library/Class/msc/src/usbd_msc_core.c
|
||||
../../../../../${FATFS_DIR}/ff.c
|
||||
../../../../../${FATFS_DIR}/ffunicode.c
|
||||
../../../../../targets/${TARGET_DIR}/${LCD_DRIVER}
|
||||
../../../../../targets/${TARGET_DIR}/backlight_driver.cpp
|
||||
../../../../../targets/${TARGET_DIR}/board.cpp
|
||||
../../../../../targets/${TARGET_DIR}/bootloader/boot_menu.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/hal_keys.inc
|
||||
../../../../../hal/key_driver.cpp
|
||||
../pwr_driver.cpp
|
||||
../usbd_usr.cpp
|
||||
../usbd_storage_msd.cpp
|
||||
@@ -77,7 +74,6 @@ if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14)
|
||||
../../../../../fonts/lvgl/lv_font_roboto_bl_16.c
|
||||
../../../../../thirdparty/libopenui/src/bitmapbuffer.cpp
|
||||
../../../../../thirdparty/libopenui/thirdparty/lz4/lz4.c
|
||||
../../../../../targets/common/arm/stm32/sdio_sd.cpp
|
||||
../../../../../targets/common/arm/stm32/diskio_sdio.cpp
|
||||
../../../../../targets/common/arm/stm32/rtc_driver.cpp
|
||||
../../../../../targets/${TARGET_DIR}/sdram_driver.c
|
||||
@@ -119,8 +115,11 @@ else()
|
||||
${BOOTLOADER_SRC}
|
||||
../../../../../gui/common/stdlcd/fonts.cpp
|
||||
../../../../../gui/common/stdlcd/utf8.cpp
|
||||
../../../../../targets/${TARGET_DIR}/eeprom_driver.cpp
|
||||
../../../../../targets/${TARGET_DIR}/diskio.cpp
|
||||
../../../../../targets/common/arm/stm32/stm32_gpio_driver.cpp
|
||||
../../../../../targets/common/arm/stm32/stm32_spi.cpp
|
||||
../../../../../targets/common/arm/stm32/sdcard_spi.cpp
|
||||
../../../../../targets/common/arm/stm32/diskio_spi.cpp
|
||||
../../../../../crc.cpp
|
||||
)
|
||||
remove_definitions(-DDEBUG)
|
||||
endif()
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "bin_files.h"
|
||||
#include "fw_version.h"
|
||||
#include "strhelpers.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
// 'private'
|
||||
static DIR dir;
|
||||
@@ -42,6 +43,8 @@ void sdInit(void)
|
||||
{
|
||||
static FATFS fatFS __DMA;
|
||||
|
||||
storageInit();
|
||||
|
||||
if (f_mount(&fatFS, "", 1) == FR_OK) {
|
||||
f_chdir("/");
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ uint32_t eepromWritten = 0;
|
||||
FlashCheckRes valid;
|
||||
MemoryType memoryType;
|
||||
uint32_t unlocked = 0;
|
||||
uint32_t timer10MsCount;
|
||||
|
||||
volatile uint32_t timer10MsCount;
|
||||
|
||||
void interrupt10ms()
|
||||
{
|
||||
@@ -117,30 +117,52 @@ void interrupt10ms()
|
||||
#endif
|
||||
}
|
||||
|
||||
void init10msTimer()
|
||||
extern "C" uint32_t HAL_GetTick(void)
|
||||
{
|
||||
return timer10MsCount * 10;
|
||||
}
|
||||
|
||||
#if !defined(SIMU)
|
||||
static volatile uint32_t _us_overflow_cnt;
|
||||
|
||||
void initTimers()
|
||||
{
|
||||
timer10MsCount = 0;
|
||||
INTERRUPT_xMS_TIMER->ARR = 9999; // 10mS in uS
|
||||
INTERRUPT_xMS_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS
|
||||
INTERRUPT_xMS_TIMER->CCER = 0;
|
||||
INTERRUPT_xMS_TIMER->CCMR1 = 0;
|
||||
INTERRUPT_xMS_TIMER->EGR = 0;
|
||||
INTERRUPT_xMS_TIMER->CR1 = 5;
|
||||
INTERRUPT_xMS_TIMER->DIER |= 1;
|
||||
INTERRUPT_xMS_TIMER->CR1 = TIM_CR1_CEN;
|
||||
INTERRUPT_xMS_TIMER->DIER = TIM_DIER_UIE;
|
||||
NVIC_EnableIRQ(INTERRUPT_xMS_IRQn);
|
||||
|
||||
_us_overflow_cnt = 0;
|
||||
TIMER_2MHz_TIMER->ARR = 65535;
|
||||
TIMER_2MHz_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 2000000 - 1; // 0.5 uS, 2 MHz
|
||||
TIMER_2MHz_TIMER->CR1 = TIM_CR1_CEN;
|
||||
TIMER_2MHz_TIMER->DIER = TIM_DIER_UIE;
|
||||
NVIC_EnableIRQ(TIMER_2MHz_IRQn);
|
||||
|
||||
while(timer10MsCount < 10);
|
||||
}
|
||||
|
||||
extern "C" uint32_t HAL_GetTick(void)
|
||||
uint32_t timersGetUsTick()
|
||||
{
|
||||
return timer10MsCount*10;
|
||||
uint32_t us;
|
||||
us = TIMER_2MHz_TIMER->CNT >> 1;
|
||||
us += _us_overflow_cnt << 15;
|
||||
return us;
|
||||
}
|
||||
|
||||
#if !defined(SIMU)
|
||||
extern "C" void INTERRUPT_xMS_IRQHandler()
|
||||
{
|
||||
INTERRUPT_xMS_TIMER->SR &= ~TIM_SR_UIF;
|
||||
interrupt10ms();
|
||||
}
|
||||
|
||||
extern "C" void TIMER_2MHz_IRQHandler()
|
||||
{
|
||||
TIMER_2MHz_TIMER->SR &= ~TIM_SR_UIF;
|
||||
_us_overflow_cnt += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t isValidBufferStart(const uint8_t * buffer)
|
||||
@@ -226,14 +248,15 @@ void writeEepromBlock()
|
||||
#if !defined(SIMU)
|
||||
void bootloaderInitApp()
|
||||
{
|
||||
RCC_AHB1PeriphClockCmd(PWR_RCC_AHB1Periph |
|
||||
LCD_RCC_AHB1Periph | BACKLIGHT_RCC_AHB1Periph |
|
||||
KEYS_BACKLIGHT_RCC_AHB1Periph | SD_RCC_AHB1Periph,
|
||||
RCC_AHB1PeriphClockCmd(PWR_RCC_AHB1Periph | LCD_RCC_AHB1Periph |
|
||||
BACKLIGHT_RCC_AHB1Periph |
|
||||
KEYS_BACKLIGHT_RCC_AHB1Periph,
|
||||
ENABLE);
|
||||
|
||||
RCC_APB1PeriphClockCmd(ROTARY_ENCODER_RCC_APB1Periph | LCD_RCC_APB1Periph |
|
||||
BACKLIGHT_RCC_APB1Periph |
|
||||
INTERRUPT_xMS_RCC_APB1Periph | SD_RCC_APB1Periph,
|
||||
INTERRUPT_xMS_RCC_APB1Periph |
|
||||
TIMER_2MHz_RCC_APB1Periph,
|
||||
ENABLE);
|
||||
|
||||
RCC_APB2PeriphClockCmd(
|
||||
@@ -251,18 +274,6 @@ void bootloaderInitApp()
|
||||
pwrInit();
|
||||
keysInit();
|
||||
|
||||
#if defined(SWSERIALPOWER)
|
||||
// TODO: replace with proper serial port query...
|
||||
// #if defined(AUX_SERIAL)
|
||||
// void set_aux_pwr(uint8_t on);
|
||||
// set_aux_pwr(0);
|
||||
// #endif
|
||||
// #if defined(AUX2_SERIAL)
|
||||
// void set_aux2_pwr(uint8_t on);
|
||||
// set_aux2_pwr(0);
|
||||
// #endif
|
||||
#endif
|
||||
|
||||
// wait a bit for the inputs to stabilize...
|
||||
if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) {
|
||||
for (uint32_t i = 0; i < 150000; i++) {
|
||||
@@ -311,7 +322,7 @@ void bootloaderInitApp()
|
||||
eepromInit();
|
||||
#endif
|
||||
|
||||
init10msTimer();
|
||||
initTimers();
|
||||
|
||||
// SD card detect pin
|
||||
sdInit();
|
||||
@@ -578,16 +589,6 @@ int bootloaderMain()
|
||||
|
||||
lcdRefresh();
|
||||
|
||||
#if defined(SDCARD) && defined(PCBTARANIS)
|
||||
static uint32_t PowerUpDelay = 0;
|
||||
|
||||
if (PowerUpDelay < 20) { // 200 mS
|
||||
PowerUpDelay += 1;
|
||||
}
|
||||
else {
|
||||
sdPoll10ms();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (state != ST_FLASHING && state != ST_USB) {
|
||||
|
||||
@@ -19,19 +19,37 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* This is a stub disk I/O module that acts as front end of the existing */
|
||||
/* disk I/O modules and attach it to FatFs module with common interface. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
#include "diskio_sdio.h"
|
||||
|
||||
#include "stm32_dma.h"
|
||||
#include "stm32_hal.h"
|
||||
#include "sdio_sd.h"
|
||||
#include "stm32_hal_ll.h"
|
||||
#include "stm32_gpio_driver.h"
|
||||
|
||||
#include "FatFs/diskio.h"
|
||||
#include "hal.h"
|
||||
|
||||
// #include "delays_driver.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
|
||||
/* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
|
||||
#if !defined(SD_SDIO_DATA_GPIO) && !defined(SD_SDIO_DATA_GPIO_PINS)
|
||||
#define SD_SDIO_DATA_GPIO GPIOC
|
||||
#define SD_SDIO_DATA_GPIO_PINS \
|
||||
(LL_GPIO_PIN_8 | LL_GPIO_PIN_9 | LL_GPIO_PIN_10 | LL_GPIO_PIN_11)
|
||||
#endif
|
||||
|
||||
/* Configure PD.02 CMD line */
|
||||
#if !defined(SD_SDIO_CMD_GPIO) && !defined(SD_SDIO_CMD_GPIO_PIN)
|
||||
#define SD_SDIO_CMD_GPIO GPIOD
|
||||
#define SD_SDIO_CMD_GPIO_PIN LL_GPIO_PIN_2
|
||||
#endif
|
||||
|
||||
#if !defined(SD_SDIO_CLK_GPIO) && !defined(SD_SDIO_CLK_GPIO_PIN)
|
||||
#define SD_SDIO_CLK_GPIO GPIOC
|
||||
#define SD_SDIO_CLK_GPIO_PIN LL_GPIO_PIN_12
|
||||
#endif
|
||||
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
#error "Variable sector size is not supported"
|
||||
@@ -40,33 +58,103 @@
|
||||
#define BLOCK_SIZE FF_MAX_SS
|
||||
#define SD_TIMEOUT 300 /* 300ms */
|
||||
|
||||
// HAL state
|
||||
static SD_HandleTypeDef sdio;
|
||||
static DMA_HandleTypeDef sdioTxDma;
|
||||
|
||||
// Disk status
|
||||
extern volatile uint32_t WriteStatus;
|
||||
extern volatile uint32_t ReadStatus;
|
||||
static volatile uint32_t WriteStatus;
|
||||
static volatile uint32_t ReadStatus;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE drv /* Physical drive nmuber (0..) */
|
||||
)
|
||||
static void sdio_low_level_init(void)
|
||||
{
|
||||
DSTATUS stat = 0;
|
||||
/* Enable the SDIO APB2 Clock */
|
||||
__HAL_RCC_SDIO_CLK_ENABLE();
|
||||
|
||||
/* Supports only single drive */
|
||||
if (drv)
|
||||
{
|
||||
stat |= STA_NOINIT;
|
||||
LL_GPIO_InitTypeDef GPIO_InitStructure;
|
||||
LL_GPIO_StructInit(&GPIO_InitStructure);
|
||||
|
||||
stm32_gpio_enable_clock(SD_SDIO_DATA_GPIO);
|
||||
stm32_gpio_enable_clock(SD_SDIO_CMD_GPIO);
|
||||
stm32_gpio_enable_clock(SD_SDIO_CLK_GPIO);
|
||||
|
||||
GPIO_InitStructure.Pin = SD_SDIO_DATA_GPIO_PINS;
|
||||
GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
GPIO_InitStructure.Pull = LL_GPIO_PULL_UP;
|
||||
GPIO_InitStructure.Alternate = LL_GPIO_AF_12; // SDIO
|
||||
LL_GPIO_Init(SD_SDIO_DATA_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.Pin = SD_SDIO_CMD_GPIO_PIN;
|
||||
LL_GPIO_Init(SD_SDIO_CMD_GPIO, &GPIO_InitStructure);
|
||||
|
||||
/* Configure PC.12 pin: CLK pin */
|
||||
GPIO_InitStructure.Pin = SD_SDIO_CLK_GPIO_PIN;
|
||||
GPIO_InitStructure.Pull = LL_GPIO_PULL_NO;
|
||||
LL_GPIO_Init(SD_SDIO_CLK_GPIO, &GPIO_InitStructure);
|
||||
|
||||
// SDIO Interrupt ENABLE
|
||||
NVIC_SetPriority(SDIO_IRQn, 0);
|
||||
NVIC_EnableIRQ(SDIO_IRQn);
|
||||
|
||||
// Init SDIO DMA instance
|
||||
sdioTxDma.Instance = SD_SDIO_DMA_STREAM;
|
||||
sdioTxDma.Init.Channel = SD_SDIO_DMA_CHANNEL;
|
||||
sdioTxDma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
sdioTxDma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
sdioTxDma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
sdioTxDma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
sdioTxDma.Init.Mode = DMA_PFCTRL;
|
||||
sdioTxDma.Init.Priority = DMA_PRIORITY_VERY_HIGH;
|
||||
sdioTxDma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
sdioTxDma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
sdioTxDma.Init.MemBurst = DMA_MBURST_INC4;
|
||||
sdioTxDma.Init.PeriphBurst = DMA_PBURST_INC4;
|
||||
|
||||
stm32_dma_enable_clock(SD_SDIO_DMA);
|
||||
HAL_DMA_Init(&sdioTxDma);
|
||||
|
||||
__HAL_LINKDMA(&sdio, hdmatx, sdioTxDma);
|
||||
__HAL_LINKDMA(&sdio, hdmarx, sdioTxDma);
|
||||
|
||||
// DMA2 STREAMx Interrupt ENABLE
|
||||
NVIC_SetPriority(SD_SDIO_DMA_IRQn, 0);
|
||||
NVIC_EnableIRQ(SD_SDIO_DMA_IRQn);
|
||||
}
|
||||
|
||||
static DSTATUS sdio_initialize(BYTE lun)
|
||||
{
|
||||
/* SDIO Peripheral Low Level Init */
|
||||
sdio_low_level_init();
|
||||
|
||||
/*!< Configure the SDIO peripheral */
|
||||
/*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */
|
||||
/*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
|
||||
sdio.Instance = SDIO;
|
||||
sdio.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
|
||||
sdio.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
|
||||
sdio.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
|
||||
sdio.Init.BusWide = SDIO_BUS_WIDE_1B;
|
||||
sdio.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
|
||||
sdio.Init.ClockDiv = SD_SDIO_TRANSFER_CLK_DIV;
|
||||
HAL_SD_DeInit(&sdio);
|
||||
|
||||
HAL_StatusTypeDef halStatus = HAL_SD_Init(&sdio);
|
||||
if (halStatus != HAL_OK) {
|
||||
TRACE("HAL_SD_Init() status=%d", halStatus);
|
||||
/*!< CMD Response TimeOut (wait for CMDSENT flag) */
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
/*-------------------------- SD Init ----------------------------- */
|
||||
SD_Error res = SD_Init();
|
||||
if (res != SD_OK)
|
||||
{
|
||||
TRACE("SD_Init() failed: %d", res);
|
||||
stat |= STA_NOINIT;
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
HAL_StatusTypeDef es = HAL_SD_GetCardInfo(&sdio, &cardInfo);
|
||||
if(es != HAL_OK) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
HAL_SD_ConfigWideBusOperation(&sdio, SDIO_BUS_WIDE_4B);
|
||||
|
||||
TRACE("SD card info:");
|
||||
TRACE("type: %u", (uint32_t)(SD_GetCardType()));
|
||||
TRACE("class: %u", (uint32_t)(SD_GetCardClass()));
|
||||
@@ -74,110 +162,95 @@ DSTATUS disk_initialize (
|
||||
TRACE("sector size: %u", (uint32_t)(SD_GetSectorSize()));
|
||||
TRACE("block size: %u", (uint32_t)(SD_GetBlockSize()));
|
||||
|
||||
return(stat);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DWORD scratch[BLOCK_SIZE / 4] __DMA;
|
||||
// DMA scratch buffer used in case the input buffer is not aligned
|
||||
static uint8_t scratch[BLOCK_SIZE] __DMA;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Return Disk Status */
|
||||
|
||||
DSTATUS disk_status (
|
||||
BYTE drv /* Physical drive nmuber (0..) */
|
||||
)
|
||||
typedef enum
|
||||
{
|
||||
DSTATUS stat = 0;
|
||||
SD_TRANSFER_OK = 0,
|
||||
SD_TRANSFER_BUSY = 1,
|
||||
SD_TRANSFER_ERROR
|
||||
} SDTransferState;
|
||||
|
||||
if (SD_Detect() != SD_PRESENT)
|
||||
stat |= STA_NODISK;
|
||||
|
||||
// STA_NOTINIT - Subsystem not initailized
|
||||
// STA_PROTECTED - Write protected, MMC/SD switch if available
|
||||
|
||||
return(stat);
|
||||
}
|
||||
|
||||
uint32_t sdReadRetries = 0;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
|
||||
|
||||
// Please note:
|
||||
// this functions assumes that buff is properly aligned
|
||||
// and in the right RAM segment for DMA
|
||||
//
|
||||
DRESULT disk_read_dma(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
static SDTransferState sdio_check_card_state()
|
||||
{
|
||||
DRESULT res = RES_ERROR;
|
||||
HAL_SD_CardStateTypeDef cardstate = HAL_SD_GetCardState(&sdio);
|
||||
|
||||
for (int retry = 0; retry < 3; retry++) {
|
||||
|
||||
ReadStatus = 0;
|
||||
SD_Error Status = SD_ReadBlocks(buff, sector, BLOCK_SIZE, count);
|
||||
if (Status != SD_OK) {
|
||||
TRACE("SD ReadBlocks=%d, s:%u c: %u", Status, sector, (uint32_t)count);
|
||||
++sdReadRetries;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait that the reading process is completed or a timeout occurs
|
||||
uint32_t timeout = HAL_GetTick();
|
||||
while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT));
|
||||
|
||||
if (ReadStatus == 0) {
|
||||
TRACE("SD read timeout, s:%u c:%u", sector, (uint32_t)count);
|
||||
++sdReadRetries;
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadStatus = 0;
|
||||
timeout = HAL_GetTick();
|
||||
|
||||
while((HAL_GetTick() - timeout) < SD_TIMEOUT) {
|
||||
if (SD_GetStatus() == SD_TRANSFER_OK) {
|
||||
res = RES_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == RES_OK) {
|
||||
// exit retry loop
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("SD getstatus timeout, s:%u c:%u", sector, (uint32_t)count);
|
||||
if (cardstate == HAL_SD_CARD_TRANSFER) {
|
||||
return SD_TRANSFER_OK;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
// If unaligned, do the single block reads with a scratch buffer.
|
||||
// If aligned and single sector, do a single block read.
|
||||
// If aligned and multiple sectors, try multi block read.
|
||||
// If multi block read fails, try single block reads without
|
||||
// an intermediate buffer (move trough the provided buffer)
|
||||
|
||||
// TRACE("disk_read %d %p %10d %d", drv, buff, sector, count);
|
||||
if (SD_Detect() != SD_PRESENT) {
|
||||
TRACE("SD_Detect() != SD_PRESENT");
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) {
|
||||
return RES_ERROR;
|
||||
else if (cardstate == HAL_SD_CARD_ERROR) {
|
||||
return SD_TRANSFER_ERROR;
|
||||
}
|
||||
|
||||
DRESULT res = RES_OK;
|
||||
if (count == 0) return res;
|
||||
return SD_TRANSFER_BUSY;
|
||||
}
|
||||
|
||||
if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) {
|
||||
// buffer is not aligned, use scratch buffer that is aligned
|
||||
TRACE("disk_read bad alignment (%p)", buff);
|
||||
static int sdio_check_card_state_with_timeout(uint32_t timeout)
|
||||
{
|
||||
uint32_t timer = HAL_GetTick();
|
||||
/* block until SDIO IP is ready again or a timeout occur */
|
||||
while(HAL_GetTick() - timer < timeout) {
|
||||
auto state = sdio_check_card_state();
|
||||
if (state != SD_TRANSFER_BUSY) {
|
||||
return state == SD_TRANSFER_OK ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static DSTATUS sdio_status(BYTE lun)
|
||||
{
|
||||
DSTATUS stat = RES_OK;
|
||||
|
||||
#if defined(SD_PRESENT_GPIO)
|
||||
if ((LL_GPIO_ReadInputPort(SD_PRESENT_GPIO) & SD_PRESENT_LL_GPIO_PIN) != 0) {
|
||||
stat |= STA_NODISK;
|
||||
}
|
||||
#endif
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
static DRESULT _read_dma(BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
ReadStatus = 0;
|
||||
if (HAL_SD_ReadBlocks_DMA(&sdio, buff, sector, count) != HAL_OK) {
|
||||
TRACE("SD ReadBlocks failed (s:%u/c:%u)", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
// Wait for the reading process to complete or a timeout to occur
|
||||
uint32_t timeout = HAL_GetTick();
|
||||
while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT));
|
||||
|
||||
if (ReadStatus == 0) {
|
||||
TRACE("SD read timeout (s:%u c:%u)", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
ReadStatus = 0;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT sdio_read(BYTE lun, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
|
||||
// TRACE("disk_read %d %p %10d %d", drv, buff, sector, count);
|
||||
|
||||
if (sdio_check_card_state_with_timeout(SD_TIMEOUT) < 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
if ((DWORD)buff < SRAM_BASE || ((DWORD)buff & 3)) {
|
||||
// TRACE("disk_read bad alignment (%p)", buff);
|
||||
while (count--) {
|
||||
res = disk_read_dma(drv, (BYTE *)scratch, sector++, 1);
|
||||
res = _read_dma(scratch, sector++, 1);
|
||||
if (res != RES_OK) break;
|
||||
memcpy(buff, scratch, BLOCK_SIZE);
|
||||
buff += BLOCK_SIZE;
|
||||
@@ -185,60 +258,25 @@ DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count)
|
||||
return res;
|
||||
}
|
||||
|
||||
res = disk_read_dma(drv, buff, sector, count);
|
||||
if (res != RES_OK && count > 1) {
|
||||
// multi-read failed, try reading same sectors, one by one
|
||||
TRACE("disk_read() multi-block failed, trying single block reads...");
|
||||
while (count--) {
|
||||
res = disk_read_dma(drv, buff, sector++, 1);
|
||||
if (res != RES_OK) break;
|
||||
buff += BLOCK_SIZE;
|
||||
res = _read_dma(buff, sector, count);
|
||||
if (res != RES_OK) return res;
|
||||
|
||||
uint32_t timeout = HAL_GetTick();
|
||||
while((HAL_GetTick() - timeout) < SD_TIMEOUT) {
|
||||
if (sdio_check_card_state() == SD_TRANSFER_OK) {
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
TRACE("SD getstatus timeout, s:%u c:%u", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
|
||||
#if _READONLY == 0
|
||||
DRESULT __disk_write(
|
||||
BYTE drv, /* Physical drive nmuber (0..) */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address (LBA) */
|
||||
UINT count /* Number of sectors to write (1..255) */
|
||||
)
|
||||
static DRESULT _write_dma(const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
|
||||
// TRACE("disk_write %d %p %10d %d", drv, buff, sector, count);
|
||||
|
||||
if (SD_Detect() != SD_PRESENT)
|
||||
return RES_NOTRDY;
|
||||
|
||||
if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) {
|
||||
//TRACE("disk_write bad alignment (%p)", buff);
|
||||
while(count--) {
|
||||
memcpy(scratch, buff, BLOCK_SIZE);
|
||||
|
||||
res = __disk_write(drv, (BYTE *)scratch, sector++, 1);
|
||||
|
||||
if (res != RES_OK)
|
||||
break;
|
||||
|
||||
buff += BLOCK_SIZE;
|
||||
}
|
||||
return(res);
|
||||
}
|
||||
|
||||
SD_Error Status = SD_WriteBlocks((uint8_t *)buff, sector, BLOCK_SIZE, count);
|
||||
if (Status != SD_OK) {
|
||||
TRACE("SD WriteBlocks=%d, s:%u c: %u", Status, sector, (uint32_t)count);
|
||||
WriteStatus = 0;
|
||||
if (HAL_SD_WriteBlocks_DMA(&sdio, (uint8_t*)buff, sector, count) != HAL_OK) {
|
||||
TRACE("SD WriteBlocks failed (s:%u/c:%u)", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
@@ -247,94 +285,148 @@ DRESULT __disk_write(
|
||||
while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT));
|
||||
|
||||
if (WriteStatus == 0) {
|
||||
TRACE("SD write timeout, s:%u c:%u", sector, (uint32_t)count);
|
||||
TRACE("SD write timeout (s:%u/c:%u)", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
WriteStatus = 0;
|
||||
res = RES_ERROR;
|
||||
timeout = HAL_GetTick();
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT sdio_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
|
||||
// TRACE("disk_write %d %p %10d %d", drv, buff, sector, count);
|
||||
|
||||
if (sdio_check_card_state_with_timeout(SD_TIMEOUT) < 0) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
if ((DWORD)buff < SRAM_BASE || ((DWORD)buff & 3)) {
|
||||
//TRACE("disk_write bad alignment (%p)", buff);
|
||||
while(count--) {
|
||||
memcpy(scratch, buff, BLOCK_SIZE);
|
||||
res = _write_dma((BYTE *)scratch, sector++, 1);
|
||||
if (res != RES_OK) break;
|
||||
buff += BLOCK_SIZE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _write_dma(buff, sector, count);
|
||||
if (res != RES_OK) return res;
|
||||
|
||||
uint32_t timeout = HAL_GetTick();
|
||||
while((HAL_GetTick() - timeout) < SD_TIMEOUT) {
|
||||
if (SD_GetStatus() == SD_TRANSFER_OK) {
|
||||
res = RES_OK;
|
||||
break;
|
||||
if (sdio_check_card_state() == SD_TRANSFER_OK) {
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (res != RES_OK) {
|
||||
TRACE("SD getstatus timeout, s:%u c: %u", sector, (uint32_t)count);
|
||||
res = RES_ERROR;
|
||||
TRACE("SD getstatus timeout, s:%u c: %u", sector, (uint32_t)count);
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
static DRESULT sdio_get_sector_count(DWORD* sectors)
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
if(HAL_SD_GetCardInfo(&sdio, &cardInfo) != HAL_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
*sectors = cardInfo.LogBlockNbr;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT sdio_get_sector_size(DWORD* sector_size)
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
if(HAL_SD_GetCardInfo(&sdio, &cardInfo) != HAL_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
*sector_size = cardInfo.LogBlockSize;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT sdio_get_block_size(DWORD* block_size)
|
||||
{
|
||||
DWORD sector_size;
|
||||
DRESULT res = sdio_get_sector_size(§or_size);
|
||||
if (res == RES_OK) {
|
||||
*block_size = sector_size / BLOCK_SIZE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif /* _READONLY */
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE drv, /* Physical drive nmuber (0..) */
|
||||
BYTE ctrl, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
DRESULT sdio_ioctl(BYTE lun, BYTE ctrl, void *buff)
|
||||
{
|
||||
DRESULT res;
|
||||
uint32_t tmp;
|
||||
|
||||
if (drv) return RES_PARERR;
|
||||
|
||||
res = RES_ERROR;
|
||||
|
||||
DRESULT res = RES_OK;
|
||||
switch (ctrl) {
|
||||
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
|
||||
tmp = SD_GetSectorCount();
|
||||
|
||||
if(tmp == 0) {
|
||||
res = RES_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*(DWORD*)buff = tmp;
|
||||
res = RES_OK;
|
||||
res = sdio_get_sector_count((DWORD*)buff);
|
||||
break;
|
||||
|
||||
case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */
|
||||
tmp = SD_GetSectorSize();
|
||||
|
||||
if(tmp == 0) {
|
||||
res = RES_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*(WORD*)buff = tmp;
|
||||
res = RES_OK;
|
||||
res = sdio_get_sector_size((DWORD*)buff);
|
||||
break;
|
||||
|
||||
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
|
||||
tmp = SD_GetBlockSize() / BLOCK_SIZE;
|
||||
|
||||
if(tmp == 0) {
|
||||
res = RES_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*(DWORD*)buff = tmp;
|
||||
res = RES_OK;
|
||||
res = sdio_get_block_size((DWORD*)buff);
|
||||
break;
|
||||
|
||||
case CTRL_SYNC:
|
||||
/* Complete pending write process (needed at _FS_READONLY == 0) */
|
||||
while (SD_GetStatus() == SD_TRANSFER_BUSY);
|
||||
res = RES_OK;
|
||||
/* Complete pending write process */
|
||||
while (sdio_check_card_state() == SD_TRANSFER_BUSY);
|
||||
break;
|
||||
|
||||
default:
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const diskio_driver_t sdio_diskio_driver = {
|
||||
.initialize = sdio_initialize,
|
||||
.status = sdio_status,
|
||||
.read = sdio_read,
|
||||
.write = sdio_write,
|
||||
.ioctl = sdio_ioctl,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Tx Transfer completed callbacks
|
||||
* @param hsd: SD handle
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
extern "C" void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
|
||||
{
|
||||
UNUSED(hsd);
|
||||
WriteStatus = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rx Transfer completed callbacks
|
||||
* @param hsd: SD handle
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
extern "C" void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
|
||||
{
|
||||
UNUSED(hsd);
|
||||
ReadStatus = 1;
|
||||
}
|
||||
|
||||
extern "C" void SDIO_IRQHandler(void)
|
||||
{
|
||||
DEBUG_INTERRUPT(INT_SDIO);
|
||||
HAL_SD_IRQHandler(&sdio);
|
||||
}
|
||||
extern "C" void SD_SDIO_DMA_IRQHANDLER(void)
|
||||
{
|
||||
DEBUG_INTERRUPT(INT_SDIO_DMA);
|
||||
HAL_DMA_IRQHandler(&sdioTxDma);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/fatfs_diskio.h"
|
||||
|
||||
extern const diskio_driver_t sdio_diskio_driver;
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* This is a stub disk I/O module that acts as front end of the existing */
|
||||
/* disk I/O modules and attach it to FatFs module with common interface. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "diskio_spi.h"
|
||||
#include "stm32_gpio_driver.h"
|
||||
#include "sdcard_spi.h"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
|
||||
static const stm32_spi_t _sd_spi_hw = {
|
||||
.SPIx = SD_SPI,
|
||||
.SPI_GPIOx = SD_GPIO,
|
||||
.SPI_Pins = SD_GPIO_PIN_SCK | SD_GPIO_PIN_MISO | SD_GPIO_PIN_MOSI,
|
||||
.CS_GPIOx = SD_GPIO,
|
||||
.CS_Pin = SD_GPIO_PIN_CS,
|
||||
.DMA = SD_SPI_DMA,
|
||||
.DMA_Channel = SD_SPI_DMA_CHANNEL,
|
||||
.txDMA_Stream = SD_SPI_DMA_TX_STREAM,
|
||||
.rxDMA_Stream = SD_SPI_DMA_RX_STREAM,
|
||||
};
|
||||
|
||||
static uint32_t _sdcard_sectors;
|
||||
|
||||
static void _sd_present_gpio_init()
|
||||
{
|
||||
#if defined(SD_PRESENT_GPIO)
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
pinInit.Pin = SD_PRESENT_GPIO_PIN;
|
||||
pinInit.Mode = LL_GPIO_MODE_INPUT;
|
||||
pinInit.Pull = LL_GPIO_PULL_UP;
|
||||
|
||||
stm32_gpio_enable_clock(SD_PRESENT_GPIO);
|
||||
LL_GPIO_Init(SD_PRESENT_GPIO, &pinInit);
|
||||
#endif
|
||||
}
|
||||
|
||||
static DSTATUS sdcard_spi_initialize(BYTE lun)
|
||||
{
|
||||
_sd_present_gpio_init();
|
||||
|
||||
sdcard_info_t card_info;
|
||||
if (sdcard_spi_init(&_sd_spi_hw, &card_info) != SDCARD_SPI_OK) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
_sdcard_sectors = sdcard_spi_get_sector_count(&card_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DSTATUS sdcard_spi_status(BYTE lun)
|
||||
{
|
||||
DSTATUS stat = 0;
|
||||
|
||||
#if defined(SD_PRESENT_GPIO)
|
||||
if (LL_GPIO_IsInputPinSet(SD_PRESENT_GPIO, SD_PRESENT_GPIO_PIN)) {
|
||||
stat |= STA_NODISK;
|
||||
}
|
||||
#endif
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
static DRESULT sdcard_spi_read(BYTE lun, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
// TRACE("disk_read %d %p %10d %d", lun, buff, sector, count);
|
||||
|
||||
sd_rw_response_t state;
|
||||
sdcard_spi_read_blocks(sector, buff, SD_HC_BLOCK_SIZE, count, &state);
|
||||
if (state == SD_RW_OK) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
static DRESULT sdcard_spi_write(BYTE lun, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
// TRACE("disk_write %d %p %10d %d", lun, buff, sector, count);
|
||||
|
||||
sd_rw_response_t state;
|
||||
sdcard_spi_write_blocks(sector, buff, SD_HC_BLOCK_SIZE, count, &state);
|
||||
if (state == SD_RW_OK) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
static DRESULT sdcard_spi_ioctl(BYTE lun, BYTE ctrl, void *buff)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
|
||||
switch (ctrl) {
|
||||
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
|
||||
*(DWORD*)buff = _sdcard_sectors;
|
||||
break;
|
||||
|
||||
case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */
|
||||
*(WORD*)buff = SD_HC_BLOCK_SIZE;
|
||||
break;
|
||||
|
||||
case CTRL_SYNC:
|
||||
sdcard_spi_wait_for_not_busy();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const diskio_driver_t sdcard_spi_driver = {
|
||||
.initialize = sdcard_spi_initialize,
|
||||
.status = sdcard_spi_status,
|
||||
.read = sdcard_spi_read,
|
||||
.write = sdcard_spi_write,
|
||||
.ioctl = sdcard_spi_ioctl,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/fatfs_diskio.h"
|
||||
|
||||
extern const diskio_driver_t sdcard_spi_driver;
|
||||
@@ -16,6 +16,7 @@ if(NOT NATIVE_BUILD)
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_ll_rcc.c
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_ll_exti.c
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_ll_adc.c
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_ll_spi.c
|
||||
# HAL drivers
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_hal.c
|
||||
${STM32CUBE_DIR}/Src/stm32f2xx_hal_pwr.c
|
||||
@@ -51,8 +52,6 @@ add_library(stm32_stdperiph OBJECT EXCLUDE_FROM_ALL
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f2xx_gpio.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f2xx_dbgmcu.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f2xx_rcc.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f2xx_spi.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f2xx_dma.c
|
||||
)
|
||||
|
||||
set(FIRMWARE_SRC ${FIRMWARE_SRC} $<TARGET_OBJECTS:stm32_stdperiph>)
|
||||
|
||||
@@ -55,8 +55,6 @@ set(STM32_STDPERIPH_SRC
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_gpio.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_dbgmcu.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_rcc.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_spi.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_dma.c
|
||||
${STM32_STDPERIPH_DIR}/src/stm32f4xx_flash.c
|
||||
)
|
||||
|
||||
|
||||
@@ -70,12 +70,6 @@ void pwrInit()
|
||||
GPIO_Init(PCBREV_GPIO, &GPIO_InitStructure);
|
||||
hardwareOptions.pcbrev = PCBREV_VALUE();
|
||||
#endif
|
||||
|
||||
#if defined(SD_PRESENT_GPIO_PIN)
|
||||
GPIO_ResetBits(SD_PRESENT_GPIO, SD_PRESENT_GPIO_PIN);
|
||||
GPIO_InitStructure.GPIO_Pin = SD_PRESENT_GPIO_PIN;
|
||||
GPIO_Init(SD_PRESENT_GPIO, &GPIO_InitStructure);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pwrOn()
|
||||
|
||||
@@ -0,0 +1,864 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "sdcard_spi.h"
|
||||
#include "stm32_spi.h"
|
||||
#include "timers_driver.h"
|
||||
#include "delays_driver.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "crc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define SD_SPI_CLK_400K (400000UL)
|
||||
|
||||
#if !defined(SD_SPI_CLK_MAX)
|
||||
#define SD_SPI_CLK_MAX (25000000UL)
|
||||
#endif
|
||||
|
||||
// Disable CRC support by default
|
||||
//
|
||||
// #if !defined(SD_CARD_SPI_DISABLE_CRC)
|
||||
// #define SD_CARD_SPI_ENABLE_CRC
|
||||
// #endif
|
||||
|
||||
#define US_PER_MS (1000UL)
|
||||
#define INIT_CMD_RETRY_US (500 * US_PER_MS)
|
||||
#define INIT_CMD0_RETRY_US (100UL)
|
||||
#define R1_POLLING_RETRY_US (100 * US_PER_MS)
|
||||
#define SD_DATA_TOKEN_RETRY_US (100 * US_PER_MS)
|
||||
#define SD_WAIT_FOR_NOT_BUSY_US (250 * US_PER_MS)
|
||||
|
||||
#define SD_BLOCK_READ_CMD_RETRY_US (100UL)
|
||||
#define SD_BLOCK_WRITE_CMD_RETRY_US (100UL)
|
||||
|
||||
#define SD_CARD_DUMMY_BYTE (0xFF)
|
||||
|
||||
typedef enum {
|
||||
SD_INIT_START,
|
||||
SD_INIT_SPI_POWER_SEQ,
|
||||
SD_INIT_SEND_CMD0,
|
||||
SD_INIT_SEND_CMD8,
|
||||
SD_INIT_CARD_UNKNOWN,
|
||||
SD_INIT_SEND_ACMD41_HCS,
|
||||
SD_INIT_SEND_ACMD41,
|
||||
SD_INIT_SEND_CMD1,
|
||||
SD_INIT_SEND_CMD58,
|
||||
SD_INIT_SEND_CMD16,
|
||||
SD_INIT_ENABLE_CRC,
|
||||
SD_INIT_READ_CID,
|
||||
SD_INIT_READ_CSD,
|
||||
SD_INIT_SET_MAX_SPI_SPEED,
|
||||
SD_INIT_FINISH
|
||||
} sd_init_fsm_state_t;
|
||||
|
||||
static sdcard_spi_t _sdcard_spi = {nullptr, false};
|
||||
|
||||
static inline void _send_dummy_byte(const stm32_spi_t* spi)
|
||||
{
|
||||
stm32_spi_transfer_byte(spi, SD_CARD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
static bool _wait_for_not_busy(const stm32_spi_t* spi, uint32_t retry_us)
|
||||
{
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
|
||||
do {
|
||||
uint8_t read_byte = stm32_spi_transfer_byte(spi, SD_CARD_DUMMY_BYTE);
|
||||
if (read_byte == 0xFF) {
|
||||
return true;
|
||||
}
|
||||
if ((read_byte & 0x0F) != 0x00) {
|
||||
// shift by some bits???
|
||||
stm32_spi_unselect(spi);
|
||||
_send_dummy_byte(spi);
|
||||
stm32_spi_select(spi);
|
||||
}
|
||||
} while (timersGetUsTick() - timeout < retry_us);
|
||||
|
||||
TRACE("_wait_for_not_busy: [FAILED]");
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t _crc_7(const uint8_t* data, int n)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
uint8_t d = data[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc <<= 1;
|
||||
if ((d & 0x80) ^ (crc & 0x80)) {
|
||||
crc ^= 0x09;
|
||||
}
|
||||
d <<= 1;
|
||||
}
|
||||
}
|
||||
return (crc << 1) | 1;
|
||||
}
|
||||
|
||||
static inline uint16_t _transfer_bytes(const stm32_spi_t* spi, const uint8_t* out,
|
||||
uint8_t* in, uint16_t length)
|
||||
{
|
||||
return stm32_spi_transfer_bytes(spi, out, in, length);
|
||||
}
|
||||
|
||||
static void _flush_block(const stm32_spi_t* spi)
|
||||
{
|
||||
for (int i = 0; i < 512 + 2; i++) {
|
||||
_send_dummy_byte(spi);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t _wait_for_r1(const stm32_spi_t* spi, uint32_t retry_us)
|
||||
{
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
uint8_t r1;
|
||||
|
||||
do {
|
||||
r1 = stm32_spi_transfer_byte(spi, SD_CARD_DUMMY_BYTE);
|
||||
|
||||
if (R1_VALID(r1)) {
|
||||
return r1;
|
||||
}
|
||||
|
||||
} while (timersGetUsTick() - timeout < retry_us);
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
static inline bool _wait_for_token(const stm32_spi_t* spi, uint8_t token, uint32_t retry_us)
|
||||
{
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
uint8_t read_byte;
|
||||
|
||||
do {
|
||||
read_byte = stm32_spi_transfer_byte(spi, SD_CARD_DUMMY_BYTE);
|
||||
if (read_byte == token) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} while ((read_byte == 0xFF) && (timersGetUsTick() - timeout < retry_us));
|
||||
|
||||
// hack to get rid of a packet in case
|
||||
// the token was shifted
|
||||
if (read_byte != 0xFF) {
|
||||
_flush_block(spi);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t sdcard_spi_send_cmd(const stm32_spi_t* spi, uint8_t sd_cmd_idx,
|
||||
uint32_t argument, uint32_t retry_us)
|
||||
{
|
||||
uint8_t r1_resu;
|
||||
uint8_t cmd_data[6];
|
||||
|
||||
cmd_data[0] = SD_CMD_PREFIX_MASK | sd_cmd_idx;
|
||||
cmd_data[1] = argument >> (3 * 8);
|
||||
cmd_data[2] = (argument >> (2 * 8)) & 0xFF;
|
||||
cmd_data[3] = (argument >> 8) & 0xFF;
|
||||
cmd_data[4] = argument & 0xFF;
|
||||
cmd_data[5] = _crc_7(cmd_data, sizeof(cmd_data) - 1);
|
||||
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
do {
|
||||
if (!_wait_for_not_busy(spi, SD_WAIT_FOR_NOT_BUSY_US)) {
|
||||
TRACE(
|
||||
"sdcard_spi_send_cmd: timeout while waiting for bus to be not busy!");
|
||||
r1_resu = SD_INVALID_R1_RESPONSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_transfer_bytes(spi, cmd_data, 0, sizeof(cmd_data)) != sizeof(cmd_data)) {
|
||||
TRACE("sdcard_spi_send_cmd: _transfer_bytes: send cmd [%d]: [ERROR]",
|
||||
sd_cmd_idx);
|
||||
r1_resu = SD_INVALID_R1_RESPONSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* received byte after cmd12 is a dummy byte and should be ignored */
|
||||
if (sd_cmd_idx == SD_CMD_12) {
|
||||
_send_dummy_byte(spi);
|
||||
}
|
||||
|
||||
r1_resu = _wait_for_r1(spi, R1_POLLING_RETRY_US);
|
||||
|
||||
if (R1_VALID(r1_resu)) {
|
||||
break;
|
||||
} else {
|
||||
TRACE("sdcard_spi_send_cmd: R1_TIMEOUT (0x%02x)", r1_resu);
|
||||
r1_resu = SD_INVALID_R1_RESPONSE;
|
||||
}
|
||||
|
||||
} while (timersGetUsTick() - timeout < retry_us);
|
||||
|
||||
return r1_resu;
|
||||
}
|
||||
|
||||
static uint8_t sdcard_spi_send_acmd(const stm32_spi_t* spi, uint8_t sd_cmd_idx,
|
||||
uint32_t argument, uint32_t retry_us)
|
||||
{
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
uint8_t r1_resu;
|
||||
|
||||
do {
|
||||
r1_resu = sdcard_spi_send_cmd(spi, SD_CMD_55, SD_CMD_NO_ARG, 0);
|
||||
if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) {
|
||||
r1_resu = sdcard_spi_send_cmd(spi, sd_cmd_idx, argument, 0);
|
||||
|
||||
if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) {
|
||||
return r1_resu;
|
||||
}
|
||||
}
|
||||
} while (timersGetUsTick() - timeout < retry_us);
|
||||
|
||||
TRACE("sdcard_spi_send_acmd: [TIMEOUT]");
|
||||
return r1_resu;
|
||||
}
|
||||
|
||||
static sd_rw_response_t _read_cid(const stm32_spi_t* spi, sdcard_info_t* card);
|
||||
static sd_rw_response_t _read_csd(const stm32_spi_t* spi, sdcard_info_t* card);
|
||||
|
||||
static sd_init_fsm_state_t _init_sd_fsm_step(const stm32_spi_t* spi,
|
||||
sdcard_info_t* card,
|
||||
sd_init_fsm_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case SD_INIT_START:
|
||||
TRACE("SD_INIT_START");
|
||||
stm32_spi_init(spi);
|
||||
stm32_spi_set_max_baudrate(spi, SD_SPI_CLK_400K);
|
||||
return SD_INIT_SPI_POWER_SEQ;
|
||||
|
||||
case SD_INIT_SPI_POWER_SEQ:
|
||||
TRACE("SD_INIT_SPI_POWER_SEQ");
|
||||
{
|
||||
// wait minimum 50 ms until card is powered
|
||||
uint32_t power_on_timeout = timersGetUsTick();
|
||||
while(timersGetUsTick() - power_on_timeout < 50 * US_PER_MS);
|
||||
}
|
||||
|
||||
// unselect sdcard for power up sequence
|
||||
stm32_spi_unselect(spi);
|
||||
|
||||
// powersequence: perform at least 74 clock cycles with mosi_pin being
|
||||
// high (same as sending dummy bytes with 0xFF)
|
||||
for (int i = 0; i < 10; i++) {
|
||||
stm32_spi_transfer_byte(spi, 0xff);
|
||||
}
|
||||
{
|
||||
// wait for "end of transfer"
|
||||
uint32_t power_on_timeout = timersGetUsTick();
|
||||
while(timersGetUsTick() - power_on_timeout <= 200);
|
||||
}
|
||||
return SD_INIT_SEND_CMD0;
|
||||
|
||||
case SD_INIT_SEND_CMD0:
|
||||
TRACE("SD_INIT_SEND_CMD0");
|
||||
stm32_spi_select(spi);
|
||||
{
|
||||
uint8_t cmd0_r1 = sdcard_spi_send_cmd(spi, SD_CMD_0, SD_CMD_NO_ARG, INIT_CMD0_RETRY_US);
|
||||
stm32_spi_unselect(spi);
|
||||
|
||||
if (R1_VALID(cmd0_r1) && !R1_ERROR(cmd0_r1) && R1_IDLE_BIT_SET(cmd0_r1)) {
|
||||
TRACE("CMD0: [OK]");
|
||||
return SD_INIT_ENABLE_CRC;
|
||||
} else {
|
||||
TRACE("CMD0: [FAILED]");
|
||||
}
|
||||
}
|
||||
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
|
||||
case SD_INIT_ENABLE_CRC:
|
||||
#if defined(SD_CARD_SPI_ENABLE_CRC)
|
||||
TRACE("SD_INIT_ENABLE_CRC");
|
||||
stm32_spi_select(spi);
|
||||
{
|
||||
uint8_t r1 = sdcard_spi_send_cmd(spi, SD_CMD_59, SD_CMD_59_ARG_EN,
|
||||
INIT_CMD_RETRY_US);
|
||||
stm32_spi_unselect(spi);
|
||||
|
||||
if (R1_VALID(r1) && !R1_ERROR(r1)) {
|
||||
TRACE("CMD59: [OK]");
|
||||
return SD_INIT_SEND_CMD8;
|
||||
}
|
||||
}
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
#endif
|
||||
/* if SD_CARD_SPI_ENABLE_CRC is not enabled,
|
||||
let's continue directly with CMD8
|
||||
*/
|
||||
|
||||
case SD_INIT_SEND_CMD8:
|
||||
TRACE("SD_INIT_SEND_CMD8");
|
||||
stm32_spi_select(spi);
|
||||
{
|
||||
int cmd8_arg = (SD_CMD_8_VHS_2_7_V_TO_3_6_V << 8) | SD_CMD_8_CHECK_PATTERN;
|
||||
uint8_t cmd8_r1 = sdcard_spi_send_cmd(spi, SD_CMD_8, cmd8_arg, INIT_CMD_RETRY_US);
|
||||
|
||||
if (R1_VALID(cmd8_r1) && !R1_ERROR(cmd8_r1)) {
|
||||
TRACE("CMD8: [OK] --> reading remaining bytes for R7");
|
||||
|
||||
uint8_t r7[4];
|
||||
|
||||
if (_transfer_bytes(spi, 0, &r7[0], sizeof(r7)) == sizeof(r7)) {
|
||||
TRACE("R7 response: 0x%02x 0x%02x 0x%02x 0x%02x", r7[0], r7[1], r7[2], r7[3]);
|
||||
/* check if lower 12 bits (voltage range and check pattern) of
|
||||
response and arg are equal to verify compatibility and
|
||||
communication is working properly */
|
||||
if (((r7[2] & 0x0F) == ((cmd8_arg >> 8) & 0x0F)) &&
|
||||
(r7[3] == (cmd8_arg & 0xFF))) {
|
||||
TRACE("CMD8: [R7 MATCH]");
|
||||
return SD_INIT_SEND_ACMD41_HCS;
|
||||
}
|
||||
|
||||
TRACE("CMD8: [R7 MISMATCH]");
|
||||
} else {
|
||||
TRACE("CMD8: _transfer_bytes (R7): [ERROR]");
|
||||
}
|
||||
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("CMD8: [ERROR / NO RESPONSE]");
|
||||
return SD_INIT_SEND_ACMD41;
|
||||
|
||||
case SD_INIT_CARD_UNKNOWN:
|
||||
TRACE("SD_INIT_CARD_UNKNOWN");
|
||||
card->card_type = SD_UNKNOWN;
|
||||
return SD_INIT_FINISH;
|
||||
|
||||
case SD_INIT_SEND_ACMD41_HCS:
|
||||
TRACE("SD_INIT_SEND_ACMD41_HCS");
|
||||
{
|
||||
uint32_t acmd41_hcs_retry_timeout = timersGetUsTick();
|
||||
do {
|
||||
uint8_t acmd41hcs_r1 = sdcard_spi_send_acmd(spi, SD_CMD_41, SD_ACMD_41_ARG_HC, 0);
|
||||
if (R1_VALID(acmd41hcs_r1) && !R1_ERROR(acmd41hcs_r1) &&
|
||||
!R1_IDLE_BIT_SET(acmd41hcs_r1)) {
|
||||
TRACE("ACMD41: [OK]");
|
||||
return SD_INIT_SEND_CMD58;
|
||||
}
|
||||
} while (timersGetUsTick() - acmd41_hcs_retry_timeout < INIT_CMD_RETRY_US);
|
||||
}
|
||||
stm32_spi_unselect(spi);
|
||||
TRACE("ACMD41_HCS: [ERROR]");
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
|
||||
case SD_INIT_SEND_ACMD41:
|
||||
TRACE("SD_INIT_SEND_ACMD41");
|
||||
{
|
||||
uint32_t acmd41_retry_timeout = timersGetUsTick();
|
||||
do {
|
||||
uint8_t acmd41_r1 =
|
||||
sdcard_spi_send_acmd(spi, SD_CMD_41, SD_CMD_NO_ARG, 0);
|
||||
if (R1_VALID(acmd41_r1) && !R1_ERROR(acmd41_r1) &&
|
||||
!R1_IDLE_BIT_SET(acmd41_r1)) {
|
||||
TRACE("ACMD41: [OK]");
|
||||
card->use_block_addr = false;
|
||||
card->card_type = SD_V1;
|
||||
return SD_INIT_SEND_CMD16;
|
||||
}
|
||||
} while (timersGetUsTick() - acmd41_retry_timeout < INIT_CMD_RETRY_US);
|
||||
}
|
||||
TRACE("ACMD41: [ERROR]");
|
||||
return SD_INIT_SEND_CMD1;
|
||||
|
||||
case SD_INIT_SEND_CMD1:
|
||||
TRACE("SD_INIT_SEND_CMD1");
|
||||
TRACE("COULD TRY CMD1 (for MMC-card)-> currently not supported");
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
|
||||
case SD_INIT_SEND_CMD58:
|
||||
TRACE("SD_INIT_SEND_CMD58");
|
||||
{
|
||||
uint8_t cmd58_r1 = sdcard_spi_send_cmd(spi, SD_CMD_58, SD_CMD_NO_ARG,
|
||||
INIT_CMD_RETRY_US);
|
||||
if (R1_VALID(cmd58_r1) && !R1_ERROR(cmd58_r1)) {
|
||||
TRACE("CMD58: [OK]");
|
||||
card->card_type = SD_V2;
|
||||
|
||||
uint8_t r3[4];
|
||||
if (_transfer_bytes(spi, 0, r3, sizeof(r3)) == sizeof(r3)) {
|
||||
uint32_t ocr = ((uint32_t)r3[0] << (3 * 8)) |
|
||||
((uint32_t)r3[1] << (2 * 8)) | (r3[2] << 8) | r3[3];
|
||||
TRACE("R3 RESPONSE: 0x%02x 0x%02x 0x%02x 0x%02x",
|
||||
r3[0], r3[1], r3[2], r3[3]);
|
||||
TRACE("OCR: 0x%" PRIx32, ocr);
|
||||
|
||||
if ((ocr & SYSTEM_VOLTAGE) != 0) {
|
||||
TRACE("OCR: SYS VOLTAGE SUPPORTED");
|
||||
|
||||
/* if power up outine is finished */
|
||||
if ((ocr & OCR_POWER_UP_STATUS) != 0) {
|
||||
TRACE("OCR: POWER UP ROUTINE FINISHED");
|
||||
/* if sd card is sdhc */
|
||||
if ((ocr & OCR_CCS) != 0) {
|
||||
TRACE("OCR: CARD TYPE IS SDHC (SD_V2 with block addressing)");
|
||||
card->use_block_addr = true;
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_READ_CID;
|
||||
}
|
||||
|
||||
TRACE("OCR: CARD TYPE IS SDSC (SD_v2 with byte addressing)");
|
||||
card->use_block_addr = false;
|
||||
return SD_INIT_SEND_CMD16;
|
||||
}
|
||||
|
||||
TRACE("OCR: POWER UP ROUTINE NOT FINISHED!");
|
||||
/* poll status till power up is finished */
|
||||
return SD_INIT_SEND_CMD58;
|
||||
}
|
||||
|
||||
TRACE("OCR: SYS VOLTAGE NOT SUPPORTED!");
|
||||
}
|
||||
|
||||
TRACE("CMD58 response: [READ ERROR]");
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("CMD58: [ERROR]");
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
|
||||
case SD_INIT_SEND_CMD16:
|
||||
TRACE("SD_INIT_SEND_CMD16");
|
||||
{
|
||||
uint8_t r1_16 = sdcard_spi_send_cmd(spi, SD_CMD_16, SD_HC_BLOCK_SIZE,
|
||||
INIT_CMD_RETRY_US);
|
||||
if (R1_VALID(r1_16) && !R1_ERROR(r1_16)) {
|
||||
TRACE("CARD TYPE IS SDSC (SD_V1 with byte addressing)");
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_READ_CID;
|
||||
} else {
|
||||
stm32_spi_unselect(spi);
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
}
|
||||
}
|
||||
// unreachable
|
||||
break;
|
||||
|
||||
case SD_INIT_READ_CID:
|
||||
TRACE("SD_INIT_READ_CID");
|
||||
if (_read_cid(spi, card) == SD_RW_OK) {
|
||||
return SD_INIT_READ_CSD;
|
||||
} else {
|
||||
TRACE("reading cid register failed!");
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
}
|
||||
|
||||
case SD_INIT_READ_CSD:
|
||||
TRACE("SD_INIT_READ_CSD");
|
||||
if (_read_csd(spi, card) == SD_RW_OK) {
|
||||
if (card->csd_structure == SD_CSD_V1) {
|
||||
TRACE("csd_structure is version 1");
|
||||
} else if (card->csd_structure == SD_CSD_V2) {
|
||||
TRACE("csd_structure is version 2");
|
||||
}
|
||||
return SD_INIT_SET_MAX_SPI_SPEED;
|
||||
} else {
|
||||
TRACE("reading csd register failed!");
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
}
|
||||
|
||||
case SD_INIT_SET_MAX_SPI_SPEED:
|
||||
TRACE("SD_INIT_SET_MAX_SPI_SPEED");
|
||||
stm32_spi_set_max_baudrate(spi, SD_SPI_CLK_MAX);
|
||||
TRACE("SD_INIT_SET_MAX_SPI_SPEED: [OK]");
|
||||
return SD_INIT_FINISH;
|
||||
|
||||
default:
|
||||
TRACE("SD-INIT-FSM REACHED INVALID STATE!");
|
||||
return SD_INIT_CARD_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int sdcard_spi_init(const stm32_spi_t* spi, sdcard_info_t* card)
|
||||
{
|
||||
sd_init_fsm_state_t state = SD_INIT_START;
|
||||
|
||||
do {
|
||||
state = _init_sd_fsm_step(spi, card, state);
|
||||
} while (state != SD_INIT_FINISH);
|
||||
|
||||
if (card->card_type != SD_UNKNOWN) {
|
||||
_sdcard_spi.spi = spi;
|
||||
_sdcard_spi.use_block_addr = card->use_block_addr;
|
||||
return SDCARD_SPI_OK;
|
||||
}
|
||||
|
||||
TRACE("sdcard_spi_init: [FAILED]");
|
||||
return SDCARD_SPI_INIT_ERROR;
|
||||
}
|
||||
|
||||
static sd_rw_response_t _read_data_packet(const stm32_spi_t* spi, uint8_t token,
|
||||
uint8_t *data, uint16_t size)
|
||||
{
|
||||
if (!_wait_for_token(spi, token, SD_DATA_TOKEN_RETRY_US)) {
|
||||
return SD_RW_NO_TOKEN;
|
||||
}
|
||||
|
||||
if (stm32_spi_dma_receive_bytes(spi, data, size) == size) {
|
||||
|
||||
uint8_t crc_bytes[2];
|
||||
if (_transfer_bytes(spi, 0, crc_bytes, sizeof(crc_bytes)) == sizeof(crc_bytes)) {
|
||||
|
||||
#if defined(SD_CARD_SPI_ENABLE_CRC)
|
||||
uint16_t data_crc16 = (crc_bytes[0] << 8) | crc_bytes[1];
|
||||
if (crc16(CRC_1021, data, size) != data_crc16) {
|
||||
return SD_RW_CRC_MISMATCH;
|
||||
}
|
||||
#endif
|
||||
return SD_RW_OK;
|
||||
}
|
||||
|
||||
return SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
return SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
static uint16_t _read_blocks(const sdcard_spi_t* card, uint8_t cmd_idx,
|
||||
uint32_t bladdr, uint8_t* data,
|
||||
uint16_t blsz, uint16_t nbl,
|
||||
sd_rw_response_t* state)
|
||||
{
|
||||
const auto* spi = card->spi;
|
||||
stm32_spi_select(spi);
|
||||
uint16_t reads = 0;
|
||||
|
||||
uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
|
||||
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(spi, cmd_idx, addr, SD_BLOCK_READ_CMD_RETRY_US);
|
||||
|
||||
if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
|
||||
|
||||
for (uint16_t i = 0; i < nbl; i++) {
|
||||
*state = _read_data_packet(spi, SD_DATA_TOKEN_CMD_17_18_24, &(data[i * blsz]), blsz);
|
||||
if (*state != SD_RW_OK) { break; }
|
||||
reads++;
|
||||
}
|
||||
|
||||
/* if this was a multi-block read */
|
||||
if (cmd_idx == SD_CMD_18) {
|
||||
cmd_r1_resu = sdcard_spi_send_cmd(spi, SD_CMD_12, 0, 1);
|
||||
if (!R1_VALID(cmd_r1_resu) || R1_ERROR(cmd_r1_resu)) {
|
||||
*state = SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("_read_blocks: send CMD%d: [RX_TX_ERROR]", cmd_idx);
|
||||
*state = SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
stm32_spi_unselect(spi);
|
||||
_send_dummy_byte(spi);
|
||||
// TODO: delay 100us?
|
||||
delay_us(100);
|
||||
|
||||
return reads;
|
||||
}
|
||||
|
||||
int sdcard_spi_read_blocks(uint32_t blockaddr, uint8_t* data,
|
||||
uint16_t blocksize, uint16_t nblocks,
|
||||
sd_rw_response_t* state)
|
||||
{
|
||||
*state = SD_RW_OK;
|
||||
if (nblocks > 1) {
|
||||
return _read_blocks(&_sdcard_spi, SD_CMD_18, blockaddr, data, blocksize,
|
||||
nblocks, state);
|
||||
} else {
|
||||
return _read_blocks(&_sdcard_spi, SD_CMD_17, blockaddr, data, blocksize,
|
||||
nblocks, state);
|
||||
}
|
||||
}
|
||||
|
||||
static sd_rw_response_t _write_data_packet(const stm32_spi_t* spi, uint8_t token,
|
||||
const uint8_t *data, uint16_t size)
|
||||
{
|
||||
if (!_wait_for_not_busy(spi, SD_WAIT_FOR_NOT_BUSY_US)) {
|
||||
return SD_RW_TIMEOUT;
|
||||
}
|
||||
|
||||
stm32_spi_transfer_byte(spi, token);
|
||||
|
||||
if (stm32_spi_dma_transmit_bytes(spi, data, size) != size) {
|
||||
TRACE("_write_data_packet: [RX_TX_ERROR] (while transmitting payload)");
|
||||
return SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
#if defined(SD_CARD_SPI_ENABLE_CRC)
|
||||
uint16_t data_crc16 = crc16(CRC_1021, data, size);
|
||||
uint8_t crc[sizeof(uint16_t)] = {
|
||||
(uint8_t)(data_crc16 >> 8),
|
||||
(uint8_t)(data_crc16 & 0xFF)
|
||||
};
|
||||
#else
|
||||
uint8_t crc[sizeof(uint16_t)] = { 0xFF, 0xFF };
|
||||
#endif
|
||||
|
||||
if (_transfer_bytes(spi, crc, 0, sizeof(crc)) != sizeof(crc)) {
|
||||
TRACE("_write_data_packet: [RX_TX_ERROR] (while transmitting CRC16)");
|
||||
return SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
uint32_t timeout = timersGetUsTick();
|
||||
|
||||
do {
|
||||
uint8_t data_response = stm32_spi_transfer_byte(spi, SD_CARD_DUMMY_BYTE);
|
||||
if (data_response == 0xFF) continue;
|
||||
|
||||
if (!DATA_RESPONSE_IS_VALID(data_response)) {
|
||||
return SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
if (DATA_RESPONSE_ACCEPTED(data_response)) {
|
||||
return SD_RW_OK;
|
||||
}
|
||||
|
||||
if (DATA_RESPONSE_WRITE_ERR(data_response) ||
|
||||
DATA_RESPONSE_CRC_ERR(data_response)) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while(timersGetUsTick() - timeout < 10 * US_PER_MS);
|
||||
|
||||
return SD_RW_WRITE_ERROR;
|
||||
}
|
||||
|
||||
static uint16_t _write_blocks(const sdcard_spi_t* card, uint8_t cmd_idx,
|
||||
uint32_t bladdr, const uint8_t *data,
|
||||
uint16_t blsz, uint16_t nbl,
|
||||
sd_rw_response_t *state)
|
||||
{
|
||||
const auto* spi = card->spi;
|
||||
stm32_spi_select(spi);
|
||||
uint16_t written = 0;
|
||||
|
||||
uint8_t token;
|
||||
if (cmd_idx == SD_CMD_25) {
|
||||
token = SD_DATA_TOKEN_CMD_25;
|
||||
sdcard_spi_send_acmd(spi, SD_CMD_23, nbl, SD_BLOCK_WRITE_CMD_RETRY_US);
|
||||
} else {
|
||||
token = SD_DATA_TOKEN_CMD_17_18_24;
|
||||
}
|
||||
|
||||
uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE);
|
||||
uint8_t cmd_r1_resu = sdcard_spi_send_cmd(spi, cmd_idx, addr, SD_BLOCK_WRITE_CMD_RETRY_US);
|
||||
|
||||
if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) {
|
||||
|
||||
for (uint16_t i = 0; i < nbl; i++) {
|
||||
*state = _write_data_packet(spi, token, &(data[i * blsz]), blsz);
|
||||
if (*state != SD_RW_OK) {
|
||||
break;
|
||||
}
|
||||
written++;
|
||||
}
|
||||
|
||||
/* if this is a multi-block write it is needed to issue a stop
|
||||
command */
|
||||
if (cmd_idx == SD_CMD_25) {
|
||||
if (!_wait_for_not_busy(spi, SD_WAIT_FOR_NOT_BUSY_US)) {
|
||||
*state = SD_RW_TIMEOUT;
|
||||
} else {
|
||||
stm32_spi_transfer_byte(spi, SD_DATA_TOKEN_CMD_25_STOP);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
*state = SD_RW_RX_TX_ERROR;
|
||||
}
|
||||
|
||||
stm32_spi_unselect(spi);
|
||||
_send_dummy_byte(spi);
|
||||
// TODO: delay 100us?
|
||||
delay_us(100);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
int sdcard_spi_write_blocks(uint32_t blockaddr, const uint8_t* data,
|
||||
uint16_t blocksize, uint16_t nblocks,
|
||||
sd_rw_response_t* state)
|
||||
{
|
||||
*state = SD_RW_OK;
|
||||
if (nblocks > 1) {
|
||||
return _write_blocks(&_sdcard_spi, SD_CMD_25, blockaddr, data, blocksize,
|
||||
nblocks, state);
|
||||
} else {
|
||||
return _write_blocks(&_sdcard_spi, SD_CMD_24, blockaddr, data, blocksize,
|
||||
nblocks, state);
|
||||
}
|
||||
}
|
||||
|
||||
int sdcard_spi_wait_for_not_busy()
|
||||
{
|
||||
auto spi = _sdcard_spi.spi;
|
||||
int res = -1;
|
||||
|
||||
stm32_spi_select(spi);
|
||||
if (_wait_for_not_busy(spi, SD_WAIT_FOR_NOT_BUSY_US)) {
|
||||
res = 0;
|
||||
}
|
||||
stm32_spi_unselect(spi);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static sd_rw_response_t _read_cid(const stm32_spi_t* spi, sdcard_info_t* card)
|
||||
{
|
||||
uint8_t cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG];
|
||||
sd_rw_response_t state;
|
||||
|
||||
sdcard_spi_t card_spi = { spi, card->use_block_addr };
|
||||
int nbl = _read_blocks(&card_spi, SD_CMD_10, 0, cid_raw_data, SD_SIZE_OF_CID_AND_CSD_REG,
|
||||
SD_BLOCKS_FOR_REG_READ, &state);
|
||||
|
||||
uint8_t crc7 = _crc_7(&(cid_raw_data[0]), SD_SIZE_OF_CID_AND_CSD_REG - 1);
|
||||
|
||||
if (nbl == SD_BLOCKS_FOR_REG_READ) {
|
||||
if (crc7 == cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1]) {
|
||||
card->cid.MID = cid_raw_data[0];
|
||||
memcpy(&card->cid.OID[0], &cid_raw_data[1], SD_SIZE_OF_OID);
|
||||
memcpy(&card->cid.PNM[0], &cid_raw_data[2], SD_SIZE_OF_PNM);
|
||||
card->cid.PRV = cid_raw_data[8];
|
||||
memcpy((uint8_t *)&card->cid.PSN, &cid_raw_data[9], 4);
|
||||
card->cid.MDT = (cid_raw_data[13] << 4) | cid_raw_data[14];
|
||||
card->cid.CID_CRC = cid_raw_data[15];
|
||||
TRACE("_read_cid: [OK]");
|
||||
return SD_RW_OK;
|
||||
}
|
||||
else {
|
||||
TRACE("_read_cid: [SD_RW_CRC_MISMATCH] (data-crc: 0x%02x | calc-crc: 0x%02x)",
|
||||
cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1], crc7);
|
||||
return SD_RW_CRC_MISMATCH;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static sd_rw_response_t _read_csd(const stm32_spi_t* spi, sdcard_info_t* card)
|
||||
{
|
||||
uint8_t c[SD_SIZE_OF_CID_AND_CSD_REG];
|
||||
sd_rw_response_t state;
|
||||
|
||||
sdcard_spi_t card_spi = { spi, card->use_block_addr };
|
||||
int read_resu = _read_blocks(&card_spi, SD_CMD_9, 0, c, SD_SIZE_OF_CID_AND_CSD_REG,
|
||||
SD_BLOCKS_FOR_REG_READ, &state);
|
||||
|
||||
if (read_resu == SD_BLOCKS_FOR_REG_READ) {
|
||||
if (_crc_7(c, SD_SIZE_OF_CID_AND_CSD_REG - 1) == c[SD_SIZE_OF_CID_AND_CSD_REG - 1]) {
|
||||
if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V1) {
|
||||
card->csd.v1.CSD_STRUCTURE = c[0] >> 6;
|
||||
card->csd.v1.TAAC = c[1];
|
||||
card->csd.v1.NSAC = c[2];
|
||||
card->csd.v1.TRAN_SPEED = c[3];
|
||||
card->csd.v1.CCC = (c[4] << 4) | ((c[5] & 0xF0) >> 4);
|
||||
card->csd.v1.READ_BL_LEN = (c[5] & 0x0F);
|
||||
card->csd.v1.READ_BL_PARTIAL = (c[6] & (1 << 7)) >> 7;
|
||||
card->csd.v1.WRITE_BLK_MISALIGN = (c[6] & (1 << 6)) >> 6;
|
||||
card->csd.v1.READ_BLK_MISALIGN = (c[6] & (1 << 5)) >> 5;
|
||||
card->csd.v1.DSR_IMP = (c[6] & (1 << 4)) >> 4;
|
||||
card->csd.v1.C_SIZE = ((c[6] & 0x03) << 10) | (c[7] << 2) | (c[8] >> 6);
|
||||
card->csd.v1.VDD_R_CURR_MIN = (c[8] & 0x38) >> 3;
|
||||
card->csd.v1.VDD_R_CURR_MAX = (c[8] & 0x07);
|
||||
card->csd.v1.VDD_W_CURR_MIN = (c[9] & 0xE0) >> 5;
|
||||
card->csd.v1.VDD_W_CURR_MAX = (c[9] & 0x1C) >> 2;
|
||||
card->csd.v1.C_SIZE_MULT = ((c[9] & 0x03) << 1) | (c[10] >> 7);
|
||||
card->csd.v1.ERASE_BLK_EN = (c[10] & (1 << 6)) >> 6;
|
||||
card->csd.v1.SECTOR_SIZE = ((c[10] & 0x3F) << 1) | (c[11] >> 7);
|
||||
card->csd.v1.WP_GRP_SIZE = (c[11] & 0x7F);
|
||||
card->csd.v1.WP_GRP_ENABLE = c[12] >> 7;
|
||||
card->csd.v1.R2W_FACTOR = (c[12] & 0x1C) >> 2;
|
||||
card->csd.v1.WRITE_BL_LEN = (c[12] & 0x03) << 2 | (c[13] >> 6);
|
||||
card->csd.v1.WRITE_BL_PARTIAL = (c[13] & (1 << 5)) >> 5;
|
||||
card->csd.v1.FILE_FORMAT_GRP = (c[14] & (1 << 7)) >> 7;
|
||||
card->csd.v1.COPY = (c[14] & (1 << 6)) >> 6;
|
||||
card->csd.v1.PERM_WRITE_PROTECT = (c[14] & (1 << 5)) >> 5;
|
||||
card->csd.v1.TMP_WRITE_PROTECT = (c[14] & (1 << 4)) >> 4;
|
||||
card->csd.v1.FILE_FORMAT = (c[14] & 0x0C) >> 2;
|
||||
card->csd.v1.CSD_CRC = c[15];
|
||||
card->csd_structure = SD_CSD_V1;
|
||||
return SD_RW_OK;
|
||||
} else if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V2) {
|
||||
card->csd.v2.CSD_STRUCTURE = c[0] >> 6;
|
||||
card->csd.v2.TAAC = c[1];
|
||||
card->csd.v2.NSAC = c[2];
|
||||
card->csd.v2.TRAN_SPEED = c[3];
|
||||
card->csd.v2.CCC = (c[4] << 4) | ((c[5] & 0xF0) >> 4);
|
||||
card->csd.v2.READ_BL_LEN = (c[5] & 0x0F);
|
||||
card->csd.v2.READ_BL_PARTIAL = (c[6] & (1 << 7)) >> 7;
|
||||
card->csd.v2.WRITE_BLK_MISALIGN = (c[6] & (1 << 6)) >> 6;
|
||||
card->csd.v2.READ_BLK_MISALIGN = (c[6] & (1 << 5)) >> 5;
|
||||
card->csd.v2.DSR_IMP = (c[6] & (1 << 4)) >> 4;
|
||||
card->csd.v2.C_SIZE = (((uint32_t)c[7] & 0x3F) << 16) | (c[8] << 8) | c[9];
|
||||
card->csd.v2.ERASE_BLK_EN = (c[10] & (1 << 6)) >> 6;
|
||||
card->csd.v2.SECTOR_SIZE = (c[10] & 0x3F) << 1 | (c[11] >> 7);
|
||||
card->csd.v2.WP_GRP_SIZE = (c[11] & 0x7F);
|
||||
card->csd.v2.WP_GRP_ENABLE = (c[12] & (1 << 7)) >> 7;
|
||||
card->csd.v2.R2W_FACTOR = (c[12] & 0x1C) >> 2;
|
||||
card->csd.v2.WRITE_BL_LEN = ((c[12] & 0x03) << 2) | (c[13] >> 6);
|
||||
card->csd.v2.WRITE_BL_PARTIAL = (c[13] & (1 << 5)) >> 5;
|
||||
card->csd.v2.FILE_FORMAT_GRP = (c[14] & (1 << 7)) >> 7;
|
||||
card->csd.v2.COPY = (c[14] & (1 << 6)) >> 6;
|
||||
card->csd.v2.PERM_WRITE_PROTECT = (c[14] & (1 << 5)) >> 5;
|
||||
card->csd.v2.TMP_WRITE_PROTECT = (c[14] & (1 << 4)) >> 4;
|
||||
card->csd.v2.FILE_FORMAT = (c[14] & 0x0C) >> 2;
|
||||
card->csd.v2.CSD_CRC = c[15];
|
||||
card->csd_structure = SD_CSD_V2;
|
||||
return SD_RW_OK;
|
||||
} else {
|
||||
return SD_RW_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
} else {
|
||||
return SD_RW_CRC_MISMATCH;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
uint64_t sdcard_spi_get_capacity(sdcard_info_t *card)
|
||||
{
|
||||
if (card->csd_structure == SD_CSD_V1) {
|
||||
uint32_t block_len = (1 << card->csd.v1.READ_BL_LEN);
|
||||
uint32_t mult = 1 << (card->csd.v1.C_SIZE_MULT + 2);
|
||||
uint32_t blocknr = (card->csd.v1.C_SIZE + 1) * mult;
|
||||
return blocknr * block_len;
|
||||
}
|
||||
else if (card->csd_structure == SD_CSD_V2) {
|
||||
return (card->csd.v2.C_SIZE + 1) * (((uint64_t)SD_HC_BLOCK_SIZE) << 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t sdcard_spi_get_sector_count(sdcard_info_t *card)
|
||||
{
|
||||
return sdcard_spi_get_capacity(card) / SD_HC_BLOCK_SIZE;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stm32_spi.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* R1 response bits (see sd spec. 7.3.2.1 Format R1) */
|
||||
#define SD_R1_RESPONSE_PARAM_ERROR (1<<6)
|
||||
#define SD_R1_RESPONSE_ADDR_ERROR (1<<5)
|
||||
#define SD_R1_RESPONSE_ERASE_SEQ_ERROR (1<<4)
|
||||
#define SD_R1_RESPONSE_CMD_CRC_ERROR (1<<3)
|
||||
#define SD_R1_RESPONSE_ILLEGAL_CMD_ERROR (1<<2)
|
||||
#define SD_R1_RESPONSE_ERASE_RESET (1<<1)
|
||||
#define SD_R1_RESPONSE_IN_IDLE_STATE (0x01)
|
||||
#define SD_INVALID_R1_RESPONSE (1<<7)
|
||||
|
||||
#define R1_VALID(X) (((X) >> 7) == 0)
|
||||
#define R1_PARAM_ERR(X) ((((X) &SD_R1_RESPONSE_PARAM_ERROR) != 0))
|
||||
#define R1_ADDR_ERR(X) ((((X) &SD_R1_RESPONSE_ADDR_ERROR) != 0))
|
||||
#define R1_ERASE_ERR(X) ((((X) &SD_R1_RESPONSE_ERASE_SEQ_ERROR) != 0))
|
||||
#define R1_CMD_CRC_ERR(X) ((((X) &SD_R1_RESPONSE_CMD_CRC_ERROR) != 0))
|
||||
#define R1_ILL_CMD_ERR(X) ((((X) &SD_R1_RESPONSE_ILLEGAL_CMD_ERROR) != 0))
|
||||
#define R1_IDLE_BIT_SET(X) (((X) &SD_R1_RESPONSE_IN_IDLE_STATE) != 0)
|
||||
#define R1_ERROR(X) (R1_PARAM_ERR(X) || R1_ADDR_ERR(X) || R1_ERASE_ERR(X) || \
|
||||
R1_CMD_CRC_ERR(X) || R1_ILL_CMD_ERR(X))
|
||||
|
||||
/* see sd spec. 7.3.3.1 Data Response Token */
|
||||
#define DATA_RESPONSE_IS_VALID(X) (((X) & 0x11) == 0x01)
|
||||
#define DATA_RESPONSE_ACCEPTED(X) (((X) & 0x0E) == 0x04)
|
||||
#define DATA_RESPONSE_CRC_ERR(X) (((X) & 0x0E) == 0x0A)
|
||||
#define DATA_RESPONSE_WRITE_ERR(X) (((X) & 0x0E) == 0x0C)
|
||||
|
||||
/* see sd spec. 5.1 OCR register */
|
||||
#define OCR_VOLTAGE_3_2_TO_3_3 (1L << 20)
|
||||
#define OCR_VOLTAGE_3_3_TO_3_4 (1L << 21)
|
||||
|
||||
/* card capacity status (CCS=0: the card is SDSD; CCS=1: card is SDHC or SDXC) */
|
||||
#define OCR_CCS (1L << 30)
|
||||
|
||||
/* This bit is set to low if the card has not finished power up routine */
|
||||
#define OCR_POWER_UP_STATUS (1L << 31)
|
||||
|
||||
/* to ensure the voltage range check on init is done properly you need to
|
||||
define this according to your actual interface/wiring with the sd-card */
|
||||
#define SYSTEM_VOLTAGE (OCR_VOLTAGE_3_2_TO_3_3 | OCR_VOLTAGE_3_2_TO_3_3)
|
||||
|
||||
/* see sd spec. 7.3.1.3 Detailed Command Description */
|
||||
#define SD_CMD_PREFIX_MASK (1<<6)
|
||||
|
||||
#define SD_CMD_0 0 /* Resets the SD Memory Card */
|
||||
#define SD_CMD_1 1 /* Sends host capacity support info and starts the cards init process */
|
||||
#define SD_CMD_8 8 /* Sends SD Card interface condition incl. host supply voltage info */
|
||||
#define SD_CMD_9 9 /* Asks the selected card to send its card-specific data (CSD) */
|
||||
#define SD_CMD_10 10 /* Asks the selected card to send its card identification (CID) */
|
||||
#define SD_CMD_12 12 /* Forces the card to stop transmission in Multiple Block Read Operation */
|
||||
#define SD_CMD_13 13 /* Sent as ACMD13 asks the card to send it's SD status */
|
||||
|
||||
#define SD_CMD_16 16 /* In case of SDSC Card, block length is set by this command */
|
||||
#define SD_CMD_17 17 /* Reads a block of the size selected by the SET_BLOCKLEN command */
|
||||
#define SD_CMD_18 18 /* Continuously transfers data blocks from card to host
|
||||
until interrupted by a STOP_TRANSMISSION command */
|
||||
#define SD_CMD_23 23 /* Used for ACMD23: write block erase count */
|
||||
#define SD_CMD_24 24 /* Writes a block of the size selected by the SET_BLOCKLEN command */
|
||||
#define SD_CMD_25 25 /* Continuously writes blocks of data until 'Stop Tran'token is sent */
|
||||
#define SD_CMD_41 41 /* Reserved (used for ACMD41) */
|
||||
#define SD_CMD_55 55 /* Defines to the card that the next command is an application specific
|
||||
command rather than a standard command */
|
||||
#define SD_CMD_58 58 /* Reads the OCR register of a card */
|
||||
#define SD_CMD_59 59 /* Turns the CRC option on or off. Argument: 1:on; 0:off */
|
||||
|
||||
#define SD_CMD_8_VHS_2_7_V_TO_3_6_V 0x01
|
||||
#define SD_CMD_8_CHECK_PATTERN 0xB5
|
||||
#define SD_CMD_NO_ARG 0x00000000
|
||||
#define SD_ACMD_41_ARG_HC 0x40000000
|
||||
#define SD_CMD_59_ARG_EN 0x00000001
|
||||
#define SD_CMD_59_ARG_DIS 0x00000000
|
||||
|
||||
/* see sd spec. 7.3.3 Control Tokens */
|
||||
#define SD_DATA_TOKEN_CMD_17_18_24 0xFE
|
||||
#define SD_DATA_TOKEN_CMD_25 0xFC
|
||||
#define SD_DATA_TOKEN_CMD_25_STOP 0xFD
|
||||
|
||||
#define SD_SIZE_OF_CID_AND_CSD_REG 16
|
||||
#define SD_SIZE_OF_SD_STATUS 64
|
||||
#define SD_BLOCKS_FOR_REG_READ 1
|
||||
#define SD_GET_CSD_STRUCTURE(CSD_RAW_DATA) ((CSD_RAW_DATA)[0] >> 6)
|
||||
#define SD_CSD_V1 0
|
||||
#define SD_CSD_V2 1
|
||||
#define SD_CSD_VUNSUPPORTED -1
|
||||
|
||||
#define SD_HC_BLOCK_SIZE (512)
|
||||
|
||||
#define SDCARD_SPI_INIT_ERROR (-1) /* returned on failed init */
|
||||
#define SDCARD_SPI_OK (0) /* returned on successful init */
|
||||
|
||||
#define SD_SIZE_OF_OID 2 /* OID (OEM/application ID field in CID reg) */
|
||||
#define SD_SIZE_OF_PNM 5 /* PNM (product name field in CID reg) */
|
||||
|
||||
#define SD_SIZE_OF_CID_AND_CSD_REG 16
|
||||
#define SD_SIZE_OF_SD_STATUS 64
|
||||
#define SD_BLOCKS_FOR_REG_READ 1
|
||||
#define SD_GET_CSD_STRUCTURE(CSD_RAW_DATA) ((CSD_RAW_DATA)[0] >> 6)
|
||||
|
||||
#define SD_CSD_V1 0
|
||||
#define SD_CSD_V2 1
|
||||
#define SD_CSD_VUNSUPPORTED -1
|
||||
|
||||
/**
|
||||
* @brief CID register see section 5.2 in SD-Spec v5.00
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t MID; /**< Manufacturer ID */
|
||||
char OID[SD_SIZE_OF_OID]; /**< OEM/Application ID*/
|
||||
char PNM[SD_SIZE_OF_PNM]; /**< Product name */
|
||||
uint8_t PRV; /**< Product revision */
|
||||
uint32_t PSN; /**< Product serial number */
|
||||
uint16_t MDT; /**< Manufacturing date */
|
||||
uint8_t CID_CRC; /**< CRC7 checksum */
|
||||
} cid_t;
|
||||
|
||||
/**
|
||||
* @brief CSD register with csd structure version 1.0
|
||||
* see section 5.3.2 in SD-Spec v5.00
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t CSD_STRUCTURE : 2; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t TAAC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t NSAC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t TRAN_SPEED : 8; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint16_t CCC : 12; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t READ_BL_LEN : 4; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t READ_BL_PARTIAL : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BLK_MISALIGN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t READ_BLK_MISALIGN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t DSR_IMP : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint16_t C_SIZE : 12; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t VDD_R_CURR_MIN : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t VDD_R_CURR_MAX : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t VDD_W_CURR_MIN : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t VDD_W_CURR_MAX : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t C_SIZE_MULT : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t ERASE_BLK_EN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t SECTOR_SIZE : 7; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t WP_GRP_SIZE : 7; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t WP_GRP_ENABLE : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t R2W_FACTOR : 3; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BL_LEN : 4; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BL_PARTIAL : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t FILE_FORMAT_GRP : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t COPY : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t PERM_WRITE_PROTECT : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t TMP_WRITE_PROTECT : 1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t FILE_FORMAT : 2; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
uint8_t CSD_CRC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
} csd_v1_t;
|
||||
|
||||
/**
|
||||
* @brief CSD register with csd structure version 2.0
|
||||
* see section 5.3.3 in SD-Spec v5.00
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t CSD_STRUCTURE : 2; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t TAAC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t NSAC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t TRAN_SPEED : 8; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint16_t CCC : 12; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t READ_BL_LEN : 4; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t READ_BL_PARTIAL : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BLK_MISALIGN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t READ_BLK_MISALIGN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t DSR_IMP : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint32_t C_SIZE : 22; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t ERASE_BLK_EN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t SECTOR_SIZE : 7; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t WP_GRP_SIZE : 7; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t WP_GRP_ENABLE : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t R2W_FACTOR : 3; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BL_LEN : 4; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t WRITE_BL_PARTIAL : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t FILE_FORMAT_GRP : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t COPY : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t PERM_WRITE_PROTECT : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t TMP_WRITE_PROTECT : 1; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t FILE_FORMAT : 2; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
uint8_t CSD_CRC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
} csd_v2_t;
|
||||
|
||||
/**
|
||||
* @brief CSD register (see section 5.3 in SD-Spec v5.00)
|
||||
*/
|
||||
typedef union {
|
||||
csd_v1_t v1; /**< see section 5.3.2 in SD-Spec v5.00 */
|
||||
csd_v2_t v2; /**< see section 5.3.3 in SD-Spec v5.00 */
|
||||
} csd_t;
|
||||
|
||||
/**
|
||||
* @brief SD status register (see section 4.10.2 in SD-Spec v5.00)
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t SIZE_OF_PROTECTED_AREA : 32; /**< see section 4.10.2 in SD-Spec v5.00 */
|
||||
uint32_t SUS_ADDR : 22; /**< see section 4.10.2.12 in SD-Spec v5.00 */
|
||||
uint32_t VSC_AU_SIZE : 10; /**< see section 4.10.2.11 in SD-Spec v5.00 */
|
||||
uint16_t SD_CARD_TYPE : 16; /**< see section 4.10.2 in SD-Spec v5.00 */
|
||||
uint16_t ERASE_SIZE : 16; /**< see section 4.10.2.5 in SD-Spec v5.00 */
|
||||
uint8_t SPEED_CLASS : 8; /**< see section 4.10.2.2 in SD-Spec v5.00 */
|
||||
uint8_t PERFORMANCE_MOVE : 8; /**< see section 4.10.2.3 in SD-Spec v5.00 */
|
||||
uint8_t VIDEO_SPEED_CLASS : 8; /**< see section 4.10.2.10 in SD-Spec v5.00 */
|
||||
uint8_t ERASE_TIMEOUT : 6; /**< see section 4.10.2.6 in SD-Spec v5.00 */
|
||||
uint8_t ERASE_OFFSET : 2; /**< see section 4.10.2.7 in SD-Spec v5.00 */
|
||||
uint8_t UHS_SPEED_GRADE : 4; /**< see section 4.10.2.8 in SD-Spec v5.00 */
|
||||
uint8_t UHS_AU_SIZE : 4; /**< see section 4.10.2.9 in SD-Spec v5.00 */
|
||||
uint8_t AU_SIZE : 4; /**< see section 4.10.2.4 in SD-Spec v5.00 */
|
||||
uint8_t DAT_BUS_WIDTH : 2; /**< see section 4.10.2 in SD-Spec v5.00 */
|
||||
uint8_t SECURED_MODE : 1; /**< see section 4.10.2 in SD-Spec v5.00 */
|
||||
} sd_status_t;
|
||||
|
||||
/**
|
||||
* @brief version type of SD-card
|
||||
*/
|
||||
typedef enum {
|
||||
SD_V2, /**< SD version 2 */
|
||||
SD_V1, /**< SD version 1 */
|
||||
MMC_V3, /**< MMC version 3 */
|
||||
SD_UNKNOWN /**< SD-version unknown */
|
||||
} sd_version_t;
|
||||
|
||||
/**
|
||||
* @brief sdcard info
|
||||
*/
|
||||
typedef struct {
|
||||
sd_version_t card_type;
|
||||
cid_t cid;
|
||||
csd_t csd;
|
||||
int csd_structure;
|
||||
bool use_block_addr;
|
||||
} sdcard_info_t;
|
||||
|
||||
typedef struct {
|
||||
const stm32_spi_t* spi;
|
||||
bool use_block_addr;
|
||||
} sdcard_spi_t;
|
||||
|
||||
/**
|
||||
* @brief sdcard_spi r/w-operation return values
|
||||
*/
|
||||
typedef enum {
|
||||
SD_RW_OK = 0, /**< no error */
|
||||
SD_RW_NO_TOKEN, /**< no token was received (on block read) */
|
||||
SD_RW_TIMEOUT, /**< cmd timed out (not-busy-state wasn't entered) */
|
||||
SD_RW_RX_TX_ERROR, /**< error while performing SPI read/write */
|
||||
SD_RW_WRITE_ERROR, /**< data-packet response indicates error */
|
||||
SD_RW_CRC_MISMATCH, /**< CRC-mismatch of received data */
|
||||
SD_RW_NOT_SUPPORTED /**< operation not supported on used card */
|
||||
} sd_rw_response_t;
|
||||
|
||||
int sdcard_spi_init(const stm32_spi_t* spi, sdcard_info_t* card);
|
||||
|
||||
uint64_t sdcard_spi_get_capacity(sdcard_info_t* card);
|
||||
|
||||
uint32_t sdcard_spi_get_sector_count(sdcard_info_t* card);
|
||||
|
||||
int sdcard_spi_read_blocks(uint32_t blockaddr, uint8_t* data,
|
||||
uint16_t blocksize, uint16_t nblocks,
|
||||
sd_rw_response_t* state);
|
||||
|
||||
int sdcard_spi_write_blocks(uint32_t blockaddr, const uint8_t* data,
|
||||
uint16_t blocksize, uint16_t nblocks,
|
||||
sd_rw_response_t* state);
|
||||
|
||||
int sdcard_spi_wait_for_not_busy();
|
||||
@@ -1,420 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "sdio_sd.h"
|
||||
#include "stm32_dma.h"
|
||||
#include "stm32_gpio_driver.h"
|
||||
|
||||
#include "stm32_hal_ll.h"
|
||||
#include "stm32_hal.h"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#include "delays_driver.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
|
||||
#if !defined(SD_SDIO_DATA_GPIO) && !defined(SD_SDIO_DATA_GPIO_PINS)
|
||||
#define SD_SDIO_DATA_GPIO GPIOC
|
||||
#define SD_SDIO_DATA_GPIO_PINS \
|
||||
(LL_GPIO_PIN_8 | LL_GPIO_PIN_9 | LL_GPIO_PIN_10 | LL_GPIO_PIN_11)
|
||||
#endif
|
||||
|
||||
/* Configure PD.02 CMD line */
|
||||
#if !defined(SD_SDIO_CMD_GPIO) && !defined(SD_SDIO_CMD_GPIO_PIN)
|
||||
#define SD_SDIO_CMD_GPIO GPIOD
|
||||
#define SD_SDIO_CMD_GPIO_PIN LL_GPIO_PIN_2
|
||||
#endif
|
||||
|
||||
#if !defined(SD_SDIO_CLK_GPIO) && !defined(SD_SDIO_CLK_GPIO_PIN)
|
||||
#define SD_SDIO_CLK_GPIO GPIOC
|
||||
#define SD_SDIO_CLK_GPIO_PIN LL_GPIO_PIN_12
|
||||
#endif
|
||||
|
||||
static SD_HandleTypeDef sdio;
|
||||
static DMA_HandleTypeDef sdioTxDma;
|
||||
|
||||
// Disk status
|
||||
volatile uint32_t WriteStatus = 0;
|
||||
volatile uint32_t ReadStatus = 0;
|
||||
|
||||
static void SD_LowLevel_Init(void)
|
||||
{
|
||||
/* Enable the SDIO APB2 Clock */
|
||||
__HAL_RCC_SDIO_CLK_ENABLE();
|
||||
|
||||
LL_GPIO_InitTypeDef GPIO_InitStructure;
|
||||
LL_GPIO_StructInit(&GPIO_InitStructure);
|
||||
|
||||
stm32_gpio_enable_clock(SD_SDIO_DATA_GPIO);
|
||||
stm32_gpio_enable_clock(SD_SDIO_CMD_GPIO);
|
||||
stm32_gpio_enable_clock(SD_SDIO_CLK_GPIO);
|
||||
|
||||
GPIO_InitStructure.Pin = SD_SDIO_DATA_GPIO_PINS;
|
||||
GPIO_InitStructure.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
GPIO_InitStructure.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStructure.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
GPIO_InitStructure.Pull = LL_GPIO_PULL_UP;
|
||||
GPIO_InitStructure.Alternate = LL_GPIO_AF_12; // SDIO
|
||||
LL_GPIO_Init(SD_SDIO_DATA_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.Pin = SD_SDIO_CMD_GPIO_PIN;
|
||||
LL_GPIO_Init(SD_SDIO_CMD_GPIO, &GPIO_InitStructure);
|
||||
|
||||
/* Configure PC.12 pin: CLK pin */
|
||||
GPIO_InitStructure.Pin = SD_SDIO_CLK_GPIO_PIN;
|
||||
GPIO_InitStructure.Pull = LL_GPIO_PULL_NO;
|
||||
LL_GPIO_Init(SD_SDIO_CLK_GPIO, &GPIO_InitStructure);
|
||||
|
||||
// SDIO Interrupt ENABLE
|
||||
NVIC_SetPriority(SDIO_IRQn, 0);
|
||||
NVIC_EnableIRQ(SDIO_IRQn);
|
||||
|
||||
// Init SDIO DMA instance
|
||||
sdioTxDma.Instance = SD_SDIO_DMA_STREAM;
|
||||
sdioTxDma.Init.Channel = SD_SDIO_DMA_CHANNEL;
|
||||
sdioTxDma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
sdioTxDma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
sdioTxDma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
sdioTxDma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
sdioTxDma.Init.Mode = DMA_PFCTRL;
|
||||
sdioTxDma.Init.Priority = DMA_PRIORITY_VERY_HIGH;
|
||||
sdioTxDma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
sdioTxDma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
sdioTxDma.Init.MemBurst = DMA_MBURST_INC4;
|
||||
sdioTxDma.Init.PeriphBurst = DMA_PBURST_INC4;
|
||||
|
||||
stm32_dma_enable_clock(SD_SDIO_DMA);
|
||||
HAL_DMA_Init(&sdioTxDma);
|
||||
|
||||
__HAL_LINKDMA(&sdio, hdmatx, sdioTxDma);
|
||||
__HAL_LINKDMA(&sdio, hdmarx, sdioTxDma);
|
||||
|
||||
// DMA2 STREAMx Interrupt ENABLE
|
||||
NVIC_SetPriority(SD_SDIO_DMA_IRQn, 0);
|
||||
NVIC_EnableIRQ(SD_SDIO_DMA_IRQn);
|
||||
}
|
||||
|
||||
SD_Error SD_Init(void)
|
||||
{
|
||||
static bool _sdio_init = false;
|
||||
|
||||
if(_sdio_init) return SD_OK;
|
||||
_sdio_init = true;
|
||||
|
||||
__IO SD_Error errorstatus = SD_OK;
|
||||
|
||||
/* SDIO Peripheral Low Level Init */
|
||||
SD_LowLevel_Init();
|
||||
|
||||
/*!< Configure the SDIO peripheral */
|
||||
/*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */
|
||||
/*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
|
||||
sdio.Instance = SDIO;
|
||||
sdio.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
|
||||
sdio.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
|
||||
sdio.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
|
||||
sdio.Init.BusWide = SDIO_BUS_WIDE_1B;
|
||||
sdio.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
|
||||
sdio.Init.ClockDiv = SD_SDIO_TRANSFER_CLK_DIV;
|
||||
HAL_SD_DeInit(&sdio);
|
||||
|
||||
HAL_StatusTypeDef halStatus = HAL_SD_Init(&sdio);
|
||||
if (halStatus != HAL_OK) {
|
||||
TRACE("SD_PowerON() status=%d", halStatus);
|
||||
/*!< CMD Response TimeOut (wait for CMDSENT flag) */
|
||||
return SD_ERROR;
|
||||
}
|
||||
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
HAL_StatusTypeDef es = HAL_SD_GetCardInfo(&sdio, &cardInfo);
|
||||
if(es != HAL_OK)
|
||||
return SD_ERROR;
|
||||
|
||||
HAL_SD_ConfigWideBusOperation(&sdio, SDIO_BUS_WIDE_4B);
|
||||
|
||||
return errorstatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the cuurent sd card data transfer status.
|
||||
* @param None
|
||||
* @retval SDTransferState: Data Transfer state.
|
||||
* This value can be:
|
||||
* - SD_TRANSFER_OK: No data transfer is acting
|
||||
* - SD_TRANSFER_BUSY: Data transfer is acting
|
||||
*/
|
||||
SDTransferState SD_GetStatus(void)
|
||||
{
|
||||
HAL_SD_CardStateTypeDef cardstate = HAL_SD_GetCardState(&sdio);
|
||||
|
||||
if (cardstate == HAL_SD_CARD_TRANSFER) {
|
||||
return SD_TRANSFER_OK;
|
||||
}
|
||||
else if (cardstate == HAL_SD_CARD_ERROR) {
|
||||
return SD_TRANSFER_ERROR;
|
||||
}
|
||||
|
||||
return SD_TRANSFER_BUSY;
|
||||
}
|
||||
|
||||
int SD_CheckStatusWithTimeout(uint32_t timeout)
|
||||
{
|
||||
uint32_t timer = HAL_GetTick();
|
||||
/* block until SDIO IP is ready again or a timeout occur */
|
||||
while(HAL_GetTick() - timer < timeout) {
|
||||
auto state = SD_GetStatus();
|
||||
if (state != SD_TRANSFER_BUSY) {
|
||||
return state == SD_TRANSFER_OK ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detect if SD card is correctly plugged in the memory slot.
|
||||
* @param None
|
||||
* @retval Return if SD is detected or not
|
||||
*/
|
||||
uint8_t SD_Detect(void)
|
||||
{
|
||||
__IO uint8_t status = SD_PRESENT;
|
||||
|
||||
/*!< Check GPIO to detect SD */
|
||||
if ((LL_GPIO_ReadInputPort(SD_PRESENT_GPIO) & SD_PRESENT_LL_GPIO_PIN) != 0) {
|
||||
status = SD_NOT_PRESENT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns information about specific card.
|
||||
* @param cardinfo: pointer to a SD_CardInfo structure that contains all SD card
|
||||
* information.
|
||||
* @retval SD_Error: SD Card Error code.
|
||||
*/
|
||||
SD_Error SD_GetCardInfo(HAL_SD_CardInfoTypeDef *cardinfo)
|
||||
{
|
||||
if(HAL_SD_GetCardInfo(&sdio, cardinfo) != HAL_OK)
|
||||
return SD_ERROR;
|
||||
|
||||
return SD_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allows to read blocks from a specified address in a card. The Data
|
||||
* transfer can be managed by DMA mode or Polling mode.
|
||||
* @note This operation should be followed by two functions to check if the
|
||||
* DMA Controller and SD Card status.
|
||||
* - SD_ReadWaitOperation(): this function insure that the DMA
|
||||
* controller has finished all data transfer.
|
||||
* - SD_GetStatus(): to check that the SD Card has finished the
|
||||
* data transfer and it is ready for data.
|
||||
* @param readbuff: pointer to the buffer that will contain the received data.
|
||||
* @param ReadAddr: Address from where data are to be read.
|
||||
* @param BlockSize: the SD card Data block size. The Block size should be 512.
|
||||
* @param NumberOfBlocks: number of blocks to be read.
|
||||
* @retval SD_Error: SD Card Error code.
|
||||
*/
|
||||
SD_Error SD_ReadBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
|
||||
{
|
||||
HAL_StatusTypeDef res = HAL_SD_ReadBlocks_DMA(&sdio, readbuff, ReadAddr, NumberOfBlocks);
|
||||
if(res == HAL_OK)
|
||||
return SD_OK;
|
||||
|
||||
return SD_ERROR;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief This function waits until the SDIO DMA data transfer is finished.
|
||||
// * This function should be called after SDIO_ReadMultiBlocks() function
|
||||
// * to insure that all data sent by the card are already transferred by
|
||||
// * the DMA controller.
|
||||
// * @param None.
|
||||
// * @retval SD_Error: SD Card Error code.
|
||||
// */
|
||||
// SD_Error SD_WaitReadOperation(uint32_t timeout)
|
||||
// {
|
||||
// volatile HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(&sdio);
|
||||
// if(state == HAL_SD_CARD_READY || state == HAL_SD_CARD_TRANSFER)
|
||||
// return SD_OK;
|
||||
// timeout = 100;
|
||||
|
||||
// while((HAL_SD_GetCardState(&sdio) == HAL_SD_CARD_SENDING) && (timeout > 0)) {
|
||||
// delay_ms(1);
|
||||
// timeout--;
|
||||
// }
|
||||
|
||||
// state = HAL_SD_GetCardState(&sdio);
|
||||
// if(timeout > 0 && state == HAL_SD_CARD_TRANSFER)
|
||||
// return SD_OK;
|
||||
|
||||
// return SD_ERROR;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Allows to write blocks starting from a specified address in a card.
|
||||
* The Data transfer can be managed by DMA mode only.
|
||||
* @note This operation should be followed by two functions to check if the
|
||||
* DMA Controller and SD Card status.
|
||||
* - SD_ReadWaitOperation(): this function insure that the DMA
|
||||
* controller has finished all data transfer.
|
||||
* - SD_GetStatus(): to check that the SD Card has finished the
|
||||
* data transfer and it is ready for data.
|
||||
* @param WriteAddr: Address from where data are to be read.
|
||||
* @param writebuff: pointer to the buffer that contain the data to be transferred.
|
||||
* @param BlockSize: the SD card Data block size. The Block size should be 512.
|
||||
* @param NumberOfBlocks: number of blocks to be written.
|
||||
* @retval SD_Error: SD Card Error code.
|
||||
*/
|
||||
SD_Error SD_WriteBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
|
||||
{
|
||||
HAL_StatusTypeDef res = HAL_SD_WriteBlocks_DMA(&sdio, writebuff, WriteAddr, NumberOfBlocks);
|
||||
if(res == HAL_OK)
|
||||
return SD_OK;
|
||||
return SD_ERROR;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief This function waits until the SDIO DMA data transfer is finished.
|
||||
// * This function should be called after SDIO_WriteBlock() and
|
||||
// * SDIO_WriteMultiBlocks() function to insure that all data sent by the
|
||||
// * card are already transferred by the DMA controller.
|
||||
// * @param None.
|
||||
// * @retval SD_Error: SD Card Error code.
|
||||
// */
|
||||
// OPTIMIZE("O0") SD_Error SD_WaitWriteOperation(uint32_t timeout)
|
||||
// {
|
||||
// HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(&sdio);
|
||||
// if(state == HAL_SD_CARD_READY || state == HAL_SD_CARD_TRANSFER)
|
||||
// return SD_OK;
|
||||
|
||||
// timeout = 1000;
|
||||
|
||||
// state = HAL_SD_GetCardState(&sdio);
|
||||
// while((state == HAL_SD_CARD_RECEIVING || state == HAL_SD_CARD_PROGRAMMING) && (timeout > 0)) {
|
||||
// delay_ms(1);
|
||||
// timeout--;
|
||||
// state = HAL_SD_GetCardState(&sdio);
|
||||
// }
|
||||
|
||||
// state = HAL_SD_GetCardState(&sdio);
|
||||
// if(timeout > 0 && state == HAL_SD_CARD_TRANSFER)
|
||||
// return SD_OK;
|
||||
|
||||
// return SD_ERROR;
|
||||
// }
|
||||
|
||||
uint32_t SD_GetSectorCount()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.LogBlockNbr;
|
||||
}
|
||||
|
||||
uint32_t SD_GetSectorSize()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.LogBlockSize;
|
||||
}
|
||||
|
||||
uint32_t SD_GetBlockSize()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.LogBlockSize;
|
||||
}
|
||||
|
||||
uint32_t SD_GetCardType()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.CardType;
|
||||
}
|
||||
|
||||
uint32_t SD_GetCardVersion()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.CardVersion;
|
||||
}
|
||||
|
||||
uint32_t SD_GetCardClass()
|
||||
{
|
||||
HAL_SD_CardInfoTypeDef cardInfo;
|
||||
|
||||
if(SD_GetCardInfo(&cardInfo) != SD_OK)
|
||||
return 0;
|
||||
|
||||
return cardInfo.Class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tx Transfer completed callbacks
|
||||
* @param hsd: SD handle
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
extern "C" void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
|
||||
{
|
||||
UNUSED(hsd);
|
||||
WriteStatus = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rx Transfer completed callbacks
|
||||
* @param hsd: SD handle
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
extern "C" void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
|
||||
{
|
||||
UNUSED(hsd);
|
||||
ReadStatus = 1;
|
||||
}
|
||||
|
||||
extern "C" void SDIO_IRQHandler(void)
|
||||
{
|
||||
DEBUG_INTERRUPT(INT_SDIO);
|
||||
HAL_SD_IRQHandler(&sdio);
|
||||
}
|
||||
extern "C" void SD_SDIO_DMA_IRQHANDLER(void)
|
||||
{
|
||||
DEBUG_INTERRUPT(INT_SDIO_DMA);
|
||||
HAL_DMA_IRQHandler(&sdioTxDma);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef _SDIO_SD_H_
|
||||
#define _SDIO_SD_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
typedef enum
|
||||
{
|
||||
SD_ERROR,
|
||||
SD_OK = 0
|
||||
} SD_Error;
|
||||
|
||||
/**
|
||||
* @brief SDIO Transfer state
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SD_TRANSFER_OK = 0,
|
||||
SD_TRANSFER_BUSY = 1,
|
||||
SD_TRANSFER_ERROR
|
||||
} SDTransferState;
|
||||
|
||||
/**
|
||||
* @brief SD detection on its memory slot
|
||||
*/
|
||||
#define SD_PRESENT ((uint8_t)0x01)
|
||||
#define SD_NOT_PRESENT ((uint8_t)0x00)
|
||||
|
||||
SD_Error SD_Init(void);
|
||||
SDTransferState SD_GetStatus(void);
|
||||
int SD_CheckStatusWithTimeout(uint32_t timeout);
|
||||
uint8_t SD_Detect(void);
|
||||
SD_Error SD_PowerOFF(void);
|
||||
SD_Error SD_ReadBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
|
||||
SD_Error SD_WriteBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
|
||||
SDTransferState SD_GetTransferState(void);
|
||||
SD_Error SD_WaitReadOperation(uint32_t timeout);
|
||||
SD_Error SD_WaitWriteOperation(uint32_t timeout);
|
||||
|
||||
uint32_t SD_GetSectorCount();
|
||||
uint32_t SD_GetSectorSize();
|
||||
uint32_t SD_GetBlockSize();
|
||||
uint32_t SD_GetCardType();
|
||||
uint32_t SD_GetCardVersion();
|
||||
uint32_t SD_GetCardClass();
|
||||
|
||||
#endif // _SDIO_SD_H_
|
||||
|
||||
@@ -27,10 +27,26 @@
|
||||
inline static bool stm32_dma_check_tc_flag(DMA_TypeDef* DMAx, uint32_t DMA_Stream)
|
||||
{
|
||||
switch(DMA_Stream) {
|
||||
case LL_DMA_STREAM_0:
|
||||
if (!LL_DMA_IsActiveFlag_TC0(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC0(DMAx);
|
||||
break;
|
||||
case LL_DMA_STREAM_1:
|
||||
if (!LL_DMA_IsActiveFlag_TC1(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC1(DMAx);
|
||||
break;
|
||||
case LL_DMA_STREAM_2:
|
||||
if (!LL_DMA_IsActiveFlag_TC2(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC2(DMAx);
|
||||
break;
|
||||
case LL_DMA_STREAM_3:
|
||||
if (!LL_DMA_IsActiveFlag_TC3(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC3(DMAx);
|
||||
break;
|
||||
case LL_DMA_STREAM_4:
|
||||
if (!LL_DMA_IsActiveFlag_TC4(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC4(DMAx);
|
||||
break;
|
||||
case LL_DMA_STREAM_5:
|
||||
if (!LL_DMA_IsActiveFlag_TC5(DMAx)) return false;
|
||||
LL_DMA_ClearFlag_TC5(DMAx);
|
||||
|
||||
@@ -49,6 +49,7 @@ extern "C" {
|
||||
#include "STM32F2xx_HAL_Driver/Inc/stm32f2xx_ll_system.h"
|
||||
#include "STM32F2xx_HAL_Driver/Inc/stm32f2xx_ll_rcc.h"
|
||||
#include "STM32F2xx_HAL_Driver/Inc/stm32f2xx_ll_adc.h"
|
||||
#include "STM32F2xx_HAL_Driver/Inc/stm32f2xx_ll_spi.h"
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
@@ -20,6 +20,18 @@
|
||||
*/
|
||||
|
||||
#include "stm32_spi.h"
|
||||
#include "stm32_dma.h"
|
||||
#include "stm32_gpio_driver.h"
|
||||
#include "definitions.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(SPI_DISABLE_DMA)
|
||||
#define USE_SPI_DMA
|
||||
#endif
|
||||
|
||||
#define SPI_DUMMY_BYTE (0xFF)
|
||||
|
||||
void stm32_spi_enable_clock(SPI_TypeDef *SPIx)
|
||||
{
|
||||
@@ -53,3 +65,279 @@ void stm32_spi_enable_clock(SPI_TypeDef *SPIx)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t _get_spi_af(SPI_TypeDef *SPIx)
|
||||
{
|
||||
#if defined(SPI3)
|
||||
if (SPIx == SPI3) return LL_GPIO_AF_6;
|
||||
#endif
|
||||
return LL_GPIO_AF_5;
|
||||
}
|
||||
|
||||
static uint32_t _get_spi_prescaler(SPI_TypeDef *SPIx, uint32_t max_freq)
|
||||
{
|
||||
LL_RCC_ClocksTypeDef RCC_Clocks;
|
||||
LL_RCC_GetSystemClocksFreq(&RCC_Clocks);
|
||||
|
||||
uint32_t pclk = RCC_Clocks.PCLK2_Frequency;
|
||||
#if defined(SPI2)
|
||||
if (SPIx == SPI2) {
|
||||
pclk = RCC_Clocks.PCLK1_Frequency;
|
||||
}
|
||||
#endif
|
||||
#if defined(SPI3)
|
||||
if (SPIx == SPI3) {
|
||||
pclk = RCC_Clocks.PCLK1_Frequency;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t divider = (pclk + max_freq) / max_freq;
|
||||
uint32_t presc;
|
||||
if (divider > 128) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV256;
|
||||
} else if (divider > 64) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV128;
|
||||
} else if (divider > 32) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV64;
|
||||
} else if (divider > 16) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV32;
|
||||
} else if (divider > 8) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV16;
|
||||
} else if (divider > 4) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV8;
|
||||
} else if (divider > 2) {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV4;
|
||||
} else {
|
||||
presc = LL_SPI_BAUDRATEPRESCALER_DIV2;
|
||||
}
|
||||
|
||||
return presc;
|
||||
}
|
||||
|
||||
static void _init_gpios(const stm32_spi_t* spi)
|
||||
{
|
||||
stm32_gpio_enable_clock(spi->SPI_GPIOx);
|
||||
stm32_gpio_enable_clock(spi->CS_GPIOx);
|
||||
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
|
||||
// SCK, MISO, MOSI
|
||||
pinInit.Pin = spi->SPI_Pins;
|
||||
pinInit.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
pinInit.Alternate = _get_spi_af(spi->SPIx);
|
||||
pinInit.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
pinInit.Pull = LL_GPIO_PULL_UP;
|
||||
LL_GPIO_Init(spi->SPI_GPIOx, &pinInit);
|
||||
|
||||
// CS
|
||||
pinInit.Pin = spi->CS_Pin;
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Alternate = LL_GPIO_AF_0;
|
||||
pinInit.Pull = LL_GPIO_PULL_NO;
|
||||
LL_GPIO_Init(spi->CS_GPIOx, &pinInit);
|
||||
}
|
||||
|
||||
#if defined(USE_SPI_DMA)
|
||||
static void _config_dma_streams(const stm32_spi_t* spi)
|
||||
{
|
||||
stm32_dma_enable_clock(spi->DMA);
|
||||
LL_DMA_DeInit(spi->DMA, spi->rxDMA_Stream);
|
||||
LL_DMA_DeInit(spi->DMA, spi->txDMA_Stream);
|
||||
|
||||
LL_DMA_InitTypeDef dmaInit;
|
||||
LL_DMA_StructInit(&dmaInit);
|
||||
|
||||
dmaInit.Channel = spi->DMA_Channel;
|
||||
dmaInit.PeriphOrM2MSrcAddress = (uint32_t)&spi->SPIx->DR;
|
||||
dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
dmaInit.FIFOMode = LL_DMA_FIFOMODE_ENABLE;
|
||||
dmaInit.FIFOThreshold = LL_DMA_FIFOTHRESHOLD_FULL;
|
||||
|
||||
dmaInit.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
|
||||
LL_DMA_Init(spi->DMA, spi->rxDMA_Stream, &dmaInit);
|
||||
|
||||
dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
LL_DMA_Init(spi->DMA, spi->txDMA_Stream, &dmaInit);
|
||||
}
|
||||
#endif
|
||||
|
||||
void stm32_spi_init(const stm32_spi_t* spi)
|
||||
{
|
||||
_init_gpios(spi);
|
||||
|
||||
auto SPIx = spi->SPIx;
|
||||
stm32_spi_enable_clock(SPIx);
|
||||
LL_SPI_DeInit(SPIx);
|
||||
|
||||
LL_SPI_InitTypeDef spiInit;
|
||||
LL_SPI_StructInit(&spiInit);
|
||||
|
||||
spiInit.TransferDirection = LL_SPI_FULL_DUPLEX;
|
||||
spiInit.Mode = LL_SPI_MODE_MASTER;
|
||||
spiInit.NSS = LL_SPI_NSS_SOFT;
|
||||
|
||||
LL_SPI_Init(SPIx, &spiInit);
|
||||
LL_SPI_Enable(SPIx);
|
||||
|
||||
#if defined(USE_SPI_DMA)
|
||||
if (spi->DMA) {
|
||||
_config_dma_streams(spi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// void stm32_spi_deinit(const stm32_spi_t* spi);
|
||||
|
||||
void stm32_spi_select(const stm32_spi_t* spi)
|
||||
{
|
||||
LL_GPIO_ResetOutputPin(spi->CS_GPIOx, spi->CS_Pin);
|
||||
}
|
||||
|
||||
void stm32_spi_unselect(const stm32_spi_t* spi)
|
||||
{
|
||||
LL_GPIO_SetOutputPin(spi->CS_GPIOx, spi->CS_Pin);
|
||||
}
|
||||
|
||||
void stm32_spi_set_max_baudrate(const stm32_spi_t* spi, uint32_t baudrate)
|
||||
{
|
||||
auto* SPIx = spi->SPIx;
|
||||
uint32_t presc = _get_spi_prescaler(SPIx, baudrate);
|
||||
LL_SPI_SetBaudRatePrescaler(SPIx, presc);
|
||||
}
|
||||
|
||||
uint8_t stm32_spi_transfer_byte(const stm32_spi_t* spi, uint8_t out)
|
||||
{
|
||||
auto* SPIx = spi->SPIx;
|
||||
while (!LL_SPI_IsActiveFlag_TXE(SPIx));
|
||||
LL_SPI_TransmitData8(SPIx, out);
|
||||
|
||||
while (!LL_SPI_IsActiveFlag_RXNE(SPIx));
|
||||
return LL_SPI_ReceiveData8(SPIx);
|
||||
}
|
||||
|
||||
uint16_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out,
|
||||
uint8_t* in, uint16_t length)
|
||||
{
|
||||
unsigned trans_bytes = 0;
|
||||
uint8_t in_temp;
|
||||
|
||||
for (trans_bytes = 0; trans_bytes < length; trans_bytes++) {
|
||||
if (out != nullptr) {
|
||||
in_temp = stm32_spi_transfer_byte(spi, out[trans_bytes]);
|
||||
} else {
|
||||
in_temp = stm32_spi_transfer_byte(spi, SPI_DUMMY_BYTE);
|
||||
}
|
||||
if (in != nullptr) {
|
||||
in[trans_bytes] = in_temp;
|
||||
}
|
||||
}
|
||||
|
||||
return trans_bytes;
|
||||
}
|
||||
|
||||
uint16_t stm32_spi_transfer_word(const stm32_spi_t* spi, uint16_t out)
|
||||
{
|
||||
auto* SPIx = spi->SPIx;
|
||||
while (!LL_SPI_IsActiveFlag_TXE(SPIx));
|
||||
LL_SPI_TransmitData16(SPIx, out);
|
||||
|
||||
while (!LL_SPI_IsActiveFlag_RXNE(SPIx));
|
||||
return LL_SPI_ReceiveData16(SPIx);
|
||||
}
|
||||
|
||||
#if defined(USE_SPI_DMA)
|
||||
static uint16_t _scratch_byte __DMA;
|
||||
static uint8_t _scratch_buffer[512] __DMA;
|
||||
|
||||
static void _dma_enable_stream(DMA_TypeDef* DMAx, uint32_t stream,
|
||||
const void* data, uint32_t length)
|
||||
{
|
||||
stm32_dma_check_tc_flag(DMAx, stream);
|
||||
LL_DMA_SetMemoryAddress(DMAx, stream, (uintptr_t)data);
|
||||
LL_DMA_SetDataLength(DMAx, stream, length);
|
||||
LL_DMA_EnableStream(DMAx, stream);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi,
|
||||
uint8_t* data, uint16_t length)
|
||||
{
|
||||
#if defined(USE_SPI_DMA)
|
||||
if (!spi->DMA) {
|
||||
return stm32_spi_transfer_bytes(spi, 0, data, length);
|
||||
}
|
||||
|
||||
if (((uintptr_t)data < SRAM_BASE) || ((uintptr_t)data & 3)) {
|
||||
_dma_enable_stream(spi->DMA, spi->rxDMA_Stream, _scratch_buffer, length);
|
||||
} else {
|
||||
_dma_enable_stream(spi->DMA, spi->rxDMA_Stream, data, length);
|
||||
}
|
||||
LL_SPI_EnableDMAReq_RX(spi->SPIx);
|
||||
|
||||
_scratch_byte = 0xFFFF;
|
||||
LL_DMA_SetMemoryIncMode(spi->DMA, spi->txDMA_Stream, LL_DMA_MEMORY_NOINCREMENT);
|
||||
_dma_enable_stream(spi->DMA, spi->txDMA_Stream, &_scratch_byte, length);
|
||||
LL_SPI_EnableDMAReq_TX(spi->SPIx);
|
||||
|
||||
// Wait for end of DMA transfer
|
||||
while(!stm32_dma_check_tc_flag(spi->DMA, spi->rxDMA_Stream));
|
||||
|
||||
// Disable SPI TX/RX DMA requests
|
||||
CLEAR_BIT(spi->SPIx->CR2, SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN);
|
||||
|
||||
// Wait for TXE=1
|
||||
while(!LL_SPI_IsActiveFlag_TXE(spi->SPIx));
|
||||
|
||||
// Wait for BSY=0
|
||||
while(LL_SPI_IsActiveFlag_BSY(spi->SPIx));
|
||||
|
||||
if (((uintptr_t)data < SRAM_BASE) || ((uintptr_t)data & 3)) {
|
||||
memcpy(data, _scratch_buffer, length);
|
||||
}
|
||||
|
||||
return length;
|
||||
#else
|
||||
return stm32_spi_transfer_bytes(spi, 0, data, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi,
|
||||
const uint8_t* data, uint16_t length)
|
||||
{
|
||||
#if defined(USE_SPI_DMA)
|
||||
if (!spi->DMA) {
|
||||
return stm32_spi_transfer_bytes(spi, data, 0, length);
|
||||
}
|
||||
|
||||
if (((uintptr_t)data < SRAM_BASE) || ((uintptr_t)data & 3)) {
|
||||
memcpy(_scratch_buffer, data, length);
|
||||
data = _scratch_buffer;
|
||||
}
|
||||
|
||||
LL_DMA_SetMemoryIncMode(spi->DMA, spi->txDMA_Stream, LL_DMA_MEMORY_INCREMENT);
|
||||
_dma_enable_stream(spi->DMA, spi->txDMA_Stream, data, length);
|
||||
LL_SPI_EnableDMAReq_TX(spi->SPIx);
|
||||
|
||||
// Wait for end of DMA transfer
|
||||
while (!stm32_dma_check_tc_flag(spi->DMA, spi->txDMA_Stream));
|
||||
|
||||
// Disable SPI TX DMA requests
|
||||
CLEAR_BIT(spi->SPIx->CR2, SPI_CR2_TXDMAEN);
|
||||
|
||||
// Wait for TXE=1
|
||||
while (!LL_SPI_IsActiveFlag_TXE(spi->SPIx));
|
||||
|
||||
// Wait for BSY=0
|
||||
while (LL_SPI_IsActiveFlag_BSY(spi->SPIx));
|
||||
|
||||
// Clear data register
|
||||
if (LL_SPI_IsActiveFlag_RXNE(spi->SPIx)) {
|
||||
(void)LL_SPI_ReceiveData8(spi->SPIx);
|
||||
}
|
||||
|
||||
return length;
|
||||
#else
|
||||
return stm32_spi_transfer_bytes(spi, data, 0, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -22,5 +22,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "stm32_hal_ll.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
struct stm32_spi_t {
|
||||
SPI_TypeDef* SPIx;
|
||||
GPIO_TypeDef* SPI_GPIOx;
|
||||
uint32_t SPI_Pins; // SCK, MISO, MOSI
|
||||
GPIO_TypeDef* CS_GPIOx;
|
||||
uint32_t CS_Pin; // CS
|
||||
|
||||
DMA_TypeDef* DMA;
|
||||
uint32_t DMA_Channel;
|
||||
uint32_t txDMA_Stream;
|
||||
uint32_t rxDMA_Stream;
|
||||
};
|
||||
|
||||
void stm32_spi_enable_clock(SPI_TypeDef *SPIx);
|
||||
|
||||
void stm32_spi_init(const stm32_spi_t* spi);
|
||||
void stm32_spi_deinit(const stm32_spi_t* spi);
|
||||
|
||||
void stm32_spi_select(const stm32_spi_t* spi);
|
||||
void stm32_spi_unselect(const stm32_spi_t* spi);
|
||||
|
||||
void stm32_spi_set_max_baudrate(const stm32_spi_t* spi, uint32_t baudrate);
|
||||
|
||||
uint8_t stm32_spi_transfer_byte(const stm32_spi_t* spi, uint8_t out);
|
||||
uint16_t stm32_spi_transfer_word(const stm32_spi_t* spi, uint16_t out);
|
||||
|
||||
uint16_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out,
|
||||
uint8_t* in, uint16_t length);
|
||||
|
||||
uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, uint8_t* data,
|
||||
uint16_t length);
|
||||
|
||||
uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, const uint8_t* data,
|
||||
uint16_t length);
|
||||
|
||||
@@ -22,17 +22,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "stm32_hal_ll.h"
|
||||
#include "stm32_spi.h"
|
||||
|
||||
struct stm32_spi_adc_t {
|
||||
SPI_TypeDef* SPIx;
|
||||
GPIO_TypeDef* GPIOx;
|
||||
uint32_t GPIO_Pins;
|
||||
uint32_t GPIO_AF;
|
||||
uint32_t GPIO_CS;
|
||||
const uint8_t* channels;
|
||||
uint8_t n_channels;
|
||||
stm32_spi_t spi;
|
||||
const uint8_t* channels;
|
||||
uint8_t n_channels;
|
||||
};
|
||||
|
||||
#define _SPI_ADC_CS_HIGH(adc) LL_GPIO_SetOutputPin(adc->GPIOx, adc->GPIO_CS)
|
||||
#define _SPI_ADC_CS_LOW(adc) LL_GPIO_ResetOutputPin(adc->GPIOx, adc->GPIO_CS)
|
||||
|
||||
|
||||
@@ -22,15 +22,21 @@
|
||||
#include "opentx.h"
|
||||
#include "watchdog_driver.h"
|
||||
|
||||
static volatile uint32_t msTickCount; // Used to get 1 kHz counter
|
||||
static volatile uint32_t msTickCount; // Used to get 1 kHz counter
|
||||
static volatile uint32_t _us_overflow_cnt;
|
||||
|
||||
// Start TIMER at 2000000Hz
|
||||
void init2MhzTimer()
|
||||
{
|
||||
_us_overflow_cnt = 0;
|
||||
TIMER_2MHz_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 2000000 - 1; // 0.5 uS, 2 MHz
|
||||
TIMER_2MHz_TIMER->ARR = 65535;
|
||||
TIMER_2MHz_TIMER->CR2 = 0;
|
||||
TIMER_2MHz_TIMER->CR1 = TIM_CR1_CEN;
|
||||
TIMER_2MHz_TIMER->DIER = TIM_DIER_UIE;
|
||||
|
||||
NVIC_EnableIRQ(TIMER_2MHz_IRQn);
|
||||
NVIC_SetPriority(TIMER_2MHz_IRQn, 4);
|
||||
}
|
||||
|
||||
// Start TIMER at 1000Hz
|
||||
@@ -38,13 +44,13 @@ void init1msTimer()
|
||||
{
|
||||
msTickCount = 0;
|
||||
|
||||
INTERRUPT_xMS_TIMER->ARR = 999; // 5mS in uS
|
||||
INTERRUPT_xMS_TIMER->ARR = 999; // 1mS in uS
|
||||
INTERRUPT_xMS_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS
|
||||
INTERRUPT_xMS_TIMER->CCER = 0;
|
||||
INTERRUPT_xMS_TIMER->CCMR1 = 0;
|
||||
INTERRUPT_xMS_TIMER->EGR = 0;
|
||||
INTERRUPT_xMS_TIMER->CR1 = TIM_CR1_CEN | TIM_CR1_URS;
|
||||
INTERRUPT_xMS_TIMER->DIER |= TIM_DIER_UIE;
|
||||
INTERRUPT_xMS_TIMER->DIER = TIM_DIER_UIE;
|
||||
NVIC_EnableIRQ(INTERRUPT_xMS_IRQn);
|
||||
NVIC_SetPriority(INTERRUPT_xMS_IRQn, 4);
|
||||
}
|
||||
@@ -60,6 +66,14 @@ uint32_t timersGetMsTick()
|
||||
return msTickCount;
|
||||
}
|
||||
|
||||
uint32_t timersGetUsTick()
|
||||
{
|
||||
uint32_t us;
|
||||
us = TIMER_2MHz_TIMER->CNT >> 1;
|
||||
us += _us_overflow_cnt << 15;
|
||||
return us;
|
||||
}
|
||||
|
||||
static uint32_t watchdogTimeout = 0;
|
||||
|
||||
void watchdogSuspend(uint32_t timeout)
|
||||
@@ -105,3 +119,9 @@ extern "C" void INTERRUPT_xMS_IRQHandler()
|
||||
interrupt1ms();
|
||||
DEBUG_INTERRUPT(INT_5MS);
|
||||
}
|
||||
|
||||
extern "C" void TIMER_2MHz_IRQHandler()
|
||||
{
|
||||
TIMER_2MHz_TIMER->SR &= ~TIM_SR_UIF;
|
||||
_us_overflow_cnt += 1;
|
||||
}
|
||||
|
||||
@@ -50,3 +50,4 @@ static inline tmr10ms_t get_tmr10ms()
|
||||
}
|
||||
|
||||
uint32_t timersGetMsTick();
|
||||
uint32_t timersGetUsTick();
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
|
||||
#include "FatFs/diskio.h"
|
||||
#include "hal/fatfs_diskio.h"
|
||||
#include "hal/storage.h"
|
||||
|
||||
#include "fw_version.h"
|
||||
#include "board.h"
|
||||
#include "debug.h"
|
||||
@@ -181,14 +183,14 @@ int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_si
|
||||
|
||||
static DWORD sector_count = 0;
|
||||
if (sector_count == 0) {
|
||||
if (disk_ioctl(0, GET_SECTOR_COUNT, §or_count) != RES_OK) {
|
||||
auto drv = storageGetDefaultDriver();
|
||||
if (drv->ioctl(0, GET_SECTOR_COUNT, §or_count) != RES_OK) {
|
||||
sector_count = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*block_num = sector_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -214,7 +216,8 @@ int8_t STORAGE_IsReady (uint8_t lun)
|
||||
return (lunReady[STORAGE_EEPROM_LUN] != 0) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
return (lunReady[STORAGE_SDCARD_LUN] != 0 && SD_CARD_PRESENT()) ? 0 : -1;
|
||||
// return (lunReady[STORAGE_SDCARD_LUN] != 0 && SD_CARD_PRESENT()) ? 0 : -1;
|
||||
return 0; // debug: "always ON"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +227,7 @@ int8_t STORAGE_IsReady (uint8_t lun)
|
||||
*/
|
||||
int8_t STORAGE_IsWriteProtected (uint8_t lun)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,8 +252,8 @@ int8_t STORAGE_Read (uint8_t lun,
|
||||
}
|
||||
#endif
|
||||
|
||||
// read without cache
|
||||
return (__disk_read(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1;
|
||||
auto drv = storageGetDefaultDriver();
|
||||
return (drv->read(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1;
|
||||
}
|
||||
/**
|
||||
* @brief Write data to the medium
|
||||
@@ -274,8 +277,8 @@ int8_t STORAGE_Write (uint8_t lun,
|
||||
}
|
||||
#endif
|
||||
|
||||
// write without cache
|
||||
return (__disk_write(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1;
|
||||
auto drv = storageGetDefaultDriver();
|
||||
return (drv->write(0, buf, blk_addr, blk_len) == RES_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+66
-97
@@ -19,6 +19,10 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "stm32_hal_ll.h"
|
||||
#include "stm32_gpio_driver.h"
|
||||
#include "stm32_spi.h"
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#if !defined(SIMU)
|
||||
@@ -70,69 +74,44 @@
|
||||
|
||||
#define MP3_BUFFER_SIZE 32
|
||||
|
||||
#define CS_HIGH() do { AUDIO_CS_GPIO->BSRRL = AUDIO_CS_GPIO_PIN; } while (0)
|
||||
#define CS_LOW() do { AUDIO_CS_GPIO->BSRRH = AUDIO_CS_GPIO_PIN; } while (0)
|
||||
#define XDCS_HIGH() do { AUDIO_XDCS_GPIO->BSRRL = AUDIO_XDCS_GPIO_PIN; } while (0)
|
||||
#define XDCS_LOW() do { AUDIO_XDCS_GPIO->BSRRH = AUDIO_XDCS_GPIO_PIN; } while (0)
|
||||
#define RST_HIGH() do { AUDIO_RST_GPIO->BSRRL = AUDIO_RST_GPIO_PIN; } while (0)
|
||||
#define RST_LOW() do { AUDIO_RST_GPIO->BSRRH = AUDIO_RST_GPIO_PIN; } while (0)
|
||||
#define CS_HIGH() stm32_spi_unselect(&_audio_spi)
|
||||
#define CS_LOW() stm32_spi_select(&_audio_spi)
|
||||
#define XDCS_HIGH() do { AUDIO_XDCS_GPIO->BSRR = AUDIO_XDCS_GPIO_PIN; } while (0)
|
||||
#define XDCS_LOW() do { AUDIO_XDCS_GPIO->BSRR = AUDIO_XDCS_GPIO_PIN << 16; } while (0)
|
||||
#define RST_HIGH() do { AUDIO_RST_GPIO->BSRR = AUDIO_RST_GPIO_PIN; } while (0)
|
||||
#define RST_LOW() do { AUDIO_RST_GPIO->BSRR = AUDIO_RST_GPIO_PIN << 16; } while (0)
|
||||
|
||||
#define READ_DREQ() (GPIO_ReadInputDataBit(AUDIO_DREQ_GPIO, AUDIO_DREQ_GPIO_PIN))
|
||||
#define READ_DREQ() (LL_GPIO_IsInputPinSet(AUDIO_DREQ_GPIO, AUDIO_DREQ_GPIO_PIN))
|
||||
|
||||
static const stm32_spi_t _audio_spi = {
|
||||
.SPIx = AUDIO_SPI,
|
||||
.SPI_GPIOx = AUDIO_SPI_SCK_GPIO,
|
||||
.SPI_Pins = AUDIO_SPI_SCK_GPIO_PIN | AUDIO_SPI_MISO_GPIO_PIN | AUDIO_SPI_MOSI_GPIO_PIN,
|
||||
.CS_GPIOx = AUDIO_CS_GPIO,
|
||||
.CS_Pin = AUDIO_CS_GPIO_PIN,
|
||||
};
|
||||
|
||||
void audioSpiInit(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
SPI_InitTypeDef SPI_InitStructure;
|
||||
stm32_gpio_enable_clock(AUDIO_XDCS_GPIO);
|
||||
stm32_gpio_enable_clock(AUDIO_RST_GPIO);
|
||||
stm32_gpio_enable_clock(AUDIO_DREQ_GPIO);
|
||||
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MISO_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(AUDIO_SPI_MISO_GPIO, &GPIO_InitStructure);
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Pin = AUDIO_XDCS_GPIO_PIN;
|
||||
LL_GPIO_Init(AUDIO_XDCS_GPIO, &pinInit);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_SCK_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_SPI_SCK_GPIO, &GPIO_InitStructure);
|
||||
pinInit.Pin = AUDIO_RST_GPIO_PIN;
|
||||
LL_GPIO_Init(AUDIO_RST_GPIO, &pinInit);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MOSI_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_SPI_MOSI_GPIO, &GPIO_InitStructure);
|
||||
pinInit.Pin = AUDIO_DREQ_GPIO_PIN;
|
||||
pinInit.Mode = LL_GPIO_MODE_INPUT;
|
||||
LL_GPIO_Init(AUDIO_DREQ_GPIO, &pinInit);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_CS_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(AUDIO_CS_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_XDCS_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_XDCS_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_RST_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_RST_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_DREQ_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_Init(AUDIO_DREQ_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_PinAFConfig(AUDIO_SPI_SCK_GPIO, AUDIO_SPI_SCK_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
GPIO_PinAFConfig(AUDIO_SPI_MISO_GPIO, AUDIO_SPI_MISO_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
GPIO_PinAFConfig(AUDIO_SPI_MOSI_GPIO, AUDIO_SPI_MOSI_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
|
||||
RCC_ClocksTypeDef RCC_Clocks;
|
||||
RCC_GetClocksFreq(&RCC_Clocks);
|
||||
|
||||
SPI_I2S_DeInit(AUDIO_SPI);
|
||||
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
|
||||
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
|
||||
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
|
||||
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
|
||||
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(AUDIO_SPI, &SPI_InitStructure);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_RXNE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TXE);
|
||||
stm32_spi_init(&_audio_spi);
|
||||
}
|
||||
|
||||
void audioWaitReady()
|
||||
@@ -175,35 +154,7 @@ void audioSpiSetSpeed(uint8_t speed)
|
||||
|
||||
uint8_t audioSpiReadWriteByte(uint8_t value)
|
||||
{
|
||||
uint16_t time_out = 0x0FFF;
|
||||
while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_TXE) == RESET) {
|
||||
if (--time_out == 0) {
|
||||
// reset SPI
|
||||
SPI_Cmd(AUDIO_SPI, DISABLE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPI_I2S_SendData(AUDIO_SPI, value);
|
||||
|
||||
time_out = 0x0FFF;
|
||||
while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_RXNE) == RESET) {
|
||||
if (--time_out == 0) {
|
||||
// reset SPI
|
||||
SPI_Cmd(AUDIO_SPI, DISABLE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SPI_I2S_ReceiveData(AUDIO_SPI);
|
||||
return stm32_spi_transfer_byte(&_audio_spi, value);
|
||||
}
|
||||
|
||||
uint8_t audioWaitDreq(int32_t delay_us)
|
||||
@@ -283,7 +234,7 @@ uint8_t audioHardReset(void)
|
||||
delay_ms(100); // 100ms
|
||||
RST_HIGH();
|
||||
|
||||
if (!audioWaitDreq(5000))
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
delay_ms(20); // 20ms
|
||||
@@ -296,15 +247,14 @@ uint8_t audioSoftReset(void)
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
audioSpiReadWriteByte(0Xff);
|
||||
audioSpiReadWriteByte(0x00); // start the transfer
|
||||
|
||||
audioSpiWriteCmd(SPI_MODE, 0x0816); // SOFT RESET, new model
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
uint8_t retry = 0;
|
||||
while (audioSpiReadReg(SPI_MODE) != 0x0800 && retry < 100) {
|
||||
retry++;
|
||||
audioSpiWriteCmd(SPI_MODE, 0x0804);
|
||||
}
|
||||
// wait for set up successful
|
||||
retry = 0;
|
||||
uint8_t retry = 0;
|
||||
while (audioSpiReadReg(SPI_CLOCKF) != 0x9800 && retry < 100) {
|
||||
retry++;
|
||||
audioSpiWriteCmd(SPI_CLOCKF, 0x9800);
|
||||
@@ -403,10 +353,10 @@ void audioMute()
|
||||
|
||||
void audioUnmute()
|
||||
{
|
||||
if(isFunctionActive(FUNCTION_DISABLE_AUDIO_AMP)) {
|
||||
setMutePin(true);
|
||||
return;
|
||||
}
|
||||
if(isFunctionActive(FUNCTION_DISABLE_AUDIO_AMP)) {
|
||||
setMutePin(true);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(AUDIO_UNMUTE_DELAY)
|
||||
// if muted
|
||||
@@ -435,12 +385,31 @@ void audioMuteInit()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PCBX12S)
|
||||
void audioShutdownInit()
|
||||
{
|
||||
LL_GPIO_InitTypeDef pinInit;
|
||||
LL_GPIO_StructInit(&pinInit);
|
||||
|
||||
pinInit.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
pinInit.Pin = AUDIO_SHUTDOWN_GPIO_PIN;
|
||||
LL_GPIO_Init(AUDIO_SHUTDOWN_GPIO, &pinInit);
|
||||
|
||||
// we never RESET it, there is a 2s delay on STARTUP
|
||||
LL_GPIO_SetOutputPin(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
void audioInit()
|
||||
{
|
||||
#if defined(AUDIO_MUTE_GPIO_PIN)
|
||||
audioMuteInit();
|
||||
#endif
|
||||
|
||||
#if defined(PCBX12S)
|
||||
audioShutdownInit();
|
||||
#endif
|
||||
|
||||
audioSpiInit();
|
||||
audioHardReset();
|
||||
audioSoftReset();
|
||||
@@ -510,7 +479,7 @@ void setScaledVolume(uint8_t volume)
|
||||
}
|
||||
// maximum volume is 0x00 and total silence is 0xFE
|
||||
if (volume == 0) {
|
||||
setVolume(0xFE); // silence
|
||||
setVolume(0xFE); // silence
|
||||
}
|
||||
else {
|
||||
uint32_t vol = (VOLUME_MIN_DB * 2) - ((uint32_t)volume * (VOLUME_MIN_DB * 2)) / VOLUME_LEVEL_MAX;
|
||||
@@ -146,10 +146,7 @@ elseif (PCB STREQUAL X12S)
|
||||
endif()
|
||||
set(INTERNAL_GPS_BAUDRATE "9600" CACHE STRING "Baud rate for internal GPS")
|
||||
set(AUX2_SERIAL ON) # wire to GPS
|
||||
set(FIRMWARE_TARGET_SRC
|
||||
${FIRMWARE_TARGET_SRC}
|
||||
audio_spi_driver.cpp
|
||||
)
|
||||
set(FIRMWARE_SRC ${FIRMWARE_SRC} targets/common/arm/stm32/vs1053b.cpp)
|
||||
set(BITMAPS_TARGET x12s_bitmaps)
|
||||
set(FONTS_TARGET x12s_fonts)
|
||||
set(LCD_DRIVER lcd_driver.cpp)
|
||||
@@ -274,7 +271,6 @@ set(FIRMWARE_SRC
|
||||
targets/common/arm/stm32/heartbeat_driver.cpp
|
||||
targets/common/arm/stm32/trainer_driver.cpp
|
||||
targets/common/arm/stm32/ads79xx.cpp
|
||||
targets/common/arm/stm32/sdio_sd.cpp
|
||||
targets/common/arm/stm32/diskio_sdio.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,459 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#if !defined(SIMU)
|
||||
|
||||
#define VS_WRITE_COMMAND 0x02
|
||||
#define VS_READ_COMMAND 0x03
|
||||
|
||||
#define SPI_MODE 0x00
|
||||
#define SPI_STATUS 0x01
|
||||
#define SPI_BASS 0x02
|
||||
#define SPI_CLOCKF 0x03
|
||||
#define SPI_DECODE_TIME 0x04
|
||||
#define SPI_AUDATA 0x05
|
||||
#define SPI_WRAM 0x06
|
||||
#define SPI_WRAMADDR 0x07
|
||||
#define SPI_HDAT0 0x08
|
||||
#define SPI_HDAT1 0x09
|
||||
#define SPI_AIADDR 0x0a
|
||||
#define SPI_VOL 0x0b
|
||||
#define SPI_AICTRL0 0x0c
|
||||
#define SPI_AICTRL1 0x0d
|
||||
#define SPI_AICTRL2 0x0e
|
||||
#define SPI_AICTRL3 0x0f
|
||||
|
||||
#define SM_DIFF 0x01
|
||||
#define SM_LAYER12 0x02
|
||||
#define SM_RESET 0x04
|
||||
#define SM_CANCEL 0x08
|
||||
#define SM_EARSPEAKER_LO 0x10
|
||||
#define SM_TESTS 0x20
|
||||
#define SM_STREAM 0x40
|
||||
#define SM_EARSPEAKER_HI 0x80
|
||||
#define SM_DACT 0x100
|
||||
#define SM_SDIORD 0x200
|
||||
#define SM_SDISHARE 0x400
|
||||
#define SM_SDINEW 0x800
|
||||
#define SM_ADPCM 0x1000
|
||||
#define SM_LINE1 0x4000
|
||||
#define SM_CLK_RANGE 0x8000
|
||||
|
||||
#define SPI_SPEED_2 0
|
||||
#define SPI_SPEED_4 1
|
||||
#define SPI_SPEED_8 2
|
||||
#define SPI_SPEED_16 3
|
||||
#define SPI_SPEED_32 4
|
||||
#define SPI_SPEED_64 5
|
||||
#define SPI_SPEED_128 6
|
||||
#define SPI_SPEED_256 7
|
||||
|
||||
#define MP3_BUFFER_SIZE 32
|
||||
|
||||
#define CS_HIGH() do { AUDIO_CS_GPIO->BSRRL = AUDIO_CS_GPIO_PIN; } while (0)
|
||||
#define CS_LOW() do { AUDIO_CS_GPIO->BSRRH = AUDIO_CS_GPIO_PIN; } while (0)
|
||||
#define XDCS_HIGH() do { AUDIO_XDCS_GPIO->BSRRL = AUDIO_XDCS_GPIO_PIN; } while (0)
|
||||
#define XDCS_LOW() do { AUDIO_XDCS_GPIO->BSRRH = AUDIO_XDCS_GPIO_PIN; } while (0)
|
||||
#define RST_HIGH() do { AUDIO_RST_GPIO->BSRRL = AUDIO_RST_GPIO_PIN; } while (0)
|
||||
#define RST_LOW() do { AUDIO_RST_GPIO->BSRRH = AUDIO_RST_GPIO_PIN; } while (0)
|
||||
|
||||
#define READ_DREQ() (GPIO_ReadInputDataBit(AUDIO_DREQ_GPIO, AUDIO_DREQ_GPIO_PIN))
|
||||
|
||||
void audioSpiInit(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
SPI_InitTypeDef SPI_InitStructure;
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MISO_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(AUDIO_SPI_MISO_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_SCK_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_SPI_SCK_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MOSI_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_SPI_MOSI_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_CS_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(AUDIO_CS_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_XDCS_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_XDCS_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_RST_GPIO_PIN;
|
||||
GPIO_Init(AUDIO_RST_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_DREQ_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_Init(AUDIO_DREQ_GPIO, &GPIO_InitStructure);
|
||||
|
||||
GPIO_PinAFConfig(AUDIO_SPI_SCK_GPIO, AUDIO_SPI_SCK_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
GPIO_PinAFConfig(AUDIO_SPI_MISO_GPIO, AUDIO_SPI_MISO_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
GPIO_PinAFConfig(AUDIO_SPI_MOSI_GPIO, AUDIO_SPI_MOSI_GPIO_PinSource, AUDIO_SPI_GPIO_AF);
|
||||
|
||||
RCC_ClocksTypeDef RCC_Clocks;
|
||||
RCC_GetClocksFreq(&RCC_Clocks);
|
||||
|
||||
SPI_I2S_DeInit(AUDIO_SPI);
|
||||
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
|
||||
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
|
||||
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
|
||||
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
|
||||
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(AUDIO_SPI, &SPI_InitStructure);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_RXNE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TXE);
|
||||
}
|
||||
|
||||
void audioWaitReady()
|
||||
{
|
||||
// The audio amp needs ~2s to start
|
||||
RTOS_WAIT_MS(2000); // 2s
|
||||
}
|
||||
|
||||
void audioSpiSetSpeed(uint8_t speed)
|
||||
{
|
||||
AUDIO_SPI->CR1 &= 0xFFC7; // Fsck=Fcpu/256
|
||||
switch (speed) {
|
||||
case SPI_SPEED_2:
|
||||
AUDIO_SPI->CR1 |= 0x00 << 3; // Fsck=Fpclk/2=36Mhz
|
||||
break;
|
||||
case SPI_SPEED_4:
|
||||
AUDIO_SPI->CR1 |= 0x01 << 3; // Fsck=Fpclk/4=18Mhz
|
||||
break;
|
||||
case SPI_SPEED_8:
|
||||
AUDIO_SPI->CR1 |= 0x02 << 3; // Fsck=Fpclk/8=9Mhz
|
||||
break;
|
||||
case SPI_SPEED_16:
|
||||
AUDIO_SPI->CR1 |= 0x03 << 3; // Fsck=Fpclk/16=4.5Mhz
|
||||
break;
|
||||
case SPI_SPEED_32:
|
||||
AUDIO_SPI->CR1 |= 0x04 << 3; // Fsck=Fpclk/32=2.25Mhz
|
||||
break;
|
||||
case SPI_SPEED_64:
|
||||
AUDIO_SPI->CR1 |= 0x05 << 3; // Fsck=Fpclk/16=1.125Mhz
|
||||
break;
|
||||
case SPI_SPEED_128:
|
||||
AUDIO_SPI->CR1 |= 0x06 << 3; // Fsck=Fpclk/16=562.5Khz
|
||||
break;
|
||||
case SPI_SPEED_256:
|
||||
AUDIO_SPI->CR1 |= 0x07 << 3; // Fsck=Fpclk/16=281.25Khz
|
||||
break;
|
||||
}
|
||||
AUDIO_SPI->CR1 |= 0x01 << 6;
|
||||
}
|
||||
|
||||
uint8_t audioSpiReadWriteByte(uint8_t value)
|
||||
{
|
||||
uint16_t time_out = 0x0FFF;
|
||||
while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_TXE) == RESET) {
|
||||
if (--time_out == 0) {
|
||||
// reset SPI
|
||||
SPI_Cmd(AUDIO_SPI, DISABLE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPI_I2S_SendData(AUDIO_SPI, value);
|
||||
|
||||
time_out = 0x0FFF;
|
||||
while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_RXNE) == RESET) {
|
||||
if (--time_out == 0) {
|
||||
// reset SPI
|
||||
SPI_Cmd(AUDIO_SPI, DISABLE);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR);
|
||||
SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE);
|
||||
SPI_Cmd(AUDIO_SPI, ENABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SPI_I2S_ReceiveData(AUDIO_SPI);
|
||||
}
|
||||
|
||||
uint8_t audioWaitDreq(int32_t delay_us)
|
||||
{
|
||||
while (READ_DREQ() == 0) {
|
||||
if (delay_us-- == 0) return 0;
|
||||
delay_01us(10);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t audioSpiReadReg(uint8_t address)
|
||||
{
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
audioSpiSetSpeed(SPI_SPEED_64);
|
||||
XDCS_HIGH();
|
||||
CS_LOW();
|
||||
audioSpiReadWriteByte(VS_READ_COMMAND);
|
||||
audioSpiReadWriteByte(address);
|
||||
uint16_t result = audioSpiReadWriteByte(0xff) << 8;
|
||||
result += audioSpiReadWriteByte(0xff);
|
||||
delay_01us(100); // 10us
|
||||
CS_HIGH();
|
||||
audioSpiSetSpeed(SPI_SPEED_8);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t audioSpiReadCmd(uint8_t address)
|
||||
{
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
audioSpiSetSpeed(SPI_SPEED_64);
|
||||
XDCS_HIGH();
|
||||
CS_LOW();
|
||||
audioSpiReadWriteByte(VS_READ_COMMAND);
|
||||
audioSpiReadWriteByte(address);
|
||||
uint16_t result = audioSpiReadWriteByte(0) << 8;
|
||||
result |= audioSpiReadWriteByte(0);
|
||||
delay_01us(50); // 5us
|
||||
CS_HIGH();
|
||||
audioSpiSetSpeed(SPI_SPEED_8);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t audioSpiWriteCmd(uint8_t address, uint16_t data)
|
||||
{
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
audioSpiSetSpeed(SPI_SPEED_64);
|
||||
XDCS_HIGH();
|
||||
CS_LOW();
|
||||
audioSpiReadWriteByte(VS_WRITE_COMMAND);
|
||||
audioSpiReadWriteByte(address);
|
||||
audioSpiReadWriteByte(data >> 8);
|
||||
audioSpiReadWriteByte(data);
|
||||
delay_01us(50); // 5us
|
||||
CS_HIGH();
|
||||
audioSpiSetSpeed(SPI_SPEED_8);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void audioResetDecodeTime(void)
|
||||
{
|
||||
audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000);
|
||||
audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000);
|
||||
}
|
||||
|
||||
uint8_t audioHardReset(void)
|
||||
{
|
||||
XDCS_HIGH();
|
||||
CS_HIGH();
|
||||
RST_LOW();
|
||||
delay_ms(100); // 100ms
|
||||
RST_HIGH();
|
||||
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
delay_ms(20); // 20ms
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t audioSoftReset(void)
|
||||
{
|
||||
audioSpiSetSpeed(SPI_SPEED_64);
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
audioSpiReadWriteByte(0x00); // start the transfer
|
||||
|
||||
audioSpiWriteCmd(SPI_MODE, 0x0816); // SOFT RESET, new model
|
||||
if (!audioWaitDreq(100))
|
||||
return 0;
|
||||
|
||||
// wait for set up successful
|
||||
uint8_t retry = 0;
|
||||
while (audioSpiReadReg(SPI_CLOCKF) != 0x9800 && retry < 100) {
|
||||
retry++;
|
||||
audioSpiWriteCmd(SPI_CLOCKF, 0x9800);
|
||||
}
|
||||
|
||||
audioResetDecodeTime(); // reset the decoding time
|
||||
audioSpiSetSpeed(SPI_SPEED_8);
|
||||
XDCS_LOW();
|
||||
audioSpiReadWriteByte(0X0);
|
||||
audioSpiReadWriteByte(0X0);
|
||||
audioSpiReadWriteByte(0X0);
|
||||
audioSpiReadWriteByte(0X0);
|
||||
delay_01us(100); // 10us
|
||||
XDCS_HIGH();
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t audioSpiWriteData(const uint8_t * buffer, uint32_t size)
|
||||
{
|
||||
XDCS_LOW();
|
||||
|
||||
uint32_t index = 0;
|
||||
while (index < size && READ_DREQ() != 0) {
|
||||
for (int i=0; i<MP3_BUFFER_SIZE && index<size; i++) {
|
||||
audioSpiReadWriteByte(buffer[index++]);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void audioSpiWriteBuffer(const uint8_t * buffer, uint32_t size)
|
||||
{
|
||||
const uint8_t * p = buffer;
|
||||
while (size > 0) {
|
||||
uint32_t written = audioSpiWriteData(p, size);
|
||||
p += written;
|
||||
size -= written;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t RiffHeader[] = {
|
||||
0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20,
|
||||
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00,
|
||||
0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
void audioSendRiffHeader()
|
||||
{
|
||||
audioSpiWriteBuffer(RiffHeader, sizeof(RiffHeader));
|
||||
}
|
||||
|
||||
#if defined(PCBX12S)
|
||||
void audioShutdownInit()
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_InitStructure.GPIO_Pin = AUDIO_SHUTDOWN_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_Init(AUDIO_SHUTDOWN_GPIO, &GPIO_InitStructure);
|
||||
GPIO_SetBits(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN); // we never RESET it, there is a 2s delay on STARTUP
|
||||
}
|
||||
#endif
|
||||
|
||||
void audioInit()
|
||||
{
|
||||
#if defined(PCBX12S)
|
||||
audioShutdownInit();
|
||||
// TODO X10 code missing
|
||||
#endif
|
||||
|
||||
audioSpiInit();
|
||||
audioHardReset();
|
||||
audioSoftReset();
|
||||
audioSpiSetSpeed(SPI_SPEED_8);
|
||||
delay_ms(1); // 1ms
|
||||
audioSendRiffHeader();
|
||||
}
|
||||
|
||||
uint8_t * currentBuffer = nullptr;
|
||||
uint32_t currentSize = 0;
|
||||
int16_t newVolume = -1;
|
||||
|
||||
void audioSetCurrentBuffer(const AudioBuffer * buffer)
|
||||
{
|
||||
if (buffer) {
|
||||
currentBuffer = (uint8_t *)buffer->data;
|
||||
currentSize = buffer->size * 2;
|
||||
}
|
||||
else {
|
||||
currentBuffer = nullptr;
|
||||
currentSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void audioConsumeCurrentBuffer()
|
||||
{
|
||||
if (newVolume >= 0) {
|
||||
uint8_t value = newVolume;
|
||||
audioSpiWriteCmd(SPI_VOL, (value << 8) + value);
|
||||
// audioSendRiffHeader();
|
||||
newVolume = -1;
|
||||
}
|
||||
|
||||
if (!currentBuffer) {
|
||||
audioSetCurrentBuffer(audioQueue.buffersFifo.getNextFilledBuffer());
|
||||
}
|
||||
|
||||
if (currentBuffer) {
|
||||
uint32_t written = audioSpiWriteData(currentBuffer, currentSize);
|
||||
currentBuffer += written;
|
||||
currentSize -= written;
|
||||
if (currentSize == 0) {
|
||||
audioQueue.buffersFifo.freeNextFilledBuffer();
|
||||
currentBuffer = nullptr;
|
||||
currentSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust this value for a volume level just above the silence
|
||||
// values is attenuation in dB, higher value - less volume
|
||||
// max value is 126
|
||||
#define VOLUME_MIN_DB 40
|
||||
|
||||
void setScaledVolume(uint8_t volume)
|
||||
{
|
||||
if (volume > VOLUME_LEVEL_MAX) {
|
||||
volume = VOLUME_LEVEL_MAX;
|
||||
}
|
||||
// maximum volume is 0x00 and total silence is 0xFE
|
||||
if (volume == 0) {
|
||||
setVolume(0xFE); // silence
|
||||
}
|
||||
else {
|
||||
uint32_t vol = (VOLUME_MIN_DB * 2) - ((uint32_t)volume * (VOLUME_MIN_DB * 2)) / VOLUME_LEVEL_MAX;
|
||||
setVolume(vol);
|
||||
}
|
||||
}
|
||||
|
||||
void setVolume(uint8_t volume)
|
||||
{
|
||||
newVolume = volume;
|
||||
}
|
||||
|
||||
int32_t getVolume()
|
||||
{
|
||||
return -1; // TODO
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -92,12 +92,6 @@ enum {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(SIMU)
|
||||
#define SD_CARD_PRESENT() true
|
||||
#else
|
||||
#define SD_CARD_PRESENT() (~SD_PRESENT_GPIO->IDR & SD_PRESENT_GPIO_PIN)
|
||||
#endif
|
||||
|
||||
// Flash Write driver
|
||||
#define FLASH_PAGESIZE 256
|
||||
void unlockFlash();
|
||||
|
||||
@@ -631,6 +631,7 @@
|
||||
#define SD_SDIO_CLK_DIV(fq) ((48000000 / (fq)) - 2)
|
||||
#define SD_SDIO_INIT_CLK_DIV SD_SDIO_CLK_DIV(400000)
|
||||
#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000)
|
||||
#define STORAGE_USE_SDIO
|
||||
|
||||
// EEPROM
|
||||
#if defined(PCBX12S) && PCBREV >= 13
|
||||
@@ -996,6 +997,8 @@
|
||||
// 2MHz Timer
|
||||
#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7
|
||||
#define TIMER_2MHz_TIMER TIM7
|
||||
#define TIMER_2MHz_IRQn TIM7_IRQn
|
||||
#define TIMER_2MHz_IRQHandler TIM7_IRQHandler
|
||||
|
||||
// Mixer scheduler timer
|
||||
#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM13
|
||||
|
||||
@@ -140,7 +140,6 @@ set(FIRMWARE_TARGET_SRC
|
||||
${TOUCH_DRIVER}
|
||||
sdram_driver.c
|
||||
battery_driver.cpp
|
||||
audio_spi_driver.cpp
|
||||
backlight_driver.cpp
|
||||
)
|
||||
|
||||
@@ -153,8 +152,8 @@ set(FIRMWARE_SRC
|
||||
targets/common/arm/stm32/stm32_usart_driver.cpp
|
||||
targets/common/arm/stm32/trainer_driver.cpp
|
||||
targets/common/arm/stm32/pwr_driver.cpp
|
||||
targets/common/arm/stm32/sdio_sd.cpp
|
||||
targets/common/arm/stm32/diskio_sdio.cpp
|
||||
targets/common/arm/stm32/vs1053b.cpp
|
||||
)
|
||||
|
||||
# Make malloc() thread-safe
|
||||
|
||||
@@ -53,12 +53,6 @@ void boardOff();
|
||||
#define LEN_CPU_UID (3*8+2)
|
||||
void getCPUUniqueID(char * s);
|
||||
|
||||
#if defined(SIMU)
|
||||
#define SD_CARD_PRESENT() true
|
||||
#else
|
||||
#define SD_CARD_PRESENT() (~SD_PRESENT_GPIO->IDR & SD_PRESENT_GPIO_PIN)
|
||||
#endif
|
||||
|
||||
// Flash Write driver
|
||||
#define FLASH_PAGESIZE 256
|
||||
void unlockFlash();
|
||||
|
||||
@@ -305,6 +305,7 @@
|
||||
#define SD_SDIO_CLK_DIV(fq) ((48000000 / (fq)) - 2)
|
||||
#define SD_SDIO_INIT_CLK_DIV SD_SDIO_CLK_DIV(400000)
|
||||
#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000)
|
||||
#define STORAGE_USE_SDIO
|
||||
|
||||
// SDRAM
|
||||
#define SDRAM_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH)
|
||||
@@ -549,6 +550,8 @@
|
||||
// 2MHz Timer
|
||||
#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7
|
||||
#define TIMER_2MHz_TIMER TIM7
|
||||
#define TIMER_2MHz_IRQn TIM7_IRQn
|
||||
#define TIMER_2MHz_IRQHandler TIM7_IRQHandler
|
||||
|
||||
// Mixer scheduler timer
|
||||
#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM12
|
||||
|
||||
@@ -95,7 +95,7 @@ DSTATUS disk_status (BYTE pdrv)
|
||||
return (DSTATUS)0;
|
||||
}
|
||||
|
||||
DRESULT __disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
if (diskImage == 0) return RES_NOTRDY;
|
||||
traceDiskStatus();
|
||||
@@ -105,7 +105,7 @@ DRESULT __disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT __disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
if (diskImage == 0) return RES_NOTRDY;
|
||||
traceDiskStatus();
|
||||
|
||||
@@ -613,5 +613,10 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs)
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
#include "hal/storage.h"
|
||||
|
||||
void storageInit() {}
|
||||
void storagePreMountHook() {}
|
||||
bool storageIsPresent() { return true; }
|
||||
|
||||
#endif // #if defined(SIMU_USE_SDCARD)
|
||||
|
||||
@@ -574,7 +574,6 @@ set(FIRMWARE_TARGET_SRC
|
||||
volume_i2c.cpp
|
||||
eeprom_driver.cpp
|
||||
backlight_driver.cpp
|
||||
diskio.cpp
|
||||
)
|
||||
|
||||
if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES OR PCBREV STREQUAL COMMANDO8)
|
||||
@@ -595,6 +594,8 @@ set(FIRMWARE_SRC
|
||||
targets/common/arm/stm32/stm32_pulse_driver.cpp
|
||||
targets/common/arm/stm32/stm32_usart_driver.cpp
|
||||
targets/common/arm/stm32/trainer_driver.cpp
|
||||
targets/common/arm/stm32/sdcard_spi.cpp
|
||||
targets/common/arm/stm32/diskio_spi.cpp
|
||||
)
|
||||
|
||||
if(LCD_DUAL_BUFFER)
|
||||
|
||||
@@ -92,7 +92,6 @@ void boardInit()
|
||||
LCD_RCC_AHB1Periph |
|
||||
AUDIO_RCC_AHB1Periph |
|
||||
BACKLIGHT_RCC_AHB1Periph |
|
||||
SD_RCC_AHB1Periph |
|
||||
HAPTIC_RCC_AHB1Periph |
|
||||
INTMODULE_RCC_AHB1Periph |
|
||||
EXTMODULE_RCC_AHB1Periph |
|
||||
@@ -110,7 +109,6 @@ void boardInit()
|
||||
HAPTIC_RCC_APB1Periph |
|
||||
INTERRUPT_xMS_RCC_APB1Periph |
|
||||
TIMER_2MHz_RCC_APB1Periph |
|
||||
SD_RCC_APB1Periph |
|
||||
TELEMETRY_RCC_APB1Periph |
|
||||
MIXER_SCHEDULER_TIMER_RCC_APB1Periph |
|
||||
BT_RCC_APB1Periph,
|
||||
|
||||
@@ -64,16 +64,6 @@ enum {
|
||||
PCBREV_X7_40 = 1,
|
||||
};
|
||||
|
||||
// SD driver
|
||||
#define __disk_read disk_read
|
||||
#define __disk_write disk_write
|
||||
|
||||
#if defined(SIMU) || !defined(SD_PRESENT_GPIO)
|
||||
#define SD_CARD_PRESENT() true
|
||||
#else
|
||||
#define SD_CARD_PRESENT() ((SD_PRESENT_GPIO->IDR & SD_PRESENT_GPIO_PIN) == 0)
|
||||
#endif
|
||||
|
||||
// Flash Write driver
|
||||
#define FLASH_PAGESIZE 256
|
||||
void unlockFlash();
|
||||
|
||||
@@ -1,933 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "debug.h"
|
||||
#include "FatFs/diskio.h"
|
||||
#include "FatFs/ff.h"
|
||||
|
||||
#include "sdcard.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define RAM_START 0x20000000
|
||||
|
||||
/* Definitions for MMC/SDC command */
|
||||
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
|
||||
#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
|
||||
#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
|
||||
#define CMD8 (0x40+8) /* SEND_IF_COND */
|
||||
#define CMD9 (0x40+9) /* SEND_CSD */
|
||||
#define CMD10 (0x40+10) /* SEND_CID */
|
||||
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
|
||||
#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
|
||||
#define CMD16 (0x40+16) /* SET_BLOCKLEN */
|
||||
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
|
||||
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
|
||||
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */
|
||||
#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
|
||||
#define CMD24 (0x40+24) /* WRITE_BLOCK */
|
||||
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
|
||||
#define CMD55 (0x40+55) /* APP_CMD */
|
||||
#define CMD58 (0x40+58) /* READ_OCR */
|
||||
|
||||
/* Card-Select Controls (Platform dependent) */
|
||||
#define SD_SELECT() GPIO_ResetBits(SD_GPIO, SD_GPIO_PIN_CS) /* MMC CS = L */
|
||||
#define SD_DESELECT() GPIO_SetBits(SD_GPIO, SD_GPIO_PIN_CS) /* MMC CS = H */
|
||||
|
||||
#define BOOL bool
|
||||
#define FALSE false
|
||||
#define TRUE true
|
||||
|
||||
/* Card type flags (CardType) */
|
||||
#define CT_MMC 0x01
|
||||
#define CT_SD1 0x02
|
||||
#define CT_SD2 0x04
|
||||
#define CT_SDC (CT_SD1|CT_SD2)
|
||||
#define CT_BLOCK 0x08
|
||||
|
||||
static const DWORD socket_state_mask_cp = (1 << 0);
|
||||
static const DWORD socket_state_mask_wp = (1 << 1);
|
||||
|
||||
/* Disk status */
|
||||
static volatile DSTATUS Stat = STA_NOINIT;
|
||||
|
||||
/* 100Hz decrement timers */
|
||||
static volatile DWORD Timer1, Timer2;
|
||||
|
||||
/* Card type flags */
|
||||
BYTE CardType;
|
||||
|
||||
enum speed_setting { INTERFACE_SLOW, INTERFACE_FAST };
|
||||
|
||||
static void interface_speed( enum speed_setting speed )
|
||||
{
|
||||
DWORD tmp;
|
||||
|
||||
tmp = SD_SPI->CR1;
|
||||
if ( speed == INTERFACE_SLOW ) {
|
||||
/* Set slow clock (100k-400k) */
|
||||
tmp = ( tmp | SPI_BaudRatePrescaler_128 );
|
||||
} else {
|
||||
/* Set fast clock (depends on the CSD) */
|
||||
tmp = ( tmp & ~SPI_BaudRatePrescaler_128 ) | SD_SPI_BaudRatePrescaler;
|
||||
}
|
||||
SD_SPI->CR1 = tmp;
|
||||
}
|
||||
|
||||
static inline DWORD socket_is_write_protected(void)
|
||||
{
|
||||
return 0; /* fake not protected */
|
||||
}
|
||||
|
||||
static inline DWORD socket_is_empty(void)
|
||||
{
|
||||
return !SD_CARD_PRESENT(); /* fake inserted */
|
||||
}
|
||||
|
||||
static void card_power(BYTE on)
|
||||
{
|
||||
on=on;
|
||||
}
|
||||
|
||||
static int chk_power(void)
|
||||
{
|
||||
return 1; /* fake powered */
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Transmit/Receive a byte to MMC via SPI (Platform dependent) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static BYTE stm32_spi_rw( BYTE out )
|
||||
{
|
||||
/* Loop while DR register in not empty */
|
||||
/// not needed: while (SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_TXE) == RESET) { ; }
|
||||
|
||||
/* Send byte through the SPI peripheral */
|
||||
SPI_I2S_SendData(SD_SPI, out);
|
||||
|
||||
/* Wait to receive a byte */
|
||||
while (SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_RXNE) == RESET) { ; }
|
||||
|
||||
/* Return the byte read from the SPI bus */
|
||||
return SPI_I2S_ReceiveData(SD_SPI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Transmit a byte to MMC via SPI (Platform dependent) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define xmit_spi(dat) stm32_spi_rw(dat)
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Receive a byte from MMC via SPI (Platform dependent) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
BYTE rcvr_spi (void)
|
||||
{
|
||||
return stm32_spi_rw(0xff);
|
||||
}
|
||||
|
||||
/* Alternative macro to receive data fast */
|
||||
#define rcvr_spi_m(dst) *(dst)=stm32_spi_rw(0xff)
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Wait for card ready */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static BYTE wait_ready (void)
|
||||
{
|
||||
BYTE res;
|
||||
|
||||
Timer2 = 50; /* Wait for ready in timeout of 500ms */
|
||||
rcvr_spi();
|
||||
do {
|
||||
res = rcvr_spi();
|
||||
} while ((res != 0xFF) && Timer2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void spi_reset()
|
||||
{
|
||||
for (int n=0; n<520; ++n) {
|
||||
stm32_spi_rw(0xFF);
|
||||
}
|
||||
TRACE_SD_CARD_EVENT(1, sd_spi_reset, 0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Deselect the card and release SPI bus */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
void release_spi (void)
|
||||
{
|
||||
SD_DESELECT();
|
||||
rcvr_spi();
|
||||
delay_us(100);
|
||||
}
|
||||
|
||||
#ifdef SD_USE_DMA
|
||||
|
||||
#if defined(STM32F4) && !defined(BOOT)
|
||||
WORD rw_workbyte[1] __DMA;
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Transmit/Receive Block using DMA (Platform dependent. STM32 here) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static
|
||||
void stm32_dma_transfer(
|
||||
BOOL receive, /* FALSE for buff->SPI, TRUE for SPI->buff */
|
||||
const BYTE *buff, /* receive TRUE : 512 byte data block to be transmitted
|
||||
receive FALSE : Data buffer to store received data */
|
||||
UINT btr /* receive TRUE : Byte count (must be multiple of 2)
|
||||
receive FALSE : Byte count (must be 512) */
|
||||
)
|
||||
{
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
#if defined(STM32F4) && !defined(BOOT)
|
||||
rw_workbyte[0] = 0xffff;
|
||||
#else
|
||||
WORD rw_workbyte[] = { 0xffff };
|
||||
#endif
|
||||
|
||||
DMA_DeInit(SD_DMA_Stream_SPI_RX);
|
||||
DMA_DeInit(SD_DMA_Stream_SPI_TX);
|
||||
|
||||
/* shared DMA configuration values between SPI2 RX & TX*/
|
||||
DMA_InitStructure.DMA_Channel = SD_DMA_Channel_SPI;//the same channel
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (DWORD)(&(SD_SPI->DR));
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_BufferSize = btr;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
|
||||
|
||||
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
|
||||
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
||||
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
||||
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
||||
|
||||
// separate RX & TX
|
||||
if (receive) {
|
||||
DMA_InitStructure.DMA_Memory0BaseAddr = (DWORD)buff;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_Init(SD_DMA_Stream_SPI_RX, &DMA_InitStructure);
|
||||
DMA_InitStructure.DMA_Memory0BaseAddr = (DWORD)rw_workbyte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
||||
DMA_Init(SD_DMA_Stream_SPI_TX, &DMA_InitStructure);
|
||||
}
|
||||
else {
|
||||
#if _FS_READONLY == 0
|
||||
DMA_InitStructure.DMA_Memory0BaseAddr = (DWORD)rw_workbyte;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
||||
DMA_Init(SD_DMA_Stream_SPI_RX, &DMA_InitStructure);
|
||||
DMA_InitStructure.DMA_Memory0BaseAddr = (DWORD)buff;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_Init(SD_DMA_Stream_SPI_TX, &DMA_InitStructure);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Enable DMA Channels */
|
||||
DMA_Cmd(SD_DMA_Stream_SPI_RX, ENABLE);
|
||||
DMA_Cmd(SD_DMA_Stream_SPI_TX, ENABLE);
|
||||
|
||||
/* Enable SPI TX/RX request */
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
|
||||
|
||||
while (DMA_GetFlagStatus(SD_DMA_Stream_SPI_TX, SD_DMA_FLAG_SPI_TC_TX) == RESET) { ; }
|
||||
while (DMA_GetFlagStatus(SD_DMA_Stream_SPI_RX, SD_DMA_FLAG_SPI_TC_RX) == RESET) { ; }
|
||||
|
||||
/* Disable DMA Channels */
|
||||
DMA_Cmd(SD_DMA_Stream_SPI_RX, DISABLE);
|
||||
DMA_Cmd(SD_DMA_Stream_SPI_TX, DISABLE);
|
||||
|
||||
/* Disable SPI RX/TX request */
|
||||
SPI_I2S_DMACmd(SD_SPI, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
|
||||
}
|
||||
#endif /* SD_USE_DMA */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Power Control and interface-initialization (Platform dependent) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
void power_on (void)
|
||||
{
|
||||
SPI_InitTypeDef SPI_InitStructure;
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
volatile BYTE dummyread;
|
||||
|
||||
card_power(1);
|
||||
|
||||
#if defined(SD_PRESENT_GPIO)
|
||||
GPIO_InitStructure.GPIO_Pin = SD_PRESENT_GPIO_PIN;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_Init(SD_PRESENT_GPIO, &GPIO_InitStructure);
|
||||
#endif
|
||||
|
||||
for (uint32_t Timer = 25000; Timer>0;Timer--); /* Wait for 250ms */
|
||||
|
||||
/* Configure I/O for Flash Chip select */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_GPIO_PIN_CS;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(SD_GPIO, &GPIO_InitStructure);
|
||||
|
||||
/* De-select the Card: Chip Select high */
|
||||
SD_DESELECT();
|
||||
|
||||
/* Configure SPI pins: SCK MISO and MOSI with alternate function push-down */
|
||||
GPIO_InitStructure.GPIO_Pin = SD_GPIO_PIN_SCK | SD_GPIO_PIN_MOSI|SD_GPIO_PIN_MISO;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_Init(SD_GPIO, &GPIO_InitStructure);
|
||||
GPIO_PinAFConfig(SD_GPIO,SD_GPIO_PinSource_SCK ,SD_GPIO_AF);
|
||||
GPIO_PinAFConfig(SD_GPIO,SD_GPIO_PinSource_MISO,SD_GPIO_AF);
|
||||
GPIO_PinAFConfig(SD_GPIO,SD_GPIO_PinSource_MOSI,SD_GPIO_AF);
|
||||
|
||||
/* SPI configuration */
|
||||
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
|
||||
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
|
||||
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
|
||||
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
|
||||
SPI_InitStructure.SPI_BaudRatePrescaler = SD_SPI_BaudRatePrescaler;
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(SD_SPI, &SPI_InitStructure);
|
||||
SPI_CalculateCRC(SD_SPI, DISABLE);
|
||||
SPI_Cmd(SD_SPI, ENABLE);
|
||||
|
||||
/* drain SPI */
|
||||
while (SPI_I2S_GetFlagStatus(SD_SPI, SPI_I2S_FLAG_TXE) == RESET) { ; }
|
||||
dummyread = SPI_I2S_ReceiveData(SD_SPI);
|
||||
|
||||
(void) dummyread; // Discard value - prevents compiler warning
|
||||
}
|
||||
|
||||
static
|
||||
void power_off (void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
|
||||
if (!(Stat & STA_NOINIT)) {
|
||||
SD_SELECT();
|
||||
wait_ready();
|
||||
release_spi();
|
||||
}
|
||||
|
||||
SPI_I2S_DeInit(SD_SPI);
|
||||
SPI_Cmd(SD_SPI, DISABLE);
|
||||
|
||||
//All SPI-Pins to input with weak internal pull-downs
|
||||
GPIO_InitStructure.GPIO_Pin = SD_GPIO_PIN_SCK | SD_GPIO_PIN_MISO | SD_GPIO_PIN_MOSI;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||
GPIO_Init(SD_GPIO, &GPIO_InitStructure);
|
||||
|
||||
card_power(0);
|
||||
|
||||
Stat |= STA_NOINIT; /* Set STA_NOINIT */
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Receive a data packet from MMC */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if defined(SD_USE_DMA) && defined(STM32F4) && !defined(BOOT)
|
||||
uint8_t sd_buff[512] __DMA;
|
||||
#endif
|
||||
|
||||
static
|
||||
BOOL rcvr_datablock (
|
||||
BYTE *buff, /* Data buffer to store received data */
|
||||
UINT btr /* Byte count (must be multiple of 4) */
|
||||
)
|
||||
{
|
||||
BYTE token;
|
||||
|
||||
|
||||
Timer1 = 10;
|
||||
do { /* Wait for data packet in timeout of 100ms */
|
||||
token = rcvr_spi();
|
||||
} while ((token == 0xFF) && Timer1);
|
||||
if(token != 0xFE) {
|
||||
TRACE_SD_CARD_EVENT(1, sd_rcvr_datablock, ((uint32_t)(Timer1) << 24) + ((uint32_t)(btr) << 8) + token);
|
||||
spi_reset();
|
||||
return FALSE; /* If not valid data token, return with error */
|
||||
}
|
||||
|
||||
#if defined(SD_USE_DMA)
|
||||
#if defined(STM32F4) && !defined(BOOT)
|
||||
if ((DWORD)buff < RAM_START || ((DWORD)buff & 3)) {
|
||||
stm32_dma_transfer(TRUE, sd_buff, btr);
|
||||
memcpy(buff, sd_buff, btr);
|
||||
} else {
|
||||
stm32_dma_transfer(TRUE, buff, btr);
|
||||
}
|
||||
#else
|
||||
stm32_dma_transfer(TRUE, buff, btr);
|
||||
#endif
|
||||
#else
|
||||
do { /* Receive the data block into buffer */
|
||||
rcvr_spi_m(buff++);
|
||||
rcvr_spi_m(buff++);
|
||||
rcvr_spi_m(buff++);
|
||||
rcvr_spi_m(buff++);
|
||||
} while (btr -= 4);
|
||||
#endif /* SD_USE_DMA */
|
||||
|
||||
rcvr_spi(); /* Discard CRC */
|
||||
rcvr_spi();
|
||||
|
||||
return TRUE; /* Return with success */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Send a data packet to MMC */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define DATA_RESPONSE_TIMEOUT 10
|
||||
|
||||
static
|
||||
BOOL xmit_datablock (
|
||||
const BYTE *buff, /* 512 byte data block to be transmitted */
|
||||
BYTE token /* Data/Stop token */
|
||||
)
|
||||
{
|
||||
BYTE resp;
|
||||
#ifndef SD_USE_DMA
|
||||
BYTE wc;
|
||||
#endif
|
||||
|
||||
if (wait_ready() != 0xFF) {
|
||||
TRACE_SD_CARD_EVENT(1, sd_xmit_datablock_wait_ready, token);
|
||||
spi_reset();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
xmit_spi(token); /* transmit data token */
|
||||
if (token != 0xFD) { /* Is data token */
|
||||
|
||||
#if defined(SD_USE_DMA)
|
||||
#if defined(STM32F4) && !defined(BOOT)
|
||||
if ((DWORD)buff < RAM_START || ((DWORD)buff & 3)) {
|
||||
memcpy(sd_buff, buff, 512);
|
||||
stm32_dma_transfer(FALSE, sd_buff, 512);
|
||||
} else {
|
||||
stm32_dma_transfer(FALSE, buff, 512);
|
||||
}
|
||||
#else
|
||||
stm32_dma_transfer(FALSE, buff, 512);
|
||||
#endif
|
||||
#else
|
||||
wc = 0;
|
||||
do { /* transmit the 512 byte data block to MMC */
|
||||
xmit_spi(*buff++);
|
||||
xmit_spi(*buff++);
|
||||
} while (--wc);
|
||||
#endif /* SD_USE_DMA */
|
||||
|
||||
xmit_spi(0xFF); /* CRC (Dummy) */
|
||||
xmit_spi(0xFF);
|
||||
|
||||
/*
|
||||
Despite what the SD card standard says, the reality is that (at least for some SD cards)
|
||||
the Data Response byte does not come immediately after the last byte of data.
|
||||
|
||||
This delay only happens very rarely, but it does happen. Typical response delay is some 10ms
|
||||
*/
|
||||
Timer2 = DATA_RESPONSE_TIMEOUT;
|
||||
do {
|
||||
resp = rcvr_spi(); /* Receive data response */
|
||||
if ((resp & 0x1F) == 0x05) {
|
||||
TRACE_SD_CARD_EVENT((Timer2 != DATA_RESPONSE_TIMEOUT), sd_xmit_datablock_rcvr_spi, ((uint32_t)(Timer2) << 16) + ((uint32_t)(resp) << 8) + token);
|
||||
return TRUE;
|
||||
}
|
||||
if (resp != 0xFF) {
|
||||
TRACE_SD_CARD_EVENT(1, sd_xmit_datablock_rcvr_spi, ((uint32_t)(Timer2) << 16) + ((uint32_t)(resp) << 8) + token);
|
||||
spi_reset();
|
||||
return FALSE;
|
||||
}
|
||||
} while (Timer2);
|
||||
TRACE_SD_CARD_EVENT(1, sd_xmit_datablock_rcvr_spi, ((uint32_t)(Timer2) << 16) + ((uint32_t)(resp) << 8) + token);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Send a command packet to MMC */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static
|
||||
BYTE send_cmd (
|
||||
BYTE cmd, /* Command byte */
|
||||
DWORD arg /* Argument */
|
||||
)
|
||||
{
|
||||
BYTE n, res;
|
||||
|
||||
|
||||
if (cmd & 0x80) { /* ACMD<n> is the command sequence of CMD55-CMD<n> */
|
||||
cmd &= 0x7F;
|
||||
res = send_cmd(CMD55, 0);
|
||||
if (res > 1) return res;
|
||||
}
|
||||
|
||||
/* Select the card and wait for ready */
|
||||
SD_SELECT();
|
||||
if (wait_ready() != 0xFF) {
|
||||
TRACE_SD_CARD_EVENT(1, sd_send_cmd_wait_ready, cmd);
|
||||
spi_reset();
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
/* Send command packet */
|
||||
xmit_spi(cmd); /* Start + Command index */
|
||||
xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
|
||||
xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
|
||||
xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
|
||||
xmit_spi((BYTE)arg); /* Argument[7..0] */
|
||||
n = 0x01; /* Dummy CRC + Stop */
|
||||
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */
|
||||
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */
|
||||
xmit_spi(n);
|
||||
|
||||
/* Receive command response */
|
||||
if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */
|
||||
|
||||
n = 10; /* Wait for a valid response in timeout of 10 attempts */
|
||||
do {
|
||||
res = rcvr_spi();
|
||||
} while ((res & 0x80) && --n);
|
||||
|
||||
TRACE_SD_CARD_EVENT((res > 1), sd_send_cmd_rcvr_spi, ((uint32_t)(n) << 16) + ((uint32_t)(res) << 8) + cmd);
|
||||
|
||||
return res; /* Return with the response value */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
Public Functions
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Initialize Disk Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE drv /* Physical drive number (0) */
|
||||
)
|
||||
{
|
||||
BYTE n, cmd, ty, ocr[4];
|
||||
|
||||
if (drv) return STA_NOINIT; /* Supports only single drive */
|
||||
if (Stat & STA_NODISK) return Stat; /* No card in the socket */
|
||||
|
||||
power_on(); /* Force socket power on and initialize interface */
|
||||
interface_speed(INTERFACE_SLOW);
|
||||
for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */
|
||||
|
||||
ty = 0;
|
||||
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
|
||||
Timer1 = 100; /* Initialization timeout of 1000 milliseconds */
|
||||
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDHC */
|
||||
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get trailing return value of R7 response */
|
||||
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at VDD range of 2.7-3.6V */
|
||||
while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */
|
||||
if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
|
||||
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
|
||||
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
|
||||
}
|
||||
}
|
||||
} else { /* SDSC or MMC */
|
||||
if (send_cmd(ACMD41, 0) <= 1) {
|
||||
ty = CT_SD1; cmd = ACMD41; /* SDSC */
|
||||
} else {
|
||||
ty = CT_MMC; cmd = CMD1; /* MMC */
|
||||
}
|
||||
while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */
|
||||
if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
|
||||
ty = 0;
|
||||
}
|
||||
}
|
||||
CardType = ty;
|
||||
release_spi();
|
||||
|
||||
if (ty) { /* Initialization succeeded */
|
||||
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
|
||||
interface_speed(INTERFACE_FAST);
|
||||
}
|
||||
else { /* Initialization failed */
|
||||
power_off();
|
||||
}
|
||||
|
||||
return Stat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Disk Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DSTATUS disk_status (
|
||||
BYTE drv /* Physical drive number (0) */
|
||||
)
|
||||
{
|
||||
if (drv) return STA_NOINIT; /* Supports only single drive */
|
||||
return Stat;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
int8_t SD_ReadSectors(uint8_t * buff, uint32_t sector, uint32_t count)
|
||||
{
|
||||
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
|
||||
|
||||
if (count == 1) { /* Single block read */
|
||||
if (send_cmd(CMD17, sector) == 0) { /* READ_SINGLE_BLOCK */
|
||||
if (rcvr_datablock(buff, 512)) {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
spi_reset();
|
||||
}
|
||||
}
|
||||
else { /* Multiple block read */
|
||||
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
|
||||
do {
|
||||
if (!rcvr_datablock(buff, 512)) {
|
||||
break;
|
||||
}
|
||||
buff += 512;
|
||||
} while (--count);
|
||||
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
|
||||
}
|
||||
else {
|
||||
spi_reset();
|
||||
}
|
||||
}
|
||||
release_spi();
|
||||
TRACE_SD_CARD_EVENT((count != 0), sd_SD_ReadSectors, (count << 24) + ((sector/((CardType & CT_BLOCK) ? 1 : 512)) & 0x00FFFFFF));
|
||||
|
||||
return count ? -1 : 0;
|
||||
}
|
||||
|
||||
DRESULT disk_read (
|
||||
BYTE drv, /* Physical drive number (0) */
|
||||
BYTE *buff, /* Pointer to the data buffer to store read data */
|
||||
DWORD sector, /* Start sector number (LBA) */
|
||||
UINT count /* Sector count (1..255) */
|
||||
)
|
||||
{
|
||||
if (drv || !count) return RES_PARERR;
|
||||
if (Stat & STA_NOINIT) return RES_NOTRDY;
|
||||
int8_t res = SD_ReadSectors(buff, sector, count);
|
||||
TRACE_SD_CARD_EVENT((res != 0), sd_disk_read, (count << 24) + (sector & 0x00FFFFFF));
|
||||
return (res != 0) ? RES_ERROR : RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
int8_t SD_WriteSectors(const uint8_t * buff, uint32_t sector, uint32_t count)
|
||||
{
|
||||
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
|
||||
|
||||
if (count == 1) { /* Single block write */
|
||||
if (send_cmd(CMD24, sector) == 0) { /* WRITE_BLOCK */
|
||||
if (xmit_datablock(buff, 0xFE)) {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
spi_reset();
|
||||
}
|
||||
}
|
||||
else { /* Multiple block write */
|
||||
if (CardType & CT_SDC) send_cmd(ACMD23, count);
|
||||
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
|
||||
do {
|
||||
if (!xmit_datablock(buff, 0xFC)) break;
|
||||
buff += 512;
|
||||
} while (--count);
|
||||
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
|
||||
count = 1;
|
||||
}
|
||||
else {
|
||||
spi_reset();
|
||||
}
|
||||
}
|
||||
release_spi();
|
||||
TRACE_SD_CARD_EVENT((count != 0), sd_SD_WriteSectors, (count << 24) + ((sector/((CardType & CT_BLOCK) ? 1 : 512)) & 0x00FFFFFF));
|
||||
|
||||
return count ? -1 : 0;
|
||||
}
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE drv, /* Physical drive number (0) */
|
||||
const BYTE *buff, /* Pointer to the data to be written */
|
||||
DWORD sector, /* Start sector number (LBA) */
|
||||
UINT count /* Sector count (1..255) */
|
||||
)
|
||||
{
|
||||
if (drv || !count) return RES_PARERR;
|
||||
if (Stat & STA_NOINIT) return RES_NOTRDY;
|
||||
if (Stat & STA_PROTECT) return RES_WRPRT;
|
||||
int8_t res = SD_WriteSectors(buff, sector, count);
|
||||
TRACE_SD_CARD_EVENT((res != 0), sd_disk_write, (count << 24) + (sector & 0x00FFFFFF));
|
||||
return (res != 0) ? RES_ERROR : RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE drv, /* Physical drive number (0) */
|
||||
BYTE ctrl, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
BYTE n, csd[16], *ptr = (BYTE *)buff;
|
||||
WORD csize;
|
||||
|
||||
if (drv) return RES_PARERR;
|
||||
|
||||
res = RES_ERROR;
|
||||
|
||||
if (ctrl == CTRL_POWER) {
|
||||
switch (*ptr) {
|
||||
case 0: /* Sub control code == 0 (POWER_OFF) */
|
||||
if (chk_power())
|
||||
power_off(); /* Power off */
|
||||
res = RES_OK;
|
||||
break;
|
||||
case 1: /* Sub control code == 1 (POWER_ON) */
|
||||
power_on(); /* Power on */
|
||||
res = RES_OK;
|
||||
break;
|
||||
case 2: /* Sub control code == 2 (POWER_GET) */
|
||||
*(ptr+1) = (BYTE)chk_power();
|
||||
res = RES_OK;
|
||||
break;
|
||||
default :
|
||||
res = RES_PARERR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Stat & STA_NOINIT) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
switch (ctrl) {
|
||||
case CTRL_SYNC : /* Make sure that no pending write process */
|
||||
SD_SELECT();
|
||||
if (wait_ready() == 0xFF) {
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_CTRL_SYNC, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
|
||||
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
|
||||
if ((csd[0] >> 6) == 1) { /* SDC version 2.00 */
|
||||
csize = csd[9] + ((WORD)csd[8] << 8) + 1;
|
||||
*(DWORD*)buff = (DWORD)csize << 10;
|
||||
}
|
||||
else { /* SDC version 1.XX or MMC*/
|
||||
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
|
||||
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
|
||||
*(DWORD*)buff = (DWORD)csize << (n - 9);
|
||||
}
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_GET_SECTOR_COUNT, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */
|
||||
*(WORD*)buff = 512;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
|
||||
if (CardType & CT_SD2) { /* SDC version 2.00 */
|
||||
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
|
||||
rcvr_spi();
|
||||
if (rcvr_datablock(csd, 16)) { /* Read partial block */
|
||||
for (n = 64 - 16; n; n--) rcvr_spi(); /* Purge trailing data */
|
||||
*(DWORD*)buff = 16UL << (csd[10] >> 4);
|
||||
res = RES_OK;
|
||||
}
|
||||
}
|
||||
} else { /* SDC version 1.XX or MMC */
|
||||
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
|
||||
if (CardType & CT_SD1) { /* SDC version 1.XX */
|
||||
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
|
||||
} else { /* MMC */
|
||||
*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
|
||||
}
|
||||
res = RES_OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MMC_GET_TYPE : /* Get card type flags (1 byte) */
|
||||
*ptr = CardType;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */
|
||||
if (send_cmd(CMD9, 0) == 0 /* READ_CSD */
|
||||
&& rcvr_datablock(ptr, 16)) {
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_MMC_GET_CSD, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */
|
||||
if (send_cmd(CMD10, 0) == 0 /* READ_CID */
|
||||
&& rcvr_datablock(ptr, 16)) {
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_MMC_GET_CID, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */
|
||||
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */
|
||||
for (n = 4; n; n--) *ptr++ = rcvr_spi();
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_MMC_GET_OCR, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case MMC_GET_SDSTAT : /* Receive SD status as a data block (64 bytes) */
|
||||
if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */
|
||||
rcvr_spi();
|
||||
if (rcvr_datablock(ptr, 64)) {
|
||||
res = RES_OK;
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_MMC_GET_SDSTAT_1, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE_SD_CARD_EVENT(1, sd_disk_ioctl_MMC_GET_SDSTAT_2, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
res = RES_PARERR;
|
||||
}
|
||||
|
||||
release_spi();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Device Timer Interrupt Procedure (Platform dependent) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* This function must be called in period of 10ms */
|
||||
|
||||
void sdPoll10ms()
|
||||
{
|
||||
static DWORD pv;
|
||||
DWORD ns;
|
||||
BYTE n, s;
|
||||
|
||||
|
||||
n = Timer1; /* 100Hz decrement timers */
|
||||
if (n) Timer1 = --n;
|
||||
n = Timer2;
|
||||
if (n) Timer2 = --n;
|
||||
|
||||
ns = pv;
|
||||
pv = socket_is_empty() | socket_is_write_protected(); /* Sample socket switch */
|
||||
|
||||
if (ns == pv) { /* Have contacts stabled? */
|
||||
s = Stat;
|
||||
|
||||
if (pv & socket_state_mask_wp) /* WP is H (write protected) */
|
||||
s |= STA_PROTECT;
|
||||
else /* WP is L (write enabled) */
|
||||
s &= ~STA_PROTECT;
|
||||
|
||||
if (pv & socket_state_mask_cp) /* INS = H (Socket empty) */
|
||||
s |= (STA_NODISK | STA_NOINIT);
|
||||
else /* INS = L (Card inserted) */
|
||||
s &= ~STA_NODISK;
|
||||
|
||||
Stat = s;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t SD_GetCardType()
|
||||
{
|
||||
return CardType;
|
||||
}
|
||||
@@ -2575,41 +2575,32 @@
|
||||
#endif
|
||||
|
||||
// SD - SPI2
|
||||
#define SD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1)
|
||||
#define SD_RCC_APB1Periph RCC_APB1Periph_SPI2
|
||||
#if defined(RADIO_T20)
|
||||
// Using chip, so no detect
|
||||
#else
|
||||
#define SD_PRESENT_GPIO GPIOD
|
||||
#if defined(PCBXLITE) || defined(PCBX9LITE)
|
||||
#define SD_PRESENT_GPIO_PIN GPIO_Pin_10 // PD.10
|
||||
#define SD_PRESENT_GPIO_PIN LL_GPIO_PIN_10 // PD.10
|
||||
#elif defined(RADIO_COMMANDO8)
|
||||
#define SD_PRESENT_GPIO_PIN GPIO_Pin_8 // PD.08
|
||||
#define SD_PRESENT_GPIO_PIN LL_GPIO_PIN_8 // PD.08
|
||||
#else
|
||||
#define SD_PRESENT_GPIO_PIN GPIO_Pin_9 // PD.09
|
||||
#define SD_PRESENT_GPIO_PIN LL_GPIO_PIN_9 // PD.09
|
||||
#endif
|
||||
#endif
|
||||
#define SD_GPIO GPIOB
|
||||
#define SD_GPIO_PIN_CS GPIO_Pin_12 // PB.12
|
||||
#define SD_GPIO_PIN_SCK GPIO_Pin_13 // PB.13
|
||||
#define SD_GPIO_PIN_MISO GPIO_Pin_14 // PB.14
|
||||
#define SD_GPIO_PIN_MOSI GPIO_Pin_15 // PB.15
|
||||
#define SD_GPIO_AF GPIO_AF_SPI2
|
||||
#define SD_GPIO_PinSource_CS GPIO_PinSource12
|
||||
#define SD_GPIO_PinSource_SCK GPIO_PinSource13
|
||||
#define SD_GPIO_PinSource_MISO GPIO_PinSource14
|
||||
#define SD_GPIO_PinSource_MOSI GPIO_PinSource15
|
||||
#define SD_SPI SPI2
|
||||
#define SD_SPI_BaudRatePrescaler SPI_BaudRatePrescaler_4 // 10.5<20MHZ, make sure < 20MHZ
|
||||
|
||||
#if !defined(BOOT)
|
||||
#define SD_USE_DMA // Enable the DMA for SD
|
||||
#define SD_DMA_Stream_SPI_RX DMA1_Stream3
|
||||
#define SD_DMA_Stream_SPI_TX DMA1_Stream4
|
||||
#define SD_DMA_FLAG_SPI_TC_RX DMA_FLAG_TCIF3
|
||||
#define SD_DMA_FLAG_SPI_TC_TX DMA_FLAG_TCIF4
|
||||
#define SD_DMA_Channel_SPI DMA_Channel_0
|
||||
#endif
|
||||
#define STORAGE_USE_SDCARD_SPI
|
||||
|
||||
#define SD_GPIO GPIOB
|
||||
#define SD_GPIO_PIN_CS LL_GPIO_PIN_12 // PB.12
|
||||
#define SD_GPIO_PIN_SCK LL_GPIO_PIN_13 // PB.13
|
||||
#define SD_GPIO_PIN_MISO LL_GPIO_PIN_14 // PB.14
|
||||
#define SD_GPIO_PIN_MOSI LL_GPIO_PIN_15 // PB.15
|
||||
|
||||
#define SD_SPI SPI2
|
||||
#define SD_SPI_DMA DMA1
|
||||
#define SD_SPI_DMA_RX_STREAM LL_DMA_STREAM_3
|
||||
#define SD_SPI_DMA_TX_STREAM LL_DMA_STREAM_4
|
||||
#define SD_SPI_DMA_CHANNEL LL_DMA_CHANNEL_0
|
||||
|
||||
// Audio
|
||||
#define AUDIO_RCC_APB1Periph (RCC_APB1Periph_TIM6 | RCC_APB1Periph_DAC)
|
||||
@@ -2818,6 +2809,8 @@
|
||||
// 2MHz Timer
|
||||
#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7
|
||||
#define TIMER_2MHz_TIMER TIM7
|
||||
#define TIMER_2MHz_IRQn TIM7_IRQn
|
||||
#define TIMER_2MHz_IRQHandler TIM7_IRQHandler
|
||||
|
||||
// Mixer scheduler timer
|
||||
#define MIXER_SCHEDULER_TIMER_RCC_APB1Periph RCC_APB1Periph_TIM12
|
||||
|
||||
Vendored
-8
@@ -34,14 +34,6 @@ DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
#if defined(DISK_CACHE)
|
||||
DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count);
|
||||
DRESULT __disk_write(BYTE drv, const BYTE * buff, DWORD sector, UINT count);
|
||||
#else
|
||||
#define __disk_read disk_read
|
||||
#define __disk_write disk_write
|
||||
#endif
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Authors (alphabetical order)
|
||||
* - Andre Bernet <bernet.andre@gmail.com>
|
||||
* - Andreas Weitl
|
||||
* - Bertrand Songis <bsongis@gmail.com>
|
||||
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
|
||||
* - Cameron Weeks <th9xer@gmail.com>
|
||||
* - Erez Raviv
|
||||
* - Gabriel Birkus
|
||||
* - Jean-Pierre Parisy
|
||||
* - Karl Szmutny
|
||||
* - Michael Blandford
|
||||
* - Michal Hlavinka
|
||||
* - Pat Mackenzie
|
||||
* - Philip Moss
|
||||
* - Rob Thomson
|
||||
* - Romolo Manfredini <romolo.manfredini@gmail.com>
|
||||
* - Thomas Husterer
|
||||
*
|
||||
* opentx is based on code named
|
||||
* gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/,
|
||||
* er9x by Erez Raviv: http://code.google.com/p/er9x/,
|
||||
* and the original (and ongoing) project by
|
||||
* Thomas Husterer, th9x: http://code.google.com/p/th9x/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "integer.h"
|
||||
#include "../rtc.h"
|
||||
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
struct gtm t;
|
||||
|
||||
gettime(&t);
|
||||
|
||||
/* Pack date and time into a DWORD variable */
|
||||
return ((DWORD)(t.tm_year - 80) << 25)
|
||||
| ((uint32_t)(t.tm_mon+1) << 21)
|
||||
| ((uint32_t)t.tm_mday << 16)
|
||||
| ((uint32_t)t.tm_hour << 11)
|
||||
| ((uint32_t)t.tm_min << 5)
|
||||
| ((uint32_t)t.tm_sec >> 1);
|
||||
}
|
||||
+12
-8
@@ -147,7 +147,9 @@ int8_t SCSI_ProcessCmd(USB_OTG_CORE_HANDLE *pdev,
|
||||
return SCSI_StartStopUnit(lun, params);
|
||||
|
||||
case SCSI_ALLOW_MEDIUM_REMOVAL:
|
||||
return SCSI_AllowRemoval( lun, params); // ST lib 2.2.0 uses SCSI_StartStopUnit which doesn't work // modified by OpenTX
|
||||
// Modified by OpenTX:
|
||||
// ST lib 2.2.0 uses SCSI_StartStopUnit which doesn't work
|
||||
return SCSI_AllowRemoval(lun, params);
|
||||
|
||||
case SCSI_MODE_SENSE6:
|
||||
return SCSI_ModeSense6 (lun, params);
|
||||
@@ -436,16 +438,18 @@ void SCSI_SenseCode(uint8_t lun, uint8_t sKey, uint8_t ASC)
|
||||
|
||||
extern uint8_t lunReady[]; // modified by OpenTX
|
||||
|
||||
#if defined(FWDRIVE)
|
||||
#define _MAX_LUN 2
|
||||
#else
|
||||
#define _MAX_LUN 1
|
||||
#endif
|
||||
|
||||
static int8_t SCSI_StartStopUnit(uint8_t lun, uint8_t *params)
|
||||
{
|
||||
MSC_BOT_DataLen = 0;
|
||||
|
||||
#if defined(BOOT) // modified by OpenTX
|
||||
if (lun < 2)
|
||||
#else
|
||||
if (lun < 1)
|
||||
#endif
|
||||
{
|
||||
|
||||
/* modified by OpenTX */
|
||||
if (lun < _MAX_LUN) {
|
||||
if (params[4] & 1) {
|
||||
// lun to be active
|
||||
lunReady[lun] = 1 ;
|
||||
|
||||
@@ -176,7 +176,6 @@ class ADCInputParser:
|
||||
adc.gpio_pin_mosi = self.hw_defs['ADC_SPI_GPIO_PIN_MOSI']
|
||||
adc.gpio_pin_sck = self.hw_defs['ADC_SPI_GPIO_PIN_SCK']
|
||||
adc.gpio_pin_cs = self.hw_defs['ADC_SPI_GPIO_PIN_CS']
|
||||
adc.gpio_af = self.hw_defs['ADC_SPI_GPIO_AF']
|
||||
adcs.append(adc)
|
||||
|
||||
adc_main = self._parse_adc('MAIN', 'ADC_MAIN', 'ADC')
|
||||
|
||||
@@ -49,11 +49,13 @@ static const stm32_spi_adc_t _ADC_spi[] = {
|
||||
{% set inputs = adc_inputs.inputs | selectattr('adc', '==', adc.name) | selectattr('channel','defined') %}
|
||||
{% set input_count = inputs | list | count %}
|
||||
{
|
||||
{{ adc.adc }},
|
||||
{{ adc.gpio }},
|
||||
{{ adc.gpio_pin_miso }} | {{ adc.gpio_pin_mosi }} | {{ adc.gpio_pin_sck }},
|
||||
{{ adc.gpio_af }},
|
||||
{{ adc.gpio_pin_cs }},
|
||||
.spi = {
|
||||
{{ adc.adc }},
|
||||
{{ adc.gpio }},
|
||||
{{ adc.gpio_pin_miso }} | {{ adc.gpio_pin_mosi }} | {{ adc.gpio_pin_sck }},
|
||||
{{ adc.gpio }},
|
||||
{{ adc.gpio_pin_cs }},
|
||||
},
|
||||
_ADC_{{ adc.name }}_channels,
|
||||
{{ input_count }}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user