Как выйти из спящего режима на PIC10F200 с помощью сторожевого таймера?

До сих пор я работал со многими другими микроконтроллерами, но не с семейством PIC. У меня проблемы с выходом из спящего режима с помощью сторожевого таймера на PIC10F200. Я программирую его на C, используя MPLAB X IDE v2.35. Все функции и макросы являются частью базовых библиотек микрочипа для процессора.

Вот минимальный пример моей проблемы:

#include <xc.h>
#pragma config CP    = OFF   // Code protection off
#pragma config MCLRE = OFF   // GP3/MCLR pin fuction is digital I/O
#pragma config WDTE  = ON    // Watchdog Timer enabled
#define _XTAL_FREQ 4000000

void main(void) {
    OPTION = 0 | nGPWU | nGPPU & ~T0CS & ~T0SE | PSA | PS2 | PS1 | PS0;    // bits: 7: no wake-up on pin change; 6: no weak pullups; 5: internal clock; 4: incremnt low to high; 3: prescale on wdt (Timer0 if cleared); 2-0: clock division by 128
    TRISGPIO = 0b00000000; // set all to output

    while(1) {
        GP0 = 1;   GP1 = 1;   GP2 = 1;   SLEEP();  // set all high and sleep a bit
        GP0 = 0;   GP1 = 0;   GP2 = 0;   SLEEP();  // set all low  and sleep a bit
    }
}

По сути, это классический пример переключателя (мигающего светодиода) с использованием сторожевого таймера. Только штифты все время высокие. Что я сделал не так?

Здесь слишком много многослойного материала. В самом деле, вам нужен компилятор на машине, которая имеет только 255 используемых инструкций!? Например, мы не знаем, что на самом деле делает SLEEP().
SLEEP — это встроенная функция и часть MPLAB X. Она вызывает одноименную ассемблерную инструкцию. PIC10F200 не имеет компаратора и не определяет регистр. Я привык к C, и у меня нет причин изучать PIC-вариант ассемблера. Спасибо за ваш комментарий.
Обычно, когда срабатывает сторожевой таймер, он снова перезапускает программу с самого начала. Таким образом, вы никогда не доберетесь до второго вызова SLEEP().
С AVR я привык продолжать программу с того места, где она остановилась, но вам нужно было бы установить сторожевой таймер в режим прерывания, а не в режим сброса. Я искал что-то похожее на PIC и подумал из таблицы данных, что это будет поведение по умолчанию: «Некоторые регистры никоим образом не сбрасываются [...] На них не влияет сброс WDT во время сна или сброс MCLR во время Сон, так как эти сбросы рассматриваются как возобновление нормальной работы». Что хорошего в сторожевом таймере со спящим режимом, если нет возможности сохранять данные между спящими режимами?
Никогда не делайте никаких предположений при смене поставщиков. Однажды Intel уменьшила размер кристалла на микроконтроллерах серии 8051 ... Производительность изменилась настолько, что сломала некоторые конструкции печатных плат. Так что даже один и тот же продавец может вас запутать. (Решением было 20 тысяч подтягиваний для тех, кому нужно знать.)

Ответы (1)

Ответ, наконец, пришел ко мне в виде Говардлонга. Сторожевой таймер действительно вызывает сброс каждый раз при входе в спящий режим. Однако можно сохранить переменные между сбросами после сна, используя persistentключевое слово в объявлении переменной.

Загвоздка в том, что таким образом переменная не может быть инициализирована обычным способом. Чтобы присвоить ему начальное значение, лучше всего обнаружить сброс при включении питания и инициализировать его исключительно после сброса. Его инициализация обычно приводит к проблеме типа курицы и яйца, когда вы хотите сохранить значение, но установить его на очень сброшенном уровне. Обратите внимание, что некоторые регистры также сбрасываются при сбросе WDT, и поэтому их необходимо устанавливать каждый раз!

Вот пример:

#include <xc.h>
#include <stdbool.h>
#pragma config CP    = OFF   // Code protection off
#pragma config MCLRE = OFF   // Master Clear Enable (GP3/MCLR pin fuction is digital I/O, MCLR internally tied to VDD)
#pragma config WDTE  = ON   // Watchdog Timer enabled
#define _XTAL_FREQ 40000000

static persistent bool _bState; // Persistent so the C startup code doesn't initialise

void main(void) {   
    // Check STATUS bits for type of reset
    if ( ! ( GPWUF==0 && nPD==0 && nTO==0 ) ) {
        // NOT a WDT wakeup from sleep, so treat as a power on reset
        _bState=false;
        OSCCALbits.FOSC4=0;
    }
    // These SFRs must be re-written after every reset
    OPTION = 0b11001100; // WDT is div-by-32 prescaled
    TRISGPIO = 0b1011;   // GP2 output

    GP2=_bState;
    _bState=!_bState;
    SLEEP();                
}
Дополнительно хочу выразить свое недовольство одним из комментариев к моему первоначальному вопросу. Спрашивать, почему я хочу использовать C, и демонстрировать незнание основных функций — не совсем конструктивно. Если вы считаете вопрос глупым, на него должно быть легко ответить. Если не хотите терять время, игнорируйте.