【问题标题】:Timer0 count register on PIC18F47K42 increments faster than expectedPIC18F47K42 上的 Timer0 计数寄存器的增量快于预期
【发布时间】:2018-06-25 11:26:32
【问题描述】:

我是嵌入式新手,遇到了一点小问题。 我认为问题出在我的代码上。但是我已经检查了几十次了,我找不到故障。

我有一个 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。我使用时钟/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 始终亮着。 用示波器,我检查信号:141.76Hz 我们应该看到 0.5Hz,周期为 2 秒,即 0.5Hz。

所以,简而言之,我们的 283 倍太高了。接近 2^8 的 256。所以我相信这是我的代码中的一个错误。也许我的延迟功能中有什么东西?有人有想法吗?

EDIT#1:我进行了其他测试。我改变了我的变量的值。不改变结果。信号保持 141Hz,+/- 10Hz。即使x16的时钟速度。

更改预标量值几乎相同。这次信号保持在 141.76Hz。

EDIT#2:我在我的pickit 中使用了调试器。看起来像我这样做的时候。

int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));

结果为 0。知道为什么吗?它不应该。

EDIT#3:当我使用 long 类型时,它给了我相当多的 2.51 亿。

Edit#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)));

【问题讨论】:

    标签: timer embedded cpu-registers


    【解决方案1】:

    在这里很难指出确切的问题。我们需要先验证几个硬件假设:

    您的主时钟实际上以 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 位部件,您使用的是“长”值,这使得它们为 16 位宽。所以这些变量的范围是 [0, 65535] 代表无符号,或 [-32768, 32767] 代表有符号。如果此算术的任何结果超出此范围,则值环绕。您可能会溢出或下溢此计算的可变宽度。尝试包含并使用类型 int32_t 或 uint32_t。如果这解决了您的问题,则表明存在上溢/下溢问题。如果这证明了问题,您可以尝试执行一些编译时表达式。

    @评论 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
    

    这能解决您的问题吗?

    【讨论】:

    • 感谢您的宝贵时间。是的,我已经验证了时钟信号,它确实是 4MHz。进行编辑。
    • 我在 Visual Studio 上用无符号长变量编写了相同的代码,它给了我 15,就像对整数的预期一样。但是整数是 16 位宽的。我可以用整数达到 65k,我在寄存器中看到了正确的值。 long 是 32 位编码的。它们以 0x00000000 的形式出现在我的调试器中。这是32位。
    • 看起来执行的数学大致正确。这意味着基础方程不正确。
    • 再次感谢您对此进行调查。是的,但是对于我的计时器,我使用时钟速度/4 + 32 的预分频器。原因是我希望能够计数超过 65 毫秒。我需要一个几乎从 1 毫秒到 1 秒的多价延迟函数,这意味着我必须使用这些参数。 4000000/4 = 1000000。1000000/32 = 31250Hz。这意味着我的计时器应该以每秒 31250 次的速度递增。因为我最多可以数到 65k,所以我应该最多可以数到 2 秒(大约是 31250 的两倍)。这意味着每次计数之间的时间大约为 0.000032 秒。
    • 假设我要数到 500 毫秒。然后,知道每个计数持续 0.000032 秒,0.5/0.000032 应该给我达到 500 毫秒所需的计数。它是 15625 计数,whixh 是有道理的,它是半秒,所以是 31250 的一半。所以这里的问题不是计数。
    【解决方案2】:

    我找到了答案!原来,有一个我的 MCC 插件自己编写的寄存器。 在 PIC18F47k42 中,寄存器

    // NOSC HFINTOSC; NDIV 1; 
        OSCCON1 = 0x60;
    

    控制系统时钟。原来它被设置为 0x61,它添加了一个由另一个寄存器设置的分频器。它的值为 4。

    故事的寓意,一一检查你的配置寄存器,不要总是相信插件!

    【讨论】:

      【解决方案3】:

      一个潜在的问题(但不确定它是否能解释一切)是延迟例程中的第三行

          Time16 = 0;
          Time16 |= TMR0L;
          Time16 |= (TMR0H<<8);
          if (Time16 >= countsNum)
          {
              endCountDelay = 1;
          }
      

      TMR0H 是一个 8 位寄存器。当您转移它时,您正在清除该寄存器。这有两个效果。首先, Time16 不应该增加得那么快,其次,您直接在寄存器上操作。根据数据表,高字节是双缓冲的,因此在写入低位寄存器之前不应提交它,但我会更改它,或者至少设置一个断点并查看 0L 和 0H 的值和随后的 Time16 值。

      【讨论】:

      • 我不知道它清除了寄存器,谢谢,我会调查一下。只是很难自己做functino,我以为会有一些预定义函数的libraires,
      • Time16 |= (TMR0H&lt;&lt;8); 不会更改寄存器本身。并且值转移为int,而不是uint8_t。但是,通过移位更改符号会调用未定义的行为。阅读它。但是让我们暂时忽略这一点:任何常见的实现都会像预期的那样简单地改变它。使用unsigned int 的操作将以定义的方式转换值(对于 PIC,这只是将 2 的补码位模式重新解释为unsigned。因此,IRL 该行将执行预期的操作。但是,OP 应该使用固定宽度的类型和使用前转换为输出类型的操作数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-08
      • 2013-03-22
      • 2020-04-15
      • 2013-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多