2024-07-31 11:32 AM
Hello I need to send a string via USART every 500ms. I tried to use DMA that seems to be easy to configure. USART transfer works but only without DMA (test in while main cycle). I do not see any problem in DMA configuration. Can you tell me it there is something that I do not set?
I start DMA transfer in Systick cycle.
Thank you
#include "main.h"
#include "stm32g0xx.h"
#include <stdint.h>
#define BAUD_RATE 9600
#define SYS_CLK 64000000
const char message[] = "Hello, world!\r\n";
void DMA_Init(void) {
// Enable DMA clock
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// Configure DMA for USART1 TX (DMA1 Channel 1)
DMA1_Channel1->CPAR = (uint32_t)&USART1->TDR;
DMA1_Channel1->CMAR = (uint32_t)message;
DMA1_Channel1->CNDTR = sizeof(message) - 1;
DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;
// Enable DMA1 interrupt
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
void USART1_Init(void) {
// Enable clocks
RCC->IOPENR |= RCC_IOPENR_GPIOBEN; // Enable GPIOB clock
RCC->APBENR2 |= RCC_APBENR2_USART1EN; // Enable USART1 clock
// Configure PB6 as USART1_TX and PB7 as USART1_RX
GPIOB->MODER &= ~(GPIO_MODER_MODE6_Msk | GPIO_MODER_MODE7_Msk);
GPIOB->MODER |= (0x2 << GPIO_MODER_MODE6_Pos) | (0x2 << GPIO_MODER_MODE7_Pos); // Set PB6 and PB7 to AF mode
GPIOB->AFR[0] |= (0x0 << GPIO_AFRL_AFSEL6_Pos) | (0x0 << GPIO_AFRL_AFSEL7_Pos); // Set AF0 (USART1)
// Configure USART1
USART1->BRR = SYS_CLK / BAUD_RATE; // Set baud rate
USART1->CR3 |= USART_CR3_DMAT; // Enable DMA for transmitter - TOTO JE NAVIC
USART1->CR1 |= USART_CR1_TE; // Enable transmitter
USART1->CR1 |= USART_CR1_UE; // Enable USART
}
void DMA1_Channel1_IRQHandler(void) {
if (DMA1->ISR & DMA_ISR_TCIF1) {
// Clear the transfer complete interrupt flag
DMA1->IFCR |= DMA_IFCR_CTCIF1;
// Optional: Disable DMA channel if needed, and re-enable if you need to prepare for the next transfer
DMA1_Channel1->CCR &= ~DMA_CCR_EN; // Disable DMA channel
DMA1_Channel1->CNDTR = sizeof(message) - 1;//1; // Reset number of data to transfer
DMA1_Channel1->CCR |= DMA_CCR_EN; // Re-enable DMA channel
}
}
void SysTick_Handler(void)
{
SysTickCounter++;
if (SysTickCounter>500) {
DMA1_Channel1->CCR |= DMA_CCR_EN;
SysTickCounter = 0;
}
}
void USART1_SendString(const char *str) {
while (*str) {
while (!(USART1->ISR & USART_ISR_TXE_TXFNF_Msk)); // Wait until TX buffer is empty
USART1->TDR = *str++; // Transmit character
}
}
int main(void)
{
CLK_Config();
GPIO_Init();
SysTick_Init();
DMA_Init();
USART1_Init();
while (1)
{
//USART1_SendString(message); - THIS WORKS, BUT NOT WITH DMA
//for (volatile int i = 0; i < 1000000; i++);
}
}
2024-07-31 11:21 PM
If your DMA target is a peripheral register, the peripheral (UART) determines the speed of the DMA transfer not to overrrun TDR. DMAMUX is used for that like so
DMAMUX1_Channel0->CCR = ?? << DMAMUX_CxCR_DMAREQ_ID_Pos;
(code was for another chip, check ?? for the request ID in the ref.man.) The line
USART1->CR3 |= USART_CR3_DMAT;
will trigger the very first byte transmitted, so it should be done last, when everthing else is setup.
Some DMA registers cannto be set when DMA is enabled.
Here is my code for the other chip for comparison:
#include <stm32c011xx.h>
void mem2uart_dma() {
static char src[64] = "The quick brown fox jumped over the lazy dog.\n";
USART1->TDR = '!'; // send one char for connection testing (without DMA))
while (!(USART1->ISR & USART_ISR_TXE_TXFNF)); // busy wait for TDR empty
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // this is good for DMA and DMAMUX
(void)RCC->AHBENR;
if( DMA1_Channel1->CCR & DMA_CCR_EN) { // channel was in use before
while(!(DMA1->ISR & DMA_ISR_TCIF1)); // wait for transfer complete (TC) channel flag
DMA1_Channel1->CCR &= ~DMA_CCR_EN; // disable DMA channel for setup
}
// route peripheral DMA request to DMA channel
// Table 34: DMAMUX usart1_tx_dma == 51
// caution: DMAMUX1_Channel0 is for DMA1_Channel1 and so on!
DMAMUX1_Channel0->CCR = 51 << DMAMUX_CxCR_DMAREQ_ID_Pos;
DMA1->IFCR = DMA_IFCR_CGIF1; // clear all (HT, TC, TE) flags for DMA channel 1
DMA1_Channel1->CPAR = (uint32_t)&(USART1->TDR);
DMA1_Channel1->CMAR = (uint32_t)src;
DMA1_Channel1->CNDTR = sizeof(src);
DMA1_Channel1->CCR =
0 << DMA_CCR_MEM2MEM_Pos // MEM2MEM 0: no memory-to-memory mode
| 0 << DMA_CCR_PL_Pos // PL priority level 0: low.. 3: very high
| 0 << DMA_CCR_MSIZE_Pos // MSIZE 0: 8-bit 1: 16-bit 2: 32-bit
| 0 << DMA_CCR_PSIZE_Pos // PSIZE 0: 8-bit 1: 16-bit 2: 32-bit
| 1 << DMA_CCR_MINC_Pos // MINC memory increment mode on (1)
| 0 << DMA_CCR_PINC_Pos // PINC peripheral increment mode off (0)
| 0 << DMA_CCR_CIRC_Pos // CIRC 1: circular mode
| 1 << DMA_CCR_DIR_Pos // DIR 0: read from peripheral, 1: memory
| 0 << DMA_CCR_TEIE_Pos // TEIE transfer error interrupt 1: enable
| 0 << DMA_CCR_HTIE_Pos // HTIE half transfer interrupt 1: enable
| 0 << DMA_CCR_TCIE_Pos // TCIE transfer complete interrupt 1: enable
;
DMA1_Channel1->CCR |= DMA_CCR_EN; // enable DMA channel
// A channel, as soon as enabled, may serve any DMA request from the peripheral
// connected to this channel, or may start a memory-to-memory block transfer.
USART1->CR3 |= USART_CR3_DMAT; // trigger usart1_tx_dma request
}
hth
KnarfB