/******************************************************************************
文 件 名：PWM.c
功能描述：PWM驱动库文件
作    者：张暄
版    本：V1.0
日    期：2016.11.3
******************************************************************************/

#include "PWM.h"

/******************************************************************************
函数名：PWM_Init
功  能：PWM初始化
        初始化PWM时钟源 clock A, clock B, clock SA 和 clock SB
        需要预先在PWM.h中定义上述时钟源的频率
参  数：无
返回值：无
******************************************************************************/
void PWM_Init(void)
{
  PWME     = 0;
  PWMPOL   = 0;
  PWMCLK   = 0;
  PWMCAE   = 0;
  PWMPRCLK = (PWM_CLKA_FREQ << 4) | PWM_CLKB_FREQ;  //设置PWM clock A/B 的频率
  PWMSCLA  = PWMSCLAREG;                            //设置PWM clock SA
  PWMSCLB  = PWMSCLBREG;                            //设置PWM clock SB
}

/******************************************************************************
函数名：PWM_Channel_Init
功  能：初始华指定PWM通道的时钟源、极性、对齐方式及输出端口
参  数：Ch     ：通道编号 0 - 7
        ClkSrc ：时钟源
                 通道 0 / 1 / 4 / 5 可选择  PWM_CLOCK_A  PWM_CLOCK_SA
                 通道 2 / 3 / 6 / 7 可选择  PWM_CLOCK_B  PWM_CLOCK_SB
        Pol    ：极性选择
                 PWM_POL_NGE  起始为低电平而后翻转为高电平,空闲时为高电平
                 PWM_POL_POS  起始为高电平而后翻转为低电平,空闲时为低电平
        Cae    : 对齐方式
                 PWM_LEFT_ALIGN    左对齐
                 PWM_CENTER_ALIGN  中心对齐
        IOSel  ：输出端口选择
                 通道 0 可使用端口  PWM0_PP0  PWM0_PS4
                 通道 1 可使用端口  PWM1_PP1  PWM1_PS5
                 通道 2 可使用端口  PWM2_PP2  PWM2_PS6
                 通道 3 可使用端口  PWM3_PP3  PWM3_PS7
                 通道 4 可使用端口  PWM4_PP4  PWM4_PS2  PWM4_PV0  PWM4_PM0
                 通道 5 可使用端口  PWM5_PP5  PWM5_PS3  PWM5_PV1  PWM5_PM1
                 通道 6 可使用端口  PWM6_PP6  PWM6_PS0  PWM6_PV2  PWM6_PM2
                 通道 7 可使用端口  PWM7_PP7  PWM7_PS1  PWM7_PV3  PWM7_PM3
返回值：无
******************************************************************************/
void PWM_Channel_Init(uint8_t Ch, uint8_t ClkSrc, uint8_t Pol, uint8_t Cae, uint8_t IOSel)
{
  uint8_t ChBit;

  if (Ch > 7)
    return;

  ChBit = 1 << Ch;

  if (ClkSrc)
    PWMCLK |= ChBit;
  else
    PWMCLK &= ~ChBit;

  if (Pol)
    PWMPOL |= ChBit;
  else
    PWMPOL &= ~ChBit;

  if (Cae)
    PWMCAE |= ChBit;
  else
    PWMCAE &= ~ChBit;

  if (Ch < 4)
  {
    PTPRRL &= ~ChBit;
    PTPRRL |= IOSel;
  }
  else
  {
    ChBit   = 0x03 << (Ch - 4) * 2;
    PTPRRH &= ~ChBit;
    PTPRRH |= IOSel;
  }
}

/******************************************************************************
函数名：PWM_Channel_Set_Freq
功  能：设置指定PWM通道的输出频率
参  数：Ch   ：通道编号 0 - 7
        Freq ：输出频率,单位 Hz
返回值：无
******************************************************************************/
void PWM_Channel_Set_Freq(uint8_t Ch, uint32_t Freq)
{
  uint8_t   ChBit;
  uint8_t   RegVal;
  uint8_t   *pReg;
  uint32_t  Prescaler;
  uint32_t  ClkFreq;

  if (Ch > 7)
    return;

  ChBit = 1 << Ch;

#pragma MESSAGE DISABLE C5912
#pragma MESSAGE DISABLE C4000
  if (ChBit & 0x33)        // 0 / 1 / 4 / 5 通道,使用clock A/SA
  {
    if (PWMCLK & ChBit)    //使用clock SA
      ClkFreq = PWMCLKSA;
    else                   //使用clock A
      ClkFreq = PWMCLKA;
  }
  else                     // 2 / 3 / 6 / 7 通道,使用clock B/SB
  {
    if (PWMCLK & ChBit)    //使用clock SB
      ClkFreq = PWMCLKSB;
    else                   //使用clock B
      ClkFreq = PWMCLKB;
  }

  if (PWMCAE & ChBit)
    ClkFreq /= 2;

  Prescaler = ClkFreq / Freq;

  if (Prescaler > 255)
    RegVal = 255;
  else
    RegVal = (uint8_t)Prescaler;

  if (RegVal == 0)
    RegVal = 1;

  pReg  = &PWMPER0 + Ch;
  *pReg = RegVal;
}

/******************************************************************************
函数名：PWM_Channel_Set_Duty_Cycle
功  能：设置指定PWM通道的占空比
参  数：Ch  ：通道编号 0 - 7
        Dty ：输出占空比的百分数值,范围 0 - 100
返回值：无
******************************************************************************/
void PWM_Channel_Set_Duty_Cycle(uint8_t Ch, uint8_t Dty)
{
  uint8_t   ChBit;
  uint8_t   *pPERReg;
  uint8_t   *pDTYReg;
  uint16_t  Val;

  if (Ch > 7)
    return;

  ChBit = 1 << Ch;

  if (Dty > 100)
    Dty = 100;

  pPERReg = &PWMPER0 + Ch;
  pDTYReg = &PWMDTY0 + Ch;

  if (PWMPOL & ChBit)
    Val = (uint16_t)Dty;
  else
    Val = (uint16_t)(100 - Dty);

  Val *= (uint16_t)(*pPERReg);
  *pDTYReg = (uint8_t)(Val / 100);
}

/******************************************************************************
函数名：PWM_Channel_Start
功  能：启动指定的PWM通道
参  数：Ch  ：通道编号 0 - 7
返回值：无
******************************************************************************/
void PWM_Channel_Start(uint8_t Ch)
{
  uint8_t ChBit;

  if (Ch > 7)
    return;

  ChBit = 1 << Ch;
  PWME |= ChBit;
}

/******************************************************************************
函数名：PWM_Channel_Stop
功  能：关闭指定的PWM通道
参  数：Ch  ：通道编号 0 - 7
返回值：无
******************************************************************************/
void PWM_Channel_Stop(uint8_t Ch)
{
  uint8_t ChBit;

  if (Ch > 7)
    return;

  ChBit = 1 << Ch;
  PWME &= ~ChBit;
}