Регистр отсчета Timer0 на PIC18F47K42 увеличивается с шагом, чем ожидалось - PullRequest
0 голосов
/ 25 июня 2018

Я новичок во внедрении, и у меня есть небольшая проблема.Я думаю, что проблема в моем коде.Но я прошел через это десятки раз, и я не могу найти сбой.

У меня есть Timer0, который я могу запрограммировать в 16 бит (может сосчитать до 65536).Существует регистровый бит TMR0L и TMR0H, которые увеличиваются на каждом фронте тактового сигнала или кратны тактовому сигналу.Я хочу, чтобы он увеличивался, скажем, каждые 0,00001 с.

В соответствии с моим DATASHEET, я установил следующие настройки:

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.Так что это дает частоту 1 МГц.1000000/32 = 31250 Гц, что дает 0,000032 секунды на счет.Миллисекунда (0,001 / 0,000032 = 31,25 счета), поэтому, чтобы получить миллисекунды, мне пришлось бы посчитать примерно 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;
        }
    }

}

И мой основной код - это только код ниже.Это заставляет светодиод мигать.Я хочу сделать это блекнутым каждую секунду.Итак, 31250 пунктов.Это не проблема, потому что я проверил с другой функцией, и таймер действительно 16 бит.Считается до 65 тыс.

void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    while (1)
    {

        countDelay(1000);
        LATA0 = 0;
        countDelay(1000);

        LATA0 = 1;

    }
}

С этим кодом я вижу постоянно включенный светодиод.С помощью осциллографа я проверяю сигнал: 141,76 Гц. Мы должны были увидеть 0,5 Гц с периодом цикла 2 секунды, то есть 0,5 Гц.

Итак, вкратце, мы в 283 раза выше.Что близко к 256 из 2 ^ 8.Поэтому я считаю, что это ошибка в моем коде.Может быть, что-то в моей функции задержки?У кого-нибудь есть идея?

РЕДАКТИРОВАТЬ # 1: Я сделал другие тесты.Я изменил значение моих переменных.Не меняет результат.Сигнал остается 141 Гц, +/- 10 Гц.Даже если x16 тактовая частота.

Изменение прескалярного значения делает почти то же самое.Сигнал остается на этот раз, ТОЧНО, на частоте 141,76 Гц.

РЕДАКТИРОВАТЬ # 2: я использовал отладчик внутри моего pickit.Похоже, когда я делаю это.

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

Результат равен 0. Есть идеи почему?Это не должно быть.

РЕДАКТИРОВАТЬ # 3: Это дает мне 251 миллион, когда я использую тип long.

Edit # 4: проверенная тактовая частота.Это нормально.Тем не менее, этот расчет, даже если все целые числа не работают.Ответ на counttsNum - 74, но он должен быть 15.

unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));

Ответы [ 3 ]

0 голосов
/ 25 июня 2018

одна потенциальная проблема (но не уверен, что это все объясняет) - третья строка в процедуре задержки

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

TMR0H - это 8-битный регистр.Когда вы сдвигаете его, вы очищаете этот регистр.Это имеет два эффекта.Во-первых, Time16 не должен увеличиваться так быстро, а во-вторых, вы действуете непосредственно в регистре.Согласно данным таблицы, старший байт имеет двойную буферизацию, поэтому его не следует фиксировать, пока вы не запишете в нижний регистр, но я бы изменил это или, по крайней мере, установил точку останова и посмотрел на значения 0L и 0H ипоследующее значение Time16.

0 голосов
/ 16 июля 2018

Я нашел ответ! Оказывается, был регистр, который мой плагин MCC написал сам. В PIC18F47k42 регистр

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

Управляет системными часами. Оказывается, это было установлено в 0x61, который добавляет разделитель, который установлен другим регистром. Его значение было 4.

Мораль истории, проверяйте свои регистры конфигурации один за другим и не всегда доверяйте плагинам!

0 голосов
/ 25 июня 2018

Трудно указать пальцем на точную проблему здесь. Сначала нам нужно проверить несколько предположений об оборудовании:

Ваши главные часы фактически работают на частоте 4 МГц.

Ваш таймер на самом деле считает на 1 МГц. Дважды проверьте предскалярные настройки.

Теперь, при условии проверки аппаратных часов, я вижу проблему с вашим расчетом времени-> подсчета.

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

Давайте оценим эту строку.

ms_delay - аргумент, передаваемый для задания переменной задержки. Похоже, это в единицах мс. Но не забывайте, что мы делаем целочисленные вычисления! Таким образом, для любого значения ниже 1000 ms_delay / 1000 будет равняться нулю.

Возможно, вам придется изучить вычисления с плавающей запятой. Или вам нужно изменить способ вычисления counttsNum, чтобы деление не возвращало ноль.

@ Редактировать 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-битный таймер имеет тактовую частоту 4 МГц с предскалярным значением 4, что дает нам эффективную тактовую частоту 1 МГц. Это имеет период 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

Решает ли это вашу проблему?

...