ตั้งค่าความถี่ PWM เป็น 25 kHz


12

ขณะนี้ฉันสามารถตั้งค่าหมุด PWM สี่เข็มเป็นประมาณ 31 kHz ด้วยรหัสต่อไปนี้:

void setup()
{
    TCCR1B = TCCR1B & B11111000 | B00000001; // Set PWM frequency for D9 & D10:
    pinMode(pwmPin9, OUTPUT); // Sets the pin as output
    pinMode(pwmPin10, OUTPUT); // Sets the pin as output


    TCCR2B = TCCR2B & B11111000 | B00000001; // Set PWM for D3 & D11
    pinMode(pwmPin3, OUTPUT); // Sets the pin as output
    pinMode(pwmPin11, OUTPUT); // Sets the pin as output
}

ฉันพบการตั้งค่านี้ที่ใดที่หนึ่ง แต่ฉันไม่รู้ว่าฉันสามารถตั้งค่าหมุด PWM สี่เหล่านี้เป็นประมาณ 25 kHz แทนได้อย่างไร เป็นไปได้อย่างไร?


3
คุณเข้าใจหรือไม่ว่าตัวจับเวลาของ AVR ทำงานอย่างไร
Ignacio Vazquez-Abrams

3
ดูหน้าของฉันเกี่ยวกับตัวนับ
Nick Gammon

1
@ IgnacioVazquez-Abrams ฉันไม่คุ้นเคยและฉันต้องตั้งพินทั้งสี่ให้อยู่ที่ประมาณ 25kHz ในตอนเริ่มต้น ฉันรีบทำโครงการให้เสร็จและฉันยินดีที่จะช่วยเหลือ รหัสที่ฉันตั้งไว้ที่ 31kHz ฉันสามารถแก้ไขเป็น 25kHz ได้หรือไม่ มอเตอร์กระแสตรงต้องการความถี่
user16307

1
@NickGammon ขอบคุณ แต่ฉันไม่มีเวลาพอที่จะศึกษาสิ่งเหล่านี้ในขณะนี้ คุณสามารถให้รหัสส่วนหนึ่งแก่ฉันเพื่อตั้งค่า 25kHz ฉันสูญเสีย
user16307

2
ฉันต้องการปรับรอบต่อนาทีที่แน่นอนของพวกเขาดังนั้นรอบการทำงานของพวกเขาจะแตกต่างกันเล็กน้อย เป็นไปได้อย่างไรที่จะสามารถตั้ง 2 พินได้ถึง 25kHz เท่านั้น?
user16307

คำตอบ:


10

ฉันโพสต์คำตอบที่สองนี้เพราะฉันรู้ว่าเป็นไปได้ที่จะมี 4 PWM channel ที่ 25 kHz ด้วย 161 ก้าวบน Arduino Uno เดียว สิ่งนี้เกี่ยวข้องกับการเปลี่ยนความถี่สัญญาณนาฬิกาหลักเป็น 8 MHzซึ่งมีผลข้างเคียงบางส่วนเนื่องจากโปรแกรมทั้งหมดจะทำงานเร็วกว่าครึ่ง นอกจากนี้ยังเกี่ยวข้องกับการกำหนดค่าตัวนับสามซึ่งหมายถึงการสูญเสียการทำงานของ Arduino ระยะเวลา ( millis(), micros(), delay()และ delayMicroseconds()) หากการแลกเปลี่ยนเหล่านี้เป็นที่ยอมรับนี่คือวิธีการ:

void setup()
{
    // Set the main system clock to 8 MHz.
    noInterrupts();
    CLKPR = _BV(CLKPCE);  // enable change of the clock prescaler
    CLKPR = _BV(CLKPS0);  // divide frequency by 2
    interrupts();

    // Configure Timer 0 for phase correct PWM @ 25 kHz.
    TCCR0A = 0;           // undo the configuration done by...
    TCCR0B = 0;           // ...the Arduino core library
    TCNT0  = 0;           // reset timer
    TCCR0A = _BV(COM0B1)  // non-inverted PWM on ch. B
        | _BV(WGM00);  // mode 5: ph. correct PWM, TOP = OCR0A
    TCCR0B = _BV(WGM02)   // ditto
        | _BV(CS00);   // prescaler = 1
    OCR0A  = 160;         // TOP = 160

    // Same for Timer 1.
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
        | _BV(COM1B1)  // same on ch. B
        | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
        | _BV(CS10);   // prescaler = 1
    ICR1   = 160;

    // Same for Timer 2.
    TCCR2A = 0;
    TCCR2B = 0;
    TCNT2  = 0;
    TCCR2A = _BV(COM2B1)  // non-inverted PWM on ch. B
        | _BV(WGM20);  // mode 5: ph. correct PWM, TOP = OCR2A
    TCCR2B = _BV(WGM22)   // ditto
        | _BV(CS20);   // prescaler = 1
    OCR2A  = 160;
}

void loop()
{
    analogWrite( 3,   1);  // duty cycle = 1/160
    analogWrite( 5,  53);  // ~ 1/3
    analogWrite( 9, 107);  // ~ 2/3
    analogWrite(10, 159);  // 159/160
}

ซึ่งแตกต่างจากคำตอบอื่น ๆนี้ไม่จำเป็นต้องมีรุ่นแก้ไข analogWrite(): หนึ่งมาตรฐานจะทำงานได้ดี ควรใช้ความระมัดระวังเท่านั้น:

  1. ค่าที่เขียนควรอยู่ระหว่าง 0 (หมายถึงต่ำเสมอ) และ 160 (สูงเสมอเสมอ) โดยรวม
  2. มีเฉพาะหมุด 3, 5, 9 และ 10 เท่านั้น ความพยายามที่analogWrite() จะตอกหมุดที่ 6 หรือ 11 นั้นไม่เพียง แต่จะล้มเหลวในการส่งสัญญาณ PWM เท่านั้น แต่ยังจะเปลี่ยนความถี่ของขาที่ 5 หรือ 3 ตามลำดับด้วย

ใช้เวลานานมากและตอนนี้ฉันติดอยู่กับสิ่งเดียวกันกับ Arduino Due ซึ่งใช้โปรเซสเซอร์อื่น ฉันจะดีใจถ้าคุณ hae อินพุตใด ๆ ที่นี่arduino.stackexchange.com/questions/67053/ …
16307

11

คุณสามารถกำหนดค่าตัวจับเวลา 1 เพื่อวนที่ 25 kHz ในโหมด PWM ที่ถูกต้องในเฟสและใช้เอาต์พุตสองตัวบนพิน 9 และ 10 ดังนี้:

// PWM output @ 25 kHz, only on pins 9 and 10.
// Output value should be between 0 and 320, inclusive.
void analogWrite25k(int pin, int value)
{
    switch (pin) {
        case 9:
            OCR1A = value;
            break;
        case 10:
            OCR1B = value;
            break;
        default:
            // no other pin will work
            break;
    }
}

void setup()
{
    // Configure Timer 1 for PWM @ 25 kHz.
    TCCR1A = 0;           // undo the configuration done by...
    TCCR1B = 0;           // ...the Arduino core library
    TCNT1  = 0;           // reset timer
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
           | _BV(COM1B1)  // same on ch; B
           | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
           | _BV(CS10);   // prescaler = 1
    ICR1   = 320;         // TOP = 320

    // Set the PWM pins as output.
    pinMode( 9, OUTPUT);
    pinMode(10, OUTPUT);
}

void loop()
{
    // Just an example:
    analogWrite25k( 9, 110);
    analogWrite25k(10, 210);
    for (;;) ;  // infinite loop
}

การเขียนค่า 0 ด้วยanalogWrite25k()หมายความว่าหมุดจะต่ำเสมอในขณะที่ 320 หมายถึงสูงเสมอ ปกติanalogWrite() ควรเกือบทำงาน แต่มันจะตีความ 255 เช่นเดียวกับ 320 (เช่นเสมอสูง)

รหัสนี้จะถือว่า Arduino Uno หรือบอร์ดที่คล้ายกัน (ATmega168 หรือ 328 @ 16 MHz) วิธีที่ใช้ในที่นี้ต้องใช้ตัวจับเวลา 16 บิตและดังนั้นจึงใช้ตัวจับเวลา 1 เนื่องจากเป็นตัวเดียวที่มีอยู่ใน Uno นั่นเป็นเหตุผลที่มีเพียงสองเอาต์พุต วิธีนี้สามารถนำไปปรับใช้กับบอร์ด AVR ที่ใช้ตัวจับเวลา 16 บิตได้ ตามที่ Gerben ตั้งข้อสังเกตว่าตัวจับเวลานั้นควรมีการลงทะเบียน ICRx ที่เกี่ยวข้อง มีตัวจับเวลาดังกล่าว 4 ตัวบน Arduino Mega แต่ละตัวมีเอาต์พุต 3 ตัว


1
อาจเป็นประโยชน์ในการอธิบายว่าวิธีนี้ใช้ได้กับ timer1 เท่านั้นเนื่องจากตัวจับเวลาอื่นไม่มีการICRxลงทะเบียน ที่ส่วนใหญ่คุณสามารถมีขาหนึ่ง PWM ต่อจับเวลาสำหรับการจับเวลา 0 และ 2
Gerben

1
@ Gerben: ไม่มีตัวจับเวลา 16 บิตทั้งหมดที่ลงทะเบียนใช่ไหม อย่างน้อยพวกเขาก็ทำ
Edgar Bonet

1
ใช่ แต่ตัวจับเวลา 1 เท่านั้นคือ 16 บิตบน ATMega328 ส่วนที่เหลือเป็น 8 บิต และ OP ต้องการเอาต์พุต 4 PWM และวิธีแก้ปัญหาของคุณให้แค่ 2 หรือฉันเข้าใจผิด?
Gerben

1
@Gerben: ไม่คุณพูดถูก ฉันแค่บอกว่าต้องใช้ ICRx ดูเหมือนจะซ้ำซ้อนโดยกำหนดให้ตัวจับเวลาเป็น 16 บิต สำหรับ Uno และ Mega อย่างน้อยก็ไม่แน่ใจเกี่ยวกับ Arduinos ที่ใช้ AVR ตัวอื่น OP เข้าใจว่าสิ่งนี้มีเพียง 2 ช่องสัญญาณ PWM: ดูความคิดเห็นของฉันในคำถามและคำตอบของเขา
Edgar Bonet

2
@techniche: 1. ทำงานให้ฉัน บางทีคุณอาจจะลืมที่จะตั้งCOM4C1ในTCCR4A? 2. หากนั่นไม่ใช่ปัญหาให้อ่านฉันจะถามคำถามที่ดีได้อย่างไร จากนั้นอัปเดตคำถามของคุณโดยใส่ซอร์สโค้ดแบบเต็มของคุณและระบุสิ่งที่คุณคาดหวังว่าโปรแกรมจะทำและสิ่งที่มันทำแทนอย่างชัดเจน (“ ฉันไม่เห็นความสำเร็จใด ๆ ” จะไม่ถือว่าเป็นคำแถลงปัญหาที่ถูกต้อง)
Edgar Bonet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.