0
0
Fork 0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2025-03-15 02:36:19 +00:00

🐛 HC32 - Add SERIAL_DMA, fix SDIO and MEATPACK (#26845)

* fix meatpack on hc32

* add support for SERIAL_DMA on HC32

* add additional checks in HC32 HAL

* migrate HC32 HAL to use app_config.h

* fix memory leak in HC32 sdio HAL
https://github.com/MarlinFirmware/Marlin/pull/26845#issuecomment-1980218771

* hc32: fail if both EMERGENCY_PARSER and SERIAL_DMA are enabled
This commit is contained in:
Chris 2024-04-14 20:42:57 +02:00 committed by GitHub
parent 19684f23bc
commit dca6afc26e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 233 additions and 47 deletions

View file

@ -2698,7 +2698,7 @@
* This feature is EXPERIMENTAL so use with caution and test thoroughly.
* Enable this option to receive data on the serial ports via the onboard DMA
* controller for more stable and reliable high-speed serial communication.
* Only some STM32 MCUs are currently supported.
* Support is currently limited to some STM32 MCUs and all HC32 MCUs.
* Note: This has no effect on emulated USB serial ports.
*/
//#define SERIAL_DMA

View file

@ -123,6 +123,11 @@ void MarlinHAL::init() {
// Register min serial
TERN_(POSTMORTEM_DEBUGGING, install_min_serial());
// warn if low memory after init
if (freeMemory() < 1024) {
SERIAL_WARN_MSG("HAL: low memory after init!\n");
}
}
void MarlinHAL::init_board() {}
@ -147,7 +152,31 @@ void MarlinHAL::delay_ms(const int ms) {
delay(ms);
}
void MarlinHAL::idletask() {}
void MarlinHAL::idletask() {
#if ENABLED(MARLIN_DEV_MODE)
// check & print serial RX errors
MSerialT *serials[] = { &MSerial1, &MSerial2 };
for (int serial = 0; serial < 2; serial++) {
usart_receive_error_t err = serials[serial]->getReceiveError();
if (err != usart_receive_error_t::None) {
// "Warning: MSerial[n] RX [Framing|Parity|Overrun] Error"
SERIAL_WARN_START();
SERIAL_ECHOPGM(" MSerial");
SERIAL_ECHO(serial + 1);
SERIAL_ECHOPGM(" RX ");
switch(err) {
case usart_receive_error_t::FramingError: SERIAL_ECHOPGM("Framing"); break;
case usart_receive_error_t::ParityError: SERIAL_ECHOPGM("Parity"); break;
case usart_receive_error_t::OverrunError: SERIAL_ECHOPGM("Overrun"); break;
case usart_receive_error_t::RxDataDropped: SERIAL_ECHOPGM("DataDropped"); break;
default: break;
}
SERIAL_ECHOPGM(" Error");
SERIAL_EOL();
}
}
#endif
}
uint8_t MarlinHAL::get_reset_source() {
// Query reset cause from RMU

View file

@ -46,14 +46,34 @@ constexpr bool serial_handles_emergency(int port) {
//
// Define serial ports
//
#define DEFINE_HWSERIAL_MARLIN(name, n) \
// serial port where RX and TX use IRQs
#define DEFINE_IRQ_SERIAL_MARLIN(name, n) \
MSerialT name(serial_handles_emergency(n), \
&USART##n##_config, \
BOARD_USART##n##_TX_PIN, \
BOARD_USART##n##_RX_PIN);
DEFINE_HWSERIAL_MARLIN(MSerial1, 1);
DEFINE_HWSERIAL_MARLIN(MSerial2, 2);
// serial port where RX uses DMA and TX uses IRQs
// all serial ports use DMA1
// since there are 4 USARTs and 4 DMA channels, we can use the USART number as the DMA channel
#define DEFINE_DMA_SERIAL_MARLIN(name, n) \
MSerialT name(serial_handles_emergency(n), \
&USART##n##_config, \
BOARD_USART##n##_TX_PIN, \
BOARD_USART##n##_RX_PIN, \
M4_DMA1, \
((en_dma_channel_t)(n - 1))); // map USART1 to DMA channel 0, USART2 to DMA channel 1, etc.
#define DEFINE_SERIAL_MARLIN(name, n) TERN(SERIAL_DMA, DEFINE_DMA_SERIAL_MARLIN(name, n), DEFINE_IRQ_SERIAL_MARLIN(name, n))
DEFINE_SERIAL_MARLIN(MSerial1, 1);
DEFINE_SERIAL_MARLIN(MSerial2, 2);
// TODO: remove this warning when SERIAL_DMA has been tested some more
#if ENABLED(SERIAL_DMA)
#warning "SERIAL_DMA may be unstable on HC32F460."
#endif
//
// Serial port assertions

View file

@ -25,17 +25,42 @@
#include <drivers/usart/Usart.h>
// Optionally set uart IRQ priority to reduce overflow errors
// #define UART_IRQ_PRIO 1
//#define UART_RX_IRQ_PRIO 1
//#define UART_TX_IRQ_PRIO 1
//#define UART_RX_DMA_IRQ_PRIO 1
struct MarlinSerial : public Usart {
MarlinSerial(struct usart_config_t *usart_device, gpio_pin_t tx_pin, gpio_pin_t rx_pin) : Usart(usart_device, tx_pin, rx_pin) {}
MarlinSerial(
struct usart_config_t *usart_device,
gpio_pin_t tx_pin,
gpio_pin_t rx_pin
#if ENABLED(SERIAL_DMA)
, M4_DMA_TypeDef *dma_unit = nullptr,
en_dma_channel_t rx_dma_channel = DmaCh0
#endif
) : Usart(usart_device, tx_pin, rx_pin) {
#if ENABLED(SERIAL_DMA)
if (dma_unit != nullptr) {
enableRxDma(dma_unit, rx_dma_channel);
}
#endif
}
#ifdef UART_IRQ_PRIO
#if defined(UART_RX_IRQ_PRIO) || defined(UART_TX_IRQ_PRIO) || defined(UART_RX_DMA_IRQ_PRIO)
void setPriority() {
NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_IRQ_PRIO);
NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_IRQ_PRIO);
NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_IRQ_PRIO);
NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_IRQ_PRIO);
#if defined(UART_RX_IRQ_PRIO)
NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_RX_IRQ_PRIO);
NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_RX_IRQ_PRIO);
#endif
#if defined(UART_TX_IRQ_PRIO)
NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_TX_IRQ_PRIO);
NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_TX_IRQ_PRIO);
#endif
#if defined(UART_RX_DMA_IRQ_PRIO) && ENABLED(SERIAL_DMA)
NVIC_SetPriority(c_dev()->dma.rx.rx_data_available_dma_btc.interrupt_number, UART_RX_DMA_IRQ_PRIO);
#endif
}
void begin(uint32_t baud) {
@ -47,7 +72,12 @@ struct MarlinSerial : public Usart {
Usart::begin(baud, config);
setPriority();
}
#endif
void begin(uint32_t baud, const stc_usart_uart_init_t *config, const bool rxNoiseFilter = true) {
Usart::begin(baud, config, rxNoiseFilter);
setPriority();
}
#endif // UART_RX_IRQ_PRIO || UART_TX_IRQ_PRIO || UART_RX_DMA_IRQ_PRIO
};
typedef Serial1Class<MarlinSerial> MSerialT;

View file

@ -0,0 +1,70 @@
/**
* app_config.h is included by the hc32f460 arduino build script for every source file.
* it is used to configure the arduino core (and ddl) automatically according
* to the settings in Configuration.h and Configuration_adv.h.
*/
#pragma once
#ifndef _HC32_APP_CONFIG_H_
#define _HC32_APP_CONFIG_H_
#include "../../inc/MarlinConfigPre.h"
//
// dev mode
//
#if ENABLED(MARLIN_DEV_MODE)
#define __DEBUG 1
#define __CORE_DEBUG 1
#endif
//
// Fault Handlers and Panic
//
#if ENABLED(POSTMORTEM_DEBUGGING)
// disable arduino core fault handler, as we define our own
#define CORE_DISABLE_FAULT_HANDLER 1
#endif
// force-enable panic handler so that we can use our custom one (in MinSerial)
#define PANIC_ENABLE 1
// use short filenames in ddl debug and core panic output
#define __DEBUG_SHORT_FILENAMES 1
#define __PANIC_SHORT_FILENAMES 1
// omit panic messages in core panic output
#define __OMIT_PANIC_MESSAGE 1
//
// Usart
//
// disable serial globals (Serial1, Serial2, ...), as we define our own
#define DISABLE_SERIAL_GLOBALS 1
// increase the size of the Usart buffers (both RX and TX)
// NOTE:
// the heap usage will increase by (SERIAL_BUFFER_SIZE - 64) * "number of serial ports used"
// if running out of heap, the system may become unstable
//#define SERIAL_BUFFER_SIZE 256
// enable support for Usart Clock Divider / Oversampling auto config
#define USART_AUTO_CLKDIV_OS_CONFIG 1
// enable USART_RX_DMA_SUPPORT core option when SERIAL_DMA is enabled
#if ENABLED(SERIAL_DMA)
#define USART_RX_DMA_SUPPORT 1
#endif
//
// Misc.
//
// redirect printf to host serial
#define REDIRECT_PRINTF_TO_SERIAL 1
// FIXME override F_CPU to PCLK1, as marlin freaks out otherwise
#define F_CPU (SYSTEM_CLOCK_FREQUENCIES.pclk1)
#endif // _HC32_APP_CONFIG_H_

View file

@ -20,6 +20,20 @@
*
*/
#pragma once
#include <core_util.h>
#if !defined(ARDUINO_CORE_VERSION_INT) || !defined(GET_VERSION_INT)
// version macros were introduced in arduino core version 1.1.0
// below that version, we polyfill them
#define GET_VERSION_INT(major, minor, patch) ((major * 100000) + (minor * 1000) + patch)
#define ARDUINO_CORE_VERSION_INT GET_VERSION_INT(1, 0, 0)
#endif
#if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0)
// because we use app_config.h introduced in arduino core version 1.1.0, the
// HAL is not compatible with older versions
#error "The HC32 HAL is not compatible with Arduino Core versions < 1.1.0. Consider updating the Arduino Core."
#endif
#ifndef BOARD_XTAL_FREQUENCY
#error "BOARD_XTAL_FREQUENCY is required for HC32F460."
@ -74,3 +88,18 @@
#error "HC32 HAL uses a custom panic handler. Do not define PANIC_USARTx_TX_PIN."
#endif
#endif
#if ENABLED(SERIAL_DMA)
#if !defined(USART_RX_DMA_SUPPORT)
#error "SERIAL_DMA requires USART_RX_DMA_SUPPORT to be enabled in the arduino core."
#endif
// USART_RX_DMA_SUPPORT does not implement core_hook_usart_rx_irq, which is required for the emergency parser
#if ENABLED(EMERGENCY_PARSER)
#error "EMERGENCY_PARSER is not supported with SERIAL_DMA. Please disable either SERIAL_DMA or EMERGENCY_PARSER."
#endif
#if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0)
#error "SERIAL_DMA is not supported with arduino core version < 1.1.0."
#endif
#endif

View file

@ -54,7 +54,7 @@
fn \
}
stc_sd_handle_t *handle;
stc_sd_handle_t *handle = nullptr;
bool SDIO_Init() {
// Configure SDIO pins
@ -66,36 +66,45 @@ bool SDIO_Init() {
GPIO_SetFunc(BOARD_SDIO_CMD, Func_Sdio);
GPIO_SetFunc(BOARD_SDIO_DET, Func_Sdio);
// If a handle is already initialized, free it before creating a new one
// otherwise, we will leak memory, which will eventually crash the system
if (handle != nullptr) {
delete handle->pstcDmaInitCfg;
delete handle->pstcCardInitCfg;
delete handle;
handle = nullptr;
}
// Create DMA configuration
stc_sdcard_dma_init_t *dmaConf = new stc_sdcard_dma_init_t;
dmaConf->DMAx = SDIO_DMA_PERIPHERAL;
dmaConf->enDmaCh = SDIO_DMA_CHANNEL;
// Create card configuration
// This should be a fairly safe configuration for most cards
stc_sdcard_init_t *cardConf = new stc_sdcard_init_t;
cardConf->enBusWidth = SdiocBusWidth4Bit;
cardConf->enClkFreq = SdiocClk400K;
cardConf->enSpeedMode = SdiocNormalSpeedMode;
cardConf->pstcInitCfg = nullptr;
// Create handle in DMA mode
handle = new stc_sd_handle_t;
handle->SDIOCx = SDIO_PERIPHERAL;
handle->enDevMode = SdCardDmaMode;
handle->pstcDmaInitCfg = dmaConf;
// Create card configuration
// This should be a fairly safe configuration for most cards
stc_sdcard_init_t cardConf = {
.enBusWidth = SdiocBusWidth4Bit,
.enClkFreq = SdiocClk400K,
.enSpeedMode = SdiocNormalSpeedMode,
//.pstcInitCfg = NULL,
};
//handle->pstcCardInitCfg = cardConf; // assigned in SDCARD_Init
// Initialize sd card
en_result_t rc = SDCARD_Init(handle, &cardConf);
en_result_t rc = SDCARD_Init(handle, cardConf);
if (rc != Ok) printf("SDIO_Init() error (rc=%u)\n", rc);
return rc == Ok;
}
bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
CORE_ASSERT(handle != NULL, "SDIO not initialized");
CORE_ASSERT(dst != NULL, "SDIO_ReadBlock dst is NULL");
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
CORE_ASSERT(dst != nullptr, "SDIO_ReadBlock dst is NULL", return false);
WITH_RETRY(SDIO_READ_RETRIES, {
en_result_t rc = SDCARD_ReadBlocks(handle, block, 1, dst, SDIO_READ_TIMEOUT);
@ -107,8 +116,8 @@ bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
}
bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
CORE_ASSERT(handle != NULL, "SDIO not initialized");
CORE_ASSERT(src != NULL, "SDIO_WriteBlock src is NULL");
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
CORE_ASSERT(src != nullptr, "SDIO_WriteBlock src is NULL", return false);
WITH_RETRY(SDIO_WRITE_RETRIES, {
en_result_t rc = SDCARD_WriteBlocks(handle, block, 1, (uint8_t *)src, SDIO_WRITE_TIMEOUT);
@ -120,12 +129,12 @@ bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
}
bool SDIO_IsReady() {
CORE_ASSERT(handle != NULL, "SDIO not initialized");
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
return bool(handle->stcCardStatus.READY_FOR_DATA);
}
uint32_t SDIO_GetCardSize() {
CORE_ASSERT(handle != NULL, "SDIO not initialized");
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return 0);
// Multiply number of blocks with block size to get size in bytes
const uint64_t cardSizeBytes = uint64_t(handle->stcSdCardInfo.u32LogBlockNbr) * uint64_t(handle->stcSdCardInfo.u32LogBlockSize);

View file

@ -235,9 +235,11 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
#error "SERIAL_XON_XOFF and SERIAL_STATS_* features not supported on USB-native AVR devices."
#endif
// Serial DMA is only available for some STM32 MCUs
// Serial DMA is only available for some STM32 MCUs and HC32
#if ENABLED(SERIAL_DMA)
#if !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
#if defined(ARDUINO_ARCH_HC32)
// checks for HC32 are located in HAL/HC32/inc/SanityCheck.h
#elif !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
#error "SERIAL_DMA is only available for some STM32 MCUs and requires HAL/STM32."
#elif !defined(HAL_UART_MODULE_ENABLED) || defined(HAL_UART_MODULE_ONLY)
#error "SERIAL_DMA requires STM32 platform HAL UART (without HAL_UART_MODULE_ONLY)."

View file

@ -33,22 +33,14 @@ build_src_filter = ${common.default_src_filter} +<src/HAL/HC32> +<src/HAL/shared
build_type = release
build_flags =
-D ARDUINO_ARCH_HC32
-D REDIRECT_PRINTF_TO_SERIAL # Redirect core-provided printf to host serial
-D F_CPU=SYSTEM_CLOCK_FREQUENCIES.pclk1 # Override F_CPU to PCLK1, as marlin freaks out otherwise...
-D PLATFORM_M997_SUPPORT # Enable M997 command
-D PLATFORM_M997_SUPPORT # Enable M997 command
# note: ddl and arduino debug mode are
# automatically enabled with MARLIN_DEV_MODE
#-D __DEBUG # force DDL debug mode
#-D __CORE_DEBUG # force Arduino core debug mode
# DDL / Arduino Configuration
-D DISABLE_SERIAL_GLOBALS # Disable global Serial objects, we use our own
-D CORE_DISABLE_FAULT_HANDLER # Disable arduino core fault handler (we use our own)
# DDL / Arduino Debug Options
#-D __DEBUG # DDL debug mode
#-D __CORE_DEBUG # Arduino core debug mode
-D PANIC_ENABLE # enable custom panic handlers (in MinSerial)
# options to reduce debug mode footprint (-16K; messages are less verbose)
-D __DEBUG_SHORT_FILENAMES # Use short filenames in DDL debug output
-D __PANIC_SHORT_FILENAMES # Use short filenames in core panic output
-D __OMIT_PANIC_MESSAGE # Omit panic messages in core panic output
# hc32 app configuration file
board_build.app_config = Marlin/src/HAL/HC32/app_config.h
# Drivers and Middleware required by the HC32 HAL
board_build.ddl.ots = true
@ -58,6 +50,11 @@ board_build.ddl.timer0 = true
board_build.ddl.timera = true
board_build.mw.sd_card = true
# extra build flags
board_build.flags.common =
-g3 # Force emit debug symbols to elf. this does not affect the final binary size
-fno-signed-char # Force unsigned chars. this is required for meatpack to work
# Additional flags to reduce binary size
board_build.flags.cpp =
-fno-threadsafe-statics # Disable thread-safe statics (only one core anyway)