Estou tentando encontrar uma maneira eficiente de calcular um inverso em um AVR (ou aproximando-o).
Estou tentando calcular o período de pulso de um motor de passo para que eu possa variar a velocidade linearmente. O período é proporcional ao inverso da velocidade ( p = K/v
), mas não consigo pensar em uma boa maneira de calcular isso em tempo real.
Minha fórmula é
p = 202/v + 298; // p in us; v varies from 1->100
Testando no Arduino, a divisão parece ser completamente ignorada, ficando p
fixa em 298
(embora talvez isso seja diferente no avr-gcc). Eu também tentei somar v
um loop até exceder 202
e contar os loops, mas isso é bem lento.
Eu poderia gerar uma tabela de pesquisa e armazená-la em flash, mas estava pensando se havia outra maneira.
Edit : Talvez o título deva ser "divisão eficiente" ...
Atualização : Como pingswept aponta, minha fórmula para mapear o período para a velocidade está incorreta. Mas o principal problema é a operação de divisão.
Edit 2 : Em uma investigação mais aprofundada, o divide está funcionando no arduino, o problema foi devido à fórmula incorreta acima e a um transbordamento int em outro lugar.
fonte
Respostas:
Uma coisa boa sobre a divisão é que mais ou menos todo mundo está fazendo isso. É um recurso bastante básico da linguagem C, e compiladores como o AVR-GCC (chamado pelo Arduino IDE) escolherão o melhor algoritmo de divisão disponível, mesmo quando o microcontrolador não tiver uma instrução de divisão de hardware.
Em outras palavras, você não precisa se preocupar sobre como a divisão é implementada, a menos que você tenha um caso especial muito estranho.
Se você se preocupa, pode gostar de ler os algoritmos de divisão sugeridos oficiais da Atmel (um otimizado para o tamanho do código e outro otimizado para a velocidade de execução; nem consome memória de dados). Eles estão dentro:
http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf
que é a Nota de aplicação "AVR200: Multiplicar e dividir rotinas" listada na página Atmel para seus (razoavelmente grandes) processadores Atmega como o Atmega 168 e Atmega 328 usados nos Arduinos padrão. A lista de fichas técnicas e notas de aplicação está em:
http://www.atmel.com/dyn/products/product_card.asp?part_id=4720
fonte
parece-me que tudo o que você precisa é uma tabela de pesquisa de 100 entradas. Não fica muito mais rápido que isso.
EDITAR você na verdade apenas uma tabela de pesquisa de 68 valores, já que os valores de v maiores que 67 sempre são avaliados como 300.
fonte
Existem algumas técnicas muito boas mencionadas no livro "Hackers Delight, de Henry Warren e em seu site hackersdelight.org . Para uma técnica que funciona bem com microcontroladores menores ao dividir por constantes, consulte este arquivo .
fonte
Sua função não parece que daria o resultado desejado. Por exemplo, o valor 50 retorna aproximadamente 302, enquanto 100 retorna aproximadamente 300. Esses dois resultados quase não causarão alterações na velocidade do motor.
Se eu entendi direito, você está realmente procurando uma maneira rápida de mapear os números de 1 a 100 para o intervalo de 300 a 500 (aproximadamente), de modo que 1 mapeie para 500 e 100 mapeie para 300.
Talvez tente: p = 500 - (2 * v)
Mas eu posso estar entendendo errado - você está tentando calcular o tempo de uma onda quadrada de frequência constante? O que é o 298?
fonte
Uma maneira eficiente de aproximar divisões é por turnos. por exemplo, se x = y / 103; dividir por 103 é o mesmo que multiplicar por 0,0097087, portanto, para aproximar esse primeiro, selecione um número de turno 'bom' (ou seja, um número de base 2, 2,4,8,16,32 e assim por diante)
Neste exemplo, 1024 é um bom ajuste, pois podemos dizer que 10/1024 = 0,009765 É possível codificar:
x = (y * 10) >> 10;
Lembrando, é claro, de garantir que a variável y não ultrapasse seu tipo quando multiplicada. Não é exato, mas é rápido.
fonte
Em outra nota, se você estiver tentando fazer uma divisão em uma CPU que não suporta divisão, há uma maneira muito legal de fazer isso neste artigo da Wiki.
http://en.wikipedia.org/wiki/Multiplicative_inverse
fonte
Esse processo aqui parece compatível com o mcu, embora possa precisar de um pouco de portabilidade.
Embora pareça que o LUT seria mais fácil. Você precisaria apenas de 100 bytes, menos se usasse alguma interpolação e, como o LUT é preenchido com constantes, o compilador pode até localizá-lo na área de código em vez da área de dados.
fonte
Verifique se a divisão está sendo executada como ponto flutuante. Eu uso o Microchip, não o AVR, mas ao usar o C18, você precisa forçar seus literais a serem tratados como ponto flutuante. Por exemplo. Tente alterar sua fórmula para:
p = 202.0/v + 298.0;
fonte
Você quer rápido, então aqui vai ..... Como o AVR não pode normalizar com eficiência (deslocando para a esquerda até que você não possa mais mudar), ignore os algoritmos de pseudo ponto flutuante. A maneira mais simples para uma divisão inteira muito precisa e rápida em um AVR é através de uma tabela de pesquisa recíproca. A tabela armazenará recíprocos dimensionados por um número grande (digamos 2 ^ 32). Em seguida, você implementa uma multiplicação não assinada32 x não assinada32 = não assinada 64 no assembler, então responda = (numerador * inverseQ32 [denominador]) >> 32.
Eu implementei a função de multiplicação usando o assembler embutido (envolvido na função ac). O GCC suporta "longos longos" de 64 bits, no entanto, para obter o resultado, você precisa multiplicar 64 bits por 64 bits, e não 32x32 = 64 devido a limitações da linguagem C na arquitetura de 8 bits ......
A desvantagem deste método é que você usará 4K x 4 = 16K de flash se desejar dividir por números inteiros de 1 a 4096 ......
Agora, uma divisão não assinada muito precisa é alcançada em cerca de 300 ciclos em C.
Você pode considerar o uso de números inteiros em escala de 24 ou 16 bits para obter mais velocidade e menos precisão.
fonte
O valor de retorno da sua equação já é
p=298
que o compilador divide primeiro e depois adiciona, use a resolução muldiv inteira que é:Usando isso é o mesmo multiplicar
a*f
, com a = número inteiro f = fração.Esse rendimento,
r=a*f
masf=b/c
depois,r=a*b/c
mas ainda não funciona, porque a posição dos operadores produz ar=(a*b)/c
função final ou muldiv, uma maneira de calcular números de fração usando apenas um número inteiro.fonte