STM32 serial port idle interrupt + DMA reads MPU6050 data

STM32 serial port idle interrupt

The process of receiving data:

The serial port receiving DMA is in the on state when it is initialized, waiting for the data to arrive, there is no need to do anything in the software, as long as the configuration is set during the initial configuration.
Judge the completion of the data data reception:
The judgement that the reception is completed here is realized by the idle interrupt of the serial port, that is, when the serial port data flow stops, an IDLE interrupt will be generated. The following things are done in this interrupt:

  1. Close the serial port receiving DMA channel, two reasons: a. To prevent the subsequent data receiving, causing interference. b. Facilitate the reconfiguration and assignment of DMA, the fourth point below.
  2. Set the reception complete flag
  3. Process the data in the receive buffer
  4. Reset the number of data bytes to be received by the DMA next time. Note that here is to reset the received 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 At that time, the count value will be reloaded again, and the count will be re-circulated, 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 writing to the DMA related register configuration, such as the write count value in Article 4, must be performed under the condition of turning off the DMA, otherwise the operation is invalid.
    Explain that the IDLE interrupt of STM32 will not be generated all the time when there is no data reception in the serial port. The conditions are such that when the IDLE flag bit is cleared, the first data must be received before it starts. Triggered, once the received data is interrupted, and no data is received, an IDLE interrupt is generated. The IDLE bit will not be set high again until the RXNE bit is set (that is, an idle bus is detected again). RXNE receiving interrupt can not be turned on, reducing the number of interrupts.
    Insert picture description here
void My_UART_Init(void)
{
    
    
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    DMA_InitTypeDef DMA_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      // 使能DMA
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   // 使能GPIOA
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);   // 使能时钟 复用USART

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);  //初始化 USART_TX 即 GPIOA.9 

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);          //初始化 USART_RX 即 GPIOA.10

    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1,&USART_InitStruct);   //初始化 USART

//  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 开启 USART 接收缓冲区非空中断
//  USART_ITConfig(USART1, USART_IT_TXE, ENABLE);   // 开启 USART 发送缓冲区空中断

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  //开启 USART1 总线空闲中断
    USART_Cmd(USART1, ENABLE);//使能USART中断

    DMA_DeInit(DMA1_Channel5);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);    //外设--->内存
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_BufferSize = BufferSize;
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
    DMA_Init(DMA1_Channel5, &DMA_InitStruct);

    DMA_Cmd(DMA1_Channel5, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);              // 使能 USART1接收DMA

    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01;   //抢占优先级 2位 00 01 10 11
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01;          //响应优先级 2位 00 01 10 11
    NVIC_Init(&NVIC_InitStruct);                                //初始化中断
}

Insert picture description here
When the MCU receives externally sent data through the USART, the main program can ignore the steps ①②③, and the DMA directly writes the received data into the buffer RxBuffer. The program will not enter the receive interrupt at this time. After the reception is completed, a receiving idle interrupt is generated, the receiving completion flag is set to 1 in the interrupt service function, the data length in the receiving buffer is calculated, the interrupt bit is cleared, and the DMA is disabled to prevent receiving data when processing data. It is detected in the main program that the receiving completion flag is set to 1, enter the data processing program, and now the receiving completion flag is set to 0, reset the number of data bytes to be received by the DMA next time, and enable the DMA to enter the receiving data state.

void USART1_IRQHandler(void)
{
    
    
    uint8_t clear = clear;  // 用来消除编译器的“没有用到”的提醒
    uint8_t data = 0;

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
    
    
        clear = USART1->SR;
        clear = USART1->DR;

//      RxCounter = BufferSize - DMA1_Channel5->CNDTR;//缓存中的字节数
        RxCounter = BufferSize - DMA_GetCurrDataCounter(DMA1_Channel5);//缓存中的字节数

//      USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

        RxStatus = 1;   //标记接收到一帧
        USART_ClearITPendingBit(USART1, USART_IT_IDLE); // 清除空闲中断

        DMA_Cmd(DMA1_Channel5, DISABLE);                // 停止DMA,清除DMA缓存
    }
}

int main(void)
{
    
    
    uint8_t i = 0;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);             // 初始化中断优先级分组

    My_UART_Init();

    while(1)
    {
    
    
        if(RxStatus == 1)
        {
    
    
            RxStatus = 0;
            i = 0;

            while(RxCounter--)
            {
    
    
                USART_SendData(USART1, RxBuffer[i++]);
                while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);
            }

            memset(RxBuffer, 0, i); // 清除缓存
            RxCounter = 0;

//          DMA1_Channel5->CNDTR = BufferSize;
            DMA_SetCurrDataCounter(DMA1_Channel5, BufferSize);
            DMA_Cmd(DMA1_Channel5, ENABLE);     
        }
    }
}

DMA introduction

Definition of DMA

DMA (Direct Memory Access, DMA) is a memory access technique in computer science. It allows the internal hardware subsystems (computer peripherals) of certain computers to independently read and write system memory without bypassing the CPU. Under the same degree of CPU burden, DMA is a fast data transfer method. It allows hardware devices of different speeds to communicate without relying on a large number of interrupt requests from the CPU.

What is the use of DMA?

Direct memory access is used to provide high-speed data transfer between peripherals and memory or between memory and memory. Without CPU intervention, data can be moved quickly through DMA. This saves CPU resources for other operations.

How many DMA resources are there?

There are two DMA controllers, DMA1 has 7 channels and DMA2 has 5 channels.

Where is the data sent from?

  • Peripherals go to SRAM (I2C/UART, etc. to obtain data and send it to SRAM);
    between the two areas of SRAM;
  • Peripheral to peripheral (ADC reads the data and sends it to TIM1 to control it to generate different PWM duty cycles);
  • SRAM to peripherals (data saved in SRAM is sent to DAC to generate various waveforms);

How much data can DMA transfer?

The traditional concept of DMA is used for the transmission of large quantities of data, but I understand that in STM32, its concept has been expanded, and perhaps more often, fast is the focus of its application. The data can be from 1 to 65535.

DMA controller and arbiter

Now more and more single-chip microcomputers adopt DMA technology to provide high-speed data transfer between peripherals and memories or between memories. When the CPU initiates this transfer action, the transfer action itself isDMA controllerTo implement and complete. STM32 has a DMA controller, it has 7 channels, each channel is dedicated to managing one or more peripherals request memory access, and oneArbiterTo coordinate the priority of each DMA request.

The DMA controller and Cortex-M3 core share the system data bus to perform direct memory data transfer. When the CPU and DMA access the same target (RAM or peripherals) at the same time, the DMA request may stop the CPU from accessing the system bus for several cycles, and the bus arbiter performs cyclic scheduling to ensure that the CPU can get at least half of the system bus (memory Or peripheral) bandwidth.

After an event occurs, the peripheral sends a request signal to the DMA controller. The DMA controller processes the request according to the priority of the channel. When the DMA controller starts to access the peripheral, the DMA controller immediately sends a response signal to the peripheral. When the response signal is received from the DMA controller, the peripheral immediately releases its request. Once the peripheral releases this request, the DMA controller simultaneously cancels the response signal. If more requests occur, the peripheral can start the next processing.

Code display

Serial port idle + DMA main receiving code based on HAL library

Below is the header file

#ifndef DMAT_H
#define DMAT_H
#include "stm32f1xx_hal.h"
#include "usart.h"

extern uint8_t usart2_rx_buffer[33];
extern uint8_t usart3_rx_buffer[25];
void MyU2Init(void);
void MyU3Init(void);
void USART2Handle(void);
void USART3Handle(void);
void MyUsartHandle(UART_HandleTypeDef *HUART);
#endif

Below is the source file

#include "dmat.h"

uint8_t usart2_rx_buffer[33]={
    
    0};
uint8_t usart3_rx_buffer[25]={
    
    0};

float ROLL_ANGLE=0;
float PITCH_ANGLE=0;
float YAW_ANGLE=0;
void MyU2Init(void)
{
    
    
	__HAL_UART_CLEAR_IDLEFLAG(&huart2);
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart2,usart2_rx_buffer,33);
}

void MyU3Init(void)
{
    
    
	__HAL_UART_CLEAR_IDLEFLAG(&huart3);
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart3,usart3_rx_buffer,25);
}
//可以在里面加上想要的功能
//以及数据处理的代码
//这是采用串口+DMA+空闲中断来处理串口接收的数据
void USART2Handle(void)
{
    
    
	if(usart2_rx_buffer[0]==0x55&&usart2_rx_buffer[11]==0x55&&usart2_rx_buffer[22]==0x55)
	{
    
    
		//总共33个字节的数据,每11个字节的数据算一个数据包,三个数据包分别是加速度包,角速度包和角度包
		if(usart2_rx_buffer[23]==0x53)
		{
    
    
			//下面的数据是针对角度包来解析的,得到三个角度
			ROLL_ANGLE=((int16_t)(usart2_rx_buffer[25]<<8|usart2_rx_buffer[24]))/32768.0*180;
			PITCH_ANGLE=((int16_t)(usart2_rx_buffer[27]<<8|usart2_rx_buffer[26]))/32768.0*180;
			YAW_ANGLE=((int16_t)(usart2_rx_buffer[29]<<8|usart2_rx_buffer[28]))/32768.0*180;
		}
	}
}
void USART3Handle(void)
{
    
    
}
void MyUsartHandle(UART_HandleTypeDef *HUART)
{
    
    
	 if(HUART==&huart2)
	{
    
    
		
	if(__HAL_UART_GET_FLAG(HUART,UART_FLAG_IDLE))
	{
    
    
		
		__HAL_UART_CLEAR_IDLEFLAG(HUART);
		HAL_UART_DMAStop(HUART);
		USART2Handle();
		HAL_UART_Receive_DMA(HUART,(uint8_t*) usart2_rx_buffer,33);
		
	}
  }
	else if(HUART==&huart3)
	{
    
    
		if(__HAL_UART_GET_FLAG(HUART,UART_FLAG_IDLE))
	{
    
    
		__HAL_UART_CLEAR_IDLEFLAG(HUART);
		HAL_UART_DMAStop(HUART);
		USART3Handle();
		HAL_UART_Receive_DMA(HUART,(uint8_t*) usart2_rx_buffer,25);
	}
	}
}

Guess you like

Origin blog.csdn.net/sharpzhen/article/details/108720110