/*
 * example pic program for the DIODER PIC
 *
 * chrysn <chrysn@fsfe.org> 2011-10-11
 */

/* ===================
 * hardware definition
 * =================*/

#include"pic/pic16f684.h"

/* =========
 * libraries
 * =======*/

#include <stdint.h>

/* ==================
 * configuration bits
 * ================*/

typedef unsigned int config;
config at 0x2007 __CONFIG =
    _FCMEN_OFF &
    _IESO_OFF &
    _BOD_OFF &
    _CPD_OFF &
    _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT;

/* ===============
 * hardware wiring
 * =============*/

#define LED_PORT PORTA
#define LED_TRIS TRISA
#define RED_BIT 2
#define GREEN_BIT 0
#define BLUE_BIT 1

#define BUTTONLEFT_PORT PORTC
#define BUTTONLEFT_BIT 5
#define BUTTONCENTER_PORT PORTA
#define BUTTONCENTER_BIT 3
#define BUTTONRIGHT_PORT PORTA
#define BUTTONRIGHT_BIT 4

#define DIAL_ADC 7

/* ============
 * global state
 * ==========*/

uint8_t color[3] = { 0, 0, 0 };

/* =================
 * generic pic setup
 * ===============*/

static void clock_set_8mhz()
{
	// wait for HFINTOSC to become stable -- not sure if required
	while ((OSCCON & (0b100)) == 0) ;
	// 8 mhz
	OSCCON = 0b1111001;
}

static void adc_setup()
{
	// only use one adc, disable all others (important for reading digital pins)
	ANSEL = (1 << DIAL_ADC);
	// use 16 as divider factor (recommended for 4mhz or 8mhz clock)
	ADCON1 = 0b101 << 4;
	// left justified, reference is Vdd, enabled and set to go
	ADCON0 = (DIAL_ADC << 2) | 0x01;
}

static void adc_start_conversion()
{
	ADCON0 |= 0x02;
}

static char adc_is_finished()
{
	return !(ADCON0 & 0x02);
}

static void pin_setup()
{
	// initialization required according to example 4-1 in the data sheet
	CMCON0 = 0x07;

	/* all the mosfet channels are output pins: RA0, RA1, RS2; leave the rest
	 * tri-stated */
	LED_TRIS &= ~(1<<RED_BIT) & ~(1<<GREEN_BIT) & ~(1<<BLUE_BIT);
}

/* ====================
 * hardware abstraction
 * ==================*/

static char button_left_pressed()
{
	return !(BUTTONLEFT_PORT & (1 << BUTTONLEFT_BIT));
}

static char button_center_pressed()
{
	return !(BUTTONCENTER_PORT & (1 << BUTTONCENTER_BIT));
}

static char button_right_pressed()
{
	return !(BUTTONRIGHT_PORT & (1 << BUTTONRIGHT_BIT));
}

/* =============
 * program logic
 * ===========*/

static void rare_tick()
{
	if (adc_is_finished()) {
		color[0] = color[1] = color[2] = ADRESH;

		adc_start_conversion();
	}

	if (button_left_pressed())
		color[0] = 255;
	if (button_center_pressed())
		color[1] = 255;
	if (button_right_pressed())
		color[2] = 255;
}

void main(void)
{
	uint8_t colorcounter = 0;

	// a faster clock is required to do other things than just PWMing the
	// LEDs
	clock_set_8mhz();
	adc_setup();
	pin_setup();

	adc_start_conversion();

	while (1) {
		// 17 is prime and close to sqrt(256)
		colorcounter += 17;
		LED_PORT = ((color[0] > colorcounter) << RED_BIT) |
		    ((color[1] > colorcounter) << GREEN_BIT) |
		    ((color[2] > colorcounter) << BLUE_BIT);

		// happens around every 6ms
		if (colorcounter == 255) {
			rare_tick();
		}
	}
}

