#include "stm32g4xx.h"
#include "drill_pwm_stage.h"
#define PERIOD 400u
#define DEADTIME 16u
#define CR1_VALUE (TIM_CR1_ARPE | TIM_CR1_CMS_0 | TIM_CR1_CMS_1)
#define CCMR1_VALUE (TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2)
#define CCMR2_VALUE (TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2)
static bool owned;
static bool clock_ok(void) {
 const uint32_t cf=RCC->CFGR;
 return (RCC->CR & (RCC_CR_HSION|RCC_CR_HSIRDY))==(RCC_CR_HSION|RCC_CR_HSIRDY) &&
  !(RCC->CR&(RCC_CR_HSEON|RCC_CR_HSEBYP)) &&
  (cf&(RCC_CFGR_SW|RCC_CFGR_SWS|RCC_CFGR_HPRE|RCC_CFGR_PPRE2))==(RCC_CFGR_SW_HSI|RCC_CFGR_SWS_HSI);
}
static bool low_output(GPIO_TypeDef *p,unsigned pin) {
 uint32_t bit=1u<<pin;
 return ((p->MODER>>(pin*2u))&3u)==1u && !(p->OTYPER&bit) && !(p->ODR&bit) && !(p->IDR&bit);
}
static bool blank_pins(void) {
 if((RCC->AHB2ENR&(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN))!=(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOFEN))return false;
 return low_output(GPIOA,5)&&low_output(GPIOF,1)&&low_output(GPIOA,7)&&low_output(GPIOA,8)&&low_output(GPIOA,9)&&low_output(GPIOA,10)&&low_output(GPIOB,0)&&low_output(GPIOB,1);
}
bool drill_pwm_stage_available(void) {
 return owned && clock_ok() && blank_pins() && (RCC->APB2ENR&RCC_APB2ENR_TIM1EN) && !(RCC->APB2RSTR&RCC_APB2RSTR_TIM1RST) &&
 TIM1->CR1==CR1_VALUE && TIM1->CR2==0u && TIM1->SMCR==0u && TIM1->DIER==0u &&
 TIM1->CCER==0u && TIM1->BDTR==DEADTIME && TIM1->PSC==0u && TIM1->ARR==PERIOD && TIM1->RCR==0u &&
 TIM1->CCMR1==CCMR1_VALUE && TIM1->CCMR2==CCMR2_VALUE;
}
bool drill_pwm_stage_init(void) {
 if(owned)return drill_pwm_stage_available();
 if(!clock_ok()||!blank_pins()||(RCC->APB2ENR&RCC_APB2ENR_TIM1EN))return false;
 RCC->APB2ENR|=RCC_APB2ENR_TIM1EN;(void)RCC->APB2ENR;__DSB();
 if(RCC->APB2RSTR&RCC_APB2RSTR_TIM1RST)return false;
 /* A previously clock-disabled timer is taken over only with driver pins blank.
  * Hardware reset clears all channels/DMA/break/lock and slave state. */
 RCC->APB2RSTR|=RCC_APB2RSTR_TIM1RST;__DSB();
 RCC->APB2RSTR&=~RCC_APB2RSTR_TIM1RST;__DSB();
 TIM1->CR1=0u;TIM1->CCER=0u;TIM1->BDTR=0u;TIM1->DIER=0u;
 TIM1->CR2=0u;TIM1->SMCR=0u;
 TIM1->PSC=0u;TIM1->ARR=PERIOD;TIM1->RCR=0u;TIM1->CNT=0u;
 TIM1->CCR1=0u;TIM1->CCR2=0u;TIM1->CCR3=0u;TIM1->CCR4=0u;
 TIM1->CCMR1=CCMR1_VALUE;TIM1->CCMR2=CCMR2_VALUE;
 TIM1->BDTR=DEADTIME; /* CKD0:16 HSI16 ticks, nominal1us only. MOE/AOE/BKE=0 */
 TIM1->CR1=CR1_VALUE;TIM1->EGR=TIM_EGR_UG;TIM1->SR=0u;__DSB();
 owned=true;return drill_pwm_stage_available();
}
static uint32_t compare(uint16_t value) {return ((uint32_t)value*PERIOD+32767u)/65535u;}
bool drill_pwm_stage_write(uint16_t a,uint16_t b,uint16_t c) {
 if(!drill_pwm_stage_available())return false;
 uint32_t x=compare(a),y=compare(b),z=compare(c);
 /* Reject narrow candidate pulses on either side; ADC acquisition and driver
  * minimum-pulse qualification are additional requirements, not satisfied here. */
 if(x<=DEADTIME||y<=DEADTIME||z<=DEADTIME||x>=PERIOD-DEADTIME||y>=PERIOD-DEADTIME||z>=PERIOD-DEADTIME)return false;
 TIM1->CCR1=x;TIM1->CCR2=y;TIM1->CCR3=z;__DSB();
 return drill_pwm_stage_available();
}
