Fix and add STM32 SDIO DMA (#21476)

This commit is contained in:
Victor Oliveira 2021-03-29 23:52:30 -03:00 committed by Scott Lahteine
parent f18da95d38
commit 9902e6fb9f
2 changed files with 199 additions and 125 deletions

View File

@ -36,9 +36,10 @@
// use USB drivers // use USB drivers
extern "C" { int8_t SD_MSC_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); extern "C" {
int8_t SD_MSC_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); int8_t SD_MSC_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
extern SD_HandleTypeDef hsd; int8_t SD_MSC_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
extern SD_HandleTypeDef hsd;
} }
bool SDIO_Init() { bool SDIO_Init() {
@ -75,7 +76,18 @@
#error "ERROR - Only STM32F103xE, STM32F103xG, STM32F4xx or STM32F7xx CPUs supported" #error "ERROR - Only STM32F103xE, STM32F103xG, STM32F4xx or STM32F7xx CPUs supported"
#endif #endif
// Fixed
#define SDIO_D0_PIN PC8
#define SDIO_D1_PIN PC9
#define SDIO_D2_PIN PC10
#define SDIO_D3_PIN PC11
#define SDIO_CK_PIN PC12
#define SDIO_CMD_PIN PD2
SD_HandleTypeDef hsd; // create SDIO structure SD_HandleTypeDef hsd; // create SDIO structure
// F4 support one dma for RX and another for TX.
// But Marlin will never do read and write at same time, so we use always one dma for both.
DMA_HandleTypeDef hdma_sdio;
/* /*
SDIO_INIT_CLK_DIV is 118 SDIO_INIT_CLK_DIV is 118
@ -96,12 +108,12 @@
// Target Clock, configurable. Default is 18MHz, from STM32F1 // Target Clock, configurable. Default is 18MHz, from STM32F1
#ifndef SDIO_CLOCK #ifndef SDIO_CLOCK
#define SDIO_CLOCK 18000000 /* 18 MHz */ #define SDIO_CLOCK 18000000 // 18 MHz
#endif #endif
// SDIO retries, configurable. Default is 3, from STM32F1 // SDIO retries, configurable. Default is 3, from STM32F1
#ifndef SDIO_READ_RETRIES #ifndef SDIO_READ_RETRIES
#define SDIO_READ_RETRIES 3 #define SDIO_READ_RETRIES 3
#endif #endif
// SDIO Max Clock (naming from STM Manual, don't change) // SDIO Max Clock (naming from STM Manual, don't change)
@ -120,24 +132,21 @@
} }
void go_to_transfer_speed() { void go_to_transfer_speed() {
SD_InitTypeDef Init;
/* Default SDIO peripheral configuration for SD card initialization */ /* Default SDIO peripheral configuration for SD card initialization */
Init.ClockEdge = hsd.Init.ClockEdge; hsd.Init.ClockEdge = hsd.Init.ClockEdge;
Init.ClockBypass = hsd.Init.ClockBypass; hsd.Init.ClockBypass = hsd.Init.ClockBypass;
Init.ClockPowerSave = hsd.Init.ClockPowerSave; hsd.Init.ClockPowerSave = hsd.Init.ClockPowerSave;
Init.BusWide = hsd.Init.BusWide; hsd.Init.BusWide = hsd.Init.BusWide;
Init.HardwareFlowControl = hsd.Init.HardwareFlowControl; hsd.Init.HardwareFlowControl = hsd.Init.HardwareFlowControl;
Init.ClockDiv = clock_to_divider(SDIO_CLOCK); hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK);
/* Initialize SDIO peripheral interface with default configuration */ /* Initialize SDIO peripheral interface with default configuration */
SDIO_Init(hsd.Instance, Init); SDIO_Init(hsd.Instance, hsd.Init);
} }
void SD_LowLevel_Init(void) { void SD_LowLevel_Init(void) {
uint32_t tempreg; uint32_t tempreg;
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE(); //enable GPIO clocks __HAL_RCC_GPIOC_CLK_ENABLE(); //enable GPIO clocks
__HAL_RCC_GPIOD_CLK_ENABLE(); //enable GPIO clocks __HAL_RCC_GPIOD_CLK_ENABLE(); //enable GPIO clocks
@ -163,11 +172,45 @@
GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
#if DISABLED(STM32F1xx) // Setup DMA
// TODO: use __HAL_RCC_SDIO_RELEASE_RESET() and __HAL_RCC_SDIO_CLK_ENABLE(); #if defined(STM32F1xx)
RCC->APB2RSTR &= ~RCC_APB2RSTR_SDIORST_Msk; // take SDIO out of reset hdma_sdio.Init.Mode = DMA_NORMAL;
RCC->APB2ENR |= RCC_APB2RSTR_SDIORST_Msk; // enable SDIO clock hdma_sdio.Instance = DMA2_Channel4;
// Enable the DMA2 Clock HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
#elif defined(STM32F4xx)
hdma_sdio.Init.Mode = DMA_PFCTRL;
hdma_sdio.Instance = DMA2_Stream3;
hdma_sdio.Init.Channel = DMA_CHANNEL_4;
hdma_sdio.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_sdio.Init.MemBurst = DMA_MBURST_INC4;
hdma_sdio.Init.PeriphBurst = DMA_PBURST_INC4;
HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
#endif
HAL_NVIC_EnableIRQ(SDIO_IRQn);
hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
__HAL_LINKDMA(&hsd, hdmarx, hdma_sdio);
__HAL_LINKDMA(&hsd, hdmatx, hdma_sdio);
#if defined(STM32F1xx)
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
#else
__HAL_RCC_SDIO_FORCE_RESET();
delay(2);
__HAL_RCC_SDIO_RELEASE_RESET();
delay(2);
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_DMA2_FORCE_RESET();
delay(2);
__HAL_RCC_DMA2_RELEASE_RESET();
delay(2);
__HAL_RCC_DMA2_CLK_ENABLE();
#endif #endif
//Initialize the SDIO (with initial <400Khz Clock) //Initialize the SDIO (with initial <400Khz Clock)
@ -179,6 +222,7 @@
// Power up the SDIO // Power up the SDIO
SDIO_PowerState_ON(SDIO); SDIO_PowerState_ON(SDIO);
hsd.Instance = SDIO;
} }
void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { // application specific init void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { // application specific init
@ -222,107 +266,82 @@
if (!status) break; if (!status) break;
if (!--retry_Cnt) return false; // return failing status if retries are exhausted if (!--retry_Cnt) return false; // return failing status if retries are exhausted
} }
go_to_transfer_speed();
} }
#endif #endif
return true; return true;
} }
/*
void init_SDIO_pins(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// SDIO GPIO Configuration static bool SDIO_ReadWriteBlock_DMA(uint32_t block, const uint8_t *src, uint8_t *dst) {
// PC8 ------> SDIO_D0 if(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) return false;
// PC12 ------> SDIO_CK
// PD2 ------> SDIO_CMD
GPIO_InitStruct.Pin = GPIO_PIN_8; TERN_(USE_WATCHDOG, HAL_watchdog_refresh());
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_12; HAL_StatusTypeDef ret;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; if (src) {
GPIO_InitStruct.Pull = GPIO_NOPULL; hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_DMA_Init(&hdma_sdio);
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; ret = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)src, block, 1);
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); }
else {
GPIO_InitStruct.Pin = GPIO_PIN_2; hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; HAL_DMA_Init(&hdma_sdio);
GPIO_InitStruct.Pull = GPIO_NOPULL; ret = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)dst, block, 1);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
*/
//bool SDIO_init() { return (bool) (SD_SDIO_Init() ? 1 : 0);}
//bool SDIO_Init_C() { return (bool) (SD_SDIO_Init() ? 1 : 0);}
bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
hsd.Instance = SDIO;
uint8_t retryCnt = SDIO_READ_RETRIES;
bool status;
for (;;) {
TERN_(USE_WATCHDOG, HAL_watchdog_refresh());
status = (bool) HAL_SD_ReadBlocks(&hsd, (uint8_t*)dst, block, 1, 1000); // read one 512 byte block with 500mS timeout
status |= (bool) HAL_SD_GetCardState(&hsd); // make sure all is OK
if (!status) break; // return passing status
if (!--retryCnt) break; // return failing status if retries are exhausted
} }
return status;
/* if (ret != HAL_OK) {
return (bool) ((status_read | status_card) ? 1 : 0); HAL_DMA_Abort_IT(&hdma_sdio);
HAL_DMA_DeInit(&hdma_sdio);
if (SDIO_GetCardState() != SDIO_CARD_TRANSFER) return false;
if (blockAddress >= SdCard.LogBlockNbr) return false;
if ((0x03 & (uint32_t)data)) return false; // misaligned data
if (SdCard.CardType != CARD_SDHC_SDXC) { blockAddress *= 512U; }
if (!SDIO_CmdReadSingleBlock(blockAddress)) {
SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS);
dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL);
return false; return false;
} }
while (!SDIO_GET_FLAG(SDIO_STA_DATAEND | SDIO_STA_TRX_ERROR_FLAGS)) {} uint32_t timeout = millis() + 500;
// Wait the transfer
dma_disable(SDIO_DMA_DEV, SDIO_DMA_CHANNEL); while (hsd.State != HAL_SD_STATE_READY) {
if (millis() > timeout) {
if (SDIO->STA & SDIO_STA_RXDAVL) { HAL_DMA_Abort_IT(&hdma_sdio);
while (SDIO->STA & SDIO_STA_RXDAVL) (void)SDIO->FIFO; HAL_DMA_DeInit(&hdma_sdio);
SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS); return false;
return false; }
} }
if (SDIO_GET_FLAG(SDIO_STA_TRX_ERROR_FLAGS)) { while (__HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_sdio)) != 0
SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS); || __HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_sdio)) != 0) { /* nada */ }
return false;
} HAL_DMA_Abort_IT(&hdma_sdio);
SDIO_CLEAR_FLAG(SDIO_ICR_CMD_FLAGS | SDIO_ICR_DATA_FLAGS); HAL_DMA_DeInit(&hdma_sdio);
*/
timeout = millis() + 500;
while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
if (millis() > timeout) return false;
return true; return true;
} }
bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) { bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
hsd.Instance = SDIO; uint8_t retries = SDIO_READ_RETRIES;
uint8_t retryCnt = SDIO_READ_RETRIES; while (retries--) if (SDIO_ReadWriteBlock_DMA(block, NULL, dst)) return true;
bool status; return false;
for (;;) {
status = (bool) HAL_SD_WriteBlocks(&hsd, (uint8_t*)src, block, 1, 500); // write one 512 byte block with 500mS timeout
status |= (bool) HAL_SD_GetCardState(&hsd); // make sure all is OK
if (!status) break; // return passing status
if (!--retryCnt) break; // return failing status if retries are exhausted
}
return status;
} }
bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
uint8_t retries = SDIO_READ_RETRIES;
while (retries--) if (SDIO_ReadWriteBlock_DMA(block, src, NULL)) return true;
return false;
}
#if defined(STM32F1xx)
#define DMA_IRQ_HANDLER DMA2_Channel4_5_IRQHandler
#elif defined(STM32F4xx)
#define DMA_IRQ_HANDLER DMA2_Stream3_IRQHandler
#else
#error "Unknown STM32 architecture."
#endif
extern "C" void SDIO_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); }
extern "C" void DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&hdma_sdio); }
#endif // !USBD_USE_CDC_COMPOSITE #endif // !USBD_USE_CDC_COMPOSITE
#endif // SDIO_SUPPORT #endif // SDIO_SUPPORT
#endif // ARDUINO_ARCH_STM32 && !STM32GENERIC #endif // ARDUINO_ARCH_STM32 && !STM32GENERIC

View File

@ -29,6 +29,7 @@
#include "../libs/hex_print.h" #include "../libs/hex_print.h"
#include "../HAL/shared/eeprom_if.h" #include "../HAL/shared/eeprom_if.h"
#include "../HAL/shared/Delay.h" #include "../HAL/shared/Delay.h"
#include "../sd/cardreader.h"
extern void dump_delay_accuracy_check(); extern void dump_delay_accuracy_check();
@ -126,19 +127,19 @@
#endif #endif
case 4: { // D4 Read / Write PIN case 4: { // D4 Read / Write PIN
// const uint8_t pin = parser.byteval('P'); //const bool is_out = parser.boolval('F');
// const bool is_out = parser.boolval('F'), //const uint8_t pin = parser.byteval('P'),
// val = parser.byteval('V', LOW); // val = parser.byteval('V', LOW);
if (parser.seenval('X')) { if (parser.seenval('X')) {
// TODO: Write the hex bytes after the X // TODO: Write the hex bytes after the X
//while (len--) { //while (len--) {
//} //}
} }
else { else {
// while (len--) { //while (len--) {
// TODO: Read bytes from EEPROM //// TODO: Read bytes from EEPROM
// print_hex_byte(eeprom_read_byte(*(adr++)); // print_hex_byte(eeprom_read_byte(adr++));
// } //}
SERIAL_EOL(); SERIAL_EOL();
} }
} break; } break;
@ -155,10 +156,10 @@
//while (len--) {} //while (len--) {}
} }
else { else {
// while (len--) { //while (len--) {
// TODO: Read bytes from EEPROM //// TODO: Read bytes from EEPROM
// print_hex_byte(eeprom_read_byte(adr++)); // print_hex_byte(eeprom_read_byte(adr++));
// } //}
SERIAL_EOL(); SERIAL_EOL();
} }
} break; } break;
@ -186,22 +187,76 @@
SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset."); SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset.");
} break; } break;
#if ENABLED(POSTMORTEM_DEBUGGING) #if ENABLED(SDSUPPORT)
case 451: { // Trigger all kind of faults to test exception catcher
SERIAL_ECHOLNPGM("Disabling heaters");
thermalManager.disable_all_heaters();
delay(1000); // Allow time to print
volatile uint8_t type[5] = { parser.byteval('T', 1) };
// The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code case 101: { // D101 Test SD Write
switch (type[0]) { card.openFileWrite("test.gco");
case 1: default: *(int*)0 = 451; break; // Write at bad address if (!card.isFileOpen()) {
case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM) SERIAL_ECHOLNPAIR("Failed to open test.gco to write.");
case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this) return;
case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction }
__attribute__((aligned(sizeof(size_t)))) uint8_t buf[512];
uint16_t c;
for (c = 0; c < COUNT(buf); c++)
buf[c] = 'A' + (c % ('Z' - 'A'));
c = 1024 * 4;
while (c--) {
TERN_(USE_WATCHDOG, watchdog_refresh());
card.write(buf, COUNT(buf));
}
SERIAL_ECHOLNPGM(" done");
card.closefile();
} break;
case 102: { // D102 Test SD Read
card.openFileRead("test.gco");
if (!card.isFileOpen()) {
SERIAL_ECHOLNPAIR("Failed to open test.gco to read.");
return;
}
__attribute__((aligned(sizeof(size_t)))) uint8_t buf[512];
uint16_t c = 1024 * 4;
while (c--) {
TERN_(USE_WATCHDOG, watchdog_refresh());
card.read(buf, COUNT(buf));
bool error = false;
for (uint16_t i = 0; i < COUNT(buf); i++) {
if (buf[i] != ('A' + (i % ('Z' - 'A')))) {
error = true;
break;
}
}
if (error) {
SERIAL_ECHOLNPGM(" Read error!");
break;
}
}
SERIAL_ECHOLNPGM(" done");
card.closefile();
} break;
#endif // SDSUPPORT
#if ENABLED(POSTMORTEM_DEBUGGING)
case 451: { // Trigger all kind of faults to test exception catcher
SERIAL_ECHOLNPGM("Disabling heaters");
thermalManager.disable_all_heaters();
delay(1000); // Allow time to print
volatile uint8_t type[5] = { parser.byteval('T', 1) };
// The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code
switch (type[0]) {
case 1: default: *(int*)0 = 451; break; // Write at bad address
case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM)
case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this)
case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction
}
break;
} }
break;
}
#endif #endif
} }
} }