/*
* 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 <xc.h>
#include <stdint.h>
// 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
Comments