Meningkatkan resolusi bit PWM

9

Saya ingin meningkatkan resolusi bit PWM dari Arduino Uno. Saat ini 8-bit yang saya anggap terlalu rendah. Apakah ini mungkin tanpa kehilangan kemampuan interupsi dan penundaan?

Koen

EDIT Pengaturan ini memberikan hasil 16-bit

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;
    }
}
KoenR
sumber

Jawaban:

15

The Arduino Uno didasarkan pada mikrokontroler ATmega382P. Chip ini memiliki dua timer 8-bit, masing-masing menggerakkan dua saluran PWM, dan satu timer 16-bit, menggerakkan dua saluran terakhir.

Anda tidak dapat meningkatkan resolusi timer 8-bit. Anda dapat, bagaimanapun, meletakkan timer 16-bit dalam mode 16-bit, bukan mode 8-bit yang digunakan oleh pustaka inti Arduino. Ini akan memberi Anda dua saluran PWM 16-bit, dengan frekuensi yang dikurangi 244 Hz (maksimum). Anda mungkin harus mengkonfigurasi timer sendiri, dan tidak akan mendapat manfaat dari fungsi yang mudah digunakan analogWrite(). Untuk detailnya, lihat bagian Timer 1 di dalam lembar data ATmega328P .

Pembaruan : Ini adalah implementasi 16-bit analogWrite(). Ini hanya bekerja pada pin 9 dan 10, karena ini adalah satu-satunya pin yang terhubung ke timer 16-bit.

/* 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;
    }
}

Anda mungkin memperhatikan bahwa bagian atas urutan penghitung dikonfigurasikan secara eksplisit. Anda dapat mengubahnya ke nilai yang lebih kecil untuk membuat PWM lebih cepat, dengan biaya resolusi yang lebih rendah.

Dan berikut ini adalah contoh sketsa yang menggambarkan penggunaannya:

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);
}
Edgar Bonet
sumber
Wow terima kasih banyak, ini yang saya butuhkan. Saya ingin hasil PWM saya sama dengan Resolusi Sensor saya. Jika saya mengubah kode Anda untuk <melihat edit saya> apakah itu hasil 13-bit? Jika demikian, berapakah frekuensinya? Saya akan mengendarai motor DC dengan itu jadi 244Hz akan sedikit kurang kurasa
KoenR
@KoenR: Tidak, prescaler tidak berpengaruh pada resolusi, tujuannya adalah untuk memperlambat penghitungan. Mengatur prescaler ke 8 akan memberi Anda frekuensi PWM 30,5 Hz. Jika Anda ingin resolusi 13 bit, atur ICR1ke 0x1fff, maka frekuensi Anda akan menjadi 1953 Hz (F_CPU / (TOP + 1)) dengan prescaler di 1.
Edgar Bonet
Terima kasih atas penjelasannya. Sunting pertanyaan saya sehingga mencakup kesalahan-kesalahan ini. Jadi orang lain bisa melihatnya langsung. Terima kasih!
KoenR
1
@Edgar Bonet Ini bagus, namun sepertinya saya tidak bisa sepenuhnya mematikan LED. Saya menggunakan ICR1 = 0x03FFdan pada 0 saya melihat pulsa kecil pada ruang lingkup cukup untuk menyalakan led. Ada ide?
davivid
1
@davivid: Ya, Anda tidak dapat memiliki siklus nol tugas. analogWrite16(pin, val)memberikan siklus tugas (val + 1) / ICR1. Sebagai solusi, Arduino analogWrite()melakukannya if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. Tapi kemudian Anda tidak bisa mendapatkan siklus tugas 1 / ICR1 ...
Edgar Bonet
3

Dengan beberapa kalibrasi Anda dapat menjumlahkan output dari dua saluran PWM dengan resistor pembobot yang berbeda. Pada ekstrem Anda dapat menggunakan satu output untuk memberikan 8 bit resolusi dan skala yang lain untuk 1/256 tingkat dan menambahkannya sehingga saluran ke-2 mencakup satu bit rentang dan Anda (sekali lagi secara naluriah) mendapatkan 16 bit resolusi. Tanpa perawatan dan penyesuaian besar-besaran, semua yang Anda dapatkan akan berantakan.
Namun dengan membagi saluran 2 dengan 16 atau 32 Anda dapat menambahkan beberapa bit tambahan resolusi PWM. Hanya dengan menambahkan 2 saluran PWM, output disaring analog Anda menambahkan bit tambahan (karena rentang potensial digandakan untuk mV / bit tidak berubah).
Secara alami (lagi) untuk setiap pembagian tambahan dengan 2 Anda mendapatkan sedikit resolusi tambahan, tetapi ini hanya dapat dilakukan untuk mungkin 4 atau 5 atau 6 bit tambahan, dengan meningkatnya persyaratan akurasi penskalaan resistor dan kalibrasi yang lebih sulit dan rawan kesalahan. .

Contoh singkat.
Jika satu PWM diperkecil untuk mengatakan 0 - 255 mV dalam langkah 1 mV, maka menjumlahkan dua PWM dengan amplitudo yang sama akan memberikan kisaran 0 - 510 mV dalam langkah 1 mV.
Jika satu PWM diperkecil dengan faktor 32 maka alih-alih menambahkan 255 mV ke rentang PWM awal, itu hanya akan menambah 8 mV ke ujung atas (0,256,32 = 8 mV, tetapi resolusi akan berada di 0,03125 (1/32) ) langkah mV.

Walaupun ini mungkin dapat dicapai sepenuhnya dengan penjumlahan resistor dan filter RC, penggunaan musim panas op amp akan sangat meningkatkan hasil.

Riak PWM juga dapat difilter dengan filter RC sederhana tetapi menggunakan satu opamp sebagai penyangga (atau bahkan hanya satu transistor sebagai pengikut emitor) akan memberi Anda 3 atau 5 kutub penyaringan low pass dan peluang yang jauh lebih baik untuk mencapai PWM tambahan resolusi. Saya belum memeriksa "fase koherensi" dari output PWM tetapi berharap bahwa mereka bergerak relatif berbaris sehingga Anda tidak mendapatkan keuntungan smoothing dengan menambahkan dua bentuk gelombang tidak berkorelasi.

Lebih banyak komentar dapat dibuat jika diperlukan. Tanyakan apakah tertarik.

Russell McMahon
sumber
Ini pintar! Tampaknya pustaka sintesis suara Mozzi menggunakan trik ini untuk yang disebut mode "HIFI".
Edgar Bonet
Itu adalah kami yang hebat dari PWM. Tapi bukankah ini akan memuluskan gelombang? Saya menanyakan hal ini karena Anda menggunakan filter RC. Belum disebutkan dalam pertanyaan saya tapi saya mengendarai motor DC dengan itu <merasa malu>. Terima kasih atas masukannya!
KoenR
@KoenR (fwiw: Saya tidak melihat ada yang perlu dipermalukan.) Saya tidak tahu apa respons frekuensi / tingkat perubahan yang Anda inginkan dalam output ADC Anda. Atau mengapa Anda ingin N bit atau seberapa besar sudah cukup. Motor biasanya tidak akan dapat dikontrol dengan lebih dari 8 bit - tergantung pada seberapa presisi aplikasi yang Anda miliki. Motor bertindak sebagai bagian dari filter smoothing karena induktansi. Anda perlu mengatakan motor apa dan bagaimana dikendarai. Dan diagram sirkuit sangat penting. Kecuali jika motornya kecil, Anda memiliki driver. Motor brush yang diumpankan PWM harus memiliki dioda tangkap untuk melewatkan arus motor ketika PWM mati. Menambahkan dua ...
Russell McMahon
... PWM di sini memang bisa dilakukan tetapi detail rangkaian perlu diketahui.
Russell McMahon
Waspadalah! Dalam beberapa kasus, memuluskan PWM dengan low pass RC tidak diinginkan. Misalnya, jika Anda mencolokkan output Arduino ke gerbang MOSFET, MOSFET akan tetap dingin selama digerakkan oleh PWM yang bersih. Tetapi jika Anda menghaluskannya, MOSFET akan mulai membuang lebih banyak panas. Terkadang itu bukan hal yang baik.
Florin Andrei