이상해 보이지 않습니다. 일반적인 MCU 코드는 실제로 다음과 같습니다.
여기에있는 것은 메모리 매핑 주변 장치 개념의 예입니다 . 기본적으로 MCU 하드웨어에는 할당 된 MCU의 SRAM 주소 공간에 특별한 위치가 있습니다. 이 주소에 쓰면 n 주소에 쓰인 바이트의 비트 가 주변 장치 m 의 동작을 제어합니다 .
기본적으로 특정 메모리 뱅크에는 문자 그대로 SRAM 셀에서 하드웨어로 연결되는 와이어가 거의 없습니다. 해당 바이트의이 비트에 "1"을 쓰면이 SRAM 셀이 논리적 하이로 설정되어 하드웨어의 일부가 켜집니다.
MCU의 헤더를 살펴보면 키워드 <-> 주소 매핑의 큰 테이블이 있습니다. 이것은 TCCR1B
컴파일 타임에 등등 과 같은 것들이 해결되는 방식입니다.
이 메모리 매핑 메커니즘은 MCU에서 매우 광범위하게 사용됩니다. arduino의 ATmega MCU는 PIC, ARM, MSP430, STM32 및 STM8 MCU 시리즈와 마찬가지로 익숙하지 않은 많은 MCU를 사용합니다.
Arduino 코드는 MCU 제어 레지스터에 간접적으로 액세스하는 기능을 가진 이상한 것들입니다. 이것은 다소 "더욱 똑똑해"보이지만 훨씬 느리며 더 많은 프로그램 공간을 사용합니다.
신비한 상수는 모두 ATmega328P 데이터 시트에 자세하게 설명되어 있으며 , arduino에서 핀으로 작동하는 토글 핀에 더 관심이 있다면 실제로 읽어보십시오.
위에 링크 된 데이터 시트에서 발췌를 선택하십시오.
예를 들어 TIMSK1 |= (1 << TOIE1);
비트를 TOIE1
로 설정합니다 TIMSK1
. 이것은 헤더 파일에 0으로 정의 된 이진 1 ( 0b00000001
)을 TOIE1
비트 단위로 왼쪽 으로 이동하여 달성됩니다. TOIE1
그런 다음 현재 값인 비트 단위로 OR됩니다 TIMSK1
.
의 비트 0에 대한 설명서를 보면 TIMSK1
다음과 같이 설명되어 있습니다.
이 비트가 1에 쓰여지고 상태 레지스터의 I- 플래그가 설정되면 (전역 적으로 인터럽트 가능) 타이머 / 카운터 1 오버 플로우 인터럽트가 활성화됩니다. TIFR1에있는 TOV1 플래그가 설정되면 해당 인터럽트 벡터 (57 페이지의 "인터럽트"참조)가 실행됩니다.
다른 모든 줄은 같은 방식으로 해석해야합니다.
몇 가지 참고 사항 :
당신은 또한 같은 것을 볼 수 있습니다 TIMSK1 |= _BV(TOIE1);
. _BV()
A는 일반적으로 사용되는 매크로 로부터 원래 구현 libc의 AVR은 . 가독성 향상 _BV(TOIE1)
을 위해 기능적으로와 동일합니다 (1 << TOIE1)
.
또한 다음과 같은 줄이 나타날 수도 있습니다. TIMSK1 &= ~(1 << TOIE1);
또는 TIMSK1 &= ~_BV(TOIE1);
. 비트의 비트 를 해제TIMSK1 |= _BV(TOIE1);
한다는 점에서 반대 기능을 합니다 . 이는에 의해 생성 된 비트 마스크를 가져 와서 비트 NOT 연산을 수행 한 다음 ( ) 이 NOT 값 (0b11111110)으로 AND를 수행 함으로써 달성됩니다.TOIE1
TIMSK1
_BV(TOIE1)
~
TIMSK1
이 모든 경우 에 컴파일 시간에(1 << TOIE1)
또는 컴파일 시간에 값 _BV(TOIE1)
이 완전히 해석 되므로 기능상으로 간단한 상수로 줄어들므로 런타임에 계산하는 데 실행 시간이 걸리지 않습니다.
올바르게 작성된 코드는 일반적으로 레지스터에 수행 할 작업을 자세히 설명하는 코드와 함께 주석이 표시됩니다. 최근에 작성한 매우 간단한 소프트 SPI 루틴은 다음과 같습니다.
uint8_t transactByteADC(uint8_t outByte)
{
// Transfers one byte to the ADC, and receives one byte at the same time
// does nothing with the chip-select
// MSB first, data clocked on the rising edge
uint8_t loopCnt;
uint8_t retDat = 0;
for (loopCnt = 0; loopCnt < 8; loopCnt++)
{
if (outByte & 0x80) // if current bit is high
PORTC |= _BV(ADC_MOSI); // set data line
else
PORTC &= ~(_BV(ADC_MOSI)); // else unset it
outByte <<= 1; // and shift the output data over for the next iteration
retDat <<= 1; // shift over the data read back
PORTC |= _BV(ADC_SCK); // Set the clock high
if (PINC & _BV(ADC_MISO)) // sample the input line
retDat |= 0x01; // and set the bit in the retval if the input is high
PORTC &= ~(_BV(ADC_SCK)); // set clock low
}
return retDat;
}
PORTC
PORTC
ATmega328P 내에서 출력 핀의 값을 제어하는 레지스터입니다 . 입력 값을 사용할 수 PINC
있는 레지스터 입니다. 기본적으로 이와 같은 것은 또는 기능 을 사용할 때 내부적으로 발생하는 것 입니다. 그러나 arduino "핀 번호"를 실제 하드웨어 핀 번호로 변환하는 검색 작업이 있으며, 이는 50 클럭 사이클의 영역에서 사용됩니다. 당신이 아마 짐작할 수 있듯이, 빨리 가려고한다면, 1을 요구 해야하는 작업에서 50 클럭 사이클을 낭비하는 것은 약간 어리 석습니다.PORTC
digitalWrite
digitalRead
위의 함수는 아마도 8 비트를 전송하기 위해 100-200 클럭 사이클의 영역에서 어딘가를 필요로합니다. 이를 위해서는 24 개의 핀 쓰기와 8 개의 읽기가 필요합니다. 이것은 digital{stuff}
함수 를 사용하는 것보다 몇 배나 빠릅니다 .