/*******************************************************
*Copyright(C),2020,Shanghai Eastsoft Microelectronics Co.,Ltd.
*文件名：pwm_adjustable_freq.c
*作  者：AE Team
*版  本：v1.0，编译于iDesigner(v4.2.3.166) + 工具链HRCC(v1.2.0.106)
*日  期：2020/12/18
*描  述：多功能定时器，T31_CH1(PB5)/T31_CH1N(PB2)输出互补pwm。频率范围100kHz~205kHz，每10ms以1kHz步进，
        大于205kHz时重新从100kHz开始。
        说明：起始频率100kHz，Fosc=16MHz，则在边沿计数模式下计数器初值=16MHz/100kHz=160(0xA0)。频率越大周期越小重装载值(T31CNTLD)越小
        假设当前频率为f(单位kHz)，步进后的频率为(f+1)kHz。则周期寄存器值变化△=16MHz/f - 16MHz/(f+1)=16MHz*1kHz/(f*(f+1))
        由此可见，当前频率越大，频率每增加1kHz，周期寄存器的值变化愈小。所以我们取每10ms周期寄存器值减1简单表示步进1kHz。
        注意：PWM模式调试状态下，须软件固定设置T31C0H<6>=1，否则PWM输出可能会异常。
*备  注：本软件仅供学习和演示使用，对用户直接引用代码所带来的风险或后果不承担任何法律责任。
*******************************************************/
#include <hic.h>

#define CLRWDT()   {__Asm CWDT;}        //宏定义清狗指令

unsigned char g_10ms_flag;        //10ms定时标志
unsigned int  period;             //pwm周期值

/******************************************************
函数名：void RAMclear(void)
描  述：RAM区数据清零，RAM区地址0x0000~0x03FF
输入值：无
输出值：无
返回值：无
*******************************************************/
void RAMclear(void)
{
    for (IAAH = 0; IAAH <= 0x03; IAAH++)
    {
        for (IAAL = 0; IAAL < 0xFF; IAAL++)
            IAD = 0x00;

        IAD = 0x00;
    }
}

/******************************************************
函数名：void GPIOInit(void)
描  述：首先初始化所有未用到的IO口为输出低电平
输入值：无
输出值：无
返回值：无
*******************************************************/
void GPIOInit(void)
{
    ANSH = 0xFF;        //选择对应端口为数字IO功能
    ANSL = 0xFF;        //选择对应端口为数字IO功能
    PAT = 0x00;         //所有IO设为输出低电平
    PBT = 0x00;
    PCT = 0x00;
    PA = 0x00;
    PB = 0x00;
    PC = 0x00;

    PB7 = 1;              //使能蜂鸣器
}

/*******************************************************
函数名：void T8Ninit(void)
描  述：T8N初始化，用于生成10ms时基
输入值：无
输出值：无
返回值：无
*******************************************************/
void T8NInit(void)
{
    T8NC = 0x0C;         //定时器模式，预分频1:(Fosc/2)/32
    T8N = 6;             //赋计数器初值
    T8NIE = 1;           //打开定时器溢出中断
    T8NIF = 0;           //清溢出标志位
}

/*******************************************************
函数名：void isr(void) interrupt
描  述：中断服务程序，若没有用到周期寄存器高字节，为了提高效率，可以用T31CNTLDL代替period
        从而不使用period变量。
输入值：无
输出值：无
返回值：无
*******************************************************/
void isr(void) interrupt
{
    if (T8NIE == 1 && T8NIF == 1)    //定时器溢出中断
    {
        T8NIF = 0;          //清标志位

        T8N += 6;        //进中断先赋计数器初值

        if ((++g_10ms_flag) >= 10)       //10ms定时到
        {
            PA5 = ~PA5;
            g_10ms_flag = 0;
            period = T31CNTLDH;          //若没有用到周期寄存器高字节，可以不使用period变量，直接使用T31CNTLDL参与计算
            period = (period << 8) | T31CNTLDL;

            if (period <= 78)            //pwm频率高于205kHz，重新从100kHz开始递增
            {
                period = 0xA0;
            }

            period--;      //pwm频率按1kHz步进
            UED = 1;       //禁止更新事件，写入值不会复制到缓冲器中(即写入值不生效)。
            T31CNTLDH = period >> 8;
            T31CNTLDL = period;
            UED = 0;       //允许更新事件，下一次计数器溢出时T31CNTLD生效
            //UPT = 1;       //更新所有寄存器的缓冲器，使写入值立即生效
        }
    }
}

/*******************************************************
函数名：void main(void)
描  述：主函数，T31_CH1(PB5)/T31_CH1N(PB2)输出死区互补pwm，周期在100kHz~205kHz连续变化
输入值：无
输出值：无
返回值：无
*******************************************************/
void main(void)
{
    RAMclear();
    GPIOInit();
    T8NInit();
    T31C0H = 0x40;          //T31C0H<6>(HTOEOFF)调试模式下需软件固定为1，死区发生器时钟频率tDTS=tCK_INT
    T31C0L = 0xE0;          //中心对齐模式3，初始递增计数(DIR=0)，使能T31CNTLD寄存器缓冲(即写入值在下一次更新事件时生效)。
    //T31CH4C = 0x60;         //通道4配置为输出，pwm1模式
    //T31CH3C = 0x60;         //通道3配置为输出，pwm1模式
    //T31CH2C = 0x60;         //通道2配置为输出，pwm1模式
    T31CH1C = 0x60;         //通道配1置为输出，pwm1模式
    //T31PINCH = 0x05;        //T31_CH3高电平有效，使能输出。T31_CH3N通道高电平有效，使能输出。
    T31PINCL = 0x05;        //T31_CH1通道高电平有效，使能输出。T31_CH1N通道高电平有效，使能输出。
    //T31C2H = 0x00;
    T31C2L = 0x00;          //禁止从模式
    T31PRSH = 0x00;         //预分频系数1:1
    T31PRSL = 0x00;
    T31CNTLDH = 0x00;       //重装载值，代表pwm周期长度，pwm模式中央对齐计数下，周期=T31CNTLD*2
    T31CNTLDL = 0x50;
    T31CH1RH = 0x00;        //比较寄存器3值，代表pwm占空比长度
    T31CH1RL = 0x28;
    T31DLYT = 0x00;         //死区时间
    //HALT_PWM = 1;           //在线调试暂停状态下，PWM停止输出，此时通道n端口状态与端口方向寄存器设置的状态(输入/输出)相同
    HALT_PWM = 0;           //在线调试暂停状态下，PWM仍输出，但频率保持不变
    UPT = 1;        //更新所有寄存器的缓冲器，使写入值立即生效
    CHOE = 1;       //使能输出
    GIE = 1;        //开全局中断
    T8NEN = 1;      //使能T8N
    T31EN = 1;      //使能计数器

    while (1)
    {
        CLRWDT();
    }
}