PWM 비트 해상도 증가


10

Arduino Uno의 PWM 비트 해상도를 높이고 싶습니다. 지금은 너무 낮은 것으로 간주되는 8 비트입니다. 인터럽트 및 지연 기능을 잃지 않고 가능합니까?

en

편집이 설정은 16 비트 결과를 제공합니다

void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS11);                    /* prescaler: clock / 8 */
    ICR1 = 0xffff;                      /* TOP counter value (freeing OCR1A*/
}
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> (16000000/8)/(65535+1)=30.5175Hz
*/

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

답변:


16

Arduino Uno는 ATmega382P 마이크로 컨트롤러를 기반으로합니다. 이 칩에는 각각 2 개의 PWM 채널을 구동하는 2 개의 8 비트 타이머와 마지막 2 개의 채널을 구동하는 1 개의 16 비트 타이머가 있습니다.

8 비트 타이머의 해상도를 높일 수 없습니다. 그러나 Arduino 코어 라이브러리에서 사용하는 8 비트 모드 대신 16 비트 타이머를 16 비트 모드로 설정할 수 있습니다. 이렇게하면 주파수가 244Hz (최대) 인 2 개의 16 비트 PWM 채널이 제공됩니다. 타이머를 직접 구성해야하며 사용하기 쉬운 analogWrite()기능의 이점이 없습니다 . 자세한 내용은 ATmega328P 데이터 시트 의 타이머 1 섹션을 참조하십시오 .

업데이트 : 다음은 16 비트 구현입니다 analogWrite(). 16 비트 타이머에 연결된 유일한 핀이므로 핀 9와 10에서만 작동합니다.

/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                    /* no prescaling */
    ICR1 = 0xffff;                      /* TOP counter value */
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

카운터 시퀀스의 상단이 명시 적으로 구성되어 있음을 알 수 있습니다. 해상도를 낮추는 대신 PWM 속도를 높이기 위해이 값을 더 작은 값으로 변경할 수 있습니다.

다음은 그 사용법을 보여주는 스케치 예입니다.

void setup() {
    setupPWM16();
}

/* Test: send very slow sawtooth waves. */
void loop() {
    static uint16_t i;
    analogWrite16(9, i);
    analogWrite16(10, 0xffff - i);
    i++;
    delay(1);
}

와, 정말 고마워요, 이것이 바로 내가 필요한 것입니다. PWM 결과가 센서 해상도와 같기를 원합니다. 코드를 <edit at my edit>로 변경하면 13 비트 결과입니까? 그렇다면 빈도는 얼마입니까? 나는 그것으로 DC 모터를 구동 할 것입니다 그래서 244Hz는 조금 덜 추측합니다
KoenR

@ KoenR : 아니요, 프리 스케일러는 해상도에 영향을 미치지 않습니다. 목적을 늦추는 것이 목적입니다. 프리스케일러를 8로 설정하면 PWM 주파수가 30.5Hz가됩니다. 당신은 13 비트 해상도, 설정을 원하는 경우 ICR10x1fff1에서 프리스케일러와, 당신의 주파수 1953 Hz의 것 (F_CPU / (TOP + 1))
에드가 보넷

설명 감사합니다. 이 실수를 다루도록 내 질문을 편집했습니다. 다른 사람들이 직접 볼 수 있습니다. 감사합니다!
KoenR

1
@ Edgar Bonet 이것은 훌륭하지만 LED를 완전히 끌 수는 없습니다. 나는 ICR1 = 0x03FF0을 사용 하고 있으며 0에 도달 할 때 스코프에서 작은 펄스를보고 있습니다. 어떤 아이디어?
davivid

1
@davivid : 예, 듀티 사이클을 0으로 설정할 수 없습니다. analogWrite16(pin, val)(val + 1) / ICR1의 듀티 사이클을 제공합니다. 해결 방법으로 Arduino는 analogWrite()않습니다 if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. 그러나 1 / ICR1의 듀티 사이클을 얻을 수 없습니다.
Edgar Bonet

3

일부 보정을 통해 두 개의 PWM 채널의 출력을 다른 가중치 저항기와 합할 수 있습니다. 극단적으로 당신은 개념적으로 하나의 출력을 사용하여 8 비트의 해상도를 제공하고 다른 하나를 1/256의 레벨로 스케일링하고 추가하여 두 번째 채널이 한 비트 범위를 커버하고 당신은 (다시 말해서) 16 비트의 해상도를 얻을 수 있습니다. 엄청난 보살핌과 조정 없이는 모든 것이 엉망이 될 것입니다.
그러나 두 번째 채널을 16 또는 32로 나누면 몇 비트의 PWM 해상도를 추가 할 수 있습니다. 2 개의 PWM 채널 아날로그 필터링 된 출력을 추가하기 만하면 여분의 비트를 추가 할 수 있습니다 (변하지 않는 mV / 비트의 경우 잠재적 범위가 두 배가 됨).
개념적으로 (다시) 2로 나눌 때마다 추가 비트의 분해능을 얻을 수 있지만 스케일링 저항의 정확도 요구 사항이 증가하고 교정이 어려워지고 오류가 발생하기 쉬운 4 또는 5 또는 6 개의 추가 비트에 대해서만 수행 할 수 있습니다 .

간단한 예입니다.
1mV 단계에서 0-255mV를주기 위해 하나의 PWM이 축소되는 경우, 동일한 진폭으로 2 개의 PWM을 합하면 1mV 단계에서 0-510mV 범위가됩니다.
하나의 PWM이 32 배로 축소되면 초기 PWM 범위에 255mV를 추가하는 대신 상단에 8mV 만 추가하지만 (0.256.32 = 8mV) 분해능은 0.03125 (1 / 32nd)입니다. ) mV 단계.

이것은 아마도 저항 합산 및 RC 필터링으로 만 달성 할 수 있지만 연산 증폭기 여름을 사용하면 결과가 크게 향상됩니다.

또한 PWM 리플은 간단한 RC 필터로 필터링 할 수 있지만 하나의 opamp를 버퍼 (또는 이미 터 팔로워로 단일 트랜지스터)로 사용하면 3 또는 5 극의 저역 통과 필터링을 제공하고 추가 PWM을 달성 할 가능성이 훨씬 높습니다 해결. PWM 출력의 "위상 일관성"을 검사하지는 않았지만 상대 잠금 단계에서 움직일 것으로 예상하므로 두 개의 상관되지 않은 파형을 추가 할 때 평활화 이점을 얻지 못합니다.

필요한 경우 더 많은 의견을 제시 할 수 있습니다. 관심이 있는지 물어보십시오.


이것은 영리하다! Mozzi 사운드 합성 라이브러리 는 소위 "HIFI"모드에이 트릭을 사용 하는 것 같습니다 .
Edgar Bonet

그것은 PWM의 위대한 우리입니다. 그러나 이것이 파형을 부드럽게하지 않습니까? RC 필터를 사용하고 있기 때문에 이것을 묻습니다. 내 질문에 이것을 언급하지는 않았지만 <부끄러운 느낌>으로 DC 모터를 운전하고 있습니다. 의견을 보내 주셔서 감사합니다!
KoenR

@KoenR (fwiw : 부끄러워 할 것이 없습니다.) ADC 출력에서 ​​어떤 주파수 응답 / 변화율을 원하는지 모르겠습니다. 또는 왜 N 비트를 원하는지 또는 얼마나 큰지 충분합니다. 모터는 일반적으로 8 비트 이상으로 유용하게 제어되지 않습니다. 어플리케이션의 정밀도에 따라 다릅니다. 모터는 인덕턴스로 인해 스무딩 필터의 일부로 작동합니다. 어떤 종류의 모터와 구동 방식을 말해야합니다. 그리고 회로도는 필수적입니다. 모터가 작지 않으면 운전자가 있습니다. 브러시 드 모터 공급 PWM에는 PWM이 꺼져있을 때 모터 전류를 전달하기위한 캐치 다이오드가 있어야합니다. 두 추가 ...
러셀 맥 마혼

... 여기서 PWM은 완전히 가능하지만 회로 세부 사항을 알아야합니다.
Russell McMahon

조심해! 경우에 따라 저역 통과 RC로 PWM을 평활화하는 것은 바람직하지 않습니다. 예를 들어 Arduino 출력을 MOSFET의 게이트에 꽂으면 깨끗한 PWM에 의해 구동되는 한 MOSFET은 차갑게 유지됩니다. 그러나 부드럽게하면 MOSFET이 훨씬 더 많은 열을 발산하기 시작합니다. 때로는 좋은 일이 아닙니다.
Florin Andrei
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.