Mega2560 定时器和 uSec
Mega2560 Timer and uSec
我知道这里有很多关于定时器以及如何配置和使用它们的问题,我已经查看了我能找到的所有内容,但无法弄清楚我做错了什么。
我需要一个 class,它包含与 Arduino micros() 函数基本相同的功能。我想继续使用纯 AVR。这是我到目前为止所拥有的,我使用的是 Timer4,所以我不会踩到任何脚趾,这是一个 16 位定时器,我使用的是 8 的预分频器,使用 Mega2560,每个时钟周期应该给我 .5us,不会'这是否等于 TCNT4 = 2 = 1us?
为了验证我的计时功能是否正确,我创建了一个简单的程序,其中仅包含计时器和来自 "util/delay.h" 的几个延迟。结果输出不是我所期望的。所以这是我的问题,我不确定 _delay_us 是否真的延迟了正确的时间,或者我的 timer/math 是否关闭。
我意识到没有检查溢出或任何东西,我专注于简单地让计时器首先输出正确的值。
系统时间:
class SystemTime{
unsigned long ovfCount = 1;
public:
SystemTime();
void Overflow();
uint32_t Micro();
void Reset();
};
/**
* Constructor
*/
SystemTime::SystemTime() {
TCCR4B |= (1 << CS41); //Set Prescale to 8
TIMSK4 |= (1 << TOIE4); //Enable the Overflow Interrupt
}
/**
* Increase the Overflow count
*/
void SystemTime::Overflow(){
this->ovfCount++;
}
/**
* Returns the number of Microseconds since start
*/
uint32_t SystemTime::Micro(){
uint32_t t;
t = (TCNT4 * 2) * this->ovfCount;
return t;
}
/**
* Resets the SystemTimer
*/
void SystemTime::Reset(){
this->ovfCount = 0;
TCNT4 = 0;
}
SystemTime sysTime;
ISR(TIMER4_OVF_vect){
sysTime.Overflow();
}
主线:
#include "inttypes.h"
#include "USARTSerial.h"
#include "SystemTime.h"
#include "util/delay.h"
#define debugSize 50
void setup(){
char debug1[debugSize];
char debug2[debugSize];
char debug3[debugSize];
char debug4[debugSize];
uSerial.Baudrate(57600);
uSerial.Write("Ready ...");
uint32_t test;
sysTime.Reset();
test = sysTime.Micro();
sprintf(debug1, "Time 1: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug2, "Time 2: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug3, "Time 3: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug4, "Time 4: %lu", test);
uSerial.Write(debug1);
uSerial.Write(debug2);
uSerial.Write(debug3);
uSerial.Write(debug4);
}
void loop(){
}
输出:
Ready ...
Time 1: 0
Time 2: 144
Time 3: 306
Time 4: 464
更新:
感谢您帮助我,我想 post 工作代码以防万一其他人遇到问题或需要知道如何做到这一点。要记住的一件事是进行 Micros 计算所需的时间。看起来(至少在我的 Mega2560 上)执行计算大约需要 36us,因此要么需要调整定时器预分频器,要么需要进行数学运算以消除双倍乘法。 None 这个 class 的效果越差,但绝不是优化的。
#define F_CPU 16000000L
#include <stdio.h>
#include <avr/interrupt.h>
class SystemTime {
private:
unsigned long ovfCount = 0;
public:
SystemTime();
void Overflow();
uint32_t Micro();
void Reset();
};
/*
* Constructor, Initializes the System Timer for keeping track
* of the time since start.
*/
SystemTime::SystemTime() {
TCCR4B |= (1 << CS41); //Set Prescale to 8
TIMSK4 |= (1 << TOIE4); //Enable the Overflow Interrupt
//Enable Interrupts
sei();
}
/**
* Increase the Overflow count
*/
void SystemTime::Overflow() {
this->ovfCount++;
}
/**
* Resets the SystemTimer
*/
void SystemTime::Reset() {
this->ovfCount = 0;
TCNT4 = 0;
}
/**
* Returns the number of Microseconds since start
*/
uint32_t SystemTime::Micro() {
uint32_t t;
t = (TCNT4 * 0.5) + ((this->ovfCount * sizeof(this->ovfCount)) * 0.5);
return t;
}
SystemTime sysTime;
ISR(TIMER4_OVF_vect) {
sysTime.Overflow();
}
假设您的 MCU 确实以 16 MHz 运行,我将在您的代码中更改以下内容。
- 如果一个计时器增量为 0.5 μs,那么您应该将
TCNT4
的值除以 2,而不是乘以。因为它是 TCNT4
乘以 0.5 μs。
此外 this->ovfCount
用法也是错误的。从启动开始经过的微秒等于:TCNT4
* 0.5 + this->ovfCount
* 65535 * 0.5。所以当前增量数(TCNT4
)乘以0.5μs加上溢出计数(this->ovfCount
)乘以最大增量数(216-1 = 65535 ) 乘以 0.5 μs.
uint32_t SystemTime::Micro(){
uint32_t t;
t = (TCNT4 * 0.5) + this->ovfCount * 65535 * 0.5;
return t;
}
最后,我看不到你在任何地方使用 sei()
启用全局中断。
我知道这里有很多关于定时器以及如何配置和使用它们的问题,我已经查看了我能找到的所有内容,但无法弄清楚我做错了什么。
我需要一个 class,它包含与 Arduino micros() 函数基本相同的功能。我想继续使用纯 AVR。这是我到目前为止所拥有的,我使用的是 Timer4,所以我不会踩到任何脚趾,这是一个 16 位定时器,我使用的是 8 的预分频器,使用 Mega2560,每个时钟周期应该给我 .5us,不会'这是否等于 TCNT4 = 2 = 1us?
为了验证我的计时功能是否正确,我创建了一个简单的程序,其中仅包含计时器和来自 "util/delay.h" 的几个延迟。结果输出不是我所期望的。所以这是我的问题,我不确定 _delay_us 是否真的延迟了正确的时间,或者我的 timer/math 是否关闭。
我意识到没有检查溢出或任何东西,我专注于简单地让计时器首先输出正确的值。
系统时间:
class SystemTime{
unsigned long ovfCount = 1;
public:
SystemTime();
void Overflow();
uint32_t Micro();
void Reset();
};
/**
* Constructor
*/
SystemTime::SystemTime() {
TCCR4B |= (1 << CS41); //Set Prescale to 8
TIMSK4 |= (1 << TOIE4); //Enable the Overflow Interrupt
}
/**
* Increase the Overflow count
*/
void SystemTime::Overflow(){
this->ovfCount++;
}
/**
* Returns the number of Microseconds since start
*/
uint32_t SystemTime::Micro(){
uint32_t t;
t = (TCNT4 * 2) * this->ovfCount;
return t;
}
/**
* Resets the SystemTimer
*/
void SystemTime::Reset(){
this->ovfCount = 0;
TCNT4 = 0;
}
SystemTime sysTime;
ISR(TIMER4_OVF_vect){
sysTime.Overflow();
}
主线:
#include "inttypes.h"
#include "USARTSerial.h"
#include "SystemTime.h"
#include "util/delay.h"
#define debugSize 50
void setup(){
char debug1[debugSize];
char debug2[debugSize];
char debug3[debugSize];
char debug4[debugSize];
uSerial.Baudrate(57600);
uSerial.Write("Ready ...");
uint32_t test;
sysTime.Reset();
test = sysTime.Micro();
sprintf(debug1, "Time 1: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug2, "Time 2: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug3, "Time 3: %lu", test);
_delay_us(200);
test = sysTime.Micro();
sprintf(debug4, "Time 4: %lu", test);
uSerial.Write(debug1);
uSerial.Write(debug2);
uSerial.Write(debug3);
uSerial.Write(debug4);
}
void loop(){
}
输出:
Ready ...
Time 1: 0
Time 2: 144
Time 3: 306
Time 4: 464
更新:
感谢您帮助我,我想 post 工作代码以防万一其他人遇到问题或需要知道如何做到这一点。要记住的一件事是进行 Micros 计算所需的时间。看起来(至少在我的 Mega2560 上)执行计算大约需要 36us,因此要么需要调整定时器预分频器,要么需要进行数学运算以消除双倍乘法。 None 这个 class 的效果越差,但绝不是优化的。
#define F_CPU 16000000L
#include <stdio.h>
#include <avr/interrupt.h>
class SystemTime {
private:
unsigned long ovfCount = 0;
public:
SystemTime();
void Overflow();
uint32_t Micro();
void Reset();
};
/*
* Constructor, Initializes the System Timer for keeping track
* of the time since start.
*/
SystemTime::SystemTime() {
TCCR4B |= (1 << CS41); //Set Prescale to 8
TIMSK4 |= (1 << TOIE4); //Enable the Overflow Interrupt
//Enable Interrupts
sei();
}
/**
* Increase the Overflow count
*/
void SystemTime::Overflow() {
this->ovfCount++;
}
/**
* Resets the SystemTimer
*/
void SystemTime::Reset() {
this->ovfCount = 0;
TCNT4 = 0;
}
/**
* Returns the number of Microseconds since start
*/
uint32_t SystemTime::Micro() {
uint32_t t;
t = (TCNT4 * 0.5) + ((this->ovfCount * sizeof(this->ovfCount)) * 0.5);
return t;
}
SystemTime sysTime;
ISR(TIMER4_OVF_vect) {
sysTime.Overflow();
}
假设您的 MCU 确实以 16 MHz 运行,我将在您的代码中更改以下内容。
- 如果一个计时器增量为 0.5 μs,那么您应该将
TCNT4
的值除以 2,而不是乘以。因为它是TCNT4
乘以 0.5 μs。 此外
this->ovfCount
用法也是错误的。从启动开始经过的微秒等于:TCNT4
* 0.5 +this->ovfCount
* 65535 * 0.5。所以当前增量数(TCNT4
)乘以0.5μs加上溢出计数(this->ovfCount
)乘以最大增量数(216-1 = 65535 ) 乘以 0.5 μs.uint32_t SystemTime::Micro(){ uint32_t t; t = (TCNT4 * 0.5) + this->ovfCount * 65535 * 0.5; return t; }
最后,我看不到你在任何地方使用
sei()
启用全局中断。