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).
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
- Popular Resources
- Popular amplifiers
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- Find the internal circuit of max2118
- C2000F2802x cyclic query button lights up LED
- GD32E231 DIY Contest (2) GD32E231C Development Environment Getting Started DEMO Test
- Analog Circuit Basics Tutorial! E-book
- [NXP Rapid IoT Review] A Bluetooth BLE communication app designed for NXP IoT in the third week
- How to calculate the input and output resistance of an operational amplifier?
- MSP430F5529 ADC analog-to-digital conversion source program
- 【Share】 RV-STAR development board transplants Chrome's offline game Little Dinosaur
- Thank you for having you + you who helped me and myself who struggled
- Fully digital servo control system based on AC permanent magnet synchronous motor