/* Megumin LED display firmware
 * Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
 * 
 * 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/>.
 */

#include "global.h"
#include "adc.h"
#include "serial.h"


volatile unsigned int sys_time_seconds = 0;
volatile union leds leds;

int main(void) {
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR&RCC_CR_HSERDY));
    RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk;
    RCC->CFGR |= ((6-2)<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV; /* PLL x6 -> 48.0MHz */
    RCC->CR |= RCC_CR_PLLON;
    while (!(RCC->CR&RCC_CR_PLLRDY));
    RCC->CFGR |= (2<<RCC_CFGR_SW_Pos);
    SystemCoreClockUpdate();
    SysTick_Config(SystemCoreClock/10); /* 100ms interval */
    NVIC_EnableIRQ(SysTick_IRQn);
    NVIC_SetPriority(SysTick_IRQn, 3<<5);

    /* Turn on lots of neat things */
    RCC->AHBENR  |= RCC_AHBENR_DMAEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_FLITFEN | RCC_AHBENR_CRCEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_DBGMCUEN |\
                    RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

    GPIOA->MODER |=
          (3<<GPIO_MODER_MODER2_Pos)  /* PA2  - LINE_MEAS */
        | (1<<GPIO_MODER_MODER3_Pos)  /* PA3  - LED_STB */
        | (1<<GPIO_MODER_MODER4_Pos)  /* PA4  - SD_CS */
        | (2<<GPIO_MODER_MODER5_Pos)  /* PA5  - SCK */
        | (2<<GPIO_MODER_MODER6_Pos)  /* PA6  - MISO */
        | (2<<GPIO_MODER_MODER7_Pos)  /* PA7  - MOSI */
        | (2<<GPIO_MODER_MODER9_Pos)  /* PA9  - HOST_RX */
        | (2<<GPIO_MODER_MODER10_Pos);/* PA10 - HOST_TX */

    /* Set shift register IO GPIO output speed */
    GPIOA->OSPEEDR |=
          (2<<GPIO_OSPEEDR_OSPEEDR3_Pos)   /* LED_STB */
        | (2<<GPIO_OSPEEDR_OSPEEDR4_Pos)   /* SD_CS */
        | (2<<GPIO_OSPEEDR_OSPEEDR5_Pos)   /* SCK */
        | (2<<GPIO_OSPEEDR_OSPEEDR7_Pos)   /* MOSI */
        | (2<<GPIO_OSPEEDR_OSPEEDR9_Pos);  /* HOST_RX */

    GPIOA->AFR[0] = (0<<GPIO_AFRL_AFRL5_Pos) | (0<<GPIO_AFRL_AFRL6_Pos) | (0<<GPIO_AFRL_AFRL7_Pos);
    GPIOA->AFR[1] = (1<<8) | (1<<4);

    GPIOB->MODER |=
          (0<<GPIO_MODER_MODER1_Pos); /* PB0  - LINE_POL */

    SPI1->CR1 =
          SPI_CR1_SSM
        | SPI_CR1_SSI
        | SPI_CR1_CPOL
        | SPI_CR1_CPHA
        | (4<<SPI_CR1_BR_Pos) /* /32 ~1.5MHz */
        | SPI_CR1_MSTR;
    SPI1->CR2 = (7<<SPI_CR2_DS_Pos);
    SPI1->CR1 |= SPI_CR1_SPE;

    TIM16->CR2 = 0;
    TIM16->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE;
    TIM16->CCMR1 = 0;
    TIM16->CCR1 = 32;
    TIM16->PSC = 48-1; /* 1us */
    TIM16->ARR = 1000-1; /* 1ms */
    TIM16->CR1 = TIM_CR1_CEN;

    NVIC_EnableIRQ(TIM16_IRQn);
    NVIC_SetPriority(TIM16_IRQn, 3<<5);

    adc_configure_scope_mode(1000000);

    usart_dma_init();

    while (42) {
        //int pol = GPIOB->IDR & (1<<1); /* Sample current polarity */
        //leds.error = pol ? 100 : 0;
        //for (int i=0; i<10000; i++) ;
        //leds.error = 100;
    }
}

void TIM16_IRQHandler(void) {
    static int leds_update_counter = 0;
    if (TIM16->SR & TIM_SR_UIF) {
        TIM16->SR &= ~TIM_SR_UIF;

        uint8_t bits = 0, mask = 1;
        for (int i=0; i<8; i++) {
            if (leds.arr[i]) {
                leds.arr[i]--;
                bits |= mask;
            }
            mask <<= 1;
        }

        if (leds_update_counter++ == 10) {
            leds_update_counter = 0;

            /* Workaround for SPI hardware bug: Even if configured to 8-bit mode, the SPI will do a 16-bit transfer if the
             * data register is accessed through a 16-bit write. Unfortunately, the STMCube register defs define DR as an
             * uint16_t, so we have to do some magic here to force an 8-bit write. */
            *((volatile uint8_t*)&(SPI1->DR)) = bits;
            GPIOA->BRR = 1<<3;
        }
    } else {
        TIM16->SR &= ~TIM_SR_CC1IF;
        GPIOA->BSRR = 1<<3;
    }
}

void NMI_Handler(void) {
    asm volatile ("bkpt");
}

void HardFault_Handler(void) __attribute__((naked));
void HardFault_Handler() {
    asm volatile ("bkpt");
}

void SVC_Handler(void) {
    asm volatile ("bkpt");
}


void PendSV_Handler(void) {
    asm volatile ("bkpt");
}

void SysTick_Handler(void) {
    static int n = 0;
    if (n++ == 10) {
        n = 0;
        sys_time_seconds++;
        leds.pps = 200; /* ms */
    }
}