/*
 * true's GAT Nametag
 * Rushed SUPERCON 8 EDITION
 * sub MCU firmware
 * 2024 true
 *
 * the sub MCU is responsible for the following:
 *  - user buttons (complete handling / filtering)
 *  - rgb chip hw enable / disable
 *  - IrDA rx/tx and IrDA transceiver enable / disable
 *  - accelerometer hardware interrupt
 *
 *  it does all this at a cost of a little less than 2mA. =(
 *  probably should've used PY32F which would've been 1mA,
 *  but oh well. wanted to stick with the same platform...
 */


#include <ch32v00x.h>

#include "device/btn.h"
#include "device/intr_out.h"
#include "device/irda.h"

#include "i2c_slave.h"



static inline void gpio_init()
{
    GPIO_InitTypeDef gpio = {0};

    gpio.GPIO_Speed = GPIO_Speed_2MHz;

    // unused pins; input pull high
    gpio.GPIO_Mode = GPIO_Mode_IPU;
    gpio.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOD, &gpio);

    // BOOT button user buttons; input pull high
    // note: default bootloader looks for BOOT to go HIGH to enter boot mode
    //       custom bootloader will instead look for BOOT to go LOW
    gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOC, &gpio);

    // unused pins; input pull low
    gpio.GPIO_Mode = GPIO_Mode_IPD;
    gpio.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_Init(GPIOD, &gpio);

    // accelerometer PC3; input pull low, active high
    gpio.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init(GPIOC, &gpio);

    // interrupt to main MCU PA1; output open drain
    GPIOA->BSHR = GPIO_Pin_1 | GPIO_Pin_2;
    gpio.GPIO_Mode = GPIO_Mode_Out_OD;
    gpio.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOA, &gpio);

    // IrDA shutdown PA2; output push/pull
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOA, &gpio);

    // AW200xx hardware enable pin PD0; output push/pull
    gpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOD, &gpio);

    // I2C PC1, PC2; output AF open drain
    gpio.GPIO_Mode = GPIO_Mode_AF_OD;
    gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_Init(GPIOC, &gpio);

    // UART TX PD5; output AF push/pull
    gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOD, &gpio);

    // UART RX PD6; input pull low
    gpio.GPIO_Mode = GPIO_Mode_IPD;
    gpio.GPIO_Pin = GPIO_Pin_6;
    GPIO_Init(GPIOD, &gpio);
}

static inline void exti_nvic_init()
{
    NVIC_InitTypeDef nvic = {0};

    nvic.NVIC_IRQChannel = EXTI7_0_IRQn;
    nvic.NVIC_IRQChannelPreemptionPriority = 0;
    nvic.NVIC_IRQChannelSubPriority = 1;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic);
}

void systick_init(void)
{
    SysTick->CMP = (SystemCoreClock / 200) - 1;     // we want a 200Hz interrupt
    SysTick->CNT = 0;                               // clear counter
    SysTick->CTLR = 0xF;                            // start counter in /1 mode, enable interrupts, auto-reset counter
    SysTick->SR = 0;                                // clear count comparison flag

    NVIC_SetPriority(SysTicK_IRQn, 1 << 6);
    NVIC_EnableIRQ(SysTicK_IRQn);                   // enable interrupt
}



/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
//__attribute__((section(".data#")))
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    SystemCoreClockUpdate();

    // enable peripheral clocks used in this program
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR    | RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO  |
                            RCC_APB2Periph_GPIOA  | RCC_APB2Periph_GPIOC |
                            RCC_APB2Periph_GPIOD, ENABLE);


    // configure gpio
    gpio_init();

    // get i2c up and running
    i2cs_init(INT_I2C_ADDR, 0);

    // configure various external peripherals
    btn_init();
    irda_init();

    // enable external interrupts
    exti_nvic_init();

    // configure systick
    systick_init();

    while (1) {
        // todo: determine why we HardFault with user button interrupts
        //       randomly but only with __WFI being used
        // __WFI();
    }
}
