AVR에 대한 효율적인 역 (1 / x)


12

AVR에서 역을 계산하는 효율적인 방법을 찾으려고합니다 (또는 근사).

스테퍼 모터의 펄스주기를 계산하여 속도를 선형으로 변경할 수 있습니다. 주기는 속도의 역수 ( p = K/v)에 비례 하지만 즉시 계산하는 좋은 방법은 생각할 수 없습니다.

내 공식은

p = 202/v + 298; // p in us; v varies from 1->100

아두 이노에 테스트, 분할 떠나 완전히 무시 될 것으로 보인다 p고정 298(하지만 아마도이 AVR-GCC 다른 것). 또한 v초과 할 때까지 루프에서 합산 을 시도 하고 루프를 202계산했지만 상당히 느립니다.

조회 테이블을 생성하여 플래시에 저장할 수는 있지만 다른 방법이 있는지 궁금합니다.

편집 : 어쩌면 제목은 "효율적인 분할"이어야합니다 ...

업데이트 : pingswept가 지적했듯이 기간을 속도에 매핑하는 공식이 잘못되었습니다. 그러나 주요 문제는 나누기 연산입니다.

편집 2 : 추가 조사에서 arduino에서 나누기가 진행되고 있으며 문제는 위의 잘못된 수식과 다른 곳의 int 오버플로 때문입니다.


2
v는 정수 또는 부동 소수점입니까?
mjh2007

정수이지만 우리에게 마침표를주기 때문에 정수 나누기는 여기에서 충분히 정확합니다.
피터 깁슨

100 개의 정수 값을 미리 계산하고 속도에 정말로 관심이 있다면 곱셈을위한 사전 스케일러의 룩업 테이블을 만들 수 있습니다. 물론 메모리 트레이드 오프가 있습니다.
RYS

답변:


7

나눗셈의 좋은 점은 모두가 그것을하고 있다는 것입니다. C 언어의 핵심 기능이며 AVR-GCC (Arduino IDE에서 호출)와 같은 컴파일러는 마이크로 컨트롤러에 하드웨어 분할 명령이없는 경우에도 사용 가능한 최상의 분할 알고리즘을 선택합니다.

다시 말해서, 매우 특별한 경우가 없다면 분할이 어떻게 구현되는지에 대해 걱정할 필요가 없습니다.


걱정된다면 Atmel의 공식 제안 알고리즘 (코드 크기에 최적화 된 알고리즘과 실행 속도에 최적화 된 알고리즘, 데이터 메모리를 사용하지 않음)을 읽는 것이 좋습니다. 그들은에 있습니다 :

http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf

이것은 표준 Arduinos에서 사용되는 Atmega 168 및 Atmega 328과 같은 Atmega 페이지에 대한 Atmel 페이지에 나열된 애플리케이션 노트 "AVR200 : 곱하기 및 나누기 루틴"입니다. 데이터 시트 및 애플리케이션 노트 목록은 다음과 같습니다.

http://www.atmel.com/dyn/products/product_card.asp?part_id=4720


4

당신이 필요로하는 모든 것은 100 항목 조회 테이블입니다. 그것보다 훨씬 빠르지 않습니다.

#define VALUE_FOR_V_EQUALS_ZERO 0
uint16_t formula_lookup[100] = {VALUE_FOR_V_EQUALS_ZERO, 500, 399, 365, 348, ..., 300};

...

//"calculate" formula
p = formula_lookup[v > 67 ? 67 : v];

67보다 큰 v의 값은 항상 300으로 평가되므로 실제로 68 개의 값 조회 테이블 만 편집 하십시오.


질문에서 말했듯이, 다른 방법이 있는지 궁금합니다.
Peter Gibson


3

함수가 원하는 결과를 얻지 못하는 것 같습니다. 예를 들어 값 50은 대략 302를, 100은 대략 300을 반환합니다.이 두 결과는 모터 속도에 거의 변화를주지 않습니다.

내가 당신을 올바르게 이해한다면, 실제로 1-100을 300-500 범위 (약)에 매핑하는 빠른 방법을 찾고 있습니다 .1은 500에 매핑되고 100은 300에 매핑됩니다.

아마도 시도해보십시오 : p = 500-(2 * v)

그러나 나는 오해 할 수도 있습니다. 일정한 주파수 구형파의 온 타임을 계산하려고합니까? 298은 무엇입니까?


예, 감사합니다. 공식이 잘못되었습니다. 요점은 목표 속도를 매 시간마다 일정하게 변화시킴으로써 스테퍼의 출력에서 ​​선형 가속을 얻는 것입니다 (speed ++ say). 이는 + ve 에지가 스테퍼 모터 컨트롤러로 전송되는주기 (주파수)에 매핑되어야하므로 역 관계 (p = 1 / v)입니다.
피터 깁슨

일정한 가속, 즉 선형 적으로 증가하는 속도를 의미합니까?
pingswept September

아 그래, 지속적인 가속, 나는 원래 질문을 쓸 때 그것을 고쳐서 거기에서 그것을 고치는 것을 기억해라
Peter Gibson

3

나누기를 근사화하는 효율적인 방법은 교대입니다. 예를 들어 x = y / 103이면; 103으로 나누는 것은 0.0097087을 곱한 것과 동일하므로 먼저 근사하기 위해 '좋은'쉬프트 번호를 선택하십시오 (즉, 2 진수, 2,4,8,16,32 등)

이 예제에서 1024는 10/1024 = 0.009765라고 말할 수 있듯이 적합합니다.

x = (y * 10) >> 10;

물론 변수 y가 곱해질 때 그 유형이 오버플로되지 않도록 기억하십시오. 정확하지는 않지만 빠릅니다.


이는 timrorr이 제공 한 링크의 기술과 유사하며 상수로 나눌 때 잘 작동하지만 컴파일 타임에 알 수없는 값으로 나눌 때는 그렇지 않습니다.
피터 깁슨

3

또 다른 참고 사항에서 나누기를 지원하지 않는 CPU에서 나누기를 시도하는 경우이 위키 기사에서 정말 멋진 방법이 있습니다.

http://en.wikipedia.org/wiki/Multiplicative_inverse

곱셈과 뺄셈 만 사용하여 x의 역수를 근사하기 위해 숫자 y를 추측 한 다음 y를 2y − xy2로 반복해서 바꿀 수 있습니다. y의 변화가 충분히 작게되고 유지되면, y는 x의 역수의 근사치입니다.


나는 다른 방법 언급에이 비교하는 방법을 궁금해 재미
피터 깁슨

1

이 과정 여기가 그 이식의 조금을해야 할 수도 있지만, MCU 친화적 보인다.

LUT가 더 쉬운 것처럼 보이지만. 보간을 사용한 경우 100 바이트 만 필요하며 LUT에 상수가 채워져 있으므로 컴파일러가 데이터 영역 대신 코드 영역에서 찾을 수도 있습니다.


나는 배당 자와 같거나 초과 할 때까지 제수를 합산하는 것과 비슷한 것을 시도했지만 상당히 느리다는 것을 알았습니다. LUT가 갈 길이 될 것 같습니다. avr-gcc를 사용하면 플래시에 저장하기 위해 <avr / progmem.h>에 특수 매크로가 필요합니다.
피터 깁슨

1

나누기가 부동 소수점으로 수행되고 있는지 확인하십시오. AVR이 아닌 Microchip을 사용하지만 C18을 사용할 때는 리터럴을 부동 소수점으로 처리해야합니다. 예 : 공식을 다음과 같이 변경하십시오.

p = 202.0/v + 298.0;


1

AVR은 효율적으로 정규화를 수행 할 수 없으므로 (더 이상 이동할 수 없을 때까지 왼쪽으로 이동) 의사 부동 소수점 알고리즘을 무시하십시오. AVR에서 매우 정확하고 빠른 정수 나누기를위한 가장 간단한 방법은 상호 조회 테이블을 이용하는 것입니다. 이 테이블은 큰 수 (2 ^ 32)로 스케일링 된 역수를 저장합니다. 그런 다음 unsigned32 x unsigned32 = unsigned 64 곱셈을 어셈블러에서 구현하므로 answer = (numerator * inverseQ32 [denominator]) >> 32입니다.
인라인 어셈블러를 사용하여 곱셈 함수를 구현했습니다 (ac 함수로 래핑 됨). 그러나 GCC는 64 비트 "long long"을 지원하지만 8 비트 아키텍처의 C 언어 제한으로 인해 32x32 = 64가 아니라 64 비트에 64 비트를 곱해야합니다.

이 방법의 단점은 1에서 4096까지의 정수로 나누려면 4K x 4 = 16K 플래시를 사용한다는 것입니다.

C에서 약 300 주기로 매우 정확한 부호없는 분할이 이루어집니다.

속도를 높이고 정확도를 높이기 위해 24 비트 또는 16 비트 스케일 정수 사용을 고려할 수 있습니다.


1
p = 202/v + 298; // p in us; v varies from 1->100

방정식의 반환 값은 이미 p=298컴파일러가 먼저 나눈 다음 더하기 정수 muldiv 해상도를 사용하기 때문에 다음과 같습니다.

p = ((202*100)/v + (298*100))/100 

이것을 사용하는 것은 a*fa = integer f = fraction과 같은 곱셈 입니다.

그 수율은 r=a*f있지만, f=b/c다음 r=a*b/c사업자의 위치가 최종 얻을 수 있기 때문에 아직 작동하지 않습니다 r=(a*b)/c또는 muldiv 기능 만 정수를 사용하여 계산 분수 숫자에 방식.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.