From c414d21ffd421ef7010e18206d5f356554eff6e8 Mon Sep 17 00:00:00 2001 From: doragasu Date: Thu, 13 Jun 2024 18:22:55 +0200 Subject: [PATCH 1/2] Add UART DMA TX example --- examples/uart_tx_dma/Makefile | 10 +++ examples/uart_tx_dma/funconfig.h | 7 ++ examples/uart_tx_dma/uart_tx_dma.c | 108 +++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 examples/uart_tx_dma/Makefile create mode 100644 examples/uart_tx_dma/funconfig.h create mode 100644 examples/uart_tx_dma/uart_tx_dma.c diff --git a/examples/uart_tx_dma/Makefile b/examples/uart_tx_dma/Makefile new file mode 100644 index 00000000..9f7fd819 --- /dev/null +++ b/examples/uart_tx_dma/Makefile @@ -0,0 +1,10 @@ +all : flash + +TARGET:=uart_tx_dma + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + + diff --git a/examples/uart_tx_dma/funconfig.h b/examples/uart_tx_dma/funconfig.h new file mode 100644 index 00000000..998cf76f --- /dev/null +++ b/examples/uart_tx_dma/funconfig.h @@ -0,0 +1,7 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif + diff --git a/examples/uart_tx_dma/uart_tx_dma.c b/examples/uart_tx_dma/uart_tx_dma.c new file mode 100644 index 00000000..ca280cc6 --- /dev/null +++ b/examples/uart_tx_dma/uart_tx_dma.c @@ -0,0 +1,108 @@ +/* + * This example uses DMA for the UART to transfer the "Hello World!\r\n" string + * once per second. Connect a UART Rx pin to D5, flash the example, setup your + * uart for 115200n1 and the messages should appear + */ + +#include "ch32v003fun.h" + +// Set UART baud rate here +#define UART_BR 115200 +// LED on D6 (nanoCH32V003 board) +#define LED_PIN 6 + +// DMA transfer completion interrupt. It will fire when the DMA transfer is +// complete. We use it just to blink the LED +__attribute__((interrupt)) __attribute__((section(".srodata"))) +void DMA1_Channel4_IRQHandler(void) +{ + // Clear flag + DMA1->INTFCR |= DMA_CTCIF4; + + // Blink LED + GPIOD->OUTDR ^= 1<APB2PCENR = RCC_APB2Periph_GPIOD; + GPIOD->CFGLR = + ((GPIO_CNF_IN_PUPD)<<(4*1)) | // Keep SWIO enabled. + (GPIO_Speed_2MHz | GPIO_CNF_OUT_PP)<<(4*LED_PIN); + + // LED ON + GPIOD->BSHR = 1<APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1; + + // Push-Pull, 10MHz Output on D5, with AutoFunction + GPIOD->CFGLR = (GPIOD->CFGLR & ~(0xF<<(4*5))) | + ((GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5)); + + // Setup UART for Tx 8n1 + USART1->CTLR1 = USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx; + USART1->CTLR2 = USART_StopBits_1; + // Enable Tx DMA event + USART1->CTLR3 = USART_DMAReq_Tx; + + // Set baud rate and enable UART + USART1->BRR = ((FUNCONF_SYSTEM_CORE_CLOCK) + (UART_BR)/2) / (UART_BR); + USART1->CTLR1 |= CTLR1_UE_Set; +} + +static void dma_uart_setup(void) +{ + // Enable DMA peripheral + RCC->AHBPCENR = RCC_AHBPeriph_SRAM | RCC_AHBPeriph_DMA1; + + // Disable channel just in case there is a transfer in progress + DMA1_Channel4->CFGR &= ~DMA_CFGR1_EN; + + // USART1 TX uses DMA channel 4 + DMA1_Channel4->PADDR = (uint32_t)&USART1->DATAR; + // MEM2MEM: 0 (memory to peripheral) + // PL: 0 (low priority since UART is a relatively slow peripheral) + // MSIZE/PSIZE: 0 (8-bit) + // MINC: 1 (increase memory address) + // CIRC: 0 (one shot) + // DIR: 1 (read from memory) + // TEIE: 0 (no tx error interrupt) + // HTIE: 0 (no half tx interrupt) + // TCIE: 1 (transmission complete interrupt enable) + // EN: 0 (do not enable DMA yet) + DMA1_Channel4->CFGR = DMA_CFGR1_MINC | DMA_CFGR1_DIR | DMA_CFGR1_TCIE; + + // Enable channel 4 interrupts + NVIC_EnableIRQ(DMA1_Channel4_IRQn); +} + +static void dma_uart_tx(const void *data, uint32_t len) +{ + // Disable DMA channel (just in case a transfer is pending) + DMA1_Channel4->CFGR &= ~DMA_CFGR1_EN; + // Set transfer length and source address + DMA1_Channel4->CNTR = len; + DMA1_Channel4->MADDR = (uint32_t)data; + // Enable DMA channel to start the transfer + DMA1_Channel4->CFGR |= DMA_CFGR1_EN; +} + +int main(void) +{ + static const char message[] = "Hello World!\r\n"; + + SystemInit(); + led_setup(); + uart_setup(); + dma_uart_setup(); + + while (1) + { + dma_uart_tx(message, sizeof(message) - 1); + Delay_Ms(1000); + } +} From f4e539a1fd923ed4e96478ec1320f1b55fd16267 Mon Sep 17 00:00:00 2001 From: doragasu Date: Tue, 10 Sep 2024 16:59:16 +0200 Subject: [PATCH 2/2] Add README.md --- examples/uart_tx_dma/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/uart_tx_dma/README.md diff --git a/examples/uart_tx_dma/README.md b/examples/uart_tx_dma/README.md new file mode 100644 index 00000000..835eda22 --- /dev/null +++ b/examples/uart_tx_dma/README.md @@ -0,0 +1,20 @@ +# UART Tx DMA example + +This example shows how to use DMA to send data through the UART peripheral. The sample code does the following: + +* Send the "Hello World!\r\n" string each second through the UART peripheral, using DMA. +* Toggling a LED each time the DMA transfer is completed, by using the transmission complete interrupt. + +The code is extensively commented, so it should be easy to follow and modify. + +# Usage + +## Wiring + +This code can be run as-is in the [nanoch32V003 board](https://github.com/wuxx/nanoCH32V003). The GPIO pin used for the UART Tx funcion is `D5`. It must be connected to a UART receiver (such as a CP2102 board or similar) configured for 115200 bps, no parity and one stop bit. + +The example also uses the nanoch32V003 on-board LED connected to `D6`. On any other board either connect a LED to `D6` or edit the code to remap the pin to match your hardware setup. + +## Running + +Build and flash the project. You should see the LED toggling each second and the UART receiver should get the "Hello World!" message once per second.