Я использую ATMega88A на частоте 8 МГц, а SPI настроен на работу на частоте Fosc/2 = 4 МГц.
Теоретически передача 5000 байт по SPI должна занимать 1/4000000 * 8 * 5000 = 10 мс . Но, согласно внутреннему таймеру, это занимает чуть более 19 мс . Это похоже на тонну накладных расходов. Это типично?
Образец кода:
#include <avr/io.h>
#define set_output(portdir,pin) portdir |= (1<<pin)
void init() {
// Set MOSI, SCK, SS as Output
set_output(DDRB, DDB5);
set_output(DDRB, DDB3);
set_output(DDRB, DDB2);
// Enable SPI, Set as Master, Set CPOL & CPHA to 1 (SPI mode 3)
SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL) | (1 << CPHA);
SPSR = (1 << SPI2X); // Enable SPI clock doubler
DDRD = 0xff; // Set PORTD as output
TCCR1B |= (1 << CS12) | (1 << CS10); // Setup Timer with 1024 prescaling
}
int main(void) {
unsigned int i;
init();
TCNT1 = 0; //zero the timer
for (i = 0; i < 5000; i++) {
SPDR = 0; // Load data into the SPI data reg
while (!(SPSR & (1 << SPIF))); //Wait until transmission complete
}
PORTD = (unsigned char) TCNT1; // Display the timer on PORTD
for (;;) {}
return 0;
}
На что настроена ваша оптимизация кода?? Я бы посмотрел на дизассемблирование вашего сгенерированного кода, вы должны помнить, что есть некоторые инструкции, которые необходимо выполнить, чтобы выполнить цикл for и интегрировать более 5000 повторений, возможно, до 9 миллисекунд. Я рекомендую вам ознакомиться с этой заметкой по применению от atmel под названием «Советы и рекомендации по оптимизации вашего кода C для 8-битных микроконтроллеров AVR», а также прочитать раздел указателя циклов и попробовать их советы, чтобы увидеть, можете ли вы уменьшить количество инструкций, необходимых для выполнения операции цикла for.
for (i = 0; i < 5000; i++)
на for (i = 5000; i; i--)
, но изменение должно быть меньше, чем разрешение таймера, потому что оно не имеет никакого эффекта.Поскольку в выходном регистре SPI нет буфера, любая задержка между тактовым циклом, когда последний бит смещается, и когда следующий байт загружается в регистр, проявляется как время простоя между байтами в выходном потоке SPI.
В приведенном выше коде (который также есть в таблице данных Atmel) есть несколько внутренних задержек...
for (i = 0; i < 5000; i++) {
SPDR = 0; // Load data into the SPI data reg
while (!(SPSR & (1 << SPIF))); //Wait until transmission complete
}
... который компилируется во что-то вроде...
В лучшем случае при хорошей оптимизации каждый из этих шагов займет 1-2 такта. В совокупности эти циклы приводят к относительно большому разрыву между передаваемыми байтами SPI и значительно снижают пропускную способность.
Столкнувшись с той же проблемой, я придумал решение, которое сокращает время простоя между байтами SPI до 1 цикла. Вот ассемблерный код...
LOOP:
// Cycles
// ------
out SPDR,__zero_reg__ // (transmit byte!)
rjmp .+0 // 2 - twiddle thumbs
rjmp .+0 // 2
rjmp .+0 // 2
rjmp .+0 // 2
rjmp .+0 // 2
nop // 1
sbiw len, 1 // 2 - dec counter
brne LOOP // 2 - loop back until done
// ======
// 17
Полностью статью можно прочитать здесь...
Маженко
как зовут
sessyargc.jp
Адам Гриффин