Skip to content

Commit dca6afc

Browse files
authored
✨🐛 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 #26845 (comment) * hc32: fail if both EMERGENCY_PARSER and SERIAL_DMA are enabled
1 parent 19684f2 commit dca6afc

9 files changed

+233
-47
lines changed

Marlin/Configuration_adv.h

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

Marlin/src/HAL/HC32/MarlinHAL.cpp

+30-1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ void MarlinHAL::init() {
123123

124124
// Register min serial
125125
TERN_(POSTMORTEM_DEBUGGING, install_min_serial());
126+
127+
// warn if low memory after init
128+
if (freeMemory() < 1024) {
129+
SERIAL_WARN_MSG("HAL: low memory after init!\n");
130+
}
126131
}
127132

128133
void MarlinHAL::init_board() {}
@@ -147,7 +152,31 @@ void MarlinHAL::delay_ms(const int ms) {
147152
delay(ms);
148153
}
149154

150-
void MarlinHAL::idletask() {}
155+
void MarlinHAL::idletask() {
156+
#if ENABLED(MARLIN_DEV_MODE)
157+
// check & print serial RX errors
158+
MSerialT *serials[] = { &MSerial1, &MSerial2 };
159+
for (int serial = 0; serial < 2; serial++) {
160+
usart_receive_error_t err = serials[serial]->getReceiveError();
161+
if (err != usart_receive_error_t::None) {
162+
// "Warning: MSerial[n] RX [Framing|Parity|Overrun] Error"
163+
SERIAL_WARN_START();
164+
SERIAL_ECHOPGM(" MSerial");
165+
SERIAL_ECHO(serial + 1);
166+
SERIAL_ECHOPGM(" RX ");
167+
switch(err) {
168+
case usart_receive_error_t::FramingError: SERIAL_ECHOPGM("Framing"); break;
169+
case usart_receive_error_t::ParityError: SERIAL_ECHOPGM("Parity"); break;
170+
case usart_receive_error_t::OverrunError: SERIAL_ECHOPGM("Overrun"); break;
171+
case usart_receive_error_t::RxDataDropped: SERIAL_ECHOPGM("DataDropped"); break;
172+
default: break;
173+
}
174+
SERIAL_ECHOPGM(" Error");
175+
SERIAL_EOL();
176+
}
177+
}
178+
#endif
179+
}
151180

152181
uint8_t MarlinHAL::get_reset_source() {
153182
// Query reset cause from RMU

Marlin/src/HAL/HC32/MarlinSerial.cpp

+23-3
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,34 @@ constexpr bool serial_handles_emergency(int port) {
4646
//
4747
// Define serial ports
4848
//
49-
#define DEFINE_HWSERIAL_MARLIN(name, n) \
49+
50+
// serial port where RX and TX use IRQs
51+
#define DEFINE_IRQ_SERIAL_MARLIN(name, n) \
5052
MSerialT name(serial_handles_emergency(n), \
5153
&USART##n##_config, \
5254
BOARD_USART##n##_TX_PIN, \
5355
BOARD_USART##n##_RX_PIN);
5456

55-
DEFINE_HWSERIAL_MARLIN(MSerial1, 1);
56-
DEFINE_HWSERIAL_MARLIN(MSerial2, 2);
57+
// serial port where RX uses DMA and TX uses IRQs
58+
// all serial ports use DMA1
59+
// since there are 4 USARTs and 4 DMA channels, we can use the USART number as the DMA channel
60+
#define DEFINE_DMA_SERIAL_MARLIN(name, n) \
61+
MSerialT name(serial_handles_emergency(n), \
62+
&USART##n##_config, \
63+
BOARD_USART##n##_TX_PIN, \
64+
BOARD_USART##n##_RX_PIN, \
65+
M4_DMA1, \
66+
((en_dma_channel_t)(n - 1))); // map USART1 to DMA channel 0, USART2 to DMA channel 1, etc.
67+
68+
#define DEFINE_SERIAL_MARLIN(name, n) TERN(SERIAL_DMA, DEFINE_DMA_SERIAL_MARLIN(name, n), DEFINE_IRQ_SERIAL_MARLIN(name, n))
69+
70+
DEFINE_SERIAL_MARLIN(MSerial1, 1);
71+
DEFINE_SERIAL_MARLIN(MSerial2, 2);
72+
73+
// TODO: remove this warning when SERIAL_DMA has been tested some more
74+
#if ENABLED(SERIAL_DMA)
75+
#warning "SERIAL_DMA may be unstable on HC32F460."
76+
#endif
5777

5878
//
5979
// Serial port assertions

Marlin/src/HAL/HC32/MarlinSerial.h

+38-8
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,42 @@
2525
#include <drivers/usart/Usart.h>
2626

2727
// Optionally set uart IRQ priority to reduce overflow errors
28-
// #define UART_IRQ_PRIO 1
28+
//#define UART_RX_IRQ_PRIO 1
29+
//#define UART_TX_IRQ_PRIO 1
30+
//#define UART_RX_DMA_IRQ_PRIO 1
2931

3032
struct MarlinSerial : public Usart {
31-
MarlinSerial(struct usart_config_t *usart_device, gpio_pin_t tx_pin, gpio_pin_t rx_pin) : Usart(usart_device, tx_pin, rx_pin) {}
33+
MarlinSerial(
34+
struct usart_config_t *usart_device,
35+
gpio_pin_t tx_pin,
36+
gpio_pin_t rx_pin
37+
#if ENABLED(SERIAL_DMA)
38+
, M4_DMA_TypeDef *dma_unit = nullptr,
39+
en_dma_channel_t rx_dma_channel = DmaCh0
40+
#endif
41+
) : Usart(usart_device, tx_pin, rx_pin) {
42+
#if ENABLED(SERIAL_DMA)
43+
if (dma_unit != nullptr) {
44+
enableRxDma(dma_unit, rx_dma_channel);
45+
}
46+
#endif
47+
}
3248

33-
#ifdef UART_IRQ_PRIO
49+
#if defined(UART_RX_IRQ_PRIO) || defined(UART_TX_IRQ_PRIO) || defined(UART_RX_DMA_IRQ_PRIO)
3450
void setPriority() {
35-
NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_IRQ_PRIO);
36-
NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_IRQ_PRIO);
37-
NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_IRQ_PRIO);
38-
NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_IRQ_PRIO);
51+
#if defined(UART_RX_IRQ_PRIO)
52+
NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_RX_IRQ_PRIO);
53+
NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_RX_IRQ_PRIO);
54+
#endif
55+
56+
#if defined(UART_TX_IRQ_PRIO)
57+
NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_TX_IRQ_PRIO);
58+
NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_TX_IRQ_PRIO);
59+
#endif
60+
61+
#if defined(UART_RX_DMA_IRQ_PRIO) && ENABLED(SERIAL_DMA)
62+
NVIC_SetPriority(c_dev()->dma.rx.rx_data_available_dma_btc.interrupt_number, UART_RX_DMA_IRQ_PRIO);
63+
#endif
3964
}
4065

4166
void begin(uint32_t baud) {
@@ -47,7 +72,12 @@ struct MarlinSerial : public Usart {
4772
Usart::begin(baud, config);
4873
setPriority();
4974
}
50-
#endif
75+
76+
void begin(uint32_t baud, const stc_usart_uart_init_t *config, const bool rxNoiseFilter = true) {
77+
Usart::begin(baud, config, rxNoiseFilter);
78+
setPriority();
79+
}
80+
#endif // UART_RX_IRQ_PRIO || UART_TX_IRQ_PRIO || UART_RX_DMA_IRQ_PRIO
5181
};
5282

5383
typedef Serial1Class<MarlinSerial> MSerialT;

Marlin/src/HAL/HC32/app_config.h

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* app_config.h is included by the hc32f460 arduino build script for every source file.
3+
* it is used to configure the arduino core (and ddl) automatically according
4+
* to the settings in Configuration.h and Configuration_adv.h.
5+
*/
6+
#pragma once
7+
#ifndef _HC32_APP_CONFIG_H_
8+
#define _HC32_APP_CONFIG_H_
9+
10+
#include "../../inc/MarlinConfigPre.h"
11+
12+
//
13+
// dev mode
14+
//
15+
#if ENABLED(MARLIN_DEV_MODE)
16+
#define __DEBUG 1
17+
#define __CORE_DEBUG 1
18+
#endif
19+
20+
//
21+
// Fault Handlers and Panic
22+
//
23+
24+
#if ENABLED(POSTMORTEM_DEBUGGING)
25+
// disable arduino core fault handler, as we define our own
26+
#define CORE_DISABLE_FAULT_HANDLER 1
27+
#endif
28+
29+
// force-enable panic handler so that we can use our custom one (in MinSerial)
30+
#define PANIC_ENABLE 1
31+
32+
// use short filenames in ddl debug and core panic output
33+
#define __DEBUG_SHORT_FILENAMES 1
34+
#define __PANIC_SHORT_FILENAMES 1
35+
36+
// omit panic messages in core panic output
37+
#define __OMIT_PANIC_MESSAGE 1
38+
39+
//
40+
// Usart
41+
//
42+
43+
// disable serial globals (Serial1, Serial2, ...), as we define our own
44+
#define DISABLE_SERIAL_GLOBALS 1
45+
46+
// increase the size of the Usart buffers (both RX and TX)
47+
// NOTE:
48+
// the heap usage will increase by (SERIAL_BUFFER_SIZE - 64) * "number of serial ports used"
49+
// if running out of heap, the system may become unstable
50+
//#define SERIAL_BUFFER_SIZE 256
51+
52+
// enable support for Usart Clock Divider / Oversampling auto config
53+
#define USART_AUTO_CLKDIV_OS_CONFIG 1
54+
55+
// enable USART_RX_DMA_SUPPORT core option when SERIAL_DMA is enabled
56+
#if ENABLED(SERIAL_DMA)
57+
#define USART_RX_DMA_SUPPORT 1
58+
#endif
59+
60+
//
61+
// Misc.
62+
//
63+
64+
// redirect printf to host serial
65+
#define REDIRECT_PRINTF_TO_SERIAL 1
66+
67+
// FIXME override F_CPU to PCLK1, as marlin freaks out otherwise
68+
#define F_CPU (SYSTEM_CLOCK_FREQUENCIES.pclk1)
69+
70+
#endif // _HC32_APP_CONFIG_H_

Marlin/src/HAL/HC32/inc/SanityCheck.h

+29
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@
2020
*
2121
*/
2222
#pragma once
23+
#include <core_util.h>
24+
25+
#if !defined(ARDUINO_CORE_VERSION_INT) || !defined(GET_VERSION_INT)
26+
// version macros were introduced in arduino core version 1.1.0
27+
// below that version, we polyfill them
28+
#define GET_VERSION_INT(major, minor, patch) ((major * 100000) + (minor * 1000) + patch)
29+
#define ARDUINO_CORE_VERSION_INT GET_VERSION_INT(1, 0, 0)
30+
#endif
31+
32+
#if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0)
33+
// because we use app_config.h introduced in arduino core version 1.1.0, the
34+
// HAL is not compatible with older versions
35+
#error "The HC32 HAL is not compatible with Arduino Core versions < 1.1.0. Consider updating the Arduino Core."
36+
#endif
2337

2438
#ifndef BOARD_XTAL_FREQUENCY
2539
#error "BOARD_XTAL_FREQUENCY is required for HC32F460."
@@ -74,3 +88,18 @@
7488
#error "HC32 HAL uses a custom panic handler. Do not define PANIC_USARTx_TX_PIN."
7589
#endif
7690
#endif
91+
92+
#if ENABLED(SERIAL_DMA)
93+
#if !defined(USART_RX_DMA_SUPPORT)
94+
#error "SERIAL_DMA requires USART_RX_DMA_SUPPORT to be enabled in the arduino core."
95+
#endif
96+
97+
// USART_RX_DMA_SUPPORT does not implement core_hook_usart_rx_irq, which is required for the emergency parser
98+
#if ENABLED(EMERGENCY_PARSER)
99+
#error "EMERGENCY_PARSER is not supported with SERIAL_DMA. Please disable either SERIAL_DMA or EMERGENCY_PARSER."
100+
#endif
101+
102+
#if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0)
103+
#error "SERIAL_DMA is not supported with arduino core version < 1.1.0."
104+
#endif
105+
#endif

Marlin/src/HAL/HC32/sdio.cpp

+26-17
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
fn \
5555
}
5656

57-
stc_sd_handle_t *handle;
57+
stc_sd_handle_t *handle = nullptr;
5858

5959
bool SDIO_Init() {
6060
// Configure SDIO pins
@@ -66,36 +66,45 @@ bool SDIO_Init() {
6666
GPIO_SetFunc(BOARD_SDIO_CMD, Func_Sdio);
6767
GPIO_SetFunc(BOARD_SDIO_DET, Func_Sdio);
6868

69+
// If a handle is already initialized, free it before creating a new one
70+
// otherwise, we will leak memory, which will eventually crash the system
71+
if (handle != nullptr) {
72+
delete handle->pstcDmaInitCfg;
73+
delete handle->pstcCardInitCfg;
74+
delete handle;
75+
handle = nullptr;
76+
}
77+
6978
// Create DMA configuration
7079
stc_sdcard_dma_init_t *dmaConf = new stc_sdcard_dma_init_t;
7180
dmaConf->DMAx = SDIO_DMA_PERIPHERAL;
7281
dmaConf->enDmaCh = SDIO_DMA_CHANNEL;
7382

83+
// Create card configuration
84+
// This should be a fairly safe configuration for most cards
85+
stc_sdcard_init_t *cardConf = new stc_sdcard_init_t;
86+
cardConf->enBusWidth = SdiocBusWidth4Bit;
87+
cardConf->enClkFreq = SdiocClk400K;
88+
cardConf->enSpeedMode = SdiocNormalSpeedMode;
89+
cardConf->pstcInitCfg = nullptr;
90+
7491
// Create handle in DMA mode
7592
handle = new stc_sd_handle_t;
7693
handle->SDIOCx = SDIO_PERIPHERAL;
7794
handle->enDevMode = SdCardDmaMode;
7895
handle->pstcDmaInitCfg = dmaConf;
79-
80-
// Create card configuration
81-
// This should be a fairly safe configuration for most cards
82-
stc_sdcard_init_t cardConf = {
83-
.enBusWidth = SdiocBusWidth4Bit,
84-
.enClkFreq = SdiocClk400K,
85-
.enSpeedMode = SdiocNormalSpeedMode,
86-
//.pstcInitCfg = NULL,
87-
};
96+
//handle->pstcCardInitCfg = cardConf; // assigned in SDCARD_Init
8897

8998
// Initialize sd card
90-
en_result_t rc = SDCARD_Init(handle, &cardConf);
99+
en_result_t rc = SDCARD_Init(handle, cardConf);
91100
if (rc != Ok) printf("SDIO_Init() error (rc=%u)\n", rc);
92101

93102
return rc == Ok;
94103
}
95104

96105
bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) {
97-
CORE_ASSERT(handle != NULL, "SDIO not initialized");
98-
CORE_ASSERT(dst != NULL, "SDIO_ReadBlock dst is NULL");
106+
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
107+
CORE_ASSERT(dst != nullptr, "SDIO_ReadBlock dst is NULL", return false);
99108

100109
WITH_RETRY(SDIO_READ_RETRIES, {
101110
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) {
107116
}
108117

109118
bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) {
110-
CORE_ASSERT(handle != NULL, "SDIO not initialized");
111-
CORE_ASSERT(src != NULL, "SDIO_WriteBlock src is NULL");
119+
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
120+
CORE_ASSERT(src != nullptr, "SDIO_WriteBlock src is NULL", return false);
112121

113122
WITH_RETRY(SDIO_WRITE_RETRIES, {
114123
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) {
120129
}
121130

122131
bool SDIO_IsReady() {
123-
CORE_ASSERT(handle != NULL, "SDIO not initialized");
132+
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false);
124133
return bool(handle->stcCardStatus.READY_FOR_DATA);
125134
}
126135

127136
uint32_t SDIO_GetCardSize() {
128-
CORE_ASSERT(handle != NULL, "SDIO not initialized");
137+
CORE_ASSERT(handle != nullptr, "SDIO not initialized", return 0);
129138

130139
// Multiply number of blocks with block size to get size in bytes
131140
const uint64_t cardSizeBytes = uint64_t(handle->stcSdCardInfo.u32LogBlockNbr) * uint64_t(handle->stcSdCardInfo.u32LogBlockSize);

Marlin/src/inc/SanityCheck.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,11 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
235235
#error "SERIAL_XON_XOFF and SERIAL_STATS_* features not supported on USB-native AVR devices."
236236
#endif
237237

238-
// Serial DMA is only available for some STM32 MCUs
238+
// Serial DMA is only available for some STM32 MCUs and HC32
239239
#if ENABLED(SERIAL_DMA)
240-
#if !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
240+
#if defined(ARDUINO_ARCH_HC32)
241+
// checks for HC32 are located in HAL/HC32/inc/SanityCheck.h
242+
#elif !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
241243
#error "SERIAL_DMA is only available for some STM32 MCUs and requires HAL/STM32."
242244
#elif !defined(HAL_UART_MODULE_ENABLED) || defined(HAL_UART_MODULE_ONLY)
243245
#error "SERIAL_DMA requires STM32 platform HAL UART (without HAL_UART_MODULE_ONLY)."

0 commit comments

Comments
 (0)