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 비트 해상도, 설정을 원하는 경우 ICR1에 0x1fff1에서 프리스케일러와, 당신의 주파수 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.