ATmega328'de 64 saat önbelleğinde çalışırken, zamanlayıcılarımdan biri yürütmedeki belirli bir zamanda bilinmeyen nedenlerle hızlanır.
Ben ihtiyaç duyduğu zamanlamanın üretmek için ATMega328 iki zamanlayıcılar kullanıyorum TLC5940 (neden üzerinde aşağıya bakınız; bu soruya önemsizdir). TIMER0
Hızlı PWM açık kullanarak bir saat sinyali üretir OC0B
ve aşağıdaki gibi ayarlanır:
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;
TIMER2
her 256 TIMER0
döngüde bir körleme darbesi oluşturmak için bir veri hattını döndürür ve aşağıdaki gibi ayarlanır:
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
TIMER2
taşma durumunda bir ISR çağırır (her 256 döngüde bir). ISR manuel olarak bir körleme darbesi ve gerekirse bir kilitleme darbesi üretir:
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);
}
nop()
Yukarıdaki kod gecikme sadece mantık analizörü izi üzerindeki darbe daha belirgin hale getirmektir. İşlevdeki döngü main()
şöyle görünür: bazı seri veriler gönderin, ISR'nin mandallama ile ilgilenmesini bekleyin ve ardından tekrar yapın:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
bazı SPI gönderileri yapar ( kısaca uğruna pastebin kodu ). Benim sorunum sendSerial()
tamamlandıktan sonra , fLatch
düşük (işlenmiş) olarak ayarlanmasını beklerken saat zamanlayıcı hızlanır. İşte mantık analizörü izi (Aynı sinyalin grafiği küçültmeye devam ettiği alanları kestim):
Sol tarafta, 0 ve 1 kanalları gönderilen SPI verilerinin kuyruk ucunu gösterir. Ayrıca solda, kanal 4'te bir körleme darbesi görebilirsiniz. Kanal 2'de saatli darbe, beklendiği gibi çekilir. Görüntüdeki boşluğun fLatch
tam 1
ortasında, main()
rutinin içine ayarlanır . Ve kısa bir süre sonra TIMER0
yaklaşık 4 kat hızlanır. Sonunda, körleme darbesi ve mandallama darbesi gerçekleştirilir (kanallar 3 ve 4, görüntünün sağ üçte biri) ve şimdi saat vurma darbesi normal frekansına geri döner ve seri veriler bir daha yolla. delay_ms(1);
Çizgiyi çıkarmayı denedim main()
, ancak aynı sonuçlar elde edildi. Neler oluyor? ATmega'nın 20Mhz kristalinden zamanlanmış olduğunu ve daha sonra aşağıdaki kodu kullanarak 64x yavaşladığını not etmeliyim:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Ne için: TLC5940 LED sürücüsünü kontrol etmeyi deniyorum : bu yongalar, harici bir saat artı saat çevriminin sonunda bir sıfırlama gerektirir.
sendSerial()
SPI aracılığıyla veri gönderen kodum: TCCR
(zamanlayıcı kontrolü) kayıtlarına dokunmaz .