/*
 * main.c.c
 *
 *  Created on: Jun 19, 2023
 *      Author: true
 *
 * HK32F030MF4P6 MCU running at 32MHz
 * (wanted to run slower, but want perfect waveform using SPI for LED)
 *
 */


#include "hk32f030m.h"

#include "adxl.h"
#include "btn.h"
#include "led_prog.h"
#include "led_sk6x_spi.h"
#include "prng.h"
#include "spi.h"
#include "timer.h"
#include "userconf.h"



#define GPIO_RCC_AHB_GPIO_ALL	RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | \
								RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD

#define NO_MOVE_SLEEP_TIME		(15 * 60) * 128



static   uint32_t uptime = 0;		// uptime in seconds
volatile uint16_t cnt = 0;			// 1/1000 second tick
static   uint16_t cnt2 = 0;			// low priority loop check

static   uint32_t no_move_ticks;	// keep track of how long we are still



static void gpio_init()
{
	GPIO_InitTypeDef gpio;

	// enable clocks
	RCC_AHBPeriphClockCmd(GPIO_RCC_AHB_GPIO_ALL, ENABLE);

	// I2C AF config
	GPIOB->AFR[0] = 0x00010000;		// _, _, _, USART RX, _, _, _, _
	GPIOC->AFR[0] = 0x20000000;		// SPI MISO, I2C SCL, I2C SDA, _, _, _, _, _
	GPIOD->AFR[0] = 0x00022210;		// _, _, _, LED MOSI, SPI SCK, SPI MOSI, USART TX, _

	/*
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_0);	// I2C SDA
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_0);	// I2C SCL

	// UART AF config
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_1);	// USART RX
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_1);	// USART TX

	// SPI AF config
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_2);	// SPI MISO
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_2);	// SPI MOSI
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource3, GPIO_AF_2);	// SPI Clock
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_2);	// LED MOSI
	*/

	// output pins, including defaults for AF pins
	gpio.GPIO_Mode   = GPIO_Mode_OUT;
	gpio.GPIO_Speed  = GPIO_Speed_2MHz;
	gpio.GPIO_OType  = GPIO_OType_PP;
	gpio.GPIO_PuPd   = GPIO_PuPd_NOPULL;
	gpio.GPIO_Schmit = GPIO_Schmit_Enable;

		// ADXL !SS
	GPIOC->ODR       = GPIO_Pin_4;
	gpio.GPIO_Pin    = GPIO_Pin_4;
	GPIO_Init(GPIOC, &gpio);

		// User LED
	GPIOD->ODR       = GPIO_Pin_7;
	gpio.GPIO_Pin    = GPIO_Pin_7;
	GPIO_Init(GPIOD, &gpio);

		// for now, float I2C pins
	gpio.GPIO_OType  = GPIO_OType_OD;
	gpio.GPIO_Pin    = GPIO_Pin_5 | GPIO_Pin_6;
	GPIOC->ODR       = GPIO_Pin_5 | GPIO_Pin_6;
	GPIO_Init(GPIOC, &gpio);

		// SPI MOSI, LED MOSI
		// SPI MOSI will always idle low
		// LED MOSI will idle high when in standby, otherwise idle low
	gpio.GPIO_Speed  = GPIO_Speed_10MHz;
	gpio.GPIO_OType  = GPIO_OType_PP;
	gpio.GPIO_Pin    = GPIO_Pin_2 | GPIO_Pin_4;
	GPIOD->ODR       = GPIO_Pin_3 | GPIO_Pin_4;
	GPIO_Init(GPIOD, &gpio);



	// --digital input pins--
	gpio.GPIO_Mode   = GPIO_Mode_IN;

		// ADXL_INT
	gpio.GPIO_Pin    = GPIO_Pin_3;
	GPIO_Init(GPIOC, &gpio);

		// BTN0-BTN2
	gpio.GPIO_PuPd   = GPIO_PuPd_UP;
	gpio.GPIO_Pin    = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_Init(GPIOA, &gpio);



	// --analog input pins--
	gpio.GPIO_Mode   = GPIO_Mode_AN;

		// VBAT
	gpio.GPIO_PuPd   = GPIO_PuPd_NOPULL;
	gpio.GPIO_Pin    = GPIO_Pin_6;
	GPIO_Init(GPIOD, &gpio);



	// --alternate function pins--
	gpio.GPIO_Mode   = GPIO_Mode_AF;

		// I2C SDA, I2C SCL
	//gpio.GPIO_Pin  = GPIO_Pin_5 | GPIO_Pin_6;
	//GPIO_Init(GPIOC, &gpio);

		// SPI MISO
	gpio.GPIO_Pin    = GPIO_Pin_7;
	GPIO_Init(GPIOC, &gpio);

		// USART TX, SPI SCK
	gpio.GPIO_Pin    = GPIO_Pin_1 | GPIO_Pin_3;
	GPIO_Init(GPIOD, &gpio);

		// USART RX
	gpio.GPIO_PuPd   = GPIO_PuPd_DOWN;
	gpio.GPIO_Pin    = GPIO_Pin_4;
	GPIO_Init(GPIOB, &gpio);
}

// timing insensitive loop
__attribute__ ((long_call, section(".ramfunc"))) void app_loop()
{
	while(1) {
		if (cnt2 != cnt) {
			cnt2++;
			if (cnt2 >= 1024) cnt2 = 0;

			// button processing (512Hz)
			if (cnt2 & 1) {
				btn_callback();
			}

			// led programs (128Hz)
			if ((cnt2 & 0x7) == 0) {
				ledprog_run();

				// stuff next LED bits
				led_sk6x_process();
			}
		}

		__WFI();
	}
}

// startup
int main(void)
{
	// configure system IO
	gpio_init();

	// configure SPI and peripheral
	spi_init();
	adxl345_init();
	adxl345_tick();

	// load user config, unless SET button is held, in which case we restore defaults
	uconf_restore(!(GPIOA->IDR & (1 << 3)));

	// configure prng
	tinymt32_init(&tinymt32_s, (adxl.x << 16) | (adxl.y << 8) | adxl.z);

	// configure addressable LEDs
	led_sk6x_init();

	// configure user input
	btn_init();
	ledprog_btn();

	// configure mainline timer
#ifdef DEBUG
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_DBGMCU, ENABLE);
	DBGMCU->CR	    = DBGMCU_CR_DBG_STOP;	// enable debug while in STOP (low power debug)
	DBGMCU->APB1FZ |= DBGMCU_TIM6_STOP;		// stop timer while debugging
#endif
	tim6_init();

	// run non-timing-constrained code
	app_loop();
}

// 1KHz main logic loop
__attribute__ ((long_call, section(".ramfunc"))) void TIM6_IRQHandler()
{
	// clear interrupt
	TIM6->SR = 0;

	// attempt sending LEDs
	if ((cnt & 0x7) == 0) {
		led_sk6x_update();
	}

	// read buttons
	if (cnt & 1) {
		btn_tick();
	}

	// accelerometer: tick 128Hz
	if ((cnt & 0x7) == 0x6) {
		adxl345_tick();

		// should we sleep?
		// todo: make this interrupt driven, and tune required accel value
		if (adxl345_movement() < 6) {
			if (no_move_ticks >= NO_MOVE_SLEEP_TIME) {
				led_sk6x_brightness(0);
			} else no_move_ticks++;
		} else {
			if (no_move_ticks >= NO_MOVE_SLEEP_TIME) {
				led_sk6x_brightness(uconf.led_bright);
			}
			no_move_ticks = 0;
		}
	}


	// timing
	cnt++;
	if (cnt >= 1024) {
		cnt = 0;
		uptime++;
	}
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(char* file , uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */    
       /* Infinite loop */
    
    while (1)
  {        
  }
}
#endif /* USE_FULL_ASSERT */
