Speedup timer AVR pada ATmega328

9

Saat berjalan pada clock prescaler 64 pada ATmega328, salah satu timer saya mempercepat untuk alasan yang tidak diketahui pada waktu tertentu dalam eksekusi.

Saya menggunakan dua timer pada ATmega328 untuk menghasilkan clocking yang dibutuhkan oleh TLC5940 (lihat di bawah tentang mengapa; ini tidak penting untuk pertanyaan). TIMER0menghasilkan sinyal jam menggunakan Fast PWM on OC0Bdan diatur sebagai berikut:

TCCR0A = 0
    |(0<<COM0A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM0A0)    // 
    |(1<<COM0B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM0B0)
    |(1<<WGM01)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(1<<WGM00)
    ;
TCCR0B = 0
    |(0<<FOC0A)     // Force Output Compare A
    |(0<<FOC0B)     // Force Output Compare B
    |(1<<WGM02)     // Bit 3 – WGM02: Waveform Generation Mode
    |(0<<CS02)      // Bits 2:0 – CS02:0: Clock Select
    |(1<<CS01)
    |(0<<CS00)      // 010 = clock/8
    ;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;

TIMER2twiddles baris data untuk menghasilkan pulsa kosong setiap 256 TIMER0siklus dan diatur sebagai berikut:

ASSR = 0;
TCCR2A = 0
    |(0<<COM2A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM2A0)    // 
    |(0<<COM2B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM2B0)
    |(0<<WGM21)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(0<<WGM20)
    ;
TCCR2B = 0
    |(0<<FOC2A)     // Force Output Compare A
    |(0<<FOC2B)     // Force Output Compare B
    |(0<<WGM22)     // Bit 3 – WGM02: Waveform Generation Mode
    |(1<<CS22)      // Bits 2:0 – CS02:0: Clock Select
    |(0<<CS21)
    |(0<<CS20)      // 100 = 64
    ;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
    |(1<<TOIE2);    // Timer/Counter0 Overflow Interrupt Enable

TIMER2memanggil ISR saat overflow (setiap 256 siklus). ISR secara manual menghasilkan pulsa pengosongan, dan penguncian pulsa jika perlu:

volatile uint8_t fLatch;

ISR(TIMER2_OVF_vect) {
    if (fLatch) {
        fLatch = 0;
        TLC5940_XLAT_PORT |=  (1<<TLC5940_XLAT_BIT);        // XLAT -> high
        for (int i=0;i<10;i++)
            nop();
        TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT);        // XLAT -> high
    }
    // Blank
    TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
    for (int i=0;i<10;i++)
        nop();
    TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}

The nop()keterlambatan dalam kode di atas adalah hanya untuk membuat denyut nadi lebih jelas pada jejak analisa logika. Begini tampilan loop dalam main()fungsi: kirim beberapa data serial, tunggu ISR untuk menjaga pengunciannya, dan kemudian lakukan lagi:

for (;;) {
    if (!fLatch) {
        sendSerial();
        fLatch = 1;
        _delay_ms(1);
    }
    nop();
}

sendSerial()apakah beberapa SPI mengirim ( kode pada pastebin demi singkatnya ). Masalah saya adalah bahwa setelah sendSerial()selesai, sambil menunggu untuk fLatchdiatur ke rendah (diproses), pencatat waktu pencatat waktu mempercepat. Inilah jejak penganalisa logika (saya memotong area di mana sinyal yang sama terus membuat grafik lebih kecil):

masukkan deskripsi gambar di sini

Di sisi kiri, saluran 0 dan 1 menunjukkan ujung ekor dari data SPI yang dikirim. Juga di sebelah kiri, di saluran 4, Anda dapat melihat pulsa yang kosong. Pada saluran 2, denyut nadi clocking sepanjang seperti yang diharapkan. Tepat di sekitar celah gambar, fLatchdiatur ke 1dalam main()rutinitas. Dan segera setelah itu TIMER0dipercepat sekitar faktor 4. Akhirnya, pulsa pengosongan dan penguncian pulsa dilakukan (saluran 3 dan 4, sepertiga kanan gambar), dan sekarang pulsa pencatat waktu memulai kembali frekuensi regulernya, dan data serial Kirim lagi. Saya mencoba mengambil delay_ms(1);garis dalam main(), tetapi hasil yang sama diperoleh. Apa yang sedang terjadi? Saya harus mencatat bahwa ATmega clock kristal 20Mhz dan kemudian melambat 64x menggunakan kode berikut:

CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);

Untuk apa ini: Saya bereksperimen dengan mengendalikan driver LED TLC5940 : chip ini membutuhkan jam eksternal plus reset pada akhir siklus clocking.

angelatlarge
sumber
Jika Anda memiliki debugger, cobalah untuk menghentikan kode Anda ketika timer terlalu cepat dan membaca kembali register konfigurasi timer itu. Setelah Anda menemukan yang memiliki nilai yang salah, memicu breakpoint pada perubahan register ini dan melihat bagian mana dari kode Anda yang melakukan kesalahan. Saya kira masalahnya terletak di perpustakaan eksternal yang dapat Anda gunakan dan yang menggunakan penghitung waktu itu untuk hal-hal internal seperti penundaan.
Blup1980
Dua masalah: a) Saya tidak punya programmer JTAG jadi saya tidak punya cara men-debug chip b) Saya tidak pernah mengubah nilai register timer setelah setup yang ditunjukkan di atas, jadi saya tidak mengharapkan nilai register timer untuk sebenarnya berubah. Apakah itu naif?
angelatlarge
1
Sebenarnya satu pustaka yang Anda gunakan dapat mengubah pengaturan UART. Saya melihat bahwa Anda menggunakan fungsi sendSerial (). Apakah itu bagian dari kode Anda atau apakah itu perpustakaan eksternal? Mungkin bukan Anda yang mengubah pengaturan, tetapi sepotong kode di dalam pustaka yang disebut. Saya sarankan Anda menggunakan port serial Anda untuk menampilkan parameter konfigurasi dan mencoba mencari tahu apa yang berubah. Anda juga dapat melihat sumber perpustakaan yang digunakan (jika ada) dan memastikan mereka tidak menggunakan timer itu juga.
Blup1980
1
Terlepas dari apa yang disarankan @ Blup1980 hal lain yang mungkin patut dicoba adalah menghapus TLC5940 untuk memastikan tidak melakukan apa pun yang aneh dengan garis jam.
PeterJ
@ Blup1980 Saya tidak yakin saya melihat relevansi UART: Saya tidak menggunakan USART untuk SPI, hanya fasilitas SPI "biasa". sendSerial()adalah kode saya yang mengirim data melalui SPI: tidak menyentuh TCCRregister (pengatur waktu).
angelatlarge

Jawaban:

1

Untuk debug cepat, saya akan mencoba melakukan hal yang sama menggunakan Perpustakaan Arduino untuk TLC5940 dan melihat apakah itu semakin cepat atau tidak. Jika berfungsi dengan perpustakaan, Anda dapat memeriksa sumbernya dan membandingkannya dengan milik Anda. Karena Anda terbiasa dengan AVR, Anda harus dengan mudah mengkonversi sumber Arduino ke AVR asli.

Kalau-kalau Anda tidak tahu cara mengunggah sketsa Arduino yang dikompilasi ke AVR: Ketika Anda mengkompilasi sketsa Anda, itu membuat file hex (Anda dapat melihat lokasi yang tepat dari file dengan mengaktifkan mode verbose dalam pengaturan). Anda dapat mengunggah hex itu ke AVR Anda dengan programmer favorit Anda.

Semoga ini bisa membantu

superkeci
sumber