✨ SERIAL_DMA (for some STM32Fx) (#26328)
This commit is contained in:
parent
01aa87c307
commit
b94a335493
|
@ -2649,10 +2649,12 @@
|
|||
//#define FULL_REPORT_TO_HOST_FEATURE // Auto-report the machine status like Grbl CNC
|
||||
#endif
|
||||
|
||||
// Bad Serial-connections can miss a received command by sending an 'ok'
|
||||
// Therefore some clients abort after 30 seconds in a timeout.
|
||||
// Some other clients start sending commands while receiving a 'wait'.
|
||||
// This "wait" is only sent when the buffer is empty. 1 second is a good value here.
|
||||
/**
|
||||
* Bad Serial-connections can miss a received command by sending an 'ok'
|
||||
* Therefore some clients abort after 30 seconds in a timeout.
|
||||
* Some other clients start sending commands while receiving a 'wait'.
|
||||
* This "wait" is only sent when the buffer is empty. 1 second is a good value here.
|
||||
*/
|
||||
//#define NO_TIMEOUTS 1000 // (ms)
|
||||
|
||||
// Some clients will have this feature soon. This could make the NO_TIMEOUTS unnecessary.
|
||||
|
@ -2665,6 +2667,15 @@
|
|||
// For serial echo, the number of digits after the decimal point
|
||||
//#define SERIAL_FLOAT_PRECISION 4
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Note: This has no effect on emulated USB serial ports.
|
||||
*/
|
||||
//#define SERIAL_DMA
|
||||
|
||||
/**
|
||||
* Set the number of proportional font spaces required to fill up a typical character space.
|
||||
* This can help to better align the output of commands like `G29 O` Mesh Output.
|
||||
|
@ -3442,9 +3453,8 @@
|
|||
/**
|
||||
* TWI/I2C BUS
|
||||
*
|
||||
* This feature is an EXPERIMENTAL feature so it shall not be used on production
|
||||
* machines. Enabling this will allow you to send and receive I2C data from slave
|
||||
* devices on the bus.
|
||||
* This feature is EXPERIMENTAL but may be useful for custom I2C peripherals.
|
||||
* Enable this to send and receive I2C data from slave devices on the bus.
|
||||
*
|
||||
* ; Example #1
|
||||
* ; This macro send the string "Marlin" to the slave device with address 0x63 (99)
|
||||
|
|
|
@ -37,10 +37,6 @@
|
|||
#include "../../core/types.h"
|
||||
#include "../../core/serial_hook.h"
|
||||
|
||||
#ifndef SERIAL_PORT
|
||||
#define SERIAL_PORT 0
|
||||
#endif
|
||||
|
||||
#ifndef USBCON
|
||||
|
||||
// The presence of the UBRRH register is used to detect a UART.
|
||||
|
|
|
@ -20,3 +20,7 @@
|
|||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef SERIAL_PORT
|
||||
#define SERIAL_PORT 0
|
||||
#endif
|
||||
|
|
|
@ -30,16 +30,6 @@
|
|||
#endif
|
||||
#include "../../core/serial_hook.h"
|
||||
|
||||
#ifndef SERIAL_PORT
|
||||
#define SERIAL_PORT 0
|
||||
#endif
|
||||
#ifndef RX_BUFFER_SIZE
|
||||
#define RX_BUFFER_SIZE 128
|
||||
#endif
|
||||
#ifndef TX_BUFFER_SIZE
|
||||
#define TX_BUFFER_SIZE 32
|
||||
#endif
|
||||
|
||||
class MarlinSerial : public HardwareSerial<RX_BUFFER_SIZE, TX_BUFFER_SIZE> {
|
||||
public:
|
||||
MarlinSerial(LPC_UART_TypeDef *UARTx) : HardwareSerial<RX_BUFFER_SIZE, TX_BUFFER_SIZE>(UARTx) { }
|
||||
|
|
|
@ -20,3 +20,7 @@
|
|||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef SERIAL_PORT
|
||||
#define SERIAL_PORT 0
|
||||
#endif
|
||||
|
|
|
@ -24,3 +24,10 @@
|
|||
#if DISABLED(NO_SD_HOST_DRIVE)
|
||||
#define HAS_SD_HOST_DRIVE 1
|
||||
#endif
|
||||
|
||||
#ifndef RX_BUFFER_SIZE
|
||||
#define RX_BUFFER_SIZE 128
|
||||
#endif
|
||||
#ifndef TX_BUFFER_SIZE
|
||||
#define TX_BUFFER_SIZE 32
|
||||
#endif
|
||||
|
|
449
Marlin/src/HAL/STM32/HardwareSerial.cpp
Normal file
449
Marlin/src/HAL/STM32/HardwareSerial.cpp
Normal file
|
@ -0,0 +1,449 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
* Copyright (c) 2017 Victor Perez
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// HAL_HardwareSerial Class. Adapted from Arduino HardwareSerial.
|
||||
//
|
||||
|
||||
#include "../platforms.h"
|
||||
|
||||
#ifdef HAL_STM32
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(SERIAL_DMA) && defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY)
|
||||
|
||||
#include <stdio.h>
|
||||
#include "HardwareSerial.h"
|
||||
#include "uart.h"
|
||||
|
||||
// USART/UART PIN MAPPING FOR STM32F0/F1/F2/F4/F7
|
||||
#ifndef PIN_SERIAL1_TX
|
||||
#define PIN_SERIAL1_TX PA9
|
||||
#endif
|
||||
#ifndef PIN_SERIAL1_RX
|
||||
#define PIN_SERIAL1_RX PA10
|
||||
#endif
|
||||
#ifndef PIN_SERIAL2_TX
|
||||
#define PIN_SERIAL2_TX PA2
|
||||
#endif
|
||||
#ifndef PIN_SERIAL2_RX
|
||||
#define PIN_SERIAL2_RX PA3
|
||||
#endif
|
||||
#ifndef PIN_SERIAL3_TX
|
||||
#define PIN_SERIAL3_TX PB10
|
||||
#endif
|
||||
#ifndef PIN_SERIAL3_RX
|
||||
#define PIN_SERIAL3_RX PB11
|
||||
#endif
|
||||
#ifndef PIN_SERIAL4_TX
|
||||
#define PIN_SERIAL4_TX PC10
|
||||
#endif
|
||||
#ifndef PIN_SERIAL4_RX
|
||||
#define PIN_SERIAL4_RX PC11
|
||||
#endif
|
||||
#ifndef PIN_SERIAL5_TX
|
||||
#define PIN_SERIAL5_TX PC12
|
||||
#endif
|
||||
#ifndef PIN_SERIAL5_RX
|
||||
#define PIN_SERIAL5_RX PD2
|
||||
#endif
|
||||
#ifndef PIN_SERIAL6_TX
|
||||
#define PIN_SERIAL6_TX PC6
|
||||
#endif
|
||||
#ifndef PIN_SERIAL6_RX
|
||||
#define PIN_SERIAL6_RX PC7
|
||||
#endif
|
||||
|
||||
// TODO: Get from include file
|
||||
|
||||
#if ANY(STM32F2xx, STM32F4xx, STM32F7xx)
|
||||
|
||||
#define RCC_AHB1Periph_DMA1 ((uint32_t)0x00200000)
|
||||
#define RCC_AHB1Periph_DMA2 ((uint32_t)0x00400000)
|
||||
|
||||
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState) {
|
||||
// Check the parameters
|
||||
assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph));
|
||||
|
||||
assert_param(IS_FUNCTIONAL_STATE(NewState));
|
||||
if (NewState != DISABLE)
|
||||
RCC->AHB1ENR |= RCC_AHB1Periph;
|
||||
else
|
||||
RCC->AHB1ENR &= ~RCC_AHB1Periph;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ANY(STM32F0xx, STM32F1xx)
|
||||
|
||||
#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001)
|
||||
#define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002)
|
||||
|
||||
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) {
|
||||
/* Check the parameters */
|
||||
assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph));
|
||||
assert_param(IS_FUNCTIONAL_STATE(NewState));
|
||||
|
||||
if (NewState != DISABLE)
|
||||
RCC->AHBENR |= RCC_AHBPeriph;
|
||||
else
|
||||
RCC->AHBENR &= ~RCC_AHBPeriph;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// END OF TODO------------------------------------------------------
|
||||
|
||||
// SerialEvent functions are weak, so when the user doesn't define them,
|
||||
// the linker just sets their address to 0 (which is checked below).
|
||||
#ifdef USING_HW_SERIAL1
|
||||
HAL_HardwareSerial HSerial1(USART1);
|
||||
void serialEvent1() __attribute__((weak));
|
||||
#endif
|
||||
#ifdef USING_HW_SERIAL2
|
||||
HAL_HardwareSerial HSerial2(USART2);
|
||||
void serialEvent2() __attribute__((weak));
|
||||
#endif
|
||||
#ifdef USING_HW_SERIAL3
|
||||
HAL_HardwareSerial Serial3(USART3);
|
||||
void serialEvent3() __attribute__((weak));
|
||||
#endif
|
||||
#ifdef USING_HW_SERIAL4
|
||||
#ifdef USART4
|
||||
HAL_HardwareSerial HSerial4(USART4);
|
||||
#else
|
||||
HAL_HardwareSerial HSerial4(UART4);
|
||||
#endif
|
||||
void serialEvent4() __attribute__((weak));
|
||||
#endif
|
||||
#ifdef USING_HW_SERIAL5
|
||||
#ifdef USART5
|
||||
HAL_HardwareSerial HSerial5(USART5);
|
||||
#else
|
||||
HAL_HardwareSerial HSerial5(UART5);
|
||||
#endif
|
||||
void serialEvent5() __attribute__((weak));
|
||||
#endif
|
||||
#ifdef USING_HW_SERIAL6
|
||||
#ifdef USART6
|
||||
HAL_HardwareSerial HSerial5(USART6);
|
||||
#else
|
||||
HAL_HardwareSerial HSerial5(UART6);
|
||||
#endif
|
||||
void serialEvent5() __attribute__((weak));
|
||||
#endif
|
||||
|
||||
// Constructors ////////////////////////////////////////////////////////////////
|
||||
|
||||
HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) {
|
||||
if (peripheral == USART1) {
|
||||
setRx(PIN_SERIAL1_RX);
|
||||
setTx(PIN_SERIAL1_TX);
|
||||
_uart_index = 0;
|
||||
#ifdef DMA2_Stream2
|
||||
RX_DMA = { USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2 };
|
||||
#endif
|
||||
#ifdef DMA1_Channel5
|
||||
RX_DMA = { USART1, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel5 };
|
||||
#endif
|
||||
}
|
||||
else if (peripheral == USART2) {
|
||||
setRx(PIN_SERIAL2_RX);
|
||||
setTx(PIN_SERIAL2_TX);
|
||||
_uart_index = 1;
|
||||
#ifdef DMA1_Stream5
|
||||
RX_DMA = { USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5 };
|
||||
#endif
|
||||
#ifdef DMA1_Channel6
|
||||
RX_DMA = { USART2, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel6 };
|
||||
#endif
|
||||
}
|
||||
else if (peripheral == USART3) {
|
||||
setRx(PIN_SERIAL3_RX);
|
||||
setTx(PIN_SERIAL3_TX);
|
||||
_uart_index = 2;
|
||||
#ifdef DMA1_Stream1
|
||||
RX_DMA = { USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1 };
|
||||
#endif
|
||||
#ifdef DMA1_Channel3 // F0 has no support for UART3, requires system remapping
|
||||
RX_DMA = { USART3, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel3 };
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USART4 // Only F2 / F4 / F7
|
||||
else if (peripheral == USART4) {
|
||||
#ifdef DMA1_Stream2
|
||||
RX_DMA = { USART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
|
||||
#endif
|
||||
setRx(PIN_SERIAL4_RX);
|
||||
setTx(PIN_SERIAL4_TX);
|
||||
_uart_index = 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UART4
|
||||
else if (peripheral == UART4) {
|
||||
#ifdef DMA1_Stream2
|
||||
RX_DMA = { UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
|
||||
#endif
|
||||
#ifdef DMA2_Channel3 // STM32F0xx has only 3 UARTs
|
||||
RX_DMA = { UART4, RCC_AHBPeriph_DMA2, DMA2, DMA2_Channel3 };
|
||||
#endif
|
||||
setRx(PIN_SERIAL4_RX);
|
||||
setTx(PIN_SERIAL4_TX);
|
||||
_uart_index = 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UART5 // Only F2 / F4 / F7
|
||||
else if (peripheral == UART5) {
|
||||
#ifdef DMA1_Stream0
|
||||
RX_DMA = { UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0 };
|
||||
#endif
|
||||
setRx(PIN_SERIAL5_RX);
|
||||
setTx(PIN_SERIAL5_TX);
|
||||
_uart_index = 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USART6 // Only F2 / F4 / F7
|
||||
else if (peripheral == USART6) {
|
||||
#ifdef DMA2_Stream1
|
||||
RX_DMA = { USART6, RCC_AHB1Periph_DMA2, 4, DMA2_Stream1 };
|
||||
#endif
|
||||
setRx(PIN_SERIAL6_RX);
|
||||
setTx(PIN_SERIAL6_TX);
|
||||
_uart_index = 5;
|
||||
}
|
||||
#endif
|
||||
|
||||
else { // else get the pins of the first peripheral occurence in PinMap
|
||||
_serial.pin_rx = pinmap_pin(peripheral, PinMap_UART_RX);
|
||||
_serial.pin_tx = pinmap_pin(peripheral, PinMap_UART_TX);
|
||||
}
|
||||
|
||||
init(_serial.pin_rx, _serial.pin_tx);
|
||||
}
|
||||
|
||||
void HAL_HardwareSerial::setRx(uint32_t _rx) {
|
||||
_serial.pin_rx = digitalPinToPinName(_rx);
|
||||
}
|
||||
|
||||
void HAL_HardwareSerial::setTx(uint32_t _tx) {
|
||||
_serial.pin_tx = digitalPinToPinName(_tx);
|
||||
}
|
||||
|
||||
void HAL_HardwareSerial::init(PinName _rx, PinName _tx) {
|
||||
_serial.pin_rx = _rx;
|
||||
_serial.rx_buff = _rx_buffer;
|
||||
_serial.rx_head = _serial.rx_tail = 0;
|
||||
|
||||
_serial.pin_tx = _tx;
|
||||
_serial.tx_buff = _tx_buffer;
|
||||
_serial.tx_head = _serial.tx_tail = 0;
|
||||
}
|
||||
|
||||
// Actual interrupt handlers //////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Read receive byte from uart
|
||||
* @param obj : pointer to serial_t structure
|
||||
* @retval last character received
|
||||
*/
|
||||
int HAL_HardwareSerial::_tx_complete_irq(serial_t *obj) {
|
||||
// If interrupts are enabled, there must be more data in the output buffer. Send the next byte
|
||||
obj->tx_tail = (obj->tx_tail + 1) % TX_BUFFER_SIZE;
|
||||
|
||||
if (obj->tx_head == obj->tx_tail) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
void HAL_HardwareSerial::begin(unsigned long baud, uint8_t config) {
|
||||
uint32_t databits = 0, stopbits = 0, parity = 0;
|
||||
|
||||
_baud = baud;
|
||||
_config = config;
|
||||
|
||||
// Manage databits
|
||||
switch (config & 0x07) {
|
||||
case 0x02: databits = 6; break;
|
||||
case 0x04: databits = 7; break;
|
||||
case 0x06: databits = 8; break;
|
||||
default: databits = 0; break;
|
||||
}
|
||||
|
||||
if ((config & 0x30) == 0x30) {
|
||||
parity = UART_PARITY_ODD;
|
||||
databits++;
|
||||
}
|
||||
else if ((config & 0x20) == 0x20) {
|
||||
parity = UART_PARITY_EVEN;
|
||||
databits++;
|
||||
}
|
||||
else
|
||||
parity = UART_PARITY_NONE;
|
||||
|
||||
stopbits = (config & 0x08) == 0x08 ? UART_STOPBITS_2 : UART_STOPBITS_1;
|
||||
|
||||
switch (databits) {
|
||||
#ifdef UART_WORDLENGTH_7B
|
||||
case 7: databits = UART_WORDLENGTH_7B; break;
|
||||
#endif
|
||||
case 8: databits = UART_WORDLENGTH_8B; break;
|
||||
case 9: databits = UART_WORDLENGTH_9B; break;
|
||||
default:
|
||||
case 0: Error_Handler(); break;
|
||||
}
|
||||
|
||||
uart_init(&_serial, (uint32_t)baud, databits, parity, stopbits);
|
||||
Serial_DMA_Read_Enable(); // Start the circular DMA serial reading process, no callback needed
|
||||
}
|
||||
|
||||
void HAL_HardwareSerial::end() {
|
||||
flush(); // Wait for transmission of outgoing data
|
||||
uart_deinit(&_serial);
|
||||
_serial.rx_head = _serial.rx_tail; // Clear any received data
|
||||
}
|
||||
|
||||
// Update buffer head for DMA progress
|
||||
void HAL_HardwareSerial::update_rx_head() {
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
static uint32_t flag = 0;
|
||||
while (flag != _serial.rx_head) { // send all available data to emergency parser immediately
|
||||
emergency_parser.update(static_cast<MSerialT*>(this)->emergency_state, _serial.rx_buff[flag]);
|
||||
flag = (flag + 1) % RX_BUFFER_SIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANY(STM32F2xx, STM32F4xx, STM32F7xx)
|
||||
_serial.rx_head = RX_BUFFER_SIZE - RX_DMA.dma_streamRX->NDTR;
|
||||
#endif
|
||||
|
||||
#if ANY(STM32F0xx, STM32F1xx)
|
||||
_serial.rx_head = RX_BUFFER_SIZE - RX_DMA.dma_channelRX->CNDTR;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int HAL_HardwareSerial::available() {
|
||||
update_rx_head();
|
||||
return ((unsigned int)(RX_BUFFER_SIZE + _serial.rx_head - _serial.rx_tail)) % RX_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
int HAL_HardwareSerial::peek() {
|
||||
update_rx_head();
|
||||
if (_serial.rx_head == _serial.rx_tail) return -1;
|
||||
return _serial.rx_buff[_serial.rx_tail];
|
||||
}
|
||||
|
||||
int HAL_HardwareSerial::read() {
|
||||
update_rx_head();
|
||||
if (_serial.rx_head == _serial.rx_tail) return -1; // No chars if the head isn't ahead of the tail
|
||||
|
||||
unsigned char c = _serial.rx_buff[_serial.rx_tail];
|
||||
_serial.rx_tail = (rx_buffer_index_t)(_serial.rx_tail + 1) % RX_BUFFER_SIZE;
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t HAL_HardwareSerial::write(uint8_t c) { // Interrupt based writing
|
||||
tx_buffer_index_t i = (_serial.tx_head + 1) % TX_BUFFER_SIZE;
|
||||
|
||||
// If the output buffer is full, there's nothing for it other than to
|
||||
// wait for the interrupt handler to empty it a bit
|
||||
while (i == _serial.tx_tail) { /* nada */ } // NOP, let the interrupt free up space for us
|
||||
|
||||
_serial.tx_buff[_serial.tx_head] = c;
|
||||
_serial.tx_head = i;
|
||||
|
||||
if (!serial_tx_active(&_serial))
|
||||
uart_attach_tx_callback(&_serial, _tx_complete_irq); // Write next byte, launch interrupt
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void HAL_HardwareSerial::flush() {
|
||||
while ((_serial.tx_head != _serial.tx_tail)) { /* nada */ } // nop, the interrupt handler will free up space for us
|
||||
}
|
||||
|
||||
#if ANY(STM32F2xx, STM32F4xx, STM32F7xx)
|
||||
|
||||
void HAL_HardwareSerial::Serial_DMA_Read_Enable() {
|
||||
RCC_AHB1PeriphClockCmd(RX_DMA.dma_rcc, ENABLE); // Enable DMA clock
|
||||
|
||||
#ifdef STM32F7xx
|
||||
RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->RDR); // RX peripheral receive address (usart) F7
|
||||
#else
|
||||
RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->DR); // RX peripheral address (usart) F2 / F4
|
||||
#endif
|
||||
RX_DMA.dma_streamRX->M0AR = (uint32_t)_serial.rx_buff; // RX destination address (memory)
|
||||
RX_DMA.dma_streamRX->NDTR = RX_BUFFER_SIZE; // RX buffer size
|
||||
|
||||
RX_DMA.dma_streamRX->CR = (RX_DMA.dma_channel << 25); // RX channel selection, set to 0 all the other CR bits
|
||||
|
||||
RX_DMA.dma_streamRX->CR |= (3 << 16); // RX priority level: Very High
|
||||
|
||||
//RX_DMA.dma_streamRX->CR &= ~(3 << 13); // RX memory data size: 8 bit
|
||||
//RX_DMA.dma_streamRX->CR &= ~(3 << 11); // RX peripheral data size: 8 bit
|
||||
RX_DMA.dma_streamRX->CR |= (1 << 10); // RX memory increment mode
|
||||
//RX_DMA.dma_streamRX->CR &= ~(1 << 9); // RX peripheral no increment mode
|
||||
RX_DMA.dma_streamRX->CR |= (1 << 8); // RX circular mode enabled
|
||||
//RX_DMA.dma_streamRX->CR &= ~(1 << 6); // RX data transfer direction: Peripheral-to-memory
|
||||
RX_DMA.uart->CR3 |= (1 << 6); // Enable DMA receiver (DMAR)
|
||||
RX_DMA.dma_streamRX->CR |= (1 << 0); // RX enable DMA
|
||||
}
|
||||
|
||||
#endif // STM32F2xx || STM32F4xx || STM32F7xx
|
||||
|
||||
#if ANY(STM32F0xx, STM32F1xx)
|
||||
|
||||
void HAL_HardwareSerial::Serial_DMA_Read_Enable() {
|
||||
RCC_AHBPeriphClockCmd(RX_DMA.dma_rcc, ENABLE); // enable DMA clock
|
||||
|
||||
RX_DMA.dma_channelRX->CPAR = (uint32_t)(&RX_DMA.uart->DR); // RX peripheral address (usart)
|
||||
RX_DMA.dma_channelRX->CMAR = (uint32_t)_serial.rx_buff; // RX destination address (memory)
|
||||
RX_DMA.dma_channelRX->CNDTR = RX_BUFFER_SIZE; // RX buffer size
|
||||
|
||||
RX_DMA.dma_channelRX->CCR = 0; // RX channel selection, set to 0 all the other CR bits
|
||||
|
||||
RX_DMA.dma_channelRX->CCR |= (3<<12); // RX priority level: Very High
|
||||
|
||||
//RX_DMA.dma_channelRX->CCR &= ~(1<<10); // RX memory data size: 8 bit
|
||||
//RX_DMA.dma_channelRX->CCR &= ~(1<<8); // RX peripheral data size: 8 bit
|
||||
RX_DMA.dma_channelRX->CCR |= (1<<7); // RX memory increment mode
|
||||
//RX_DMA.dma_channelRX->CCR &= ~(1<<6); // RX peripheral no increment mode
|
||||
RX_DMA.dma_channelRX->CCR |= (1<<5); // RX circular mode enabled
|
||||
//RX_DMA.dma_channelRX->CCR &= ~(1<<4); // RX data transfer direction: Peripheral-to-memory
|
||||
|
||||
RX_DMA.uart->CR3 |= (1<<6); // enable DMA receiver (DMAR)
|
||||
RX_DMA.dma_channelRX->CCR |= (1<<0); // RX enable DMA
|
||||
}
|
||||
|
||||
#endif // STM32F0xx || STM32F1xx
|
||||
|
||||
#endif // SERIAL_DMA && HAL_UART_MODULE_ENABLED && !HAL_UART_MODULE_ONLY
|
||||
#endif // HAL_STM32
|
85
Marlin/src/HAL/STM32/HardwareSerial.h
Normal file
85
Marlin/src/HAL/STM32/HardwareSerial.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
* Copyright (c) 2017 Victor Perez
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// HAL_HardwareSerial Class. Adapted from Arduino HardwareSerial.
|
||||
//
|
||||
|
||||
#if RX_BUFFER_SIZE == 0
|
||||
#undef RX_BUFFER_SIZE
|
||||
#define RX_BUFFER_SIZE 128
|
||||
#endif
|
||||
|
||||
#if TX_BUFFER_SIZE == 0
|
||||
#undef TX_BUFFER_SIZE
|
||||
#define TX_BUFFER_SIZE 64
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
USART_TypeDef * uart;
|
||||
uint32_t dma_rcc;
|
||||
#if ANY(STM32F0xx, STM32F1xx) // F0 / F1
|
||||
DMA_TypeDef * dma_controller;
|
||||
DMA_Channel_TypeDef * dma_channelRX;
|
||||
#else // F2 / F4 / F7
|
||||
uint32_t dma_channel;
|
||||
DMA_Stream_TypeDef * dma_streamRX;
|
||||
#endif
|
||||
} DMA_CFG;
|
||||
|
||||
class HAL_HardwareSerial : public Stream {
|
||||
protected:
|
||||
// Don't put any members after these buffers, since only the first
|
||||
// 32 bytes of this struct can be accessed quickly using the ldd instruction.
|
||||
unsigned char _rx_buffer[RX_BUFFER_SIZE];
|
||||
unsigned char _tx_buffer[TX_BUFFER_SIZE];
|
||||
|
||||
serial_t _serial;
|
||||
|
||||
public:
|
||||
HAL_HardwareSerial(void *peripheral);
|
||||
void begin(unsigned long, uint8_t);
|
||||
void end();
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int peek();
|
||||
virtual size_t write(uint8_t);
|
||||
virtual void flush();
|
||||
operator bool() { return true; }
|
||||
|
||||
void setRx(uint32_t _rx);
|
||||
void setTx(uint32_t _tx);
|
||||
|
||||
static int _tx_complete_irq(serial_t *obj); // Interrupt handler
|
||||
|
||||
private:
|
||||
uint8_t _uart_index;
|
||||
bool _rx_enabled;
|
||||
uint8_t _config;
|
||||
unsigned long _baud;
|
||||
void init(PinName _rx, PinName _tx);
|
||||
void update_rx_head();
|
||||
DMA_CFG RX_DMA;
|
||||
void Serial_DMA_Read_Enable();
|
||||
};
|
|
@ -47,10 +47,15 @@
|
|||
#define USART9 UART9
|
||||
#endif
|
||||
|
||||
#define DECLARE_SERIAL_PORT(ser_num) \
|
||||
void _rx_complete_irq_ ## ser_num (serial_t * obj); \
|
||||
MSerialT MSerial ## ser_num (true, USART ## ser_num, &_rx_complete_irq_ ## ser_num); \
|
||||
void _rx_complete_irq_ ## ser_num (serial_t * obj) { MSerial ## ser_num ._rx_complete_irq(obj); }
|
||||
#if ENABLED(SERIAL_DMA)
|
||||
#define DECLARE_SERIAL_PORT(ser_num) \
|
||||
MSerialT MSerial ## ser_num (true, USART ## ser_num);
|
||||
#else
|
||||
#define DECLARE_SERIAL_PORT(ser_num) \
|
||||
void _rx_complete_irq_ ## ser_num (serial_t * obj); \
|
||||
MSerialT MSerial ## ser_num (true, USART ## ser_num, &_rx_complete_irq_ ## ser_num); \
|
||||
void _rx_complete_irq_ ## ser_num (serial_t * obj) { MSerial ## ser_num ._rx_complete_irq(obj); }
|
||||
#endif
|
||||
|
||||
#if USING_HW_SERIAL1
|
||||
DECLARE_SERIAL_PORT(1)
|
||||
|
@ -87,33 +92,38 @@
|
|||
#endif
|
||||
|
||||
void MarlinSerial::begin(unsigned long baud, uint8_t config) {
|
||||
HardwareSerial::begin(baud, config);
|
||||
// Replace the IRQ callback with the one we have defined
|
||||
TERN_(EMERGENCY_PARSER, _serial.rx_callback = _rx_callback);
|
||||
#if ENABLED(SERIAL_DMA)
|
||||
HAL_HardwareSerial::begin(baud, config);
|
||||
#else
|
||||
HardwareSerial::begin(baud, config);
|
||||
// Replace the IRQ callback with the one we have defined
|
||||
TERN_(EMERGENCY_PARSER, _serial.rx_callback = _rx_callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is Copyright (c) 2006 Nicholas Zambetti.
|
||||
void MarlinSerial::_rx_complete_irq(serial_t *obj) {
|
||||
// No Parity error, read byte and store it in the buffer if there is room
|
||||
unsigned char c;
|
||||
#if DISABLED(SERIAL_DMA)
|
||||
|
||||
if (uart_getc(obj, &c) == 0) {
|
||||
// This function Copyright (c) 2006 Nicholas Zambetti.
|
||||
void MarlinSerial::_rx_complete_irq(serial_t *obj) {
|
||||
// No Parity error, read byte and store it in the buffer if there is room
|
||||
unsigned char c;
|
||||
if (uart_getc(obj, &c) == 0) {
|
||||
|
||||
rx_buffer_index_t i = (unsigned int)(obj->rx_head + 1) % SERIAL_RX_BUFFER_SIZE;
|
||||
rx_buffer_index_t i = (unsigned int)(obj->rx_head + 1) % SERIAL_RX_BUFFER_SIZE;
|
||||
|
||||
// if we should be storing the received character into the location
|
||||
// just before the tail (meaning that the head would advance to the
|
||||
// current location of the tail), we're about to overflow the buffer
|
||||
// and so we don't write the character or advance the head.
|
||||
if (i != obj->rx_tail) {
|
||||
obj->rx_buff[obj->rx_head] = c;
|
||||
obj->rx_head = i;
|
||||
// If tail overlaps head the buffer is overflowed
|
||||
// so don't write the character or advance the head.
|
||||
if (i != obj->rx_tail) {
|
||||
obj->rx_buff[obj->rx_head] = c;
|
||||
obj->rx_head = i;
|
||||
}
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
emergency_parser.update(static_cast<MSerialT*>(this)->emergency_state, c);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
emergency_parser.update(static_cast<MSerialT*>(this)->emergency_state, c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !SERIAL_DMA
|
||||
|
||||
#endif // HAL_STM32
|
||||
|
|
|
@ -27,23 +27,38 @@
|
|||
#include "../../feature/e_parser.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(SERIAL_DMA)
|
||||
#include "HardwareSerial.h"
|
||||
#endif
|
||||
|
||||
#include "../../core/serial_hook.h"
|
||||
|
||||
typedef void (*usart_rx_callback_t)(serial_t * obj);
|
||||
#if ENABLED(SERIAL_DMA)
|
||||
|
||||
struct MarlinSerial : public HardwareSerial {
|
||||
MarlinSerial(void *peripheral, usart_rx_callback_t rx_callback) :
|
||||
HardwareSerial(peripheral), _rx_callback(rx_callback)
|
||||
{ }
|
||||
struct MarlinSerial : public HAL_HardwareSerial {
|
||||
MarlinSerial(void *peripheral) : HAL_HardwareSerial(peripheral) { }
|
||||
void begin(unsigned long baud, uint8_t config);
|
||||
inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
|
||||
};
|
||||
|
||||
void begin(unsigned long baud, uint8_t config);
|
||||
inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
|
||||
#else // Arduino non-DMA
|
||||
|
||||
void _rx_complete_irq(serial_t *obj);
|
||||
typedef void (*usart_rx_callback_t)(serial_t * obj);
|
||||
|
||||
protected:
|
||||
usart_rx_callback_t _rx_callback;
|
||||
};
|
||||
struct MarlinSerial : public HardwareSerial {
|
||||
MarlinSerial(void *peripheral, usart_rx_callback_t rx_callback)
|
||||
: HardwareSerial(peripheral), _rx_callback(rx_callback) { }
|
||||
|
||||
void begin(unsigned long baud, uint8_t config);
|
||||
inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
|
||||
|
||||
void _rx_complete_irq(serial_t *obj);
|
||||
|
||||
protected:
|
||||
usart_rx_callback_t _rx_callback;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef Serial1Class<MarlinSerial> MSerialT;
|
||||
extern MSerialT MSerial1;
|
||||
|
|
|
@ -254,6 +254,15 @@ 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
|
||||
#if ENABLED(SERIAL_DMA)
|
||||
#if !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)."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Multiple Stepper Drivers Per Axis
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@ restore_configs
|
|||
opt_set MOTHERBOARD BOARD_BTT_SKR_E3_DIP \
|
||||
SERIAL_PORT 1 SERIAL_PORT_2 -1 \
|
||||
X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130
|
||||
opt_enable SERIAL_DMA
|
||||
exec_test $1 $2 "BTT SKR E3 DIP 1.0 | Mixed TMC Drivers" "$3"
|
||||
|
||||
# clean up
|
||||
|
|
Loading…
Reference in a new issue