Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
118 changes: 82 additions & 36 deletions drivers/serial/uart_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ uint32_t lpuartdiv_calc(const uint64_t clock_rate, const uint32_t baud_rate)
#endif /* USART_PRESC_PRESCALER */
#endif /* HAS_LPUART */

#ifdef CONFIG_UART_ASYNC_API
#define STM32_ASYNC_STATUS_TIMEOUT (DMA_STATUS_BLOCK + 1)
#endif

#ifdef CONFIG_PM
static void uart_stm32_pm_policy_state_lock_get(const struct device *dev)
{
Expand Down Expand Up @@ -1117,11 +1121,16 @@ static inline void async_evt_rx_rdy(struct uart_stm32_data *data)
.data.rx.offset = data->dma_rx.offset
};

/* update the current pos for new data */
data->dma_rx.offset = data->dma_rx.counter;
/* When cyclic DMA is used, buffer positions are not updated - call callback every time*/
if (data->dma_rx.dma_cfg.cyclic == 0) {
/* update the current pos for new data */
data->dma_rx.offset = data->dma_rx.counter;

/* send event only for new data */
if (event.data.rx.len > 0) {
/* send event only for new data */
if (event.data.rx.len > 0) {
async_user_callback(data, &event);
}
} else {
async_user_callback(data, &event);
}
}
Expand Down Expand Up @@ -1204,20 +1213,45 @@ static inline void async_timer_start(struct k_work_delayable *work,
}
}

static void uart_stm32_dma_rx_flush(const struct device *dev)
static void uart_stm32_dma_rx_flush(const struct device *dev, int status)
{
struct dma_status stat;
struct uart_stm32_data *data = dev->data;

if (dma_get_status(data->dma_rx.dma_dev,
data->dma_rx.dma_channel, &stat) == 0) {
size_t rx_rcv_len = data->dma_rx.buffer_length -
stat.pending_length;
if (rx_rcv_len > data->dma_rx.offset) {
data->dma_rx.counter = rx_rcv_len;
size_t rx_rcv_len = 0;

async_evt_rx_rdy(data);
switch (status) {
case DMA_STATUS_COMPLETE:
/* fully complete */
data->dma_rx.counter = data->dma_rx.buffer_length;
break;
case DMA_STATUS_BLOCK:
/* half complete */
data->dma_rx.counter = data->dma_rx.buffer_length / 2;

break;
default: /* likely STM32_ASYNC_STATUS_TIMEOUT */
if (dma_get_status(data->dma_rx.dma_dev, data->dma_rx.dma_channel, &stat) == 0) {
rx_rcv_len = data->dma_rx.buffer_length - stat.pending_length;
data->dma_rx.counter = rx_rcv_len;
}
break;
}

async_evt_rx_rdy(data);

switch (status) { /* update offset*/
case DMA_STATUS_COMPLETE:
/* fully complete */
data->dma_rx.offset = 0;
break;
case DMA_STATUS_BLOCK:
/* half complete */
data->dma_rx.offset = data->dma_rx.buffer_length / 2;
break;
default: /* likely STM32_ASYNC_STATUS_TIMEOUT */
data->dma_rx.offset += rx_rcv_len - data->dma_rx.offset;
break;
}
}

Expand Down Expand Up @@ -1269,7 +1303,7 @@ static void uart_stm32_isr(const struct device *dev)
LOG_DBG("idle interrupt occurred");

if (data->dma_rx.timeout == 0) {
uart_stm32_dma_rx_flush(dev);
uart_stm32_dma_rx_flush(dev, STM32_ASYNC_STATUS_TIMEOUT);
} else {
/* Start the RX timer not null */
async_timer_start(&data->dma_rx.timeout_work,
Expand Down Expand Up @@ -1417,7 +1451,7 @@ static int uart_stm32_async_rx_disable(const struct device *dev)

LL_USART_DisableIT_IDLE(usart);

uart_stm32_dma_rx_flush(dev);
uart_stm32_dma_rx_flush(dev, STM32_ASYNC_STATUS_TIMEOUT);

async_evt_rx_buf_release(data);

Expand Down Expand Up @@ -1517,27 +1551,32 @@ void uart_stm32_dma_rx_cb(const struct device *dma_dev, void *user_data,

(void)k_work_cancel_delayable(&data->dma_rx.timeout_work);

/* true since this functions occurs when buffer if full */
data->dma_rx.counter = data->dma_rx.buffer_length;
/* If we are in NORMAL MODE */
if (data->dma_rx.dma_cfg.cyclic == 0) {

async_evt_rx_rdy(data);

if (data->rx_next_buffer != NULL) {
async_evt_rx_buf_release(data);
/* true since this functions occurs when buffer is full */
data->dma_rx.counter = data->dma_rx.buffer_length;
async_evt_rx_rdy(data);
if (data->rx_next_buffer != NULL) {
async_evt_rx_buf_release(data);

/* replace the buffer when the current
* is full and not the same as the next
* one.
*/
uart_stm32_dma_replace_buffer(uart_dev);
/* replace the buffer when the current
* is full and not the same as the next
* one.
*/
uart_stm32_dma_replace_buffer(uart_dev);
} else {
/* Buffer full without valid next buffer,
* an UART_RX_DISABLED event must be generated,
* but uart_stm32_async_rx_disable() cannot be
* called in ISR context. So force the RX timeout
* to minimum value and let the RX timeout to do the job.
*/
k_work_reschedule(&data->dma_rx.timeout_work, K_TICKS(1));
}
} else {
/* Buffer full without valid next buffer,
* an UART_RX_DISABLED event must be generated,
* but uart_stm32_async_rx_disable() cannot be
* called in ISR context. So force the RX timeout
* to minimum value and let the RX timeout to do the job.
*/
k_work_reschedule(&data->dma_rx.timeout_work, K_TICKS(1));
/* CIRCULAR MODE */
uart_stm32_dma_rx_flush(data->uart_dev, status);
}
}

Expand Down Expand Up @@ -1722,7 +1761,7 @@ static void uart_stm32_async_rx_timeout(struct k_work *work)
if (data->dma_rx.counter == data->dma_rx.buffer_length) {
uart_stm32_async_rx_disable(dev);
} else {
uart_stm32_dma_rx_flush(dev);
uart_stm32_dma_rx_flush(dev, STM32_ASYNC_STATUS_TIMEOUT);
}
}

Expand Down Expand Up @@ -1829,9 +1868,10 @@ static int uart_stm32_async_init(const struct device *dev)
data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
}

/* RX disable circular buffer */
data->dma_rx.blk_cfg.source_reload_en = 0;
data->dma_rx.blk_cfg.dest_reload_en = 0;
/* Enable/disable RX circular buffer */
data->dma_rx.blk_cfg.source_reload_en = data->dma_rx.dma_cfg.cyclic;
data->dma_rx.blk_cfg.dest_reload_en = data->dma_rx.dma_cfg.cyclic;

data->dma_rx.blk_cfg.fifo_mode_control = data->dma_rx.fifo_threshold;

data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg;
Expand Down Expand Up @@ -1868,6 +1908,10 @@ static int uart_stm32_async_init(const struct device *dev)
data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
}

/* Enable/disable TX circular buffer */
data->dma_tx.blk_cfg.source_reload_en = data->dma_tx.dma_cfg.cyclic;
data->dma_tx.blk_cfg.dest_reload_en = data->dma_tx.dma_cfg.cyclic;

data->dma_tx.blk_cfg.fifo_mode_control = data->dma_tx.fifo_threshold;

data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg;
Expand Down Expand Up @@ -2225,6 +2269,8 @@ static int uart_stm32_pm_action(const struct device *dev,
.dma_slot = STM32_DMA_SLOT(index, dir, slot),\
.channel_direction = STM32_DMA_CONFIG_DIRECTION( \
STM32_DMA_CHANNEL_CONFIG(index, dir)),\
.cyclic = STM32_DMA_CONFIG_CYCLIC( \
STM32_DMA_CHANNEL_CONFIG(index, dir)), \
.channel_priority = STM32_DMA_CONFIG_PRIORITY( \
STM32_DMA_CHANNEL_CONFIG(index, dir)), \
.source_data_size = STM32_DMA_CONFIG_##src_dev##_DATA_SIZE(\
Expand Down
2 changes: 2 additions & 0 deletions include/zephyr/drivers/dma/dma_stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
DT_INST_DMAS_CELL_BY_IDX(id, idx, channel_config)

/* macros for channel-config */
/* enable circular buffer */
#define STM32_DMA_CONFIG_CYCLIC(config) ((config >> 5) & 0x1)
/* direction defined on bits 6-7 */
/* 0 -> MEM_TO_MEM, 1 -> MEM_TO_PERIPH, 2 -> PERIPH_TO_MEM */
#define STM32_DMA_CONFIG_DIRECTION(config) ((config >> 6) & 0x3)
Expand Down
8 changes: 8 additions & 0 deletions samples/boards/st/uart/circular_dma/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(circular_dma)

target_sources(app PRIVATE src/main.c)
49 changes: 49 additions & 0 deletions samples/boards/st/uart/circular_dma/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.. zephyr:code-sample:: uart
:name: UART circular mode
:relevant-api: uart_interface

Read data from the console and echo it back using a circular dma configuration.

Overview
********

This sample demonstrates how to use STM32 UART serial driver with DMA in circular mode.
It reads data from the console and echoes it back.

By default, the UART peripheral that is normally assigned to the Zephyr shell
is used, hence the majority of boards should be able to run this sample.

Building and Running
********************

Build and flash the sample as follows, changing ``nucleo_g071rb`` for
your board:

.. zephyr-app-commands::
:zephyr-app: samples/boards/st/uart/circular_dma
:board: nucleo_g071rb
:goals: build flash
:compact:

Sample Output
=============

.. code-block:: console

Enter message to fill RX buffer size and press enter :
# Type e.g. :
# "Lorem Ipsum is simply dummy text of the printing and typesetting industry.
# Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
# when an unknown printer took a galley of type and scrambled it to make a type specimen book.
# It has survived not only five centuries, but also the leap into electronic typesetting,
# remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset
# sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
# like Aldus PageMaker including versions of Lorem Ipsum."

Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book.
It has survived not only five centuries, but also the leap into electronic typesetting,
remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset
sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
like Aldus PageMaker including versions of Lorem Ipsum.
20 changes: 20 additions & 0 deletions samples/boards/st/uart/circular_dma/boards/nucleo_c031c6.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/

&usart2 {
dmas = <&dmamux1 0 53 (STM32_DMA_PERIPH_TX)>,
<&dmamux1 3 52 (STM32_DMA_PERIPH_RX | STM32_DMA_MEM_8BITS | STM32_DMA_MODE_CYCLIC)>;
dma-names = "tx", "rx";
fifo-enable;
};

&dma1 {
status = "okay";
};

&dmamux1 {
status = "okay";
};
16 changes: 16 additions & 0 deletions samples/boards/st/uart/circular_dma/boards/nucleo_f091rc.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2024 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/

&usart2 {
dmas = <&dma1 4 (STM32_DMA_PERIPH_TX)>,
<&dma1 5 (STM32_DMA_PERIPH_RX | STM32_DMA_MEM_8BITS | STM32_DMA_MODE_CYCLIC)>;
dma-names = "tx", "rx";
fifo-enable;
};

&dma1 {
status = "okay";
};
20 changes: 20 additions & 0 deletions samples/boards/st/uart/circular_dma/boards/nucleo_g071rb.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/

&usart2 {
dmas = <&dmamux1 0 53 (STM32_DMA_PERIPH_TX)>,
<&dmamux1 3 52 (STM32_DMA_PERIPH_RX | STM32_DMA_MEM_8BITS | STM32_DMA_MODE_CYCLIC)>;
dma-names = "tx", "rx";
fifo-enable;
};

&dma1 {
status = "okay";
};

&dmamux1 {
status = "okay";
};
3 changes: 3 additions & 0 deletions samples/boards/st/uart/circular_dma/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_RING_BUFFER=y
11 changes: 11 additions & 0 deletions samples/boards/st/uart/circular_dma/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
sample:
name: UART driver sample
tests:
sample.boards.stm32.uart.circular_dma:
integration_platforms:
- nucleo_g071rb
tags:
- serial
- uart
filter: dt_chosen_enabled("zephyr,shell-uart")
harness: keyboard
Loading
Loading