Por que nem sempre usar o DMA em favor de interrupções com o UART no STM32? [fechadas]


9

Passei muito tempo no mês passado fazendo o UART (para MIDI) trabalhar com um STM (STM32F103C8T6) usando interrupções, sem muito sucesso.

No entanto, esta noite, usando DMA, funcionou muito rápido.

Como, tanto quanto eu leio, o DMA é mais rápido e alivia a CPU, por que nem sempre o DMA é utilizado para interrupções? Especialmente porque no STM32 parece haver alguns problemas.

Estou usando STM32CubeMx / HAL.


2
Por que não? Isso é uma questão de opinião, alguém que tenta adivinhar qual possível razão técnica ou da mesma maneira ampla demais e, portanto, não é uma questão que pertence aqui. Para nomear um exemplo aleatório, o DMA significará mais latência na reivindicação dos dados, especialmente porque você não obtém nenhum benefício real, a menos que permita que ele colete vários caracteres. Muitas vezes isso pode ser bom, às vezes não.
Chris Stratton

6
Se a interrupção do trabalho levou semanas, é porque você abordou a tarefa da maneira errada; fazer o DMA funcionar pode levar mais tempo - na verdade, é uma tarefa mais complexa; portanto, a aparente facilidade da tarefa mais complexa sobre a mais simples presumivelmente se resume aos recursos que você usou para orientação em cada um, não no mecanismo em si.
Chris Stratton

5
Nunca assuma que o dma libera o processador, às vezes sim, o processador continua, às vezes, o processador não está congelado para segurar o barramento do mecanismo do dma. Trivial para fazer isso com uma implementação de braço, então não posso dizer que todos os braços são assim e todos os x86 são assim ou o que quer que seja, não é tão simples, você sempre deve examinar o design do sistema e talvez fazer um pouco de hacking. O chip que você possui pode muito bem liberar o núcleo do braço, este é apenas um comentário no dma. No que diz respeito à sua pergunta, não faz sentido que você não consiga acompanhar e dma + int é provavelmente a solução completa se você não puder apenas pesquisar.
old_timer

5
As interrupções são bastante triviais na porta serial STM32F. Por que você não posta uma pergunta com seu código para que alguns de nós tentem descobrir onde você está errado? Nunca é uma boa idéia hackear código até que funcione sem entender qual era o problema subjacente.
21717 Jon

7
Na minha (não tão) humilde opinião, esse é um dos aspectos negativos do uso do cubo horrível e inchado. Escreva o software a partir do zero, você aprenderá exatamente como o UART funciona (porque você precisa), entenderá o periférico muito melhor e, a longo prazo, economizará muito tempo.
DiBosco

Respostas:


24

Embora o DMA libere a CPU e, portanto, possa reduzir a latência de outros aplicativos acionados por interrupção em execução no mesmo núcleo, há custos associados:

  • Há apenas uma quantidade limitada de canais DMA e existem limitações sobre como esses canais podem interagir com os diferentes periféricos. Outro periférico no mesmo canal pode ser mais adequado para uso de DMA.

    Por exemplo, se você tem uma transferência I2C em massa a cada 5ms, isso parece ser um candidato melhor para DMA do que um comando de depuração ocasional que chega no UART2.

  • Configurar e manter o DMA é um custo por si só. (Normalmente, configurar o DMA é considerado mais complexo do que configurar a transferência acionada por interrupção normal por caractere, devido ao gerenciamento de memória, mais periféricos envolvidos, o uso do DMA interrompe-se e a possibilidade de você precisar analisar os primeiros caracteres fora do DMA de qualquer forma, veja abaixo.)

  • O DMA pode usar energia adicional , uma vez que é outro domínio do núcleo que precisa ser cronometrado. Por outro lado, você pode suspender a CPU enquanto a transferência do DMA estiver em andamento, se o núcleo suportar isso.

  • O DMA exige que os buffers de memória funcionem (a menos que você esteja usando DMA periférico a periférico), para que haja algum custo de memória associado a ele.

    (O custo da memória também pode estar presente ao usar interrupções por caractere, mas também pode ser muito menor ou desaparecer se as mensagens forem interpretadas imediatamente dentro da interrupção.)

  • O DMA produz uma latência porque a CPU é notificada apenas quando a transferência é concluída / metade da conclusão (consulte as outras respostas).

  • Exceto ao transmitir dados para / de um buffer de anel, você precisa saber com antecedência quantos dados você receberá / enviará.

    • Isso pode significar que é necessário processar os primeiros caracteres de uma mensagem usando interrupções por caractere: por exemplo, ao fazer interface com um XBee, você primeiro lê o tipo e tamanho do pacote e, em seguida, aciona uma transferência de DMA para um buffer alocado.

    • Para outros protocolos, isso pode não ser possível, se eles usam apenas delimitadores de final de mensagem: por exemplo, protocolos baseados em texto que usam '\n'como delimitador. (A menos que o periférico DMA suporte a correspondência em um caractere.)

Como você pode ver, há muitas compensações a serem consideradas aqui. Alguns estão relacionados a limitações de hardware (número de canais, conflitos com outros periféricos, correspondência de caracteres), outros são baseados no protocolo usado (delimitadores, comprimento conhecido, buffers de memória).

Para adicionar alguma evidência anedótica, eu enfrentei todas essas compensações em um projeto de hobby que usava muitos periféricos diferentes com protocolos muito diferentes. Havia algumas compensações a serem feitas, principalmente com base na pergunta "quantos dados estou transferindo e com que frequência vou fazer isso?". Isso essencialmente fornece uma estimativa aproximada do impacto de uma simples transferência acionada por interrupção na CPU. Assim, dei prioridade à transferência I2C acima mencionada a cada 5ms na transferência UART a cada poucos segundos que usava o mesmo canal DMA. Outra transferência UART acontecendo com mais frequência e com mais dados, por outro lado, tem prioridade sobre outra transferência I2C, o que ocorre mais raramente. É tudo uma troca.

Obviamente, usar o DMA também tem vantagens, mas não foi isso que você pediu.


Obrigado pela sua resposta detalhada. MIDI será a parte mais crítica, então acho que o DMA é adequado para ele (embora a velocidade seja baixa: 31250 baud). Eu tenho canais DMA suficientes, mais tarde vou usar outro STM32 ao usar 4 USARTs. Não preciso suspender a CPU, pois ela terá alimentação USB de 5V e preciso processar entre as mensagens (para processar as mensagens no loop principal). Eu tenho uma leitura de 256 bytes e buffer de transmissão de 256 bytes. Posso aumentá-lo mais tarde, se necessário. O STM32f103c8t6 possui 20 KB de RAM, o eventual STM que utilizarei possui 192 KB.
10247 Michel Quijzers

E você me dá uma idéia muito boa de como melhorar. Até agora, sempre leio 1 byte e verifico continuamente quando uma mensagem (MIDI) completa é recebida. Mas eu posso ler o primeiro byte e, dependendo disso, o tamanho é conhecido e posso pedir o resto. Isso me custou outro pequeno buffer, mas tudo bem.
10249 Michel Michelijijers

Ler bytes únicos com DMA é muito ineficiente. Para menor latência e maior eficiência, o uso de interrupções por caractere até você saber o tamanho e a mudança para o DMA seria favorável.
Jonas Schäfer

Bem, eu tive muitos problemas ao usar interrupções (sem DMA), acho que vou usar um recebimento de 1 byte DMA e, depois disso, sei quantos bytes esperarei e faço uma solicitação de DMA para obter mais.
Michel Keijzers

6
Provavelmente isso é um erro - você deve corrigir seu código de interrupção simples, sem DMA.
Chris Stratton

10

Usar DMA geralmente significa que você não está mais interrompendo todos os caracteres, mas apenas depois que um "buffer cheio" de caracteres foi recebido (ou transmitido). Isso aumenta a latência do processamento desses caracteres - o primeiro caractere não é processado até que o último caractere no buffer tenha sido recebido.

Essa latência pode ser uma coisa ruim, especialmente em um aplicativo sensível à latência, como o MIDI, onde alguns ms aqui e ali podem resultar em sérios problemas de jogabilidade para apresentações ao vivo.


O que faço é receber 1 byte de cada vez (portanto, um buffer 'DMA' de 1 byte) e depois de cada retorno de chamada DMA desse byte, para armazená-lo em um buffer de anel que manipulo manualmente. No meu loop principal, pretendo verificar se há mensagens MIDI completas e processá-las.
Michel Keijzers

3
O DMA geralmente é usado para obter vários bytes e interromper apenas quando todos foram recebidos. Interromper após apenas um byte é normal quando não estiver usando o DMA, por isso me faz pensar: qual é o ponto da complicação extra de usar o DMA para isso?
Steve Melnikoff

5
@MichelKeijzers Então, o que você faz é exatamente o mesmo que faria em implementações orientadas a interrupções. Portanto, não há nenhum benefício em usar o DMA nesse caso, e seu problema original provavelmente não será resolvido pelo DMA, mas pela reescrita do seu código (ISR, configuração).
JimmyB

@ JimmyB ... obrigado ... no entanto, devido à resposta de Jonas abaixo, farei uma melhoria para ler tantos bytes quanto a mensagem for longa. Eu sei disso depois de receber o primeiro byte (na maioria dos casos). Do que será mais benéfico usar o DMA sobre interrupções.
27516 Michel Michelijijers

8

O DMA não substitui as interrupções - elas geralmente são usadas juntas! Se você estiver usando o DMA para enviar dados por um UART, por exemplo, ainda precisará de uma interrupção para informar quando o envio está concluído.


Na verdade, talvez apenas no STM32 o mecanismo de interrupção (não DMA puro) seja um pouco desajeitado em comparação com o DMA diretamente.
Michel Keijzers

2
@duskwuff Na verdade não; você pode pesquisar para ver quando o DMA está concluído, e talvez você queira, porque um dos principais motivos para o uso do DMA é não ter que se preocupar com a porta serial até que o programa esteja em um estado em que possa atuar sobre os dados recebidos. dados. Ou, para o DMA de saída, você pode apenas pesquisar para ver se é possível adicionar mais ao buffer de envio.
Chris Stratton

1
@MichelKeijzers: IDK o chip específico, mas geralmente a alternativa ao DMA não é literalmente interrompida, é IO programada (onde você usa instruções da CPU para ler / gravar dados de / para um registro de E / S). Em um manipulador de interrupção, você tipicamente faria uma leitura e depois outra, caso um personagem aparecesse enquanto você lia a primeira, especialmente se isso não desencadear outra interrupção. Ou leia até que um buffer interno esteja vazio, se houver um buffer. Obviamente, você precisa de mais interrupções para o PIO e as configura de maneira diferente.
Peter Cordes

@ ChrisStratton Bom ponto ... até agora não verifiquei se é possível transmitir, apenas transmito algo, sem verificar se está ok. Provavelmente, se não, tento novamente mais tarde.
10119 Michel Michelijijers

@ PeterCordes Parece que o STM32 tem interrupções suficientes para DMA e eu leio toda vez que apenas 1 byte. Mesmo o STM32 mais simples (F103c8t6) tem portas / interrupções DMA suficientes disponíveis.
Michel Keijzers

5

O uso do DMA apresenta algumas questões e desafios interessantes além de todas as outras considerações sobre o uso de periféricos UART. Vou dar alguns exemplos: Suponha que o seu uC esteja sentado em um barramento RS485 (ou o que seja) com outros dispositivos. Existem muitas mensagens no barramento, algumas são destinadas ao seu uC, outras não. Além disso, suponha que esses vizinhos de barramento falem um protocolo de dados diferente, o que implica que os comprimentos das mensagens são diferentes.

Algumas perguntas que surgem apenas ao usar o DMA são:

  • quando eu interrompo?
    • Os DMAs realmente gostam de interromper quando transferem uma quantidade predefinida de dados.
    • O que você faz se nunca receber dados suficientes para acionar uma interrupção de DMA?
  • E se você receber apenas uma mensagem parcial quando o DMA for interrompido?
  • Como são os seus buffers RX? Eles são lineares ou circulares?
    • O DMA pode ser um participante irregular do buffer circular, no sentido de que apenas obedece ao limite do endereço, mas não tem nenhum problema em passar além dos outros ponteiros no sistema de buffer circular.

Enfim, apenas comida para pensar.


Obrigado por essas considerações. Atualmente, eu sempre recebo 1 byte e o armazeno em um buffer de anel, pois minhas mensagens (MIDI) podem ter comprimentos diferentes e não sei qual será o próximo. No loop principal, verifico se há mensagens completas para processá-las (e, se concluídas, removo-as do buffer de anel). Portanto, sempre receberei dados suficientes (a menos que eu perca bytes, tenho que verificar isso). Meu buffer RX tem apenas 1 byte, mas eu o copio em um buffer circular / anel. Não verifiquei se está cheio (preciso adicioná-lo).
Michel Keijzers

Ei, não se preocupe. Tenho certeza de que seu aplicativo será bem programado. Como outros mencionaram, o DMA é ótimo, mas não é gratuito, é tudo. Introduz considerações extras no sistema que não existem se você puder fugir sem usá-lo.
Pgvoorhees 17/11

bem, espero, ainda sou iniciante.
Michel Keijzers

3

No lado de recebimento (pelo que me lembro), o DMA termina em uma correspondência de caracteres ou na contagem de terminais. Alguns protocolos e muitos aplicativos interativos não se encaixam facilmente neste modelo e você realmente precisa lidar com as coisas, caractere por caractere. As técnicas de DMA também podem ser frágeis se o link de comunicação não for confiável, perder um único caractere no fluxo pode facilmente atrapalhar sua máquina de estado de DMA.


Na verdade, estou recebendo byte a byte e copio-o manualmente para um buffer de anel para processá-lo mais tarde.
Michel Keijzers

1

Eu usei o STM32CubeMx / HAL em alguns projetos agora e constatei que o software de manipulação UART que ele gera possui deficiências definidas no lado de recebimento.

Na transmissão, você normalmente deseja enviar um bloco de dados ou linha de texto. Nesse caso, você sabe de antemão quanto tempo dura a transferência de dados e, portanto, o uso do DMA é uma solução óbvia. Você recebe uma interrupção assim que a transferência é concluída e pode usar a função de retorno de chamada completa UART TX para indicar ao código principal que a transmissão está concluída e você pode enviar outro bloco de dados.

Quando se trata de recepção de dados, todas as funções fornecidas pelo ST assumem que você sabe quantos caracteres o dispositivo remetente fornecerá antes de começar a enviar. Normalmente isso não é conhecido. A funcionalidade de interrupção coloca os dados recebidos em um buffer e indica apenas que existem dados disponíveis quando o número predefinido de caracteres foi recebido. Se você tentar usar o DMA ou interromper a funcionalidade para receber dados configurando transferências sequenciais de caracteres únicos, o tempo de configuração de cada uma delas significará que você perderá caracteres com algo diferente das taxas de dados mais lentas (a taxa de transmissão que você deseja começar a perder dados dependerá da velocidade do clock do processador) e carregará o processador excessivamente, não deixando ciclos de instruções para nenhum outro processamento

Para contornar isso, escrevi minha própria função de manipulador de interrupção que armazena os dados em um pequeno buffer circular local e define uma contagem que é lida pelo código principal (um semáforo de contagem RTOS) para indicar que há dados recebidos prontos. O código principal pode, então, coletar os dados desse buffer à vontade, não importa se houver algum atraso na coleta dos dados, desde que o buffer local não transborde antes que os dados sejam coletados.


Eu faço exatamente o mesmo (eu acho). Eu leio 1 byte de cada vez e o armazeno em um buffer cíclico, e pretendo verificar no loop principal se há mensagens completas. Pode ser melhorado um pouco embora.
10119 Michel Michelijijers

Você acha que posso encontrar o problema de que a configuração do DMA sempre sobrecarregará meu processador / caracteres ausentes em 31.250 bauds?
12117 Michel Quijzers

1
Contanto que você configure o DMA para transferir um número de caracteres por vez, isso não será um problema. Eu tenho 4 UARTs executando 115200 e superior e I2C usando DMA sem problemas. As transmissões UART são todas de ~ 20 bytes ou mais. O problema estava usando o DMA para receber no UART (processador L4 a 80MHz, 9600baud).
uɐɪ

Atualmente, defino-o como 1 byte por vez, mas posso melhorá-lo (executando o primeiro byte e depois verifique quantos bytes adicionais são necessários).
19117 Michel Keijzers
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.