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 1 commit
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
Prev Previous commit
Next Next commit
DMA SERIAL READING TO CIRCULAR RX BUFFER
STM32F2/F4 only
  • Loading branch information
rondlh committed Oct 7, 2023
commit 01dcee1a0e149efa8ab39641a3638b32a27b4b18
352 changes: 352 additions & 0 deletions Marlin/src/HAL/STM32/HardwareSerial2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
// IRON, HARDWARESERIAL2 CLASS, ADAPTATION FROM ARDUINO'S HARDWARESERIAL
#include <stdio.h>
#include "Arduino.h"
#include "../../inc/MarlinConfig.h"
#include "HardwareSerial2.h"
#include "uart.h"

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

#define HAVE_HWSERIAL1 // SELECT THE SERIAL PORT HERE!!!

#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 // PB10
#define PIN_SERIAL3_RX 27 // PB11
#define PIN_SERIAL4_TX 42 // PC10
#define PIN_SERIAL4_RX 43 // PC11
#define PIN_SERIAL5_TX 44 // PC12
#define PIN_SERIAL5_RX 50 // 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).
#if defined(HAVE_HWSERIAL1)
HardwareSerial2 HSerial1(USART1);
void serialEvent1() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL2)
HardwareSerial2 HSerial2(USART2);
void serialEvent2() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL3)
HardwareSerial2 Serial3(USART3);
void serialEvent3() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL4)
#if defined(USART4)
HardwareSerial2 HSerial4(USART4);
#else
HardwareSerial2 HSerial4(UART4);
#endif
void serialEvent4() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIAL5)
#if defined(USART5)
HardwareSerial2 HSerial5(USART5);
#else
HardwareSerial2 HSerial5(UART5);
#endif
void serialEvent5() __attribute__((weak));
#endif

#if defined(HAVE_HWSERIALLP1)
HardwareSerial2 SerialLP1(LPUART1);
void serialEventLP1() __attribute__((weak));
#endif

// Constructors ////////////////////////////////////////////////////////////////
HardwareSerial2::HardwareSerial2(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;
}
else

#ifdef USART4
if (peripheral == USART4)
{
RX_DMA = { USART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
#endif

#ifdef UART4
if (peripheral == UART4)
{
RX_DMA = { UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 };
#endif

setRx(PIN_SERIAL4_RX);
setTx(PIN_SERIAL4_TX);
_uart_index = 3;
}
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 HardwareSerial2::setRx(uint32_t _rx)
{
_serial.pin_rx = digitalPinToPinName(_rx);
}

void HardwareSerial2::setTx(uint32_t _tx)
{
_serial.pin_tx = digitalPinToPinName(_tx);
}

void HardwareSerial2::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 HardwareSerial2::_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 HardwareSerial2::begin(unsigned long baud, uint8_t config)
{
uint32_t databits = 0;
uint32_t stopbits = 0;
uint32_t 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;
}

if ((config & 0x08) == 0x08)
{
stopbits = UART_STOPBITS_2;
}
else
{
stopbits = 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 HardwareSerial2::end(void)
{
flush(); // wait for transmission of outgoing data

uart_deinit(&_serial);

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

void HardwareSerial2::update_rx_head(void) // 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 HardwareSerial2::available(void)
{
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 HardwareSerial2::peek(void)
{
update_rx_head(); // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
if (_serial.rx_head == _serial.rx_tail)
{
return -1;
}
else
{
return _serial.rx_buff[_serial.rx_tail];
}
}

int HardwareSerial2::read(void)
{
update_rx_head(); // IRON, ADDED, UPDATE HEAD FROM DMA PROGRESS
if (_serial.rx_head == _serial.rx_tail) // if the head isn't ahead of the tail, we don't have any characters
{
return -1;
}
else
{
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 HardwareSerial2::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) { } // 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 HardwareSerial2::Serial_DMA_Read_Enable(void)
{
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
Loading
Loading