Hardware platform: STM32F103ZET6;
Development environment: KEIL 4;
Let's talk about the application communication mode first. The working mode of the serial terminal is similar to that of the DWIN screen. The terminal passively accepts the instructions sent by the MCU, and the terminal will occasionally actively send some data to the MCU (such as the touch information upload of the DWIN screen).
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.
USART and DMA hardware initialization configuration
/*--- LumModule Usart Config ---------------------------------------*/
#define LUMMOD_UART USART3
#define LUMMOD_UART_GPIO GPIOC
#define LUMMOD_UART_CLK RCC_APB1Periph_USART3
#define LUMMOD_UART_GPIO_CLK RCC_APB2Periph_GPIOC
#define LUMMOD_UART_RxPin GPIO_Pin_11
#define LUMMOD_UART_TxPin GPIO_Pin_10
#define LUMMOD_UART_IRQn USART3_IRQn
#define LUMMOD_UART_DR_Base (USART3_BASE + 0x4) //0x40013804
#define LUMMOD_UART_Tx_DMA_Channel DMA1_Channel2
#define LUMMOD_UART_Tx_DMA_FLAG DMA1_FLAG_GL2//DMA1_FLAG_TC2 | DMA1_FLAG_TE2
#define LUMMOD_UART_Tx_DMA_IRQ DMA1_Channel2_IRQn
#define LUMMOD_UART_Rx_DMA_Channel DMA1_Channel3
#define LUMMOD_UART_Rx_DMA_FLAG DMA1_FLAG_GL3//DMA1_FLAG_TC3 | DMA1_FLAG_TE3
#define LUMMOD_UART_Rx_DMA_IRQ DMA1_Channel3_IRQn
void Uart_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* System Clocks Configuration */
//= System Clocks Configuration ====================================================================//
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(LUMMOD_UART_GPIO_CLK, ENABLE); // Enable the clock of the IO port where the serial port is located
/* Enable USART Clock */
RCC_APB1PeriphClockCmd(LUMMOD_UART_CLK, ENABLE); // Start serial port clock
//=NVIC_Configuration==============================================================================//
/* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
/* Enable the DMA Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_Tx_DMA_IRQ; // Send DMA channel interrupt configuration
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // Priority setting
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the USART Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_IRQn; // Serial port interrupt configuration
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//=GPIO_Configuration==============================================================================//
GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE); // I didn't use the default IO port here, so I remapped it. You can configure it according to your hardware situation.
/* Configure USART3 Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Setting of serial port receiving IO port
GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_RxPin;
GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);
/* Configure USART3 Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // Setting of serial port sending IO port
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Set this to multiplexed push-pull output
GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_TxPin;
GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);
DMA_Uart_Init(); // Serial port DMA configuration
/* USART Format configuration ------------------------------------------------------*/
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // Serial port format configuration
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USART3 */
USART_InitStructure.USART_BaudRate = 115200; // baud rate setting
USART_Init(LUMMOD_UART, &USART_InitStructure);
/* Enable USART3 Receive and Transmit interrupts */
USART_ITConfig(LUMMOD_UART, USART_IT_IDLE, ENABLE); // Enable the IDLE interrupt of the serial port
/* Enable the USART3 */
USART_Cmd(LUMMOD_UART, ENABLE); // Enable the serial port
/* Enable USARTy DMA TX request */
USART_DMACmd(LUMMOD_UART, USART_DMAReq_Tx, ENABLE); // Enable serial port DMA transmission
USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // Enable serial port DMA reception
}
void DMA_Uart_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* DMA clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
//=DMA_Configuration==============================================================================//
/*--- LUMMOD_UART_Tx_DMA_Channel DMA Config ---*/
DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // Disable DMA channel
DMA_DeInit(LUMMOD_UART_Tx_DMA_Channel); // Restore default values
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR); // Set the serial port send data register
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Tx_Buf; // Set the first address of the send buffer
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Set peripheral bit destination, memory buffer -> peripheral register
DMA_InitStructure.DMA_BufferSize = LUMMOD_TX_BSIZE; // The number of bytes to be sent. This can actually be set to 0, because the value will be reset when the data is actually sent.
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // The peripheral address is not adjusted. The adjustment is automatically implemented by DMA
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Memory buffer address increase and adjustment
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // Peripheral data width 8 bits, 1 byte
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // Memory data width 8 bits, 1 byte
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Single transfer mode
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // Priority setting
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Disable memory-to-memory DMA mode
DMA_Init(LUMMOD_UART_Tx_DMA_Channel, &DMA_InitStructure); // Write configuration
DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG); // Clear all DMA flags
DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // 关闭DMA
DMA_ITConfig(LUMMOD_UART_Tx_DMA_Channel, DMA_IT_TC, ENABLE); // Enable the transmit DMA channel interrupt
/*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/
DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE); // Disable DMA channel
DMA_DeInit(LUMMOD_UART_Rx_DMA_Channel); // Restore default values
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR); // Set the serial port receive data register
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf; // Set the first address of the receive buffer
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Set the peripheral as the data source, peripheral register -> memory buffer
DMA_InitStructure.DMA_BufferSize = LUMMOD_RX_BSIZE; // Maximum number of bytes that can be received
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // The peripheral address is not adjusted. The adjustment is automatically implemented by DMA
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Memory buffer address increase and adjustment
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // Peripheral data width 8 bits, 1 byte
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // Memory data width 8 bits, 1 byte
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Single transfer mode
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // Priority setting
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Disable memory-to-memory DMA mode
DMA_Init(LUMMOD_UART_Rx_DMA_Channel, &DMA_InitStructure); // Write configuration
DMA_ClearFlag(LUMMOD_UART_Rx_DMA_FLAG); // Clear all DMA flags
DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE); // Enable the receive DMA channel and wait for receiving data
}
void BSP_Init(void)
{
Uart_Init();
}
//============================================================//
DMA send application source code
void DMA1_Channel2_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_FLAG_TC2))
{
LumMod_Uart_DAM_Tx_Over();
}
}
void LumMod_Uart_DAM_Tx_Over(void)
{
DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG); // Clear flag
DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // Disable DMA channel
OSMboxPost(mbLumModule_Tx, (void*)1); // Set the flag. Here I use UCOSII, you can modify it according to your needs
}
void LumMod_Cmd_WriteParam( uint8 sample_num, uint8 *psz_param )
{
uint8 err;
uint8 LumMod_Tx_Index ;
LumMod_Tx_Index = 0;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 1;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 2;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 3;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 4;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 5;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 6;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 7;
LumMod_Tx_Buf[LumMod_Tx_Index++] = 8;
LumMod_Uart_Start_DMA_Tx( LumMod_Tx_Index );
OSMboxPend(mbLumModule_Tx, 0, &err);
}
void LumMod_Uart_Start_DMA_Tx(uint16_t size)
{
LUMMOD_UART_Tx_DMA_Channel->CNDTR = (uint16_t)size; // Set the number of bytes to send
DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, ENABLE); //Start DMA transmission
}
//============================================================//
DMA receiving application source code
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) // Idle interrupt
{
LumMod_Uart_DMA_Rx_Data();
USART_ReceiveData( USART3 ); // Clear IDLE interrupt flag bit
}
}
void LumMod_Uart_DMA_Rx_Data(void)
{
DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE); // Disable DMA to prevent interference
DMA_ClearFlag( LUMMOD_UART_Rx_DMA_FLAG ); // Clear DMA flag
LumMod_Rx_Data.index = LUMMOD_RX_BSIZE - DMA_GetCurrDataCounter(LUMMOD_UART_Rx_DMA_Channel); //Get the number of bytes received
LUMMOD_UART_Rx_DMA_Channel->CNDTR = LUMMOD_RX_BSIZE; // Reassign the count value, which must be greater than or equal to the maximum number of data frames that can be received
DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE); /* DMA is turned on and waiting for data. Note that if the interrupt sends data frames at a very fast rate and the MCU does not have time to process the received data, and the interrupt sends data again, this cannot be turned on, otherwise the data will be overwritten. There are two ways to solve this problem.
1. Before re-enabling the receive DMA channel, copy the data in the LumMod_Rx_Buf buffer to another array, then re-enable DMA, and then process the copied data immediately.
2. Create double buffering. In the LumMod_Uart_DMA_Rx_Data function, reconfigure the buffer address of DMA_MemoryBaseAddr. Then the next received data will be saved in the new buffer and will not be overwritten. */
OSMboxPost(mbLumModule_Rx, LumMod_Rx_Buf); // Send and receive new data flag for the foreground program to query
}
Previous article:Take stm32's TIM2 as an example and configure it step by step to become a timer
Next article:WAV audio format player based on STM32
Recommended ReadingLatest update time:2024-11-16 04:45
- Popular Resources
- Popular amplifiers
- Siemens PLC from Beginner to Mastery with Color Illustrations (Yang Rui)
- Operational Amplifier Practical Reference Handbook (Edited by Liu Changsheng, Zhao Mingying, Liu Xu, etc.)
- How to read electrical control circuit diagrams (Classic best-selling books on electronics and electrical engineering) (Zheng Fengyi)
- usb_host_device_code
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Share a book suitable for C language beginners
- How to use the stepper motor double four beats, please teach me
- Embedded MCU Programming Learning: Understanding State Machines from the Basics
- EEWORLD University Hall ---- Electrical Engineering North China Electric Power University Li Yonggang
- Where can I find a complete list of chip companies?
- ECG display based on ST
- TI revolutionizes car access with Bluetooth low energy technology
- [GD32L233C-START Review] Serial communication function test and program analysis
- Qorvo PAC Series Chips in High-Speed DC Motor Control
- Today at 14:00 | ST Award-winning Live Broadcast "ST60 Contactless Connector and Application Exploration"