1. Introduction to G4 series DMA
First, introduce the DMA of G4 series.
Although the G4 series of 32 is based on the M4 kernel, its DMA channel mapping is quite different from the common F407. First, paste some pictures in the reference manual:
I don't know what you can think of after seeing these two pictures. I believe friends who have seen DMA of H7 and F4 can react. Yes, STM32G4 series is based on M4 kernel and has the same DMAMUX (DMA request multiplexer) as H7. It doesn't matter if you're not familiar with it. DMAMUX is actually a selector that can map the DMA requests of peripherals to our DMA channel (this explanation may be wrong, but I think it's more vivid). And most importantly, there are 115 peripheral DMA requests in this selection, that is, each of the 115 DMA requests can be mapped to any DMA channel. This is more convenient than F4. I remember that the DMA channel of F4 is fixed, and G4 and even H7 are much more free.
One difference between G4 series and H7 and F4 is that G4 does not use the term "STREAMS", but uses CHANNELS instead. This may cause misunderstanding. In fact, they are the same. Then, DMAMUX has a total of 16 CHANNELS, 0 ~ 7 corresponds to DMA1 and 8 ~ 15 corresponds to DMA2.
2. Coding
(1) The code of serial port is not much to say, as shown below:
void my_uart_init(uint32_t baud){ __HAL_RCC_USART1_CLK_ENABLE(); UART_HandleTypeStr.Instance = USART1; UART_HandleTypeStr.Init.BaudRate = baud; UART_HandleTypeStr.Init.HwFlowCtl = UART_HWCONTROL_NONE; UART_HandleTypeStr.Init.ClockPrescaler = UART_PRESCALER_DIV1; UART_HandleTypeStr.Init.Mode = UART_MODE_TX_RX; UART_HandleTypeStr.Init.Parity = UART_PARITY_NONE; UART_HandleTypeStr.Init.StopBits = UART_STOPBITS_1; UART_HandleTypeStr.Init.WordLength = UART_WORDLENGTH_8B; HAL_UART_Init(&UART_HandleTypeStr); HAL_UART_Receive_IT(&UART_HandleTypeStr,receive_buf,sizeof(receive_buf)); }
(2) DMA initialization
In fact, DMA initialization is very similar to our previous peripheral initialization:
① DMA1 and DMAMUX clock;
② Use__ HAL_LINKDMA links peripherals and DMA channels;
③ Structure configuration;
④ DMA DeIint, the purpose of this step is to reset the DMA channel and its parameters first;
⑤ DMA Init to initialize DMA and its parameters.
*⑥ Interrupt configuration, this step can not be configured. DMA interrupt is a more complex topic, so I won't repeat it here.
void my_dma_init(void){ __HAL_RCC_DMA1_CLK_ENABLE(); //Turn on the clock __HAL_RCC_DMAMUX1_CLK_ENABLE(); //Turn on DMA multiplexer clock __HAL_LINKDMA(&UART_HandleTypeStr,hdmatx,DMA_HandleTypeStr); //Peripheral DMA link establishment DMA_HandleTypeStr.Instance = DMA1_Channel1;DMA_HandleTypeStr.Instance = DMA1_Channel1; DMA_HandleTypeStr.Init.Direction = DMA_MEMORY_TO_PERIPH; DMA_HandleTypeStr.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; DMA_HandleTypeStr.Init.MemInc = DMA_MINC_ENABLE; DMA_HandleTypeStr.Init.Mode = DMA_NORMAL; //Mode configuration: normal mode / cyclic mode DMA_HandleTypeStr.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; DMA_HandleTypeStr.Init.PeriphInc = DMA_PINC_DISABLE; DMA_HandleTypeStr.Init.Priority = DMA_PRIORITY_HIGH; DMA_HandleTypeStr.Init.Request = DMA_REQUEST_USART1_TX; if(HAL_DMA_DeInit(&DMA_HandleTypeStr) != HAL_OK) while(1); if(HAL_DMA_Init(&DMA_HandleTypeStr) != HAL_OK) while(1); // HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // DMA interrupt is configured here. If you don't want to interrupt, you can // HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,2,2); // Use the query method and then manually clear the flag bit } //__HAL_DMA_GET_FLAG() //__HAL_DMA_CLEAR_FLAG()
Here are some parameters:
① DMA_HandleTypeStr.Instance = DMA1_Channel1;
This parameter is the choice of STREAM similar to F4 or H7. DMA1 and 2 have six channel choices respectively.
② DMA_HandleTypeStr.Init.Mode = DMA_NORMAL;
In this mode, as the name suggests, NORMAL is the NORMAL transmission, and the transmission stops once. CIRCULAR is the CIRCULAR transmission. After measuring these two parameters, the CIRCULAR will be transmitted continuously. After NORMAL is transmitted once, the flag bit is set, and then we can stop DMA transmission.
③ DMA_HandleTypeStr.Init.Request = DMA_REQUEST_USART1_TX;
This parameter is very important. For the 115 peripheral DMA requests we mentioned earlier, we select the required peripheral requests.
I won't mention the remaining parameters, which are basically the same as those of other models.
(3) Main function
int main(void) { HAL_Init(); my_system_clk_config(); uart_init(115200); my_gpio_init(); my_dma_init(); printf("USART1 DMA TEST\n"); HAL_UART_Transmit_DMA(&UART_HandleTypeStr,send_data,sizeof(send_data)); //Enable serial port 1DMA transmission while(1){ if(__HAL_DMA_GET_FLAG(&DMA_HandleTypeStr,DMA_FLAG_TC1)){ __HAL_DMA_CLEAR_FLAG(&DMA_HandleTypeStr,DMA_FLAG_TC1); //Clear DMA transmission completion flag bit HAL_UART_DMAStop(&UART_HandleTypeStr); //Stop DMA after transfer } HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_4); //Let MCU control LED flip HAL_Delay(500); } }
Test, imitate the DMA test procedure of the point atom, call HAL_ before while. UART_ Transmit_ The DMA function sends, then the light flashes in the while loop, and then wait until the DMA transmission completion flag bit is set, clear the flag bit and stop DMA transmission.
The above is the main content of the project. The phenomenon will not be posted. It is to print "Hello World!" On the upper computer.
Project link:
Add link description
3. Insert description
I wonder if you have the same question as me, HAL_DMA_Start and HAL_DMA_Start_ Neither it function is used, so how are these two functions called? (no doubt friends automatically ignore HA)
Actually, open Hal_ UART_ Transmit_ The DMA function body is as follows
HAL has been called here_ DMA_ Start_ That is, we call HAL_ UART_ Transmit_ After the DMA function, the HAL library automatically starts DMA interrupt, but we have not set the interrupt priority (this is not a big problem. 32 has the default priority. Refer to the section of NVIC in the manual), The interrupt service function is not defined (it seems that the problem is not very big. Some people on the Internet say that they will enter the default interrupt service function, which I don't know and needs to be studied). But the real problem is that the interrupt flag is not cleared! Here is a passage from anfulai's Manual:
That's all. The DMA interrupt system is quite complex. I don't know if I have completely understood it. If any friends understand it, they are welcome to criticize and correct 😀.