/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef INCLUDE_ECXX_PLATFORM_SPECIFIC_AVR_COMMON_WS2812B_DRIVER_CPP
#error "this file cannot be compiled directly"
#endif
#undef INCLUDE_ECXX_PLATFORM_SPECIFIC_AVR_COMMON_WS2812B_DRIVER_CPP


#include "Internal.h"

#include <eCxx/Platform/WS2812BDriver.h>


namespace eCxx {


/*
 * https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them
 *
 * Datasheet timing constraints
 *     Symbol    Time H (nS)   Time L (nS)
 *     -------   -----------   -----------
 *     Logic 0   350 ± 150         800 ± 150
 *     Logic 1   700 ± 150         600 ± 150
 *     Reset                   ≥ 50000
 *
 * Simplified timing constraints
 *     Symbol    Time H (nS)   Time L (nS)
 *     -------   -----------   -----------
 *     Logic 0   200 -  500    450 ~ 5000
 *     Logic 1   550 - 5500    450 ~ 5000
 *     Reset                   ≥ 6000
 *
 * Possible illustration
 *              t0   t1   t2   t3
 *              ┌────┐
 *     Logic 0  ╵    └──────────
 *              ┌─────────┐
 *     Logic 1  ╵         └─────
 *
 *      200 nS < (t1 - t0) <   500 nS    --->    use    350 nS for logic 0 high time
 *      550 nS < (t2 - t0) <  5500 nS                   900 nS for logic 1 high time
 *     1200 nS < (t3 - t0) < 11000 nS                >= 900 nS for         low  time
 *
 * Using 7 MHz MCU => each cycle ~= 142.857 nS, therefore:
 *     Logic 0 high time ~=  2 cycles
 *     Logic 1 high time ~=  6 cycles
 *     Logic   low  time ~=  6 cycles
 *
 * Using 8 MHz MCU => each cycle ~= 125 nS, therefore:
 *     Logic 0 high time ~=  3 cycles
 *     Logic 1 high time ~=  7 cycles
 *     Logic   low  time ~=  7 cycles
 *
 * Using 9 MHz MCU => each cycle ~= 111.111 nS, therefore:
 *     Logic 0 high time ~=  3 cycles
 *     Logic 1 high time ~=  8 cycles
 *     Logic   low  time ~=  8 cycles
 *
 * Using 10 MHz MCU => each cycle ~= 100 nS, therefore:
 *     Logic 0 high time ~=  4 cycles
 *     Logic 1 high time ~=  9 cycles
 *     Logic   low  time ~=  9 cycles
 *
 * Using 11 MHz MCU => each cycle ~= 90.909 nS, therefore:
 *     Logic 0 high time ~=  4 cycles
 *     Logic 1 high time ~= 10 cycles
 *     Logic   low  time ~= 10 cycles
 *
 * Using 12 MHz MCU => each cycle ~= 83.333 nS, therefore:
 *     Logic 0 high time ~=  4 cycles
 *     Logic 1 high time ~= 11 cycles
 *     Logic   low  time ~= 11 cycles
 *
 * Using 13 MHz MCU => each cycle ~= 76.923 nS, therefore:
 *     Logic 0 high time ~=  5 cycles
 *     Logic 1 high time ~= 12 cycles
 *     Logic   low  time ~= 12 cycles
 *
 * Using 14 MHz MCU => each cycle ~= 71.429 nS, therefore:
 *     Logic 0 high time ~=  5 cycles
 *     Logic 1 high time ~= 13 cycles
 *     Logic   low  time ~= 13 cycles
 *
 * Using 15 MHz MCU => each cycle ~= 66.667 nS, therefore:
 *     Logic 0 high time ~=  5 cycles
 *     Logic 1 high time ~= 13 cycles
 *     Logic   low  time ~= 13 cycles
 *
 * Using 16 MHz MCU => each cycle ~= 62.5 nS, therefore:
 *     Logic 0 high time ~=  6 cycles
 *     Logic 1 high time ~= 14 cycles
 *     Logic   low  time ~= 14 cycles
 *
 * Using 17 MHz MCU => each cycle ~= 58.824 nS, therefore:
 *     Logic 0 high time ~=  6 cycles
 *     Logic 1 high time ~= 15 cycles
 *     Logic   low  time ~= 15 cycles
 *
 * Using 18 MHz MCU => each cycle ~= 55.556 nS, therefore:
 *     Logic 0 high time ~=  6 cycles
 *     Logic 1 high time ~= 16 cycles
 *     Logic   low  time ~= 16 cycles
 *
 * Using 19 MHz MCU => each cycle ~= 52.632 nS, therefore:
 *     Logic 0 high time ~=  7 cycles
 *     Logic 1 high time ~= 17 cycles
 *     Logic   low  time ~= 17 cycles
 *
 * Using 20 MHz MCU => each cycle ~= 50 nS, therefore:
 *     Logic 0 high time ~=  7 cycles
 *     Logic 1 high time ~= 18 cycles
 *     Logic   low  time ~= 18 cycles
 *
 * Using 21 MHz MCU => each cycle ~= 47.619 nS, therefore:
 *     Logic 0 high time ~=  7 cycles
 *     Logic 1 high time ~= 19 cycles
 *     Logic   low  time ~= 19 cycles
 *
 * Using 22 MHz MCU => each cycle ~= 45.455 nS, therefore:
 *     Logic 0 high time ~=  8 cycles
 *     Logic 1 high time ~= 20 cycles
 *     Logic   low  time ~= 20 cycles
 *
 * Using 23 MHz MCU => each cycle ~= 43.478 nS, therefore:
 *     Logic 0 high time ~=  8 cycles
 *     Logic 1 high time ~= 21 cycles
 *     Logic   low  time ~= 21 cycles
 *
 * Using 24 MHz MCU => each cycle ~= 41.667 nS, therefore:
 *     Logic 0 high time ~=  8 cycles
 *     Logic 1 high time ~= 22 cycles
 *     Logic   low  time ~= 22 cycles
 *
 * Using 25 MHz MCU => each cycle ~= 40 nS, therefore:
 *     Logic 0 high time ~=  9 cycles
 *     Logic 1 high time ~= 23 cycles
 *     Logic   low  time ~= 23 cycles
 */

/*
 * Delay macros:
 *      DELAY_1()             : delay for outputting the high pulse for WS2812B logic 0
 *      DELAY_1() + DELAY_2() : delay for outputting the high pulse for WS2812B logic 1
 *      DELAY_3()             : inter-bit delay
 *
 * The amount of fixed delay produced by the assembly code:
 *     # Logic 0 high pulse =             3 cycles (see lines marked with #1, #2        )
 *     # Logic 1 high pulse = DELAY_1() + 5 cycles (see lines marked with #1, #3, #4    )
 *     # Inter-bit          =             6 cycles (see lines marked with #5, #6, #7, #8)
 */
#if (F_CPU > 6500000UL) && (F_CPU <= 7500000UL)
    // --- Delay for F_CPU ~7 MHz ---
    #define DELAY_1() "         "
    #define DELAY_2() " nop\n\t "
    #define DELAY_3() " nop\n\t "
    // ------------------------------
#elif (F_CPU > 7500000UL) && (F_CPU <= 8500000UL)
    // --- Delay for F_CPU ~8 MHz ---
    #define DELAY_1() "                 "
    #define DELAY_2() " nop\n\t nop\n\t "
    #define DELAY_3() " nop\n\t         "
    // ------------------------------
#elif (F_CPU > 8500000UL) && (F_CPU <= 9500000UL)
    // --- Delay for F_CPU ~9 MHz ---
    #define DELAY_1() "                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t "
    #define DELAY_3() " nop\n\t nop\n\t         "
    // ------------------------------
#elif (F_CPU > 9500000UL) && (F_CPU <= 10500000UL)
    // --- Delay for F_CPU ~10 MHz ---
    #define DELAY_1() " nop\n\t                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 10500000UL) && (F_CPU <= 11500000UL)
    // --- Delay for F_CPU ~11 MHz ---
    #define DELAY_1() " nop\n\t                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 11500000UL) && (F_CPU <= 12500000UL)
    // --- Delay for F_CPU ~12 MHz ---
    #define DELAY_1() " nop\n\t                                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 12500000UL) && (F_CPU <= 13500000UL)
    // --- Delay for F_CPU ~13 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t                                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t         "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 13500000UL) && (F_CPU <= 14500000UL)
    // --- Delay for F_CPU ~14 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 14500000UL) && (F_CPU <= 15500000UL)
    // --- Delay for F_CPU ~15 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 15500000UL) && (F_CPU <= 16500000UL)
    // --- Delay for F_CPU ~16 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 16500000UL) && (F_CPU <= 17500000UL)
    // --- Delay for F_CPU ~17 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t                                                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 17500000UL) && (F_CPU <= 18500000UL)
    // --- Delay for F_CPU ~18 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 18500000UL) && (F_CPU <= 19500000UL)
    // --- Delay for F_CPU ~19 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                         "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 19500000UL) && (F_CPU <= 20500000UL)
    // --- Delay for F_CPU ~20 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t                                                                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                         "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 20500000UL) && (F_CPU <= 21500000UL)
    // --- Delay for F_CPU ~21 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t                                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                         "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 21500000UL) && (F_CPU <= 22500000UL)
    // --- Delay for F_CPU ~22 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 22500000UL) && (F_CPU <= 23500000UL)
    // --- Delay for F_CPU ~23 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                                                                 "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 23500000UL) && (F_CPU <= 24500000UL)
    // --- Delay for F_CPU ~24 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                 "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 24500000UL) && (F_CPU <= 25500000UL)
    // --- Delay for F_CPU ~25 MHz ---
    #define DELAY_1() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                                                                         "
    #define DELAY_2() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t                                         "
    #define DELAY_3() " nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t "
    // -------------------------------
#elif (F_CPU > 25500000UL)
    // --- Unsupported F_CPU - too fast - raise error ---
    #error "this WS2812B driver is meant for AVR MCU clocked between ~7 MHz to ~25 MHz"
    // ------------------------------
#else
    // --- Unsupported F_CPU - too slow - use the smallest possible delay ---
    #define DELAY_1() "         "
    #define DELAY_2() " nop\n\t "
    #define DELAY_3() " nop\n\t "
    #warning "this WS2812B driver is meant for AVR MCU clocked between ~7 MHz to ~25 MHz"
    // ------------------------------
#endif


//
// A simple WS2812B-based LED strip/matrix driver
//
WS2812BDriver::WS2812BDriver()
: _pin(0), _rawByteBuffer(nullptr), _rawByteSize(0)
{}


WS2812BDriver::~WS2812BDriver()
{ if(_rawByteBuffer) end(); }


bool WS2812BDriver::begin(uint8_t pin, uint16_t numberOfLEDs, const uint8_t* rawByteBuffer)
{
    // Check if already initialized
    if(_rawByteBuffer) return false;

    // Check the parameters
    if(!numberOfLEDs || numberOfLEDs > MaximumNumberOfLEDs || !rawByteBuffer) return false;

    // Save the settings
    _pin           = pin;
    _rawByteBuffer = rawByteBuffer;
    _rawByteSize   = numberOfLEDs * 3;

    // Set pin mode to output and set it to low
    Application::setPinMode(_pin, Application::PinMode::Output);
    Application::digitalWrite(_pin, false);

    // Done
    return true;
}


void WS2812BDriver::end()
{
    // Check if it is not yet initialized
    if(!_rawByteBuffer) return;

    // Set pin mode to input
    Application::setPinMode(_pin, Application::PinMode::Input);

    // Clear the settings
    _rawByteBuffer = nullptr;
    _rawByteSize   = 0;
}


void WS2812BDriver::update()
{
    // Check if it is not yet initialized
    if(!_rawByteBuffer) return;

    // Use fast-digital-write wrapper class to get direct access to the registers
    Application::_PinFDW pinFDW;
    Application::_preparePinFDW(pinFDW, _pin);

    // Reset the LEDs first
    pinFDW(false);
    Application::delayUS(300);

    // Ensure atomic operation
    eCxx::AtomicGuard g;

    // Get the bitmask values needed to output logic 0 and logic 1
#ifdef __AVR_ARCH_X__
    #define SFR_SET pinFDW.sfrPortOutSet()
    #define SFR_CLR pinFDW.sfrPortOutClr()
    eCxx::uint8_t logic0 = pinFDW.bitmask();
    eCxx::uint8_t logic1 = pinFDW.bitmask();
#else
    #define SFR_SET pinFDW.sfrPort()
    #define SFR_CLR pinFDW.sfrPort()
    eCxx::uint8_t logic0 = *pinFDW.sfrPort() & ~pinFDW.bitmask();
    eCxx::uint8_t logic1 = *pinFDW.sfrPort() |  pinFDW.bitmask();
#endif

    // Preparation
    const uint8_t* data = _rawByteBuffer;
    uint16_t       size = _rawByteSize;
    uint8_t        bctr;

    // Send the data bytes
    while(size--) {
        // Get the current byte
        const eCxx::uint8_t chr = *data++;
        // Send the bits
        __asm__ __volatile__ (          //     Cycles   --- Operation

            "        ldi  %0, 8   \n\t" //    [  1    ] --- Loop 8 times
            "lbit%=:              \n\t"
            "        st   %a2, %4 \n\t" // #8 [  2    ] --- Output logic 1
            DELAY_1()                   //              --- Delay part #1
            "        sbrs %1, 7   \n\t" // #1 [F=1 T=2] --- Skip the next instruction if bit #7 is set
            "        st   %a3, %5 \n\t" // #2 [  2      --- Output logic 0
            "        lsl  %1      \n\t" // #3 [  1    ] --- Logical shift left
            DELAY_2()                   //              --- Delay part #2
            "        st   %a3, %5 \n\t" // #4 [  2    ] --- Output logic 0
            "        wdr          \n\t" // #5 [  1    ] --- Reset watchdog timer and delay
            DELAY_3()                   //              --- Delay part #3
            "        dec  %0      \n\t" // #6 [  1    ] --- Decrement the bit counter
            "        brne lbit%=  \n\t" // #7 [F=1 T=2] --- Loop to top if not zero
            : "=&r"(bctr)
            : "r"(chr), "e"(SFR_SET), "e"(SFR_CLR), "a"(logic1), "a"(logic0)
        );
    }
}


} // namespace eCxx
