Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DMA Serial Reading to Circular buffer #26328

Merged
merged 44 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fbbf539
Update README.md
rondlh Oct 7, 2023
01dcee1
DMA SERIAL READING TO CIRCULAR RX BUFFER
rondlh Oct 7, 2023
88c976b
Revert "Update README.md"
rondlh Oct 8, 2023
bd49014
Changed HAVE_HWSERIALx to USING_HW_SERIALx
rondlh Oct 8, 2023
2988878
formatting
thinkyhead Oct 8, 2023
f01ca5c
HardwareSerial2 => HAL_HardwareSerial
thinkyhead Oct 8, 2023
f92e20f
misc.
thinkyhead Oct 8, 2023
7506508
Added STM32F1xx support, untested!
rondlh Oct 8, 2023
abd47af
Merge branch 'bugfix-2.1.x' of https://github.com/rondlh/Marlin-DMA_R…
rondlh Oct 8, 2023
6d3d4ef
Merging STM32F1 update with Scott's good work, convert to HAL_Hardwar…
rondlh Oct 8, 2023
da14958
Merge branch 'bugfix-2.1.x' into pr/26328
thinkyhead Oct 8, 2023
d458fd8
format and fix
thinkyhead Oct 8, 2023
44b4e3f
etc
thinkyhead Oct 8, 2023
40d0b36
cleanup. best guess for USART4
thinkyhead Oct 8, 2023
10e866b
etc
thinkyhead Oct 8, 2023
bb77078
Leave STM32F1 (Maple) as-is
thinkyhead Oct 8, 2023
1c1d8dd
fix ctor
thinkyhead Oct 9, 2023
7037a52
Don't break other build, keep old code if not STM32F1 F2 F4
rondlh Oct 9, 2023
b355f8c
RX and TX buffers are required to make this work
rondlh Oct 9, 2023
1a3ea93
Support F1 DMA Structure in the same file
rondlh Oct 9, 2023
0f74099
Set SERIAL_PORT 1 for testing
rondlh Oct 9, 2023
f3ac9c7
Corrected issues with STM32F1
rondlh Oct 9, 2023
ef7ac35
Corrected DMA start register for STM32F1
rondlh Oct 9, 2023
f6ab50b
SERIAL_PORT set back to 0
rondlh Oct 10, 2023
4ddba70
Disable Arduino to resolve variable naming conflicts
rondlh Oct 10, 2023
0ecbb7e
Guard Serial DMA writing code with "SERIAL_DMA"
rondlh Oct 10, 2023
9a155af
STM32F0 and STM32F7 support, added Configuration_adv.h
rondlh Oct 11, 2023
8240dc9
Cleanup items not needed for SERIAL_DMA
rondlh Oct 12, 2023
8fb3072
Increase undefined or 0 size RX Buffer from 64 to 128 bytes
rondlh Oct 12, 2023
20ef32e
Improve code readability, STM32F0 support
rondlh Oct 12, 2023
0da5e5f
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 12, 2023
9fde816
Merge branch 'MarlinFirmware:bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 12, 2023
31eaa0f
Merge branch 'bugfix-2.1.x' of https://github.com/rondlh/Marlin-DMA_R…
rondlh Oct 12, 2023
9b4be3e
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 13, 2023
b644553
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 14, 2023
68229ab
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 20, 2023
34dd6bc
Fixed compilation error because UART5 has no DMA support on F0 and F1
rondlh Oct 28, 2023
e096917
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Oct 28, 2023
423e822
check for defines
thinkyhead Dec 11, 2023
1b42724
Merge branch 'bugfix-2.1.x' into pr/26328
thinkyhead Dec 11, 2023
e9eba89
apply standards
thinkyhead Dec 11, 2023
28ff5d4
update comments
thinkyhead Dec 11, 2023
2f75203
add a test
thinkyhead Dec 11, 2023
6b1b5dd
Move some serial defaults
thinkyhead Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 307 additions & 0 deletions Marlin/src/HAL/STM32/HardwareSerial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
/**
* 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/>.
*
*/

// IRON, HARDWARESERIAL2 CLASS, ADAPTATION FROM ARDUINO'S HARDWARESERIAL

#include "../../inc/MarlinConfig.h"

#if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY)

#include <stdio.h>
#include "Arduino.h"
#include "HardwareSerial.h"
#include "uart.h"

#define PIN_SERIAL1_TX PA9
#define PIN_SERIAL1_RX PA10
#define PIN_SERIAL2_TX 2 // PIN_A2
#define PIN_SERIAL2_RX 3 // PIN_A3
#define PIN_SERIAL3_TX 26// PIN_PB10
#define PIN_SERIAL3_RX 27// PIN_PB11
#define PIN_SERIAL4_TX 42// PIN_PC10
#define PIN_SERIAL4_RX 43// PIN_PC11
#define PIN_SERIAL5_TX 44// PIN_PC12
#define PIN_SERIAL5_RX 50// PIN_PD2

// GET FROM INCLUDE FILE!!!
#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;
}

// ------------------------------------------------------------------------

// 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

// Constructors ////////////////////////////////////////////////////////////////

HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) {
if (peripheral == USART1) {
setRx(PIN_SERIAL1_RX);
setTx(PIN_SERIAL1_TX);
RX_DMA = { USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2 };
_uart_index = 0;
}
else if (peripheral == USART2) {
setRx(PIN_SERIAL2_RX);
setTx(PIN_SERIAL2_TX);
RX_DMA = { USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5 };
_uart_index = 1;
}
else if (peripheral == USART3) {
setRx(PIN_SERIAL3_RX);
setTx(PIN_SERIAL3_TX);
RX_DMA = { USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1 };
_uart_index = 2;
}

#ifdef USART4
else if (peripheral == USART4) {
RX_DMA = { USART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
setRx(PIN_SERIAL4_RX);
setTx(PIN_SERIAL4_TX);
_uart_index = 3;
}
#endif

#ifdef UART4
else if (peripheral == UART4) {
RX_DMA = { UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
setRx(PIN_SERIAL4_RX);
setTx(PIN_SERIAL4_TX);
_uart_index = 3;
}
#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;
}

/**
* @brief Read receive byte from uart
* @param obj : pointer to serial_t structure
* @retval last character received
*/

// Actual interrupt handlers //////////////////////////////////////////////////////////////

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);

// IRON, USING FREE RUNNING DMA READING, NO CALLBACK NEEDED
//uart_attach_rx_callback(&_serial, _rx_complete_irq); // IRON, start the interrupt reading, indicate where to callback

Serial_DMA_Read_Enable(); // IRON, START THE DMA READING PROCESS
}

void HAL_HardwareSerial::end() {
flush(); // Wait for transmission of outgoing data

uart_deinit(&_serial);

_serial.rx_head = _serial.rx_tail; // Clear any received data
}

void HAL_HardwareSerial::update_rx_head() { // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS

#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 % RX_BUFFER_SIZE;
}
#endif

_serial.rx_head = RX_BUFFER_SIZE - RX_DMA.dma_streamRX->NDTR; // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
}

int HAL_HardwareSerial::available() {
update_rx_head(); // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
return ((unsigned int)(RX_BUFFER_SIZE + _serial.rx_head - _serial.rx_tail)) % RX_BUFFER_SIZE;
}

int HAL_HardwareSerial::peek() {
update_rx_head(); // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
if (_serial.rx_head == _serial.rx_tail) return -1;
return _serial.rx_buff[_serial.rx_tail];
}

int HAL_HardwareSerial::read() {
update_rx_head(); // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
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, the interrupt handler will 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::Serial_DMA_Read_Enable() {
RCC_AHB1PeriphClockCmd(RX_DMA.dma_rcc, ENABLE); // enable DMA clock

RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->DR); // RX peripheral address (usart)
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

// primary serial port priority at highest level (TX higher than RX)
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 // HAL_UART_MODULE_ENABLED && !HAL_UART_MODULE_ONLY
67 changes: 67 additions & 0 deletions Marlin/src/HAL/STM32/HardwareSerial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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

typedef struct {
USART_TypeDef * uart;
uint32_t dma_rcc;
uint32_t dma_channel;
DMA_Stream_TypeDef * dma_streamRX;
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved
} 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);

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();
};
3 changes: 2 additions & 1 deletion Marlin/src/HAL/STM32/MarlinSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@
#endif

void MarlinSerial::begin(unsigned long baud, uint8_t config) {
HardwareSerial::begin(baud, config);
HAL_HardwareSerial::begin(baud, config); // IRON, MODIFIED TO "HAL_HardwareSerial"
// Replace the IRQ callback with the one we have defined
TERN_(EMERGENCY_PARSER, _serial.rx_callback = _rx_callback);
}

// IRON, IT SEEMS THIS CODE IS NEVER USED (STM32)??????????
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved
// 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
Expand Down
Loading
Loading