/* main.c - Protothreads example for Dragon12-Plus
 * Copyright 2008 Colin D. Bennett <colin@gibibit.com>.
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *    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, see <http://www.gnu.org/licenses/>.
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This program demostrates the use of protothreads, which are extremely
 * lightweight, stackless threads implemented in portable C through
 * creative (ab)use of the switch/case construct.
 *
 * One of the most important things to know about protothreads is that
 * you cannot use local variables in a protothread, since stack variables
 * are not preserved between the protothread context switches.
 *
 * For more information on protothreads, see <http://www.sics.se/~adam/pt/>.
 */

#include "pt.h"
#include "hc12drivers/keypad.h"
#include "hc12drivers/led7seg.h"
#include "hc12/hcs12.h"
#include "hc12/interrupt.h"
#include "hc12/delay.h"

/* Protothread function declarations. */
static PT_THREAD(input_thread(struct pt *pt));
static PT_THREAD(led_thread(struct pt *pt));

/* Protothread state structures. */
static struct pt pt1, pt2;

int main()
{
	keypad_init();
	led7seg_init();

	/* Main loop. Allows each protothread to run until it
	 * returns because of a "wait", or any other reason. */
	PT_INIT(&pt1);
	PT_INIT(&pt2);
	while (1) 
	{
		input_thread(&pt1);
		led_thread(&pt2);
	}
}


static char selected = 0;
static const char map[] = "123A456B789C*0#D";

static PT_THREAD(input_thread(struct pt *pt))
{
	static int8_t key;
	PT_BEGIN(pt);

	// Wait for a button to be pressed.
	key = keypad_read_scancode();

	// Convert the scan code into a character.
	if (key == -1)
		selected = 0;
	else
		selected = map[key];

	PT_END(pt);
}

static PT_THREAD(led_thread(struct pt *pt))
{
	static char last;
	static uint8_t i;
	PT_BEGIN(pt);
	last = 0;

	// Wait for a key to be pressed
	PT_WAIT_UNTIL(pt, selected != 0);
	last = selected;
	if ((selected >= '0' && selected <= '9') 
		|| (selected >= 'A' && selected <= 'D'))
	{
		disable_interrupts();
		// Display on 7-segment display
		// Shift digits left 1 place.
		for (i = 0; i < 3; i++) 
		{
			led7seg_bits[i] = led7seg_bits[i + 1];
		}
		led7seg_bits[3] = led7seg_lookup_char(selected);
		enable_interrupts();

		// Wait for release.
		PT_WAIT_UNTIL(pt, selected != last);
	}
	else if (selected == '*')
	{
		LC_SET(pt->lc);

		// Rotate digits left
		disable_interrupts();
		i = led7seg_bits[0];
		led7seg_bits[0] = led7seg_bits[1];
		led7seg_bits[1] = led7seg_bits[2];
		led7seg_bits[2] = led7seg_bits[3];
		led7seg_bits[3] = i;
		enable_interrupts();

		delay_ms(30);   // Note: A delay loop is suboptimal for threads.
		if (selected == last)
			return PT_WAITING;
	}
	else if (selected == '#')
	{
		LC_SET(pt->lc);

		// Rotate digits right
		disable_interrupts();
		i = led7seg_bits[3];
		led7seg_bits[3] = led7seg_bits[2];
		led7seg_bits[2] = led7seg_bits[1];
		led7seg_bits[1] = led7seg_bits[0];
		led7seg_bits[0] = i;
		enable_interrupts();

		delay_ms(30);   // Note: A delay loop is suboptimal for threads.
		if (selected == last)
			return PT_WAITING;
	}

	PT_END(pt);
}

