저는 Arduino Uno로 작은 프로젝트를하고있었습니다. 엔코더를 사용하여 차동 휠 시스템이 전진하는 정도를 측정 할 때 인터럽트가 포함되었습니다. 내 로봇은 앞으로 만 움직입니다. 따라서 각 인코더에서 단일 채널 만 사용합니다. 다음은 두 가지 인터럽트 루틴입니다.
ISR (INT0_vect){ encoderRPos = encoderRPos + 1; } ISR (INT1_vect){ encoderLPos = encoderLPos + 1; }
변수 encoderRPos
및 encoderLPos
는 volatile int
유형입니다. 인터럽트 루틴에서 변경되는 변수는 휘발성 유형이어야 함을 이해합니다. 이는 이러한 변수를 사용하는 코드의 다른 부분에 언제든지 변경 될 수 있음을 경고하기위한 것입니다.
하지만 내 코드에서 일어난 일은 조금 이상했고 설명 할 수 없었습니다. 왼쪽 휠로 이동 한 거리를 계산하는 방법은 다음과 같습니다.
#define distancePerCount 0.056196868 float SR = distancePerCount * (encoderRPos - encoderRPosPrev); float SL = distancePerCount * (encoderLPos - encoderLPosPrev); encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos;
하지만 직렬 모니터에 다음을 인쇄하면 이상 현상이 발견됩니다.
p>
세 번째를 보면 열, (SL) 그 값이 얼마 동안 너무 높습니다. 이것은 모든 계산을 혼란스럽게합니다.
내가 얻은 SL의 값을 취하면 얻을 수있는 유일한 단서 ( 3682), 이는 항상 상수이고 (encodeLPos - encoderLPosPrev)
를 다시 계산하면 65519.66이됩니다. 이는 unsigned int
의 최대 값에 가깝습니다. . 즉, (encoderLPos - encoderLPosPrev)
가 오버플로를 일으키고 차이가있는 두 값이 모두 약 5000 정도 밖에되지 않습니다!
그리고 나는 그것을 해결할 수있었습니다. 행운을 빕니다. 이것이 제가 코드를 수정 한 방법입니다.
static int encoderRPosPrev = 0; static int encoderLPosPrev = 0; int diffL = (encoderLPos - encoderLPosPrev); int diffR = (encoderRPos - encoderRPosPrev); float SR = distancePerCount * diffR; float SL = distancePerCount * diffL; encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos;
무슨 일이 일어 났는지 이해할 수 없습니다. 내가 알아야 할 휘발성 변수에 대한 것이 있습니까?
업데이트 : 여기 가보고 싶은 경우 전체 코드입니다. 허용 된 답변에서 제안 된 내용으로 변경 한 후에도 잘 작동합니다.
댓글
답변
중요 섹션 에 대해 알아야합니다.
내용 아마도 발생하는 것은 변수가 계산 중간에 인터럽트 루틴에 의해 변경되고 있다는 것입니다. “수정”은 휘발성 변수로 계산을 수행하는 데 소요되는 시간을 줄여 충돌이 발생할 가능성을 줄입니다.
당신이해야 할 일은 휘발성 변수를 인터럽트가 비활성화 된 지역 변수에 복사하는 것입니다. 짧은 기간.
cli(); int l = encoderLpos; int r = encoderRpos; sei();
Arduino는 8 비트 CPU이기 때문에 16 비트 값에 대한 수학적 연산을 수행하려면 여러 어셈블리 명령이 필요합니다. 단순한 추가를 위해 많은 명령어를 사용하면 부동 소수점이 더욱 나빠집니다. 나누기와 곱하기는 훨씬 더 많이 사용합니다. 인터럽트는 해당 명령 목록 중에 발생하는 많은 기회를가집니다. 이와 같은 할당을 수행 한 다음 계산에서 새로운 지역 변수를 사용하면 휘발성 변수를 처리하는 데 필요한 지침이 절대적으로 최소화됩니다. 할당하는 동안 인터럽트를 해제하면 변수를 사용하는 동안 변수를 변경할 수 없습니다.이 코드 조각을 중요 섹션 이라고합니다.
설명
- 그냥 그런 경우 일 수 있습니다.이 문제가 무작위로 발생하지 않고 코드를 실행할 때마다 특정 시간에 발생하는 이유를 설명해 주시겠습니까? 특정 값?
- 여기 cli / sei에 대한 훌륭한 참조가 있습니다. nongnu.org/avr-libc/user-manual/ … . 메모리 장벽을 사용하면 위의 코드에서 휘발성 선언이 실제로 필요하지 않습니다. 다음은이 주제에 대한 재미있는 읽기입니다. 커널 .org / doc / Documentation / volatile-considered-harmful.txt
- @MikaelPatel 멋지지만 MCU와는 관련이 없습니다.컴파일러가 사용되지 않는다고 생각하는 인스턴스를 ' 최적화하지 못하도록이 상황에서 Volatile이 필요합니다 (값은 변경되지 않음). cli / sei는 작업 원자 WRT를 실행하는 유일한 다른 스레드 (인터럽트)로 만들기 위해 존재합니다.
- 휘발성을 사용하거나 사용하지 않고 코드를 컴파일 해 보셨습니까? 그러나 중요한 섹션 (cli / sei)이 있습니다. 제가 논의하고자하는 것은 메모리 장벽의 개념과 변수를 휘발성으로 선언해야하는 컴파일러에서 휘발성 액세스 (및 올바른 순서 지정)를 제공하는 방법입니다. 대부분의 프로그래머는 ISR에서 액세스되는 모든 변수가 휘발성으로 선언되어야한다고 배웠지 만이 이야기에는 훨씬 더 많은 것이 있습니다.
- 나는 컴파일러를 생각하지 않습니다 ' cli () 및 sei ()가 수행하는 작업과 최적화되지 않아야하는 변수 최적화와 같은 작업에 어떤 영향을 미치는지에 대한 많은 개념이 있습니다. ' 모든 sei () 및 cli ()는 레지스터에서 전역 인터럽트 활성화 플래그를 조작합니다. 그들은 코드 흐름을 위해 아무것도하지 않습니다.
could you explain why it is not happening randomly but at a specific time every time I run the code? Also why does it give the particular value?
-전체 코드를 본다면 그렇게 할 수있을 것입니다. 그동안 다음 내용을 읽어보세요 : gammon.com.au/interrupts3683 / .056196868 = 65537
잘못된 순간에 증가하는 것처럼 보입니다. 예? 인터럽트로 변경 될 수있는 변수에 액세스하고 있습니다. 해당 코드에서 여러 번 사용하므로 인터럽트가 꺼져있는 동안 로컬 복사본을 얻는 것이 훨씬 안전합니다.