STM32 USART serial port DMA receive and send mode

Publisher:雅意盎然Latest update time:2017-10-02 Source: eefocusKeywords:STM32  USART  DMA  receive Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Serial port DMA sending:

The process of sending data:

If there is data to be sent in the foreground program, you need to do the following things

1. Place the data to be sent in the data sending buffer. Note: The first address of this data buffer must be written into the DMA configuration when the DMA is initialized.

2. Assign the number of data bytes to be sent in the data buffer to the sending DMA channel (the serial port sending DMA and the serial port receiving DMA are not the same DMA channel)

3. Turn on DMA. Once turned on, DMA starts to send data. Note: When KEIL is debugged, DMA and debugging are not synchronized, that is, no matter what state Keil is in, DMA always sends data.

4. Wait for the send completion flag, which is the flag set in point 3 of the terminal service function below. Or you can decide whether to wait for this flag all the time according to your actual situation, or you can query it in a loop through the state machine. Or other methods.

Determine whether data sending is completed:

After starting DMA and sending, a DMA sending completion interrupt is generated, and the following things are done in the interrupt function:

1. Clear the DMA transmission completion interrupt flag

2. Disable the serial port send DMA channel

3. Set a software flag for the foreground program to indicate that the data has been sent.

Serial port DMA receiving:

The process of receiving data:

The serial port receive DMA is turned on during initialization and is always waiting for data to arrive. There is no need to do anything in the software, just set the configuration during initialization.

Determine whether data reception is completed:

       Here, the reception completion is judged by the serial port idle interrupt, that is, when the serial port data flow stops, an IDLE interrupt will be generated. This interrupt does the following:

1. Turn off the serial port receiving DMA channel for two reasons: 1. To prevent interference caused by receiving data later. 2. To facilitate the reconfiguration of DMA, see point 4 below.

2. Clear all DMA flags

3. Get the number of received data bytes from the DMA register

4. Reset the number of data bytes that DMA will receive next time. Note that here we reset the receive count value for the DMA register. This number can only be greater than or equal to the number of bytes that may be received. Otherwise, when the DMA receive counter decrements to 0, the count value will be reloaded and the count will be decremented again, so the data in the receive buffer will be overwritten and lost.

5. Turn on the DMA channel and wait for the next data reception. Note that the writing of the DMA-related register configuration, such as the writing of the count value in item 4, must be done under the condition of turning off DMA, otherwise the operation will be invalid.

To explain, the IDLE interrupt of STM32 will not be generated all the time when there is no data received at the serial port. The condition for generation is that after the IDLE flag is cleared, it must be triggered after the first data is received. Once the received data flow is interrupted and no data is received, an IDLE interrupt is generated.


/**************************************************************************************************Purely transported*******************************************************************************************/


The above textual analysis is already well written, so I won't repeat it. The original author gave an example, which I transplanted to Battleship, so I'll just post the project.

Here is another example of F207 (same for F4), which is quite different from the F1 series.

/******************************************************************************* 
* Function name: dma_uart2_init 
* Description: Initialize DMA serial port 2 
* DMA1 
* DMA_Channel_4 channel 4 
* DMA2_Stream4 data stream 6 
* Parameter:  
* Return value: None 
********************************************************************************/
void dma_uart2_init(void) 

    DMA_InitTypeDef DMA_InitStructure; 
    NVIC_InitTypeDef NVIC_InitStructure; 
      
    /* DMA clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // Turn on DMA2 clock 
    
//=DMA_Configuration=============================================================================//
// DMA_Cmd(DMA2_Stream7, DISABLE); // Turn off DMA channelDMA_DeInit 
      
    (DMA1_Stream6); // Restore default valueswhile 
    (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){}//Wait for DMA to be configurableDMA_InitStructure.DMA_Channel 

    = DMA_Channel_4; //Channel 4 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DR); // Set the serial port send data 
    registerDMA_InitStructure.DMA_Memory0BaseAddr = (u32)DMA_UART2_SendBuf; // Set the first address of the send 
    bufferDMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // Set the peripheral bit target, memory buffer -> peripheral registerDMA_InitStructure.DMA_BufferSize 
    = 0; // The number of bytes to be sent can actually be set to 0 here, because when it is actually sent, the secondary value will be resetDMA_InitStructure.DMA_PeripheralInc 
    = DMA_PeripheralInc_Disable; // The peripheral address is not increased or adjusted. Whether to adjust or not is automatically implemented by 
    DMADMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Memory buffer address is increased and adjustedDMA_InitStructure.DMA_PeripheralDataSize 
    = DMA_PeripheralDataSize_Byte; // Peripheral data width 8 bits, 1 byteDMA_InitStructure.DMA_MemoryDataSize 
    = DMA_MemoryDataSize_Byte; // Memory data width 8 bits, 1 byte 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Single transfer mode, no loop 
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // Priority setting 
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //DMA_FIFOMode_Disable       
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; 
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst 
        = DMA_PeripheralBurst_Single; 
    DMA_Init(DMA1_Stream6, &DMA_InitStructure); // Write configuration 
      
    /* Enable the DMA Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn; // Send DMA channel interrupt configuration 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; // Priority setting 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStructure); 
      
    DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE);// Enable DMA channel transfer completion interrupt 
    USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);// Enable serial port DMA transmission 


/*********************************************************************************** 
* Function name: uart2_init 
* Description: Initialize serial port 2 
* Parameter: bound: baud rate 
* Return value: None 
*********************************************************************************/
void uart2_init(u32 bound)//Initialize serial port 2 

    //GPIO port settings 
        NVIC_InitTypeDef NVIC_InitStructure; 
        GPIO_InitTypeDef GPIO_InitStructure; 
        USART_InitTypeDef USART_InitStructure; 

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//Enable GPIOb clock 
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//USART2 
      
    GPIO_PinAFConfig(GPIOA, GPIO _PinSource2, GPIO_AF_USART2); //Remapping, TX 
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); //Remapping, RX 
          
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; 
        GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz; //Flip speedGPIO_InitStructure.GPIO_Mode 
        = GPIO_Mode_AF; //Input and output settings, input/output/multiplexing/analogGPIO_InitStructure.GPIO_OType 
        = GPIO_OType_PP; //Output mode, open drain/push- 
        pullGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //Input mode, floating/pull-up/pull- 
        downGPIO_Init(GPIOA, &GPIO_InitStructure); 

   //USART initialization settingsUSART_InitStructure.USART_BaudRate 
        = bound;//Generally set to 9600; 
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//Word length is 8-bit data formatUSART_InitStructure.USART_StopBits 
        = USART_StopBits_1;//One stop 
        bitUSART_InitStructure.USART_Parity = USART_Parity_No;//No parity bit 
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//Transceiver mode 
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//No hardware data flow control 
      
        USART_Init(USART2, &USART_InitStructure);//Initialize serial port  
      
    //Usart1 NVIC configuration         
        NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; 
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//Preemption priority 2 
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //Sub priority 3, sub priority cannot be 0??? 
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ channel enable 
        NVIC_Init(&NVIC_InitStructure); //Initialize VIC register according to the specified parameters 

        USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //Enable interrupt 
        USART_Cmd(USART2, ENABLE); //Enable serial port 
      
    dma_uart2_init(); //Initialize serial port DMA transmission 


/******************************************************************************* 
* Function name: USART2_IRQHandler 
* Description: Serial port 2 interrupt service function, receiving acquisition board data 
* Parameter: None 
* Return value: None 
******************************************************************************/
void USART2_IRQHandler(void)//Serial port 2 interrupt service routine 
{         
        u8 c; 

        if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)//Receive interrupt 
        { 
        USART_ClearITPendingBit(USART2, USART_IT_RXNE); 
          
        LED2 = !LED2; 
                c = USART_ReceiveData(USART2);//Read the receive register, reading data will clear the interrupt 
                write_loop_2_buf(c); 
        } 


//DMA sending application source code 
void DMA1_Stream6_IRQHandler(void) 

    static portBASE_TYPE xHigherPriorityTaskWoken; 
      
    xHigherPriorityTaskWoken = pdFALSE; 
      
    if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6)) 
    { 
// printf("DMA1 interrupt\r\n"); 
        DMA_ClearFlag(DMA1_Stream6, DMA_IT_TCIF6);// Clear flag 
        DMA_Cmd(DMA1_Stream6, DISABLE); // Close DMA channel 
          
        //Send semaphore 
        xSemaphoreGiveFromISR(xSemaphoreHandle_DMA_USART2_SendFlag, &xHigherPriorityTaskWoken);//Send completion flag 
          
        if(xHigherPriorityTaskWoken == pdTRUE) //Give semaphore to unblock the task. If the priority of the unblocked task is higher than the priority of the current task, force a task switch 
        { 
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); 
        } 
    } 


void DMA_USART2_SendData(u8 *buf, u16 size)//DMA serial port 2 sends data 

    memcpy(DMA_UART2_SendBuf, buf, size);//Copy to DMA bufferDMA1_Stream6- 
      
    >NDTR = (u16)size; // Set the number of bytes to send 
// printf("Number of bytes sent = %d\r\n",size); 
    DMA_Cmd(DMA1_Stream6, ENABLE); //Start DMA sendingxSemaphoreTake 
      
    (xSemaphoreHandle_DMA_USART2_SendFlag, portMAX_DELAY);//Use semaphore to wait for sending to complete, no timeout 


Serial port DMA reception is quite suitable for full-duplex mode, but the practicality of half-duplex mode is average, not as good as interrupt plus FIFIO. I haven't studied whether DMA+FIFO is feasible.

There is a problem that cannot be ignored for 485:
 
when the DMA completion interrupt is triggered, there are about 2 bytes that have not been sent. Generally, a delay of 2ms is enough. In order to be safe and adapt to different baud rates, I wrote a query + blocking (OS delay).


Keywords:STM32  USART  DMA  receive Reference address:STM32 USART serial port DMA receive and send mode

Previous article:STM32 anti-aliasing Chinese display alternative solution sharing
Next article:How to set JTAG pins as normal IO ports in STM32

Recommended ReadingLatest update time:2024-11-23 06:34

STM32 communication interface (II) IIC--software simulation
I. Overview         IIC stands for Inter-Integrated Circuit (IC bus). This bus type was designed by Philips Semiconductor in the early 1980s. It is a simple, bidirectional, two-wire, synchronous serial bus. It is mainly used to connect integrated circuits (ICS). IIC is a multi-directional control bus, which means th
[Microcontroller]
STM32 communication interface (II) IIC--software simulation
STM32 external interrupt (EXTI) analysis and application
If you have learned the external interrupt of 51 microcontroller, you will get started quickly;  this blog post is based on STM32F103ZET6 chip, which is compatible with most STM32F10x chips;  the code is based on the 3.5.0 standard library provided by ST official website.  If there are any deficiencies, I hope seniors
[Microcontroller]
How to use printf() function in STM32
STM32 serial communication using printf to send data configuration method (development environment Keil RVMDK It is very convenient to use printf to send data in the STM32 serial communication program. However, there are always problems when using it for the first time. The most common problem is that the main functio
[Microcontroller]
A note on the use of STM32 timers
When we usually use the timer, it is usually in the on state. The usual format for writing timer interrupts is: void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //Event content to be processed. . . . } }    However, during the experiment of th
[Microcontroller]
STM32 FSMC-SRAM routine
The SRAM uses the 55ns IS62WV51216, and the timing requirements of the IS62WV51216 for reading and writing need to be analyzed first. Read Timing   By analyzing the timing diagram, the following information can be extracted (the high and low byte bits and the enable bit are not analyzed because the FSMC access m
[Microcontroller]
STM32 FSMC-SRAM routine
STM32 GPIO and the first STM32 program (marquee)
Today let's talk about GPIO. For a novice like me, GPIO is just like how to open the door before I learn to drive. From this, we can see the importance of learning STM32. Well, without further ado, let me summarize that there are 7 groups of IO ports in the STM32F103ZE development board, each group of IO ports has 16
[Microcontroller]
Study notes on stm32 serial port and 485 communication
stm32 serial port interrupt: USART_IT_PE (Parity Interrupt) USART_IT_TXE (Transmit Interrupt) USART_IT_TC (Transfer Complete Interrupt) USART_IT_RXNE (Receive Interrupt) USART_IT_IDLE (Idle Bus Interrupt) USART_IT_LBD (LIN Break Detect Interrupt) USART_IT_CTS (CTS interrupt) USART_IT_ERR (Error Interrupt) Interrupts
[Microcontroller]
STM32F407 ADC DMA sampling experiment
    Recently, the company has made a 407 prototype, which happens to use the ADC function of 407. PC.2 is needed to detect the RF power of the RFID chip. First, I looked at the data sheet of 407.    It can be configured to channel 12 of any controller of ADC1/2/3. I plan to map PC.2 to ADC3 and use DMA function. I l
[Microcontroller]
STM32F407 ADC DMA sampling experiment
Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号