AVR 변수에 volatile
키워드를 사용하는 이유는 컴파일러가 변수에 대한 액세스를 최적화하지 않기 위해서입니다. 이제 문제는 어떻게됩니까?
변수에는 상주 할 수있는 두 개의 위치가 있습니다. 범용 레지스터 파일이나 RAM의 일부 위치에 있습니다. 변수가 RAM에있는 경우를 고려하십시오. 변수의 최신 값에 액세스하려면 컴파일러에서 ld
명령어의 일부 양식 (예 : lds r16, 0x000f
)을 사용하여 RAM에서 변수를로드합니다. 이 경우 변수는 RAM 위치 0x000f
에 저장되고 프로그램은 r16
에이 변수의 복사본을 만들었습니다. 인터럽트가 활성화되면 흥미로운 부분이 생깁니다. 변수를로드 한 후 inc r16
이 발생하면 인터럽트 트리거와 해당 ISR이 실행됩니다.ISR 내에서 변수도 사용됩니다. 그러나 문제가 있습니다. 변수는 RAM에 하나, r16
에 하나씩 두 가지 버전으로 존재합니다. 이상적으로 컴파일러는 r16
의 버전을 사용해야하지만,이 버전은 존재하지 않을 것이므로 대신 RAM에서로드하므로 필요에 따라 코드가 작동하지 않습니다. volatile
키워드를 입력하십시오. 변수가 여전히 RAM에 저장됩니다, 그러나, 컴파일러는 다른 어떤 일이 발생하기 전에 변수에 따라서 다음과 같은 어셈블리가 생성 될 수있다, RAM으로 업데이트되어 있는지 확인해야합니다 :
cli
lds r16, 0x000f
inc r16
sei
sts 0x000f, r16
첫째, 인터럽트를 사용할 수 없습니다. 그런 다음 변수가 r16에로드됩니다. 변수가 증가하고 인터럽트가 활성화 된 다음 변수가 저장됩니다. 변수가 RAM에 다시 저장되기 전에 전역 인터럽트 플래그가 활성화 되기는 어렵지만 명령어 세트 수동 :
보류중인 인터럽트보다 먼저 명령어가 실행됩니다.
이것은 모든 인터럽트가 다시 트리거되기 전에 sts
명령이 실행되고 가능한 최소 시간 동안 인터럽트가 비활성화됨을 의미합니다.
이제 변수가 레지스터에 바인드 된 경우를 생각해보십시오. 변수에 대해 수행 된 모든 작업은 레지스터에서 직접 수행됩니다. 이러한 작업은 RAM의 변수에 수행 된 작업과 달리 원자 적으로 간주 할 수 있습니다. 말할 것도없이 읽기 - 수정 -> 쓰기 사이클이 없기 때문입니다. 변수가 업데이트 된 후에 인터럽트가 트리거되면 변수에 바인딩 된 레지스터에서 변수를 읽을 것이므로 변수의 새 값을 가져옵니다.
또한 변수가 레지스터에 바인딩되어 있기 때문에 모든 테스트 명령어는 레지스터 자체를 활용할 것이고 컴파일러가 정적 인 값을 갖는 이유 때문에 최적화되지 않을 것입니다. 그들의 본질은 휘발성이다.
이제 경험상 AVR에서 인터럽트를 사용할 때 전역 가변 변수가 RAM에 절대로 도달하지 않는다는 것을 종종 눈치 챘습니다. 컴파일러는 레지스터에서 항상 읽기 -> 수정 -> 쓰기 사이클을 건너 뛰었습니다. 그러나 이것은 컴파일러 최적화에 기인 한 것이므로 의존해서는 안됩니다. 다른 컴파일러는 동일한 코드 조각에 대해 다른 어셈블리를 생성 할 수 있습니다. avr-objdump
유틸리티를 사용하여 최종 파일 또는 특정 오브젝트 파일의 디스 어셈블리를 생성 할 수 있습니다.
건배. volatile
의
있습니까? – Beryllium
정상적인 HW 타이머를 사용하고 있습니다. AVR의 저전력 타이머는 최저 속도 설정 (내부 8MHz 시스템 클록, 1024 비트, 8 비트 폭의 프리스케일러)에서 32.768ms 후에 오버플로하며, 표준 시간 확장 방법은 카운트 다운 ISR을 사용하는 것입니다. – chrylis