//*****************************************************************************
//
//! @file buckzx_demo.c
//!
//! @brief Demonstrate operation of the buck zero-cross implementation.
//!
//! See Errata ERR019 for additional details on this issue.
//!
//! Heavily based on deepsleep_wake, this example demonstrates the
//! operation of the buck zero-cross implementation.
//!
//! The example uses 5 GPIOs in total for the demonstration as follows:
//!         GPIO 3: Shows the CTIMER A buck pulse.
//!         GPIO 4: Shows The CTIMER B buck pulse.
//!         GPIO 5: Demarcates the sleep cycle; high while sleeping,
//!                 low when awake.
//!         GPIO 6: Demarcates am_ctimer_isr(), high when the ISR is
//!                 entered, low on exit. Basically envelopes each of
//!                 the buck pulses.
//!         GPIO 7: Toggling pattern of approximately 1us width during
//!                 the time the bucks are being restored.
//!
//! To run the buckzx_demo example:
//! - Connect an analyzer to GPIOs 3-7 on the apollo2_evb board with
//!   a trigger (high-to-low) on GPIO5.
//! - Flash the example binary and reset.
//! - As on deepsleep_wake, the Apollo2 will incur an RTC interrupt
//!   once every second. It will stay awake for one second, then it
//!   will deep sleep for one second.
//! - The sleep time is indicated by GPIO 5 being high. The awake
//!   time is indicated by GPIO 5 being low.
//! - Zooming in on the GPIO5 low-going pulse shows the buck handling
//!   in action.
//! - GPIO 3 & 4 will each pulse once during the wake time. These
//!   pulses show the core and memory bucks.
//!   Note that the 2 signals occur differently depending on the
//!   CTIMER used.  CTIMERs 0 and 1 will see core buck on GPIO4
//!   (CTimer B) and mem buck on GPIO3 (CTimer A).  CTIMERs 2 and 3
//!   are vice-versa.
//! - GPIO 6 pulses each time the CTIMER fires.  Therefore, there
//!   should be as many GPIO6 pulses as 3 & 4 combined.
//! - GPIO 7 simply demonstrates MCU usage while the bucks are resumed.
//! - Modify BUCK_TIMER (0 - 3) to change the timer that is actually
//!   used for the operation.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2019, Ambiq Micro
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v2.2.0-7-g63f7c2ba1 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"

//
// BUCK_TIMER defines the timer number, 0 - 3, to be used.
//
#define BUCK_TIMER      AM_HAL_SYSCTRL_BUCK_CTIMER_TIMER3

#define XT              1
#define LFRC            2
#define RTC_CLK_SRC     XT

//*****************************************************************************
//
// GPIO signaling
//
//*****************************************************************************
//
// These define GPIOs to use for the signaling
//
#define GPIO_BUCKA                  3
#define GPIO_BUCKB                  4
#define GPIO_SLEEP                  5
#define GPIO_CTIMER_ISR             6
#define GPIO_WIGGLER                7

//*****************************************************************************
//
// Globals
//
//*****************************************************************************
volatile uint32_t g_ui32CountRTC = 0;
volatile uint32_t g_TestMask = 0;

//*****************************************************************************
//
// UART configuration settings.
//
//*****************************************************************************
am_hal_uart_config_t g_sUartConfig =
{
    .ui32BaudRate = 115200,
    .ui32DataBits = AM_HAL_UART_DATA_BITS_8,
    .bTwoStopBits = false,
    .ui32Parity   = AM_HAL_UART_PARITY_NONE,
    .ui32FlowCtrl = AM_HAL_UART_FLOW_CTRL_NONE,
};

//*****************************************************************************
//
// Initialize the UART
//
//*****************************************************************************
void
uart_init(uint32_t ui32UartModule)
{
    //
    // Make sure the UART RX and TX pins are enabled.
    //
    am_bsp_pin_enable(COM_UART_TX);
    am_bsp_pin_enable(COM_UART_RX);

    //
    // Power on the selected UART
    //
    am_hal_uart_pwrctrl_enable(ui32UartModule);

    //
    // Start the UART interface, apply the desired configuration settings, and
    // enable the FIFOs.
    //
    am_hal_uart_clock_enable(ui32UartModule);

    //
    // Disable the UART before configuring it.
    //
    am_hal_uart_disable(ui32UartModule);

    //
    // Configure the UART.
    //
    am_hal_uart_config(ui32UartModule, &g_sUartConfig);

    //
    // Enable the UART FIFO.
    //
    am_hal_uart_fifo_config(ui32UartModule, AM_HAL_UART_TX_FIFO_1_2 | AM_HAL_UART_RX_FIFO_1_2);

    //
    // Enable the UART.
    //
    am_hal_uart_enable(ui32UartModule);
}

//*****************************************************************************
//
// Disable the UART
//
//*****************************************************************************
void
uart_disable(uint32_t ui32UartModule)
{
    //
    // Before disabling the UART, wait a little time to be sure all
    // printing has completed.
    //
    am_util_delay_ms(10);

    //
    // Disable and power down the UART.
    //
    am_hal_uart_disable(ui32UartModule);
    am_hal_uart_pwrctrl_disable(ui32UartModule);

    //
    // Turn off UART clock.
    // Note - this is automatically handled in hardware on Apollo2.
    //
    am_hal_uart_clock_disable(ui32UartModule);

    //
    // Disable the UART pins.
    //
    am_bsp_pin_disable(COM_UART_TX);
    am_bsp_pin_disable(COM_UART_RX);
}

//*****************************************************************************
//
// CTIMER ISR
//
//*****************************************************************************
void
am_ctimer_isr(void)
{
    uint32_t ui32Status, ui32IntMask;

    //
    // Set the GPIO indicating that the ISR has been entered
    //
    am_hal_gpio_out_bit_set(GPIO_CTIMER_ISR);

    //
    // Get interrupt status and clear the interrupt.
    //
    ui32Status = am_hal_ctimer_int_status_get(false);
    am_hal_ctimer_int_clear(ui32Status);

    //
    // Determine which interrupt it is and set the GPIO.
    // This will indicate that the buck has pulsed.
    //
    ui32IntMask = ui32Status;
    while ( ui32IntMask )
    {
        if ( ui32IntMask & 0x01 )
        {
            //
            // This is CTIMER A
            //
            am_hal_gpio_out_bit_set(GPIO_BUCKA);
        }

        if ( ui32IntMask & 0x02 )
        {
            //
            // This is CTIMER B
            //
            am_hal_gpio_out_bit_set(GPIO_BUCKB);
        }

        //
        // Check the next 2 timers
        //
        ui32IntMask >>= 2;
    }

    //
    // And call the appropriate CTIMER ISR
    am_hal_ctimer_int_service(ui32Status);

    //
    // Now that the buck has been handled, clear the appropriate GPIO.
    //
    ui32IntMask = ui32Status;
    while ( ui32IntMask )
    {
        if ( ui32IntMask & 0x01 )
        {
            //
            // This is CTIMER A
            //
            am_hal_gpio_out_bit_clear(GPIO_BUCKA);
        }

        if ( ui32IntMask & 0x02 )
        {
            //
            // This is CTIMER B
            //
            am_hal_gpio_out_bit_clear(GPIO_BUCKB);
        }

        //
        // Check the next 2 timers
        //
        ui32IntMask >>= 2;
    }

    //
    // Clear the GPIO indicating that the ISR is exiting.
    //
    am_hal_gpio_out_bit_clear(GPIO_CTIMER_ISR);
}

//*****************************************************************************
//
// GPIO ISR
//
//*****************************************************************************
void
am_gpio_isr(void)
{
    //
    // Delay for debounce.
    //
    am_util_delay_ms(200);

    //
    // Clear the GPIO Interrupt (write to clear).
    //
    am_hal_gpio_int_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));

    //
    // Toggle LED 0.
    //
    am_devices_led_toggle(am_bsp_psLEDs, 0);
}

//*****************************************************************************
//
// RTC ISR
//
//*****************************************************************************
void
am_clkgen_isr(void)
{
    //
    // Clear the RTC alarm interrupt.
    //
    am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);

    //
    // Toggle LED 1.
    //
    am_devices_led_toggle(am_bsp_psLEDs, 1);

    //
    // Increment counter.
    //
    g_ui32CountRTC++;
}

//*****************************************************************************
//
// Main function.
//
//*****************************************************************************
int
main(void)
{
    //
    // Set the clock frequency.
    //
    am_hal_clkgen_sysclk_select(AM_HAL_CLKGEN_SYSCLK_MAX);

    //
    // Set the default cache configuration
    //
    am_hal_cachectrl_enable(&am_hal_cachectrl_defaults);

    //
    // Configure the board for low power operation.
    //
    am_bsp_low_power_init();

#if RTC_CLK_SRC == LFRC
    //
    // Enable the LFRC for the RTC.
    //
    am_hal_clkgen_osc_start(AM_HAL_CLKGEN_OSC_LFRC);

    //
    // Select LFRC for RTC clock source.
    //
    am_hal_rtc_osc_select(AM_HAL_RTC_OSC_LFRC);
#endif

#if RTC_CLK_SRC == XT
    //
    // Enable the XT for the RTC.
    //
    am_hal_clkgen_osc_start(AM_HAL_CLKGEN_OSC_XT);

    //
    // Select XT for RTC clock source
    //
    am_hal_rtc_osc_select(AM_HAL_RTC_OSC_XT);
#endif

    //
    // Enable the RTC.
    //
    am_hal_rtc_osc_enable();

    //
    // Initialize the printf interface for UART output.
    //
    am_util_stdio_printf_init((am_util_stdio_print_char_t)am_bsp_uart_string_print);

    //
    // Initialize the UART
    //
    uart_init(AM_BSP_UART_PRINT_INST);

    //
    // Print the banner.
    //
    am_util_stdio_terminal_clear();
    am_util_stdio_printf("Buck Zero-Cross Demo/Example\n");

    //
    // To minimize power during the run, disable the UART.
    //
    uart_disable(AM_BSP_UART_PRINT_INST);

#ifdef AM_PART_APOLLO
    //
    // Power down all but the first SRAM banks.
    //
    am_hal_mcuctrl_sram_power_set(AM_HAL_MCUCTRL_SRAM_POWER_DOWN_1 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_2 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_3 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_4 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_5 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_6 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_7,
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_1 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_2 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_3 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_4 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_5 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_6 |
                                  AM_HAL_MCUCTRL_SRAM_POWER_DOWN_7);
#endif // AM_PART_APOLLO

#ifdef AM_PART_APOLLO2

    //
    // Turn OFF Flash1
    //
    AM_BFW(PWRCTRL, MEMEN, FLASH1, 0);
    while (AM_BFR(PWRCTRL, PWRONSTATUS, PD_FLAM1) != 0) {}

    //
    // Power down SRAM
    //
    AM_BFWe(PWRCTRL, SRAMPWDINSLEEP, SRAMSLEEPPOWERDOWN, ALLBUTLOWER8K);
#endif // AM_PART_APOLLO2

    //
    // Configure the LEDs GPIO and button pins.
    //
    am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS);
    am_hal_gpio_pin_config(AM_BSP_GPIO_BUTTON0, AM_HAL_GPIO_INPUT);

    //
    // Turn the LEDs off.
    //
    am_devices_led_off(am_bsp_psLEDs, 0);
    am_devices_led_off(am_bsp_psLEDs, 1);

    //
    // Configure the GPIO/button interrupt polarity.
    //
    am_hal_gpio_int_polarity_bit_set(AM_BSP_GPIO_BUTTON0, AM_HAL_GPIO_RISING);

    //
    // Clear the GPIO Interrupt (write to clear).
    //
    am_hal_gpio_int_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));

    //
    // Enable the GPIO/button interrupt.
    //
    am_hal_gpio_int_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));

    //
    // Set the alarm repeat interval to be every second.
    //
    am_hal_rtc_alarm_interval_set(AM_HAL_RTC_ALM_RPT_SEC);

    //
    // Clear the RTC alarm interrupt.
    //
    am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);

    //
    // Enable the RTC alarm interrupt.
    //
    am_hal_rtc_int_enable(AM_HAL_RTC_INT_ALM);

    //
    // Enable GPIO interrupts to the NVIC.
    //
    am_hal_interrupt_enable(AM_HAL_INTERRUPT_GPIO);
    am_hal_interrupt_enable(AM_HAL_INTERRUPT_CLKGEN);

    //
    // Set the timer to be used for tracking the buck.
    //
    am_hal_sysctrl_buck_ctimer_isr_init(BUCK_TIMER);

    am_hal_gpio_pin_config(GPIO_BUCKA, AM_HAL_GPIO_OUTPUT);
    am_hal_gpio_out_bit_clear(GPIO_BUCKA);

    am_hal_gpio_pin_config(GPIO_BUCKB, AM_HAL_GPIO_OUTPUT);
    am_hal_gpio_out_bit_clear(GPIO_BUCKB);

    am_hal_gpio_pin_config(GPIO_SLEEP, AM_HAL_GPIO_OUTPUT);
    am_hal_gpio_out_bit_clear(GPIO_SLEEP);

    //
    // GPIO 6 = am_ctimer_isr
    //
    am_hal_gpio_pin_config(GPIO_CTIMER_ISR, AM_HAL_GPIO_OUTPUT);
    am_hal_gpio_out_bit_clear(GPIO_CTIMER_ISR);

    //
    // GPIO 7 = wiggler while waiting for buck trim completion
    //
    am_hal_gpio_pin_config(GPIO_WIGGLER, AM_HAL_GPIO_OUTPUT);
    am_hal_gpio_out_bit_clear(GPIO_WIGGLER);

    //
    // Enable interrupts to the core.
    //
    am_hal_interrupt_master_enable();

    while (1)
    {
        //
        // Go to Deep Sleep.
        // First, set the GPIO to show we're going to sleep.
        //
        am_hal_gpio_out_bit_set(GPIO_SLEEP);

        am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);

        am_hal_gpio_out_bit_clear(GPIO_SLEEP);

        while ( !am_hal_sysctrl_buck_update_complete() )
        {
            am_hal_gpio_out_bit_toggle(GPIO_WIGGLER);
            am_util_delay_us(1);
        }

        am_hal_gpio_out_bit_clear(GPIO_WIGGLER);

        //
        // Buck workaround is completed.
        // Now, keep the buck operating for the rest of this second.
        //
        while ( g_ui32CountRTC & 1 ){};
    }
} // main()
