/*
  * stm32f4_pwm.c
 *
 *  Created on: Aug 9, 2016
 *      Author: tkl
 */
#include <stdint.h>
#include <stddef.h>

#include "stm32f4xx.h"

#include "pwm.h"
#include "stm32f4_pwm.h"

struct stm32f4_pwm_object {
	uint8_t used_channels;
	uint32_t channel_1_max_period;
	uint32_t channel_2_max_period;
	uint32_t channel_3_max_period;
	uint32_t channel_4_max_period;
};

static struct stm32f4_pwm_object stm32f4_pwm_object = {
		.used_channels = 0,
		.channel_1_max_period = 0,
		.channel_2_max_period = 0,
		.channel_3_max_period = 0,
		.channel_4_max_period = 0,
};

int stm32f4_pwm_open(const void *pwm)
{
	if(NULL == pwm)
		return -1;
	struct stm32f4_pwm *this = (struct stm32f4_pwm *)pwm;
	uint32_t clk_ahb_timer = 0, clk_ahb_gpio = 0;
	uint8_t gpio_af_timer = 0;
	if(this->timer == TIM4) {
		clk_ahb_timer = RCC_APB1Periph_TIM4;
		gpio_af_timer = GPIO_AF_TIM4;
	}
	RCC_APB1PeriphClockCmd(clk_ahb_timer, ENABLE);
	if(this->port == GPIOD) {
		clk_ahb_gpio = RCC_AHB1Periph_GPIOD;
	}
	RCC_AHB1PeriphClockCmd(clk_ahb_gpio, ENABLE);
	GPIO_Init(this->port, (GPIO_InitTypeDef *)this->port_cfg);
	GPIO_PinAFConfig(this->port, this->pin_src, gpio_af_timer);

	TIM_TimeBaseInit(this->timer, (TIM_TimeBaseInitTypeDef *)this->timer_cfg);

	switch(this->channel) {
	case channel_1:
		TIM_OC1Init(this->timer, (TIM_OCInitTypeDef *)this->output_compare_cfg);
		TIM_OC1PreloadConfig(this->timer, TIM_OCPreload_Enable);
		stm32f4_pwm_object.channel_1_max_period = this->timer_cfg->TIM_Period + 1;
		break;
	case channel_2:
		TIM_OC2Init(this->timer, (TIM_OCInitTypeDef *)this->output_compare_cfg);
		TIM_OC2PreloadConfig(this->timer, TIM_OCPreload_Enable);
		stm32f4_pwm_object.channel_2_max_period = this->timer_cfg->TIM_Period + 1;
		break;
	case channel_3:
		TIM_OC3Init(this->timer, (TIM_OCInitTypeDef *)this->output_compare_cfg);
		TIM_OC3PreloadConfig(this->timer, TIM_OCPreload_Enable);
		stm32f4_pwm_object.channel_3_max_period = this->timer_cfg->TIM_Period + 1;
		break;
	case channel_4:
		TIM_OC4Init(this->timer, (TIM_OCInitTypeDef *)this->output_compare_cfg);
		TIM_OC4PreloadConfig(this->timer, TIM_OCPreload_Enable);
		stm32f4_pwm_object.channel_4_max_period = this->timer_cfg->TIM_Period + 1;
		break;
	}
	TIM_ARRPreloadConfig(this->timer, ENABLE);
	TIM_Cmd(this->timer, ENABLE);
	stm32f4_pwm_object.used_channels++;
	return 0;
}

int stm32f4_pwm_close(const void *pwm)
{
	if(NULL == pwm)
		return -1;
	struct stm32f4_pwm *this = (struct stm32f4_pwm *)pwm;
	stm32f4_pwm_set_duty_cycle(pwm, 0);
	stm32f4_pwm_object.used_channels--;
	if(stm32f4_pwm_object.used_channels == 0) {
		TIM_Cmd(this->timer, DISABLE);
	}
	return 0;
}

int stm32f4_pwm_set_duty_cycle(const void *pwm, unsigned int duty_cycle_percent)
{
	if(NULL == pwm)
		return -1;
	struct stm32f4_pwm *this = (struct stm32f4_pwm *)pwm;
	switch(this->channel) {
	case channel_1:
		TIM_SetCompare1(this->timer, stm32f4_pwm_object.channel_1_max_period * duty_cycle_percent / 100);
		break;
	case channel_2:
		TIM_SetCompare2(this->timer, stm32f4_pwm_object.channel_2_max_period * duty_cycle_percent / 100);
		break;
	case channel_3:
		TIM_SetCompare3(this->timer, stm32f4_pwm_object.channel_3_max_period * duty_cycle_percent / 100);
		break;
	case channel_4:
		TIM_SetCompare4(this->timer, stm32f4_pwm_object.channel_4_max_period * duty_cycle_percent / 100);
		break;
	}
	return 0;
}