megacode/pic/318LPW1K-L/318LPW1K-L.c

181 lines
5.7 KiB
C

/* micro-controller firmware fot the Monarch 318LIPW1K(-L) remote
Copyright (C) 2014 Kévin Redon <kingkevin@cuvoodoo.info>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* libraries */
#define __12f629
#include <pic12f629.h>
#include <stdint.h>
/* the peripherals connected to the pins */
#define LED_ANODE _GP0
#define TX _GP1
#define SWITCH _GP4
#define LED_CATHODE _GP5
/* simple functions */
// note: LED and TX can not be enabled at the same time
#define led_on() GPIO |= LED_ANODE;
#define led_off() GPIO &= ~LED_ANODE;
#define tx_on() GPIO |= TX;
#define tx_off() GPIO &= ~TX;
#define sleep() __asm sleep __endasm
/* variables */
//volatile uint32_t code = 0; // the code to transmit (read from EEPROM)
volatile int8_t transmit = 0; // transmitting (0: do not transmit, 1: transmit, -1: finish transmitting)
volatile uint8_t phase = 0; // the 4 phases of the bit (2ms pause, 1ms pulse, 2ms pause, 1ms pulse. only transmit during one of the two pulse period depending on the bit value)
/* configuration bits
* all set (to 1) per default
* clear (to 0) relevant bits
*/
uint16_t __at(_CONFIG) __CONFIG = _CPD_OFF & // no data code protect
_CP_OFF & // no code project
_BODEN_OFF & // no brown out detect
_MCLRE_OFF & // disable master clear reset
_PWRTE_ON & // enable power-up timeout
_WDTE_OFF & // no watchdog
_INTRC_OSC_NOCLKOUT; // use internal oscillator and both I/O pins
/* default code to transmit in EEPROM
a megacode is 3 bytes long (MSB of byte 1 is 1)
append 0x00 to indicate end
*/
// 0x2100 comes from the PIC12F629 Memory Programming document
__code uint8_t __at(0x2100) EEPROM[] = {0xc9, 0x17, 0xc2, 0x00};
void timer_1ms() {
TMR1ON = 0; // disable timer 1 (to write value safely)
TMR1L = 0x6e; // set time (tuned per hand)
TMR1H = 0xff; // set time
TMR1ON = 1; // start timer 1
}
void timer_2ms() {
TMR1ON = 0; // disable timer 1 (to write value safely)
TMR1L = 0x22; // set time (tunef per hand)
TMR1H = 0xff; // set time
TMR1ON = 1; // start timer 1
}
/* read EEPROM data */
uint8_t read_eeprom(uint8_t address) {
EEADR = address;
RD = 1;
return EEDATA;
}
/* transmit the megacode */
void megacode (void) {
static uint8_t byte;
uint8_t bit = phase / 4;
if (transmit != 0) {
if (bit & 0x7 == 0) { // read byte to transmit
byte = read_eeprom(bit / 8);
}
if (bit < 24) { // transmit bit
if (phase & 0x1) {
uint8_t pulse = (byte >> ((23 - bit) & 0x7)) & 0x01;
if ((phase & 0x3 == 1 && !pulse) || (phase & 0x3 == 3 && pulse)) {
led_off();
tx_on();
}
timer_1ms();
} else {
tx_off();
led_on();
timer_2ms();
}
} else if (bit == 24) { // 25th bit is a blank
led_on();
tx_off();
timer_2ms();
} else { // restart after 25th bit
phase = 0xff; // phase will be 0 after incrementing
if (transmit == -1) { // stop transmitting if requested
led_off();
transmit = 0; // stop transmitting
}
timer_1ms();
}
phase++; // go to next phase
}
}
/* initialize micro-controller */
void init (void) {
TRISIO |= SWITCH; // switch is input
WPU |= SWITCH; // enable pull-up on switch
NOT_GPPU = 0; // enable global weak pull-up for inputs
TRISIO &= ~(LED_ANODE | LED_CATHODE | TX); // all other are outputs
GPIO &= ~(LED_ANODE | LED_CATHODE | TX); // clear output
}
// function called on interrupts
// interrupt 0 is only one on PIC12
static void interrupt(void) __interrupt 0
{
if (GPIF) { // pin state changed
if (GPIO & SWITCH) { // switch is released stop transmission
if (transmit == 1) {
transmit = -1; // stop transmitting after last transmission
}
} else { // switch is pressed, start transmission
if (transmit == 0) { // only transmit if code is loaded
transmit = 1;
phase = 0;
megacode(); // start sending megacode
}
}
GPIF = 0; // clear interrupt
}
if (TMR1IF) { // timer 1 overflow
megacode(); // continue sending megacode
TMR1IF = 0; // clean interrupt
}
}
void main (void)
{
init(); // configure micro-controller
IOC |= SWITCH; // enable interrupt for the switch
GPIE = 1; // enable interrupt on GPIO
T1CON = _T1CKPS1 | _T1CKPS0; // set prescaler to 8
// 0 is per default
//TMR1ON = 0; // stop timer 1
//TMR1GE = 0; // enable timer 1
//TMR1CS = 0; // use internal clock / 4 (=1MHz)
//TMR1IF = 0; // clear interrupt
PIE1 = 1; // enable timer 1 interrupt
PEIE = 1; // enable timer interrupt
if (read_eeprom(0) & 0x80) { // verify if the code is program (the MSB bit is always 1 for megacode codes)
GIE = 1; // globally enable interrupts
} else { // show error and got to sleep forever
led_on();
}
while (1) {
if (transmit == 0) {
sleep(); // sleep to save power (this will shut down timer 1)
}
}
}