PIC18F47K42 上的 Timer0 计数寄存器递增速度快于预期
Timer0 count register on PIC18F47K42 increments faster than expected
我是嵌入式新手,有点小问题。
我认为问题出在我的代码上。但是我翻了几十遍都没找到bug
我有一个 Timer0,我可以用 16 位编程(最多可以计数到 65536)。有一个寄存器位 TMR0L 和 TMR0H,它们在每个时钟边沿或时钟信号的倍数递增。我希望它增加,比如说,每 0.00001 秒。
根据我的数据表,我设置了这些设置:
OSCFRQ = 0x02; //--- HFFRQ 4_MHz
T0CON0 = 10010000; //--- Module Enabled; Timer is 16bits; 1:1 postscaler
T0CON1 = 01010101; //--- Fosc/4; 1:32 prescaler
我的数学不是特别好,但我当然可以做基本的算术。
我的时钟是 4Mhz。我使用 clock/4 作为我的 Timer0 的输入。所以这给出了 1MHz 的频率。 1000000/32 = 31250Hz,每次计数 0.000032 秒。一毫秒 (0.001/0.000032 = 31.25counts),所以为了有一个毫秒,我必须用这些参数计算大约 31 次。对吗?
//Delay function that can delay from 1 milisecond to 2000 miliseconds.
//Uses timer0.
void countDelay(int ms_delay)
{
//unsigned int oscFreq = ((1<<(00001111&OSCFRQ))*1000000)/4;
//unsigned int Prescaler = (1<<(00001111&T0CON1));
unsigned int oscFreq = 4000000;
unsigned int Prescaler = 32;
float countTime = (Prescaler/(oscFreq/4));
int countsNum = (int)(((ms_delay/1000)/countTime));
char endCountDelay = 0;
TMR0L = 0x00;
TMR0H = 0x00;
unsigned int Time16 = 0x0000;
while(endCountDelay == 0)//PORBLEM
{
Time16 = 0;
Time16 |= TMR0L;
Time16 |= (TMR0H<<8);
if (Time16 >= countsNum)
{
endCountDelay = 1;
}
}
}
而我的主要代码就是下面的代码。它使 LED 闪烁。我想让它每秒都变亮。所以 31250 很重要。这不是问题,因为我检查了另一个函数,定时器真的是 16 位。最高可达 65k。
void main(void)
{
// Initialize the device
SYSTEM_Initialize();
while (1)
{
countDelay(1000);
LATA0 = 0;
countDelay(1000);
LATA0 = 1;
}
}
使用该代码,我可以看到 LED 始终亮着。
使用示波器,我检查 signal:141.76Hz
我们应该看到0.5Hz,循环周期为2秒,即0.5Hz。
所以,简而言之,我们 283 倍太高了。这接近于 2^8 的 256。所以我相信这是我的代码中的一个错误。也许我的延迟功能中有什么?有人有想法吗?
编辑#1:我做了其他测试。我改变了变量的值。不改变结果。信号保持 141Hz,+/- 10Hz。即使x16的时钟速度。
改变预分频值几乎是一样的。这次信号保持在 141.76Hz。
编辑#2:我在我的 pickit 中使用了调试器。看起来像我这样做的时候。
int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));
结果为 0。知道为什么吗?不应该。
编辑#3:当我使用 long 类型时,它给了我 2.51 亿。
编辑#4:验证时钟速度。没关系。
但是,这种计算,即使所有整数都行不通。 countsNum的答案是74,但应该是15.
unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));
这里很难指出确切的问题。我们需要先验证几个硬件假设:
您的主时钟实际上 运行ning 为 4 MHz。
您的计时器实际上以 1MHz 的频率计数。仔细检查预分频器设置。
现在,假设硬件时钟已检查完毕,我发现您的时间->计数计算存在问题。
int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));
让我们评估一下这条线。
ms_delay 是传递给特定可变时间延迟的参数。看起来这是以毫秒为单位的。但是,不要忘记我们正在做整数数学!
因此,对于任何低于 1000 的值,ms_delay/1000 将计算为零。
您可能需要研究浮点计算。或者您需要更改 countsNum 的计算方式以防止除法返回零。
@编辑 4:
unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));
让我们用整数来计算一下。
countsNum = (500)/((32768/(4000000/4000))
countsNum = (500)/((32768)/(1000))
countsNum = (500)/(32)
countsNum = 15
即使所有这些数字都使用浮点数,结果也是 15.259。
而你得到的是 74?
PIC18F47K42 是一个 8 位部件,您使用的是 'long' 值,这使得它们有 16 位宽。因此,这些变量的范围是 [0, 65535](无符号)或 [-32768, 32767](有符号)。如果此算术的任何结果超出此范围,则值回绕。对于此计算,您可能溢出或下溢了可变宽度。尝试包含并使用类型 int32_t 或 uint32_t。如果这解决了您的问题,则表明存在 overflow/underflow 问题。如果可以的话,你可以尝试做一些编译时的表达式。
@评论2:
我重新阅读了这个问题。我想我们可能把它搞得太复杂了。
如上所述,您的 16 位定时器有一个 4MHz 的时钟,预分频器为 4,留给我们一个有效的 1MHz 时钟。这具有 1 微秒的周期。使用 16 位计数器,这意味着我们溢出:
65536 * 1uS = .065535 seconds = 65.536 millisecond
现在,如果我们想找到 运行 1 毫秒的适当计数量,那么我们需要等待多少次 1 微秒的计数才能达到 1 毫秒?直观上,我们需要 1000 微秒才能得到 1 毫秒。
所以,我们还剩下多少滴答声@1 微秒,我们需要得到 1 毫秒?让我们向后工作,然后根据给定的变量进行计算:
.001 = ntics * (1 / 1000000)
.001 = ntics * (1 / (4000000/4))
.001 = ntics * (1 / (timer_clk / timer_psc))
-so-
.001 * 1000000 = ntics
1000 = ntics
这是否解决了您的问题?
一个潜在的问题(但不确定它是否解释了所有问题)是延迟例程中的第三行
Time16 = 0;
Time16 |= TMR0L;
Time16 |= (TMR0H<<8);
if (Time16 >= countsNum)
{
endCountDelay = 1;
}
TMR0H为8位寄存器。当您移动它时,您正在清除该寄存器。这有两个作用。首先,Time16 不应增加得那么快,其次,您直接作用于寄存器。根据数据 sheet,高字节是双缓冲的,因此在写入低位寄存器之前不应提交,但我会更改此设置,或者至少设置一个断点并查看0L 和 0H 以及随后的 Time16 值。
我找到答案了!原来,有一个寄存器是我的MCC插件自己写的。
在 PIC18F47k42 中,寄存器
// NOSC HFINTOSC; NDIV 1;
OSCCON1 = 0x60;
控制系统时钟。原来它被设置为 0x61,它添加了一个由另一个寄存器设置的分频器。它的值为 4。
故事的寓意,请一一检查您的配置寄存器,不要总是相信插件!
我是嵌入式新手,有点小问题。 我认为问题出在我的代码上。但是我翻了几十遍都没找到bug
我有一个 Timer0,我可以用 16 位编程(最多可以计数到 65536)。有一个寄存器位 TMR0L 和 TMR0H,它们在每个时钟边沿或时钟信号的倍数递增。我希望它增加,比如说,每 0.00001 秒。
根据我的数据表,我设置了这些设置:
OSCFRQ = 0x02; //--- HFFRQ 4_MHz
T0CON0 = 10010000; //--- Module Enabled; Timer is 16bits; 1:1 postscaler
T0CON1 = 01010101; //--- Fosc/4; 1:32 prescaler
我的数学不是特别好,但我当然可以做基本的算术。 我的时钟是 4Mhz。我使用 clock/4 作为我的 Timer0 的输入。所以这给出了 1MHz 的频率。 1000000/32 = 31250Hz,每次计数 0.000032 秒。一毫秒 (0.001/0.000032 = 31.25counts),所以为了有一个毫秒,我必须用这些参数计算大约 31 次。对吗?
//Delay function that can delay from 1 milisecond to 2000 miliseconds.
//Uses timer0.
void countDelay(int ms_delay)
{
//unsigned int oscFreq = ((1<<(00001111&OSCFRQ))*1000000)/4;
//unsigned int Prescaler = (1<<(00001111&T0CON1));
unsigned int oscFreq = 4000000;
unsigned int Prescaler = 32;
float countTime = (Prescaler/(oscFreq/4));
int countsNum = (int)(((ms_delay/1000)/countTime));
char endCountDelay = 0;
TMR0L = 0x00;
TMR0H = 0x00;
unsigned int Time16 = 0x0000;
while(endCountDelay == 0)//PORBLEM
{
Time16 = 0;
Time16 |= TMR0L;
Time16 |= (TMR0H<<8);
if (Time16 >= countsNum)
{
endCountDelay = 1;
}
}
}
而我的主要代码就是下面的代码。它使 LED 闪烁。我想让它每秒都变亮。所以 31250 很重要。这不是问题,因为我检查了另一个函数,定时器真的是 16 位。最高可达 65k。
void main(void)
{
// Initialize the device
SYSTEM_Initialize();
while (1)
{
countDelay(1000);
LATA0 = 0;
countDelay(1000);
LATA0 = 1;
}
}
使用该代码,我可以看到 LED 始终亮着。 使用示波器,我检查 signal:141.76Hz 我们应该看到0.5Hz,循环周期为2秒,即0.5Hz。
所以,简而言之,我们 283 倍太高了。这接近于 2^8 的 256。所以我相信这是我的代码中的一个错误。也许我的延迟功能中有什么?有人有想法吗?
编辑#1:我做了其他测试。我改变了变量的值。不改变结果。信号保持 141Hz,+/- 10Hz。即使x16的时钟速度。
改变预分频值几乎是一样的。这次信号保持在 141.76Hz。
编辑#2:我在我的 pickit 中使用了调试器。看起来像我这样做的时候。
int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));
结果为 0。知道为什么吗?不应该。
编辑#3:当我使用 long 类型时,它给了我 2.51 亿。
编辑#4:验证时钟速度。没关系。 但是,这种计算,即使所有整数都行不通。 countsNum的答案是74,但应该是15.
unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));
这里很难指出确切的问题。我们需要先验证几个硬件假设:
您的主时钟实际上 运行ning 为 4 MHz。
您的计时器实际上以 1MHz 的频率计数。仔细检查预分频器设置。
现在,假设硬件时钟已检查完毕,我发现您的时间->计数计算存在问题。
int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));
让我们评估一下这条线。
ms_delay 是传递给特定可变时间延迟的参数。看起来这是以毫秒为单位的。但是,不要忘记我们正在做整数数学! 因此,对于任何低于 1000 的值,ms_delay/1000 将计算为零。
您可能需要研究浮点计算。或者您需要更改 countsNum 的计算方式以防止除法返回零。
@编辑 4:
unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));
让我们用整数来计算一下。
countsNum = (500)/((32768/(4000000/4000))
countsNum = (500)/((32768)/(1000))
countsNum = (500)/(32)
countsNum = 15
即使所有这些数字都使用浮点数,结果也是 15.259。 而你得到的是 74?
PIC18F47K42 是一个 8 位部件,您使用的是 'long' 值,这使得它们有 16 位宽。因此,这些变量的范围是 [0, 65535](无符号)或 [-32768, 32767](有符号)。如果此算术的任何结果超出此范围,则值回绕。对于此计算,您可能溢出或下溢了可变宽度。尝试包含并使用类型 int32_t 或 uint32_t。如果这解决了您的问题,则表明存在 overflow/underflow 问题。如果可以的话,你可以尝试做一些编译时的表达式。
@评论2: 我重新阅读了这个问题。我想我们可能把它搞得太复杂了。 如上所述,您的 16 位定时器有一个 4MHz 的时钟,预分频器为 4,留给我们一个有效的 1MHz 时钟。这具有 1 微秒的周期。使用 16 位计数器,这意味着我们溢出:
65536 * 1uS = .065535 seconds = 65.536 millisecond
现在,如果我们想找到 运行 1 毫秒的适当计数量,那么我们需要等待多少次 1 微秒的计数才能达到 1 毫秒?直观上,我们需要 1000 微秒才能得到 1 毫秒。
所以,我们还剩下多少滴答声@1 微秒,我们需要得到 1 毫秒?让我们向后工作,然后根据给定的变量进行计算:
.001 = ntics * (1 / 1000000)
.001 = ntics * (1 / (4000000/4))
.001 = ntics * (1 / (timer_clk / timer_psc))
-so-
.001 * 1000000 = ntics
1000 = ntics
这是否解决了您的问题?
一个潜在的问题(但不确定它是否解释了所有问题)是延迟例程中的第三行
Time16 = 0;
Time16 |= TMR0L;
Time16 |= (TMR0H<<8);
if (Time16 >= countsNum)
{
endCountDelay = 1;
}
TMR0H为8位寄存器。当您移动它时,您正在清除该寄存器。这有两个作用。首先,Time16 不应增加得那么快,其次,您直接作用于寄存器。根据数据 sheet,高字节是双缓冲的,因此在写入低位寄存器之前不应提交,但我会更改此设置,或者至少设置一个断点并查看0L 和 0H 以及随后的 Time16 值。
我找到答案了!原来,有一个寄存器是我的MCC插件自己写的。 在 PIC18F47k42 中,寄存器
// NOSC HFINTOSC; NDIV 1;
OSCCON1 = 0x60;
控制系统时钟。原来它被设置为 0x61,它添加了一个由另一个寄存器设置的分频器。它的值为 4。
故事的寓意,请一一检查您的配置寄存器,不要总是相信插件!