提问人:lautar0 提问时间:11/10/2023 更新时间:11/16/2023 访问量:61
使用 timer0 测量 atmega2560 上的执行时间
Using timer0 to measure execution time on atmega2560
问:
我正在尝试测量 atmega2560 执行矩阵乘法所需的时间。
为此,我在正常模式下使用 Timer0 并计算溢出中断的数量。
我设置了两种可能的配置,一种是每 1 毫秒获得一次中断,另一种是每 1 毫秒获得一次中断。 问题是:当以毫秒计时,我得到 44 毫秒,但当以微秒计时,我得到 366us(我认为我至少应该得到 44,000 左右,反之亦然)。
这是 timer0 配置的代码,为了设置 TCNT0 寄存器,我按照此处指示的公式进行操作,我还与数据表进行了检查:https://arcmicrocontrollers.files.wordpress.com/2022/07/imagen-22.png
/*
*
* Contador con TIMER0
* Normal mode
* Interrupciones. Datasheet capitulo 16.
*
*/
#include "timer.h"
volatile uint16_t timer0_overflow_count = 0;
void timer0_init(uint8_t resolution) {
uint8_t prescaler = 0;
if (resolution == RESOLUTION_MS) {
prescaler = (1 << CS01) | (1 << CS00); // Prescaler 64
TCNT0 = 6;
}
else if (resolution == RESOLUTION_US) {
prescaler = (1 << CS01); // Prescaler 8
TCNT0 = 254;
}
// Habilitar la interrupción de overflow
TIMSK0 |= (1 << TOIE0);
// Iniciar el timer
TCCR0B = prescaler;
// Habilitar interrupciones globales
sei();
}
// Función para obtener el tiempo transcurrido
uint16_t timer0_getCount() {
return timer0_overflow_count;
}
// Rutina de interrupción para el desbordamiento del TIMER0
ISR(TIMER0_OVF_vect) {
timer0_overflow_count++;
}
这是我的主要程序
/*
*
* El tamaño máximo de memoria disponible para matrices en ram es
* 8 Kb
*
*/
#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <uart.h>
#include <timer.h>
#define F_CPU 16000000
uint8_t const n = 32;
uint8_t* A, * B, * C;
// blink builtin led
void error() {
while (1) {
PORTB |= (1 << 7);
_delay_ms(20);
PORTB &= ~(1 << 7);
_delay_ms(200);
}
}
// perform matrix product
void multiplicar() {
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
for (uint8_t k = 0; k < n; k++) {
C[i * n + j] += A[i * n + k] * B[j * n + k];
}
}
}
}
// Validates operation result
void validar() {
for (uint16_t i = 0; i < n * n; i++) {
if (C[i] != n) {
error(); //bloqueante
}
}
}
int main() {
DDRB |= (1<<7); // Led
UART_Init();
A=(uint8_t*)malloc(n*n*sizeof(uint8_t));
B=(uint8_t*)malloc(n*n*sizeof(uint8_t));
C=(uint8_t*)malloc(n*n*sizeof(uint8_t));
// Check if there was enough memory
if (A == NULL || B==NULL || C == NULL){
error();
}
// Initialize matrices
for (int i = 0; i < n*n; i++)
{
A[i]=1;
B[i]=1;
C[i]=0;
}
uint8_t res= RESOLUTION_US;
timer0_init(res);
multiplicar();
uint16_t time = timer0_getCount();
validar();
// Print elapsed time
UART_PrintStr("Tiempo transcurrido: ");
UART_PrintNumber(time);
(res == RESOLUTION_MS) ? UART_PrintStr(" ms\n") : UART_PrintStr(" us\n");
while (1) {
}
return 0;
}
答:
如果通过在函数启动时将引脚驱动为高电平,在函数结束时将其驱动为低电平,并在示波器上查看信号来测量算法的运行时间,则将更加可靠和准确。
如果您真的想在 AVR 上使用计时器来测量运行时间,您可以这样做,但最好避免使用中断或尽量减少中断的数量,因为每个中断都会占用一些 CPU 周期,影响您尝试进行的测量。因此,您要做的是重置计时器的计数器,将预分频器设置为一个相当高的值以最大程度地减少开销,运行您的算法,然后最后您将记录发生的溢出中断数和计时器的当前计数。避免由于溢出而导致的 bug 有点棘手,但您可以同时使用这两者来计算运行时间。
尝试在可能以 16 MHz 或更低频率运行的 8 位 AVR 上每微秒运行一次中断不是一个好主意。请记住,16 MHz AVR 在一微秒内最多只能执行 16 条指令,而您的简单 ISR 需要 8 条指令。
我在您的代码中看到的主要问题是,当您从毫秒更改为微秒时,您所做的唯一相关事情是您将预分频器更改了 8 倍。(请注意,您的结果仅更改了 8 倍,而不是您想要的 1000 倍。对 TCNT0 的写入无关紧要,因为一旦计时器达到其 TOP 值并溢出,TCNT0 中的计数应返回到 0。您需要查阅 ATMega2560 数据表,查看 Timer 0 的 TOP 值存储在哪个寄存器中,并在其中写入适当的值以设置中断速率。
您可能还需要解决其他问题。我没有仔细查看数据表。
评论
您根本不需要中断来准确测量时间。用预分频器启动计数器(最好是 16 位)就足够了,这样测量的时间就比定时器溢出的时间短。然后,只需在测量事件的开始和测量事件的结束时读取计数器的值。持续时间由减法结束值 - 开始值给出。由于将电流值保存在 16 位辅助寄存器中,因此可以在一个时钟周期内读取 16 位计数器。(详见DS第17.3章) 对于原子访问,必须在禁用中断的情况下执行读取。
例如
start value 0xFFFE
end value 0x0010
time = 0x0010 - 0xFFFE = 0x0012 = 18 tick of the pre-divider
评论