Khi chạy ở chế độ đặt trước đồng hồ 64 trên ATmega328, một trong những bộ tính giờ của tôi tăng tốc mà không rõ lý do tại một thời điểm cụ thể trong quá trình thực thi.
Tôi đang sử dụng hai bộ định thời trên ATmega328 để tạo xung nhịp cần thiết cho TLC5940 (xem bên dưới về lý do; đây là điều không quan trọng đối với câu hỏi). TIMER0
tạo tín hiệu đồng hồ bằng cách sử dụng Fast PWM OC0B
và được thiết lập như sau:
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
xoay một dòng dữ liệu để tạo xung trống sau mỗi 256 TIMER0
chu kỳ và được thiết lập như sau:
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
gọi ISR khi tràn (cứ sau 256 chu kỳ). ISR tự tạo một xung trống và xung chốt nếu cần:
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);
}
Sự nop()
chậm trễ trong đoạn mã trên chỉ là làm cho xung rõ ràng hơn trên dấu vết của bộ phân tích logic. Đây là vòng lặp trong main()
hàm trông như thế nào: gửi một số dữ liệu nối tiếp, đợi ISR xử lý chốt và sau đó thực hiện lại:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
một số SPI gửi ( mã trên pastebin vì lý do ngắn gọn ). Vấn đề của tôi là sau khi sendSerial()
hoàn thành, trong khi chờ fLatch
đặt ở mức thấp (đã xử lý), bộ đếm thời gian sẽ tăng tốc. Đây là dấu vết của bộ phân tích logic (Tôi đã cắt ra các khu vực nơi tín hiệu tương tự tiếp tục làm cho đồ họa nhỏ hơn):
Ở phía bên trái, các kênh 0 và 1 hiển thị phần đuôi của dữ liệu SPI được gửi. Ngoài ra ở bên trái, trên kênh 4, bạn có thể thấy một xung trống. Trên kênh 2, các xung xung nhịp như mong đợi. Ngay xung quanh vị trí khoảng trống trong hình ảnh, fLatch
được đặt vào 1
bên trong main()
thói quen. Và ngay sau đó TIMER0
tăng tốc lên khoảng 4. Gửi lại. Tôi đã cố gắng đưa ra delay_ms(1);
dòng trong main()
, nhưng kết quả tương tự thu được. Chuyện gì đang xảy ra vậy? Tôi nên lưu ý rằng ATmega được tắt một tinh thể 20Mhz và sau đó giảm tốc độ 64 lần bằng mã sau:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Cái này dùng để làm gì: Tôi đang thử nghiệm điều khiển trình điều khiển LED TLC5940 : những con chip này yêu cầu đồng hồ bên ngoài cộng với thiết lập lại ở cuối chu kỳ xung nhịp.
sendSerial()
là mã của tôi gửi dữ liệu qua SPI: nó không chạm vào các thanh ghi TCCR
(điều khiển hẹn giờ).