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:
Raphael Coeffic
2023-09-23 19:31:56 +10:00
committed by GitHub
parent 870fe7eca3
commit 87df8a4f2c
61 changed files with 2697 additions and 2628 deletions
+1 -8
View File
@@ -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
View File
@@ -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, &sectorCount) != RES_OK) {
if (drv->ioctl(0, GET_SECTOR_COUNT, &sectorCount) != 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
View File
@@ -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
View File
@@ -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, &sectors);
}
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
View File
@@ -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);
+1 -1
View File
@@ -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:
+21
View File
@@ -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()
+163
View File
@@ -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
+47
View File
@@ -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);
+80
View File
@@ -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;
}
+40
View File
@@ -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();
+1
View File
@@ -21,6 +21,7 @@
#include "opentx.h"
#include "hal/adc_driver.h"
#include "hal/storage.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
-16
View File
@@ -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);
+1 -4
View File
@@ -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);
}
}
+26 -51
View File
@@ -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) {
+316 -224
View File
@@ -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(&sector_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, &sector_count) != RES_OK) {
auto drv = storageGetDefaultDriver();
if (drv->ioctl(0, GET_SECTOR_COUNT, &sector_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;
}
/**
@@ -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;
+1 -5
View File
@@ -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
-6
View File
@@ -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();
+3
View File
@@ -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
+1 -2
View File
@@ -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
-6
View File
@@ -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();
+3
View File
@@ -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
+2 -2
View File
@@ -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();
+5
View File
@@ -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)
+2 -1
View File
@@ -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)
-2
View File
@@ -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,
-10
View File
@@ -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();
-933
View File
@@ -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;
}
+18 -25
View File
@@ -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
-8
View File
@@ -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 */
-53
View File
@@ -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);
}
@@ -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 ;
-1
View File
@@ -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')
+7 -5
View File
@@ -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 }}
},