/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com  -----------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/

/*************  功能说明    **************

本例程基于STC32F核心转接板（追风剑）进行编写测试。

从P1.0(PWM1P)、P1.1(PWM1N)输出16位的互补对称PWM, 输出的PWM经过RC滤波成直流电压送P1.4、P1.5做ADC采样并用CDC串口打印结果.

通过USB生成的CDC串口设置占空比.

串口命令使用ASCII码的数字，比如： 10，就是设置占空比为10/256， 100： 就是设置占空比为100/256。

可以设置的值为0~256, 0为连续低电平.

核心转接板ADC默认基准电压是2.5V，ADC正常采样电压范围是0~2.5V，AD口电压大于等于2.5V时采样的ADC值为4095。

此外程序演示两种复位进入USB下载模式的方法：
1. 通过每1毫秒执行一次“KeyResetScan”函数，实现长按P3.2口按键触发MCU复位，进入USB下载模式。
   (如果不希望复位进入USB下载模式的话，可在复位代码里将 IAP_CONTR 的bit6清0，选择复位进用户程序区)
2. 通过加载“stc_usb_cdc_32.lib”库函数，实现使用STC-ISP软件发送指令触发MCU复位，进入USB下载模式并自动下载。
   (注意：使用CDC接口触发MCU复位并自动下载功能，需要勾选端口设置：下次使用STC-HID接口进行ISP下载)

下载时, 选择时钟 52MHz (用户可自行修改频率)。

******************************************/

#define PRINTF_HID           //printf输出直接重定向到USB接口(包含usb.h前定义)

#include "../comm/STC32F.h"  //包含此头文件后，不需要再包含"reg51.h"头文件
#include "../comm/usb.h"     //USB调试及复位所需头文件
#include "stdio.h"
#include "string.h"
#include "intrins.h"

/****************************** 用户定义宏 ***********************************/

#define MAIN_Fosc       52000000UL  //定义主时钟
#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒

/*****************************************************************************/

bit B_1ms;          //1ms标志
u16 cnt200ms;

#define     UART1_BUF_LENGTH    128     //串口缓冲长度

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";                      //设置自动复位到ISP区的用户接口命令

//P3.2口按键复位所需变量
bit Key_Flag;
u16 Key_cnt;

u8  TX1_Cnt;    //发送计数
u8  RX1_Cnt;    //接收计数
bit B_TX1_Busy; //发送忙标志
u16 adc;

bit RX_Flag;
u8  RX1_Buffer[UART1_BUF_LENGTH]; //接收缓冲

void UART1_config(u8 brt);   // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
void UART1_TxByte(u8 dat);
void UpdatePwm(u16 pwm_value);
u16  Get_ADC12bitResult(u8 channel); //channel = 0~15
void KeyResetScan(void);
void Timer0_Init(void);
void ICacheOn(void);
void ICacheOff(void);

/********************* 主函数 *************************/
void main(void)
{
    u8  i;
    u16 j;

    EAXFR = 1; //扩展寄存器(XFR)访问使能

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域，否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x30;   P1M0 = 0x00;   //设置 P1.4,P1.5 为 ADC 口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    usb_init(); //USB接口配置
    ICacheOn(); //开启ICache
    Timer0_Init();

    //  ADC初始化
    ADCTIM = 0x3f;      //设置 ADC 内部时序，ADC采样时间建议设最大值
    ADCCFG = 0x2f;      //设置 ADC 转换结果右对齐,时钟为系统时钟/2/16
    ADC_CONTR = 0x84;   //使能 ADC 模块

    PWMA_CCER1 = 0x00;  //写 CCMRx 前必须先清零 CCxE 关闭通道
    PWMA_CCMR1 = 0x60;  //设置 PWM1 模式1 输出
    PWMA_CCER1 = 0x05;  //使能 CC1NE,CC1E 通道

    PWMA_ARRH = 0;      //设置周期时间
    PWMA_ARRL = 255;
    PWMA_CCR1H = 0;
    PWMA_CCR1L = 220; //设置占空比时间

    PWMA_ENO = 0x03;  //使能 PWM1P/PWM1N 输出
    PWMA_PS = 0x00;   //高级 PWM 通道输出脚选择位, 0x00:P1.0/P1.1, 0x01:P2.0/P2.1, 0x02:P6.0/P6.1
    PWMA_BKR = 0x80;  //使能主输出
    PWMA_CR1 |= 0x01; //开始计时

    EUSB = 1;   //IE2相关的中断使能后，需要重新设置EUSB
    EA = 1;     //打开总中断

    while(DeviceState != DEVSTATE_CONFIGURED);  //等待USB完成配置
    printf("PWM和ADC测试程序, 输入占空比为 0~256!\r\n");  //UART1发送一个字符串
    
    while (1)
    {
        if(B_1ms)   //1ms到
        {
            B_1ms = 0;
            KeyResetScan();   //P3.2口按键触发软件复位，进入USB下载模式，不需要此功能可删除本行代码

            if(++cnt200ms >= 1000)   //1000ms读一次ADC
            {
                cnt200ms = 0;
                Get_ADC12bitResult(4);
                Get_ADC12bitResult(4);
                j = Get_ADC12bitResult(4);  //参数0~15,查询方式做一次ADC, 返回值就是结果, == 4096 为错误
                printf("ADC4=0x%04x ",j);   //显示ADC值
                Get_ADC12bitResult(5);
                Get_ADC12bitResult(5);
                j = Get_ADC12bitResult(5);  //参数0~15,查询方式做一次ADC, 返回值就是结果, == 4096 为错误
                printf("ADC5=0x%04x ",j);
                printf("\r\n");
            }

            if(RX_Flag)     //超时计数
            {
                RX_Flag = 0;

                if((RX1_Cnt > 0) && (RX1_Cnt <= 3)) //限制为3位数字
                {
                    F0 = 0; //错误标志
                    j = 0;
                    for(i=0; i<RX1_Cnt; i++)
                    {
                        if((RX1_Buffer[i] >= '0') && (RX1_Buffer[i] <= '9'))    //限定为数字
                        {
                            j = j * 10 + RX1_Buffer[i] - '0';
                        }
                        else
                        {
                            F0 = 1; //接收到非数字字符, 错误
                            printf("错误! 接收到非数\xfd字字符! 占空比为0~256!\r\n");
                            break;
                        }
                    }
                    if(!F0)
                    {
                        if(j > 256) printf("错误! 输入占空比过\xfd大, 请不要大于256!\r\n");
                        else
                        {
                            UpdatePwm(j);
                            printf("已更新占空比!\r\n");
                        }
                    }
                }
                else  printf("错误! 输入字符过\xfd多! 输入占空比为0~256!\r\n");
                RX1_Cnt = 0;
            }
        }

        if (bUsbOutReady) //USB调试及复位所需代码
        {
            //USB_SendData(UsbOutBuffer,OutNumber);    //返回收到的数据, 用于测试(发送数据缓冲区，长度)
            RX1_Cnt = OutNumber;
            memcpy(RX1_Buffer,UsbOutBuffer,OutNumber);
            RX_Flag = 1;
            usb_OUT_done();
        }
    }
}
//========================================================================
void ICacheOn(void)     //打开ICACHE功能
{
    bit fEA;
    
    if(WTST > 0)
    {
        fEA = EA;
        EA = 0;         //关闭中断，防止写触发命令序列中途产生中断
        _nop_();
        _nop_();
        TA = 0xaa;      //写入触发命令序列1
                        //此处不能有其他任何指令
        TA = 0x55;      //写入触发命令序列2
                        //此处不能有其他任何指令
        ICHECR = 0x01;  //写保护暂时关闭，可以修改ICHECR中的EN位
                        //EN为再次进入写保护状态
        _nop_();
        _nop_();
        EA = fEA;
    }
}

void ICacheOff(void)//关闭ICACHE功能
{
    bit fEA;

    fEA = EA;
    EA = 0;         //关闭中断，防止写触发命令序列中途产生中断
    _nop_();
    _nop_();
    TA = 0xaa;      //写入触发命令序列1
                    //此处不能有其他任何指令
    TA = 0x55;      //写入触发命令序列2
                    //此处不能有其他任何指令
    ICHECR = 0x00;  //写保护暂时关闭，可以修改ICHECR中的EN位
                    //EN位再次进入写保护状态
    _nop_();
    _nop_();
    EA = fEA;
}

//========================================================================
// 函数: void Timer0_Init(void)
// 描述: 定时器0初始化函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-6-2
// 备注: 
//========================================================================
void Timer0_Init(void)
{
    // Timer0初始化
    TMOD &= 0xf0;//16 bits timer auto-reload
    T0x12 = 1;  //Timer0 set as 1T
    TH0 = (u8)(Timer0_Reload / 256);
    TL0 = (u8)(Timer0_Reload % 256);
    ET0 = 1;    //Timer0 interrupt enable
    TR0 = 1;    //Tiner0 run
}

/********************** Timer0 1ms中断函数 ************************/
void timer0(void) interrupt 1
{
    B_1ms = 1;      //1ms标志
}

//========================================================================
// 函数: u16 Get_ADC12bitResult(u8 channel)
// 描述: 查询法读一次ADC结果.
// 参数: channel: 选择要转换的ADC.
// 返回: 12位ADC结果.
// 版本: V1.0, 2012-10-22
//========================================================================
u16 Get_ADC12bitResult(u8 channel)  //channel = 0~15
{
    ADC_RES = 0;
    ADC_RESL = 0;

    ADC_CONTR = (ADC_CONTR & 0xf0) | channel; //设置ADC转换通道
    ADC_START = 1;//启动ADC转换
    _nop_();
    _nop_();
    _nop_();
    _nop_();

    while(ADC_FLAG == 0);   //wait for ADC finish
    ADC_FLAG = 0;     //清除ADC结束标志
    return  (((u16)ADC_RES << 8) | ADC_RESL);
}

//========================================================================
// 函数: UpdatePwm(u16 pwm_value)
// 描述: 更新PWM值. 
// 参数: pwm_value: pwm值, 这个值是输出高电平的时间.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void UpdatePwm(u16 pwm_value)
{
    PWMA_CCR1H = (u8)(pwm_value >> 8); //设置占空比时间
    PWMA_CCR1L = (u8)(pwm_value);
}

//========================================================================
// 函数: void delay(void)
// 描述: 延时函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2023-6-29
// 备注: 
//========================================================================
void delay(void)
{
    u8 ms = 200;
    while(ms--);
}

//========================================================================
// 函数: void KeyResetScan(void)
// 描述: P3.2口按键长按1秒触发软件复位，进入USB下载模式。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-6-11
// 备注: 
//========================================================================
void KeyResetScan(void)
{
    if(!P32)
    {
        if(!Key_Flag)
        {
            Key_cnt++;
            if(Key_cnt >= 1000)		//连续1000ms有效按键检测
            {
                Key_Flag = 1;		//设置按键状态，防止重复触发

                USBCON = 0x00;      //清除USB设置
                USBCLK = 0x00;
                IRC48MCR = 0x00;
                
                delay();
                IAP_CONTR = 0x60;   //触发软件复位，从ISP开始执行
                while (1);
            }
        }
    }
    else
    {
        Key_cnt = 0;
        Key_Flag = 0;
    }
}
