Explicação detalhada do modo de buffer duplo DMA do STM32

O que é buffer duplo DMA?

Ao usar o DMA do STM32, geralmente usamos transmissão DMA comum, mas o STM32 vem com uma configuração de modo de buffer duplo. Quais são as vantagens deste modo?
A seguir explicarei em detalhes na introdução abaixo:

DMA significa Direct Memory Access, que é acesso direto à memória.
O uso do DMA comum é realizado no fluxo de dados DMA. Após definir o ponto inicial e final do DMA e a quantidade de dados a serem transferidos, a transmissão do DMA pode ser iniciada.
Insira a descrição da imagem aqui
Depois que o DMA é ligado, ele começa a transferir dados do ponto inicial para o ponto final. Cada vez que é transferido, o tamanho dos dados transferidos é reduzido em 1. Quando a quantidade de dados transferidos é 0, o DMA para transferindo.

Se o modo de transmissão DMA for o modo normal, quando a quantidade de dados de transmissão for 0, será necessário redefinir manualmente a quantidade de dados de transmissão para total no programa para iniciar a próxima transmissão de dados DMA.
Se o modo de transmissão DMA for circular, quando a quantidade de dados de transmissão for 0, a quantidade de dados de transmissão será automaticamente definida como total, para que os dados possam ser transmitidos continuamente.
Insira a descrição da imagem aqui
Então, o que significa buffer duplo?
Sabemos que o DMA comum possui apenas uma área de armazenamento de dados alvo, ou seja, se novos dados forem transferidos quando os dados estiverem cheios, os dados antigos serão substituídos pelos novos dados. (Esta é a deficiência do DMA comum)
Insira a descrição da imagem aqui

No modo de buffer duplo, nosso DMA possui duas áreas de armazenamento de dados de destino, ou seja, buffer duplo.
Quando uma transferência completa de dados for concluída (ou seja, o valor do contador muda do valor inicial para 0), ele apontará automaticamente para outra memória área.
Insira a descrição da imagem aqui

Como usar o buffer duplo DMA?

Então, como usamos buffer duplo em nosso programa?
Aqui temos duas soluções para usar buffer duplo, uma é usar buffer duplo para pular automaticamente para a área de memória e a outra é pular ativamente para a área de memória.

O modo de transmissão que usamos DMA aqui é o modo circular

DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;

O primeiro é o primeiro tipo:
Aqui tomamos como exemplo o uso do controle remoto do DJI RoboMaster:
Primeiro, definimos as duas áreas de memória usadas pelo buffer duplo.

uint8_t sbus_rx_buffer[2][18u]; //double sbus rx buffer to save data

Aqui definimos o comprimento como 18, que é o comprimento de um quadro de dados de controle remoto.

DMA_InitStruct.DMA_BufferSize = 18;

Em seguida, configure os dois endereços de área de memória do buffer duplo

DMA_InitStruct.DMA_Memory0BaseAddr =  (uint32_t)&sbus_rx_buffer[0][0];

Configure aqui o endereço da área de memória 2 e aponte o DMA atual para o endereço da área de memória 1

DMA_DoubleBufferModeConfig(DMA1_Stream1,(uint32_t)&sbus_rx_buffer[1][0],DMA_Memory_0); //first used memory configuration

Habilitar buffer duplo de DMA

DMA_DoubleBufferModeCmd(DMA1_Stream1, ENABLE);

Quando usamos DMA, usamos a interrupção ociosa da porta serial para receber dados quadro a quadro.
Processar dados na função de processamento de interrupção da porta serial

 
 uint16_t this_time_rx_len = 0;	//当前剩余数据长度

 if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)	//判断是否为空闲中断
    {
    
    
        USART_ReceiveData(USART3);	//清除空闲中断标志位

        if(DMA_GetCurrentMemoryTarget(DMA1_Stream1) == DMA_Memory_0)	//获取当前目标内存是否为 DMA_Memory_0
        {
    
    
            //重新设置DMA
            DMA_Cmd(DMA1_Stream1, DISABLE);
					  this_time_rx_len = DMA_GetCurrDataCounter(DMA1_Stream1);	//获取当前剩余数据量
					
					 DMA_SetCurrDataCounter(DMA1_Stream1, 18);	//重新设置数据量
					  
					 DMA_Cmd(DMA1_Stream1, ENABLE);
            if(this_time_rx_len == 18)	//接收成功18个字节长度
            {
    
    
                //处理遥控器数据
                RemoteDataProcess(sbus_rx_buffer[1]);	//Memory_1
            }

        }
        else	//获取当前目标内存是否为 DMA_Memory_1
        {
    
    
            //重新设置DMA
            DMA_Cmd(DMA1_Stream1, DISABLE);
					  this_time_rx_len = DMA_GetCurrDataCounter(DMA1_Stream1);		//获取当前剩余数据量

						DMA_SetCurrDataCounter(DMA1_Stream1, 18);	//重新设置数据量

						DMA_Cmd(DMA1_Stream1, ENABLE);

            if( this_time_rx_len== 18)	//接收成功18个字节长度
            {
    
    
                //处理遥控器数据
                RemoteDataProcess(sbus_rx_buffer[0]);	//Memory_0
            }

        }
    }

Esta lógica ocorre após o programa ser redefinido e quando um quadro de dados de controle remoto é enviado, a área Memory0 é preenchida com 18 bytes e o DMA aponta automaticamente para Memory1, portanto, quando a função de processamento de interrupção é inserida pela primeira vez, o ponteiro obtido quando a memória é apontada é obtido.É Memória1.
Em seguida, determine se o tamanho dos dados restantes é 18. Em caso afirmativo, significa que a recepção de dados do quadro anterior é normal e os dados podem ser processados. Se não for 18, significa que o comprimento de recepção de dados do quadro anterior é anormal e o os dados não serão processados ​​​​neste momento.Quando o próximo quadro de dados for enviado, os dados serão verificados e os dados serão processados ​​somente depois que estiverem normais.

Depois, há o segundo tipo:
aqui alteramos o BufferSize para um valor maior que o comprimento dos dados de um quadro (maior que 18), de modo que após a conclusão da transmissão de dados de um quadro, o DMA não aponte para a próxima memória área devido ao valor do contador mudar para 0.

DMA_InitStruct.DMA_BufferSize = 36;
 uint16_t this_time_rx_len = 0;	//当前剩余数据长度

 if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)	//判断是否为空闲中断
    {
    
    
        USART_ReceiveData(USART3);	//清除空闲中断标志位

        if(DMA_GetCurrentMemoryTarget(DMA1_Stream1) == DMA_Memory_0)	//获取当前目标内存是否为 DMA_Memory_0
        {
    
    
            //重新设置DMA
            DMA_Cmd(DMA1_Stream1, DISABLE);
					  this_time_rx_len = DMA_GetCurrDataCounter(DMA1_Stream1);	//获取当前剩余数据量  接收18字节,则剩余数据量为36-18=18
					
					  DMA_SetCurrDataCounter(DMA1_Stream1, 36);	//重新设置数据量
						DMA1_Stream1->CR |= (uint32_t)(DMA_SxCR_CT);  //将DMA指向Memory1
					
						DMA_Cmd(DMA1_Stream1, ENABLE);
            if(this_time_rx_len == 18)	//接收成功18个字节长度
            {
    
    
                //处理遥控器数据
                RemoteDataProcess(sbus_rx_buffer[0]);	//Memory_0
            }

        }
        else	//获取当前目标内存是否为 DMA_Memory_1
        {
    
    
            //重新设置DMA
            DMA_Cmd(DMA1_Stream1, DISABLE);
					  this_time_rx_len = DMA_GetCurrDataCounter(DMA1_Stream1);		//获取当前剩余数据量 接收18字节,则剩余数据量为36-18=18

						DMA_SetCurrDataCounter(DMA1_Stream1, 36);	//重新设置数据量
						DMA1_Stream1->CR &= ~(uint32_t)(DMA_SxCR_CT);  将DMA指向Memory0

						DMA_Cmd(DMA1_Stream1, ENABLE);

            if( this_time_rx_len == 18)	//接收成功18个字节长度
            {
    
    
                //处理遥控器数据
                RemoteDataProcess(sbus_rx_buffer[1]);	//Memory_1
            }

        }
    }

A diferença entre dois métodos de uso de buffer duplo DMA

A diferença entre o segundo método e o primeiro método é semelhante: este método define o BufferSize para um valor maior que 18, ou seja, quando o primeiro quadro de transmissão de dados for concluído, o valor do contador não será preenchido automaticamente e a memória área ainda será Ponto para Memória0, então salvamos a quantidade de dados restante, preenchemos o valor do Contador, então apontamos DMA para Memória1 e, finalmente, decidimos se processaremos os dados julgando a quantidade de dados restante.

Como você pode ver, no primeiro método não usamos

DMA1_Stream1->CR &= ~(uint32_t)(DMA_SxCR_CT);  将DMA指向Memory0
DMA1_Stream1->CR |= (uint32_t)(DMA_SxCR_CT);  将DMA指向Memory0

Isso ocorre porque quando a quantidade de dados transmitidos é consistente com o tamanho do BufferSize, após o recebimento de um quadro completo de dados, o DMA apontará automaticamente para o próximo endereço de memória, de modo que a alteração manual do ponteiro de memória no segundo método é automaticamente gerenciado pelo DMA.

Os direitos autorais pertencem ao autor (Alliance Team Mizuki Mining). Caso precise reimprimir ou citar, indique a fonte e o autor. Caso queira utilizá-lo para outros fins ou tenha alguma outra dúvida, entre em contato com o autor deste artigo. .

Acho que você gosta

Origin blog.csdn.net/zhang1079528541/article/details/121049656
Recomendado
Clasificación