/* * Picadom board demo program * by Ido Gendel, 2018 * * Hardware on board: * - LED anode connected to RA0 * - Momentary switch (N.O.) between GND and RA2 * * Software: * - MPLAB X IDE v5.00 * - XC8 compiler v2.00 (Free edition) * * Reference: * - PIC12(L)F1840 Datasheet, revision DS40001441F */ // PIC12F1840 Configuration Bit Settings // These settings are defined outside the normal code. // See menu Window->Target Memory Views->Configuration Bits #pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin) #pragma config WDTE = SWDTEN // Watchdog Timer Enable (WDT controlled by the SWDTEN bit in the WDTCON register) #pragma config PWRTE = ON // Power-up Timer Enable (PWRT enabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled) #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config PLLEN = OFF // PLL Enable (4x PLL disabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled) // This is used for the built-in delay functions // Make sure it agrees with any clock settings in the code #define _XTAL_FREQ 8000000UL #define BLINK_PADDING_MS 1000 #define BLINK_HIGH_MS 100 #define BLINK_LOW_MS 200 #include #include // This demo will be run as a "state machine" #define DEMO_MODES 2 struct tDemoMode { // Function pointers are fun! void (*runCallback)(void); } demoMode[DEMO_MODES]; // This variable will be changed in an interrupt service routine // So we declare it volatile to avoid problematic compiler optimizations volatile uint8_t switchPressed = 0; //========================================================= // Power-saving blink, Watchdog-based // Make sure the configuration bit WDTE is SWDTEN (see above) void runM0(void) { const uint8_t stateWDTPS[2] = {0x0A, 0x04}; uint8_t state = 1; // Enable watchdog WDTCONbits.SWDTEN = 1; while (!switchPressed) { // Set watchdog prescaler (1s or 16ms - see Datasheet p. 83) WDTCONbits.WDTPS = stateWDTPS[state]; // LED OFF or ON LATAbits.LATA0 = state; // Save CPU power! SLEEP(); // Woke up either by watchdog or interrupt... state = 1 - state; } // Disable watchdog WDTCONbits.SWDTEN = 0; } // runM0 //========================================================= // Run a single cycle of the software-based PWM signal void swPWM(uint8_t v) { uint16_t n; // A dumb loop, but the timing turns out easy on the eyes for (n = 0; n < 256; ++n) { LATAbits.LATA0 = (n < v) ? 1 : 0; } } // swPWM //========================================================= // LED breath effect, lazy software-based PWM void runM1(void) { uint8_t v = 0; int8_t dir = -1; while (!switchPressed) { swPWM(v); if ((0 == v) || (255 == v)) { dir = -dir; } v += dir; } } // runM1 //========================================================= void setup(void) { // Internal oscillator set to 500KHz on reset. We'll set it to 8MHz, no PLL // (note that on PIC MCUs, each instruction cycle is 4 clock cycles) // Datasheet p. 53 // The "[register name]bits" structs give us direct access to register sub-byte units OSCCONbits.IRCF = 0x0EU; // Assign callback functions for the state machine demoMode[0].runCallback = runM0; demoMode[1].runCallback = runM1; // Set up RA0 as regular digital output and LOW // In PIC MCUs, a tri-state value of 0 means output, 1 means input TRISAbits.TRISA0 = 0; // Disable analog input ANSELAbits.ANSA0 = 0; // Latch the output to LOW LATAbits.LATA0 = 0; // Set up RA2 for external interrupt; Datasheet p. 71 // In PIC MCUs, a tri-state value of 0 means output, 1 means input TRISAbits.TRISA2 = 1; // Disable analog input ANSELAbits.ANSA2 = 0; // Enable weak pull-up, but disable on all other pins WPUA = 0; WPUAbits.WPUA2 = 1; // Global weak pull-up enable, negative logic OPTION_REGbits.nWPUEN = 0; // Interrupt on falling edge OPTION_REGbits.INTEDG = 0; // Enable external interrupt, then interrupts globally INTCONbits.INTE = 1; INTCONbits.GIE = 1; } // setup //========================================================= void blink(uint8_t times) { // Blink with pre- and post-padding intervals __delay_ms(BLINK_PADDING_MS); while (times--) { LATAbits.LATA0 = 1; __delay_ms(BLINK_HIGH_MS); LATAbits.LATA0 = 0; __delay_ms(BLINK_LOW_MS); } // while __delay_ms(BLINK_PADDING_MS); } // blink //========================================================= void main(void) { uint8_t currentMode = 0; setup(); while (1) { // Blink current mode. +1, because we can't blink 0 times blink(currentMode + 1); // That also served as a debounce period, so don't press too long :-) // The switch press serves as a generic "stop" command switchPressed = 0; demoMode[currentMode].runCallback(); ++currentMode; if (DEMO_MODES == currentMode) { currentMode = 0; } } // while return; } // main //========================================================= // Most PICs only have a single interrupt vector. // The interrupt source has to be determined inside this function void __interrupt() ISR(void) { // We defined only one interrupt source (external), so no checks necessary switchPressed = 1; // Clear the interrupt flag INTCONbits.INTF = 0; } // ISR