You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATmega2560使用Timer0测量矩阵乘法执行时间异常问题

问题:ATmega2560矩阵乘法计时结果不匹配

我正在尝试测量ATmega2560执行矩阵乘法的耗时,采用正常模式的Timer0,通过计数溢出中断次数实现。设置了两种配置:1ms触发一次中断,1μs触发一次中断,但结果异常:毫秒计时得到44ms,微秒计时仅得到366μs(预期至少约44000μs,两种模式结果应对应匹配)。

Timer0配置代码

/*
*
* 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;
}

问题根源分析

  1. Timer0未重载初始值:正常模式下,Timer0溢出后会从0开始计数,而非你设置的初始值。你的代码只在初始化时设置了TCNT0,但未在中断服务函数(ISR)中重新加载该值,导致第一次溢出后,后续溢出周期变为256个时钟周期(远大于预期的1μs/1ms),计数结果完全失真。
  2. 16位计数变量读取无原子操作:timer0_overflow_count是uint16_t类型,主程序读取时可能被中断打断,导致读取到错误的中间值。
  3. 微秒模式中断开销过大:每1μs触发一次中断会占用大量CPU时间,不仅拉长矩阵乘法的实际执行时间,还可能因ISR执行时间超过中断周期导致中断丢失。

解决方法

1. 修复Timer0重载逻辑

修改Timer0代码,在ISR中重新加载初始TCNT0值,保证每次溢出周期一致:

#include "timer.h"

volatile uint16_t timer0_overflow_count = 0;
static uint8_t tcnt0_initial; // 存储初始计数值

void timer0_init(uint8_t resolution) {
    uint8_t prescaler = 0;

    if (resolution == RESOLUTION_MS) {
        prescaler = (1 << CS01) | (1 << CS00);  // Prescaler 64
        tcnt0_initial = 6;
    }
    else if (resolution == RESOLUTION_US) {
        prescaler = (1 << CS01);  // Prescaler 8    
        tcnt0_initial = 254;
    }

    TCNT0 = tcnt0_initial; // 设置初始计数
    TIMSK0 |= (1 << TOIE0); // 启用溢出中断
    TCCR0B = prescaler;     // 启动定时器
    sei();                  // 开启全局中断
}

// 原子操作读取计数,避免中断干扰
uint16_t timer0_getCount() {
    uint16_t count;
    cli(); // 临时关闭中断
    count = timer0_overflow_count;
    sei(); // 恢复中断
    return count;
}

ISR(TIMER0_OVF_vect) {
    timer0_overflow_count++;
    TCNT0 = tcnt0_initial; // 重新加载初始值,维持固定溢出周期
}

2. 优化微秒计时模式

将微秒模式的溢出周期调整为10μs或100μs,减少中断开销:

  • 以10μs为例:计算初始TCNT0值为256 - (10μs / (1/(16e6/8))) = 236,修改tcnt0_initial = 236,最终计时结果需乘以10得到实际微秒数。

3. 矩阵乘法内存优化

将矩阵B转置,让内存访问变为连续模式,减少缓存失效,提升运算速度:

// 转置矩阵B
void transpose(uint8_t* mat, uint8_t size) {
    for (uint8_t i = 0; i < size; i++) {
        for (uint8_t j = i+1; j < size; j++) {
            uint8_t temp = mat[i*size + j];
            mat[i*size + j] = mat[j*size + i];
            mat[j*size + i] = temp;
        }
    }
}

// 修改multiplicar函数
void multiplicar() {
    transpose(B, n); // 先转置B
    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];
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者lautar0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:17:32