/*
 * led.c: rgbled (and piezo) routines
 *
 * LED functions expect 11-bit values (0-2047).
 * Actual output is right shifted by one and clamped 0-999.
 *
 * file creation: 20231015 0037
 */


#include "testo.h"



#define RED     		0
#define GRN     		1
#define BLU     		2

#define MODE_RGB		0
#define MODE_BUZZ		1



static uint8_t led_mode;		// RGBLED mode or piezo mode
static uint8_t led_sel;			// which RGBLED active?

static uint8_t snd_buzz;		// piezo sounder active?

static uint16_t rgb[2][3];		// RGBLED actual values to set

static uint8_t dualtone = 0;	// buzzer dualtone mode
static uint8_t dt_cntr = 0;		// buzzer dualtone counter



/*
 * deselects, configures, then outputs the next RGBLED.
 * also sounds the buzzer if buzzer mode is enabled.
 * run this at an interrupt frequency twice the buzzer frequency.
 */
void led_next()
{
	// next LED
	led_sel++;
	led_sel &= 1;

	// clear outputs
	RGBSEL0_PORT->BSRR = (1 << RGBSEL0_PIN);
	RGBSEL1_PORT->BSRR = (1 << RGBSEL1_PIN);

	if (!led_sel || (led_mode == MODE_RGB)) {
		// set PWMs in RGB mode, or when right LED set
		PWM_RGB_R = (rgb[led_sel][RED] >> 2) + (rgb[led_sel][RED] >> 3);	//  75%
		PWM_RGB_G = (rgb[led_sel][GRN] >> 2) + (rgb[led_sel][GRN] >> 4);	//  62%
		PWM_RGB_B = (rgb[led_sel][BLU] >> 1);								// 100%
	} else {
		// clear PWMs in piezo mode when piezo should be active
		PWM_RGB_R = PWM_RGB_G = PWM_RGB_B = 0;
	}

	// reset timer counter for consistency
	TIM_RGB->CNT = 0;

	// enable selected LED
	if (led_sel) {
		switch (led_mode) {
			case MODE_RGB: {
				RGBSEL1_PORT->BRR = (1 << RGBSEL1_PIN);
				break;
			}
			case MODE_BUZZ: {
				if (!snd_buzz) return;

				// dual tone
				dt_cntr++;
				dt_cntr &= 1;
				if (!dt_cntr) {
					dualtone ^= 1;
				}

				if (!dt_cntr || dualtone) {
					RGBSEL1_PORT->BRR = (1 << RGBSEL1_PIN);
				}
			}	
		}	
	} else {
		RGBSEL0_PORT->BRR = (1 << RGBSEL0_PIN);
	}
}

/*
 * sets RGBLED values directly.
 */
void led_setrgb(uint8_t i, uint16_t r, uint16_t g, uint16_t b)
{
	i &= 1;
	rgb[i][RED] = r;
	rgb[i][GRN] = g;
	rgb[i][BLU] = b;
}

/*
 * configures system to use right RGBLED instead of piezo sounder.
 */
void led_mode_rgb()
{
	led_mode = MODE_RGB;
}

/*
 * configures system to use piezo sounder instead of right RGBLED.
 */
void led_mode_buzzer()
{
	led_mode = MODE_BUZZ;
}

/*
 * activates piezo buzzer output.
 */
void led_buzz(uint8_t buzz)
{
	snd_buzz = buzz & 1;
}

/*
 * initializes RGBLED PWMs and variables.
 * assumes GPIO has already been configured.
 */
void led_init()
{
    LL_TIM_InitTypeDef    tim = {0};
	LL_TIM_OC_InitTypeDef pwm = {0};


	// configure timer
    tim.ClockDivision       = LL_TIM_CLOCKDIVISION_DIV1;
    tim.CounterMode         = LL_TIM_COUNTERMODE_UP;
    tim.Prescaler           = 0;
    /* PWM period = 1000 */
    tim.Autoreload          = 1000 - 1;     // ~8KHz operation, roughly 10-bit
    tim.RepetitionCounter   = 0;
    
    LL_TIM_Init(TIM_RGB, &tim);


	// configure timer outputs
	pwm.OCMode = LL_TIM_OCMODE_PWM2;
    pwm.OCState = LL_TIM_OCSTATE_ENABLE;
    pwm.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
    pwm.OCIdleState = LL_TIM_OCIDLESTATE_LOW;

    pwm.CompareValue = 0;
    LL_TIM_OC_Init(TIM_RGB, TIM_RGB_R_CH, &pwm);
    LL_TIM_OC_Init(TIM_RGB, TIM_RGB_G_CH, &pwm);
    LL_TIM_OC_Init(TIM_RGB, TIM_RGB_B_CH, &pwm);


	// clear all outputs
	for (uint8_t i = 0; i < 3; i++) {
		rgb[0][i] = rgb[1][i] = 0;
	}

	// enable timer
	LL_TIM_EnableAllOutputs(TIM_RGB);
    LL_TIM_EnableCounter(TIM_RGB);

	// other setup
	led_mode = MODE_RGB;
	led_sel = 1;
	snd_buzz = 0;

	// start outputting data
	led_next();
}