
/*
 ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
 2011,2012 Giovanni Di Sirio.

 This file is part of ChibiOS/RT.

 ChibiOS/RT 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.

 ChibiOS/RT 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/>.

 ---

 A special exception to the GPL can be applied should you wish to distribute
 a combined work that includes ChibiOS/RT, without being obliged to provide
 the source code for any proprietary components. See the file exception.txt
 for full details of how and when the exception can be applied.
 */

#include <stdio.h>
#include <string.h>

#include "ch.h"
#include "hal.h"
// #include "test.h"
#include "shell.h"
#include "evtimer.h"
#include "chprintf.h"

#include "shellCmds.h"

#include "ap_math.h"
#include "spiInterface.h"
#include "SSD1306.h"
#include "font.h"

static MUTEX_DECL(mutexDisplay);

/*===========================================================================*/
/* Command line related.                                                     */
/*===========================================================================*/

#define SHELL_WA_SIZE THD_WORKING_AREA_SIZE(1024)
#define TEST_WA_SIZE THD_WORKING_AREA_SIZE(256)

#define RPM_ID 0x10d


/*
 * Internal loopback mode, 500KBaud, automatic wakeup, automatic recover
 * from abort mode.
 */

// CAN_BTR_SJW(n), where n = SJW - 1
// CAN_BTR_BRP(n), where n = prescaler - 1
// CAN_BTR_TS1(n), where n = Seg 1 - 1
// CAN_BTR_TS2(n), where n = Seg 2 - 1

static const CANConfig cancfg500 = {
	CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
	CAN_BTR_LBKM | CAN_BTR_SJW(0) | CAN_BTR_TS2(2) |
		CAN_BTR_TS1(3) | CAN_BTR_BRP(8)};

static const CANConfig cancfg1000 = {
	CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
	CAN_BTR_LBKM | CAN_BTR_SJW(0) | CAN_BTR_TS2(2) |
		CAN_BTR_TS1(4) | CAN_BTR_BRP(3)};

static const ShellConfig shell_cfg1 = {
	(BaseSequentialStream *)&SD1,
	shellCommands};
////////
// end of shell stuff

uint16_t sampIndex;

void setDriveA(uint8_t bit)
{
	if (bit)
	{
		palSetPad(GPIOA, GPIOA_A1);
		palClearPad(GPIOA, GPIOA_A2);
	}
	else
	{
		palClearPad(GPIOA, GPIOA_A1);
		palSetPad(GPIOA, GPIOA_A2);
	}
}

void setDriveB(uint8_t bit)
{
	if (bit)
	{
		palSetPad(GPIOA, GPIOA_B1);
		palClearPad(GPIOA, GPIOA_B2);
	}
	else
	{
		palClearPad(GPIOA, GPIOA_B1);
		palSetPad(GPIOA, GPIOA_B2);
	}
}

// dial settings
volatile int origin = 0;
;
volatile int target = 0;
volatile int count = 0;

volatile bool pause;
//

void setPause(bool p)
{
	pause = p;
}

static THD_WORKING_AREA(waGaugeThread, 512);
static THD_FUNCTION(gaugeThread, p) {
	(void)p;
	unsigned const fast = 10;
	unsigned const slow = 30;
	unsigned const range = slow - fast;
	unsigned del = fast;
	int step = 1;
	chRegSetThreadName("Step");
	while (TRUE)
	{

		while (pause)
			chThdSleep(1000);

		switch (count % 4)
		{
		case 0:
			setDriveA(1);
			setDriveB(0);
			break;
		case 1:
			setDriveA(1);
			setDriveB(1);
			break;
		case 2:
			setDriveA(0);
			setDriveB(1);
			break;
		case 3:
			setDriveA(0);
			setDriveB(0);
			break;
		}

		// all this calculates minimum distance from
		// target or origin
		int d1 = abs(count - origin);
		int d2 = abs(count - target);
		// finally, minimum distance
		int dist = min(d1,d2); 
		
		del = fast;
		if (dist < range) // inside lower bound of distance
		{
			del = slow - dist;
		}
		chThdSleep(del);

		if (count < target)
		{
			step = 1;
		}
		if (count > target)
		{
			step = -1;
		}
		if (count == target)
		{
			step = 0;
		}
		count = count + step;
	}
}

/*
 * Command Shell Thread
 */
static THD_WORKING_AREA(waShell, 512);
static THD_FUNCTION(shell, p) {
	(void) p;
	thread_t *shelltp = NULL;

	chRegSetThreadName("Shell ");
	/*
	 * in this demo it just performs
	 * a shell respawn upon its termination.
	 */
	while (true)
	{
		if (!shelltp)
		{

			shelltp = chThdCreateFromHeap(NULL, SHELL_WA_SIZE, "shell", NORMALPRIO, shellThread, (void *)&shell_cfg1);
		}
		else
		{
			/* If the previous shell exited.*/
			if (chThdTerminatedX(shelltp))
			{
				/* Recovers memory of the previous shell.*/
				chThdRelease(shelltp);
				shelltp = NULL;
			}
		}
		chThdSleepMilliseconds(500);
	}
}


	static THD_WORKING_AREA(waCanRx, 1024);
static THD_FUNCTION(canRx, p) {
	(void) p;

	// initialize can bus hardware
	event_listener_t el;
	CANRxFrame rxmsg;

	canStart(&CAND1, &cancfg1000);

	chRegSetThreadName("CAN receiver");
	chEvtRegister(&CAND1.rxfull_event, &el, 0);
	while (true)
	{
		if (chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)) == 0)
			continue;
		while (canReceive(&CAND1, CAN_ANY_MAILBOX, &rxmsg, TIME_IMMEDIATE) == MSG_OK)
		{
			/* Process message.*/
		}
	}
	chEvtUnregister(&CAND1.rxfull_event, &el);
}
/*
 * Application entry point.
 */
int main(void)
{
	//  struct EventListener el0, el1;

	/*
	 * System initializations.
	 * - HAL initialization, this also initializes the configured device drivers
	 *   and performs the board-specific initializations.
	 * - Kernel initialization, the main() function becomes a thread and the
	 *   RTOS is active.
	 */
	halInit();
	chSysInit();

	/*
	 * Initializes serial port.
	 */
	sdStart(&SD1, NULL);

	/*
	 * Shell manager initialization.
	 */
	//	shellInit();
	/*
	 * initialise approximate maths
	 */
	ap_init();

	chMtxLock(&mutexDisplay);

	/* start the SPI hardware for display */
	ssd1306spiInit();

	ssd1306_begin(SSD1306_SWITCHCAPVCC, 0);

	clearDisplay();

	display();

	chMtxUnlock(&mutexDisplay);

	/*
	 * Creates the PLL thread
	 */
	chThdCreateStatic(waGaugeThread, sizeof(waGaugeThread), NORMALPRIO, gaugeThread, NULL);

	chThdCreateStatic(waCanRx, sizeof(waCanRx), NORMALPRIO, canRx, NULL);

	chThdCreateStatic(waShell, sizeof(waShell), NORMALPRIO, shell, NULL);

	// reset gauge
	origin = 540;
	count = 540;
	target = 0;
	chThdSleepMilliseconds(1000);
	target = 0;

	int frac = 0;
	/* start the SPI hardware for display */
	while (1)
	{

		chThdSleepMilliseconds(100);
		unsigned const SCALE = frac * 32 + 16;
		frac = (++frac) % 10;

		// read the dial

		origin = count;

		target = target + 1;

		// target = lidar * 630L / 2000L;

		if (target > 540)
			target = 0;

		chMtxLock(&mutexDisplay);

		font_gotoxy(0, 0);
		clearDisplay();

		//    print_scaled_string("cm:",0,16,4,SCALE);

		print_digits(0, 0, 4, 1, target, SCALE);

		chMtxUnlock(&mutexDisplay);

		display();
	}
}
