/*---------------------------------------------------------------------*/
/* --- 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核心转接板（追风剑）进行编写测试。

按一次P3.3按键，修改一次IRC主频，串口打印IRC选择序号。

按一次P3.4按键，修改一次RST管脚功能，串口打印当前的RST管脚功能。

按一次P3.5按键，修改一次LVDS门槛电压级别，串口打印当前的LVDS门槛电压级别。

默认串口(P3.0,P3.1)参数：115200,N,8,1。

P2口流水灯在不同IRC主频时切换速度不同。

主频提高后WTST需要进行相应的调整，指令执行时间受WTST设置影响。

此外程序演示按键复位进入USB下载模式的方法：
1. 通过每1毫秒执行一次“KeyResetScan”函数，实现长按P3.2口按键触发MCU复位，进入USB下载模式。
   (如果不希望复位进入USB下载模式的话，可在复位代码里将 IAP_CONTR 的bit6清0，选择复位进用户程序区)
   (主频修改后USB HID设备会丢失，通过USB HID触发自动下载功能也会失效，所以这里不演示通过USB发命令不停电下载)

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

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

#include "../comm/STC32F.h"  //包含此头文件后，不需要再包含"reg51.h"头文件
#include "stdio.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

/*************  本地常量声明    **************/
//VRTRIM_24M
#define T22M_ADDR   CHIPID11   //22.1184MHz
#define T24M_ADDR   CHIPID12   //24MHz
//VRTRIM_44M
#define T40M_ADDR   CHIPID13   //40MHz
#define T45M_ADDR   CHIPID14   //45.1584MHz
#define T48M_ADDR   CHIPID15   //48MHz
#define T50M_ADDR   CHIPID16   //50.8032MHz
#define T52M_ADDR   CHIPID17   //52MHz
#define T56M_ADDR   CHIPID18   //56MHz
#define T60M_ADDR   CHIPID19   //60MHz
#define T64M_ADDR   CHIPID20   //64MHz

#define VRT20M_ADDR CHIPID21   //VRTRIM_20M
#define VRT24M_ADDR CHIPID22   //VRTRIM_24M
#define VRT44M_ADDR CHIPID23   //VRTRIM_44M
#define VRT72M_ADDR CHIPID24   //VRTRIM_72M

#define Baudrate      9600L   //波特率 115200 需要5M以上IRC频率

/*************  频率定义    **************/

typedef enum
{
	IRC_500K = 0,		/*0*/
	IRC_3M,
	IRC_5M,
	IRC_6M,
	IRC_11M,
	IRC_12M,
	IRC_22M,
	IRC_24M,
	IRC_30M,
	IRC_32M,
	IRC_40M,
	IRC_45M,
	IRC_48M,
	IRC_50M,
	IRC_52M,
	IRC_56M,
	IRC_60M,
	IRC_64M,
}IRC_IndexType;

typedef enum
{
	IRCBAND_20M = 0,		/*0*/
	IRCBAND_24M,
	IRCBAND_44M,
	IRCBAND_72M,
}IRCBAND_SelType;

/*************  本地变量声明    **************/
u8 index = IRC_24M;
u32 MAIN_Fosc =	24000000L;	//定义主时钟

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

bit Key1_Flag;
u16 Key1_cnt;

bit Key2_Flag;
u16 Key2_cnt;

bit Key3_Flag;
u16 Key3_cnt;

void KeyResetScan(void);
void delay_ms(u8 ms);
void HardwareMarquee(void);
void Timer0_Init(u32 clk);
void SetMCLK(u8 clk);

/******************** 串口打印函数 ********************/
void UartInit(void)
{
	P_SW1 = (P_SW1 & 0x3f) | 0x00;    //USART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
	SCON = (SCON & 0x3f) | 0x40; 
	T1x12 = 1;          //定时器时钟1T模式
	S1BRT = 0;          //串口1选择定时器1为波特率发生器
	TL1 = (65536 -(MAIN_Fosc/Baudrate/4));
	TH1 = (65536 -(MAIN_Fosc/Baudrate/4))>>8;
	TR1 = 1;			//定时器1开始计时
}

void UartPutc(unsigned char dat)
{
	SBUF = dat; 
	while(TI==0);
	TI = 0;
}

char putchar(char c)
{
	UartPutc(c);
	return c;
}

/********************** 主函数 ************************/
void main(void)
{
    EAXFR = 1; //扩展寄存器(XFR)访问使能

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

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x40;   //设置为准双向口, P1.6设置位推挽输出
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    Timer0_Init(MAIN_Fosc/1000);	//Timer 0 中断频率, 1000次/秒
    UartInit();
    
    MCLKOCR = 0x8A; //主时钟频率10分频输出到P1.6口

    EA = 1;     //允许总中断

    //芯片复位指示
    P20 = 0;		//LED On
    delay_ms(250);
    P20 = 1;		//LED Off
    delay_ms(250);
    P20 = 0;		//LED On
    delay_ms(250);
    P20 = 1;		//LED Off
    delay_ms(250);
    printf("核心硬件功能初始化程序.\r\n");

    while(1)
    {
        HardwareMarquee();
    }
} 
/**********************************************/

void HardwareMarquee(void)
{
    P20 = 0;		//LED On
    delay_ms(250);
    P20 = 1;		//LED Off
    P21 = 0;		//LED On
    delay_ms(250);
    P21 = 1;		//LED Off
    P22 = 0;		//LED On
    delay_ms(250);
    P22 = 1;		//LED Off
    P23 = 0;		//LED On
    delay_ms(250);
    P23 = 1;		//LED Off
    P24 = 0;		//LED On
    delay_ms(250);
    P24 = 1;		//LED Off
    P25 = 0;		//LED On
    delay_ms(250);
    P25 = 1;		//LED Off
    P26 = 0;		//LED On
    delay_ms(250);
    P26 = 1;		//LED Off
    P27 = 0;		//LED On
    delay_ms(250);
    P27 = 1;		//LED Off
    P26 = 0;		//LED On
    delay_ms(250);
    P26 = 1;		//LED Off
    P25 = 0;		//LED On
    delay_ms(250);
    P25 = 1;		//LED Off
    P24 = 0;		//LED On
    delay_ms(250);
    P24 = 1;		//LED Off
    P23 = 0;		//LED On
    delay_ms(250);
    P23 = 1;		//LED Off
    P22 = 0;		//LED On
    delay_ms(250);
    P22 = 1;		//LED Off
    P21 = 0;		//LED On
    delay_ms(250);
    P21 = 1;		//LED Off
}

//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-6-3
// 备注: 
//========================================================================
void delay_ms(u8 ms)
{
    u16 i;
    do{
        i = 1000;   //固定用6M主频计算，不同IRC延时就不同（延时时间受WTST设置影响）
        while(--i);
    }while(--ms);
}

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

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

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

	if(!P33)
	{
		if(!Key1_Flag)
		{
			Key1_cnt++;
			if(Key1_cnt >= 50)		//50ms防抖
			{
				Key1_Flag = 1;			//设置按键状态，防止重复触发

                index++;
                if(index > IRC_60M) index = 0;
                SetMCLK(index);
                printf("IRC index=%bd.\r\n",index);
			}
		}
	}
	else
	{
		Key1_cnt = 0;
		Key1_Flag = 0;
	}

	if(!P34)
	{
		if(!Key2_Flag)
		{
			Key2_cnt++;
			if(Key2_cnt >= 50)		//50ms防抖
			{
				Key2_Flag = 1;			//设置按键状态，防止重复触发

                if(RSTCFG & 0x10)
                {
                    RSTCFG &= ~0x10;
                    printf("RST管脚用做IO口.\r\n");
                }
                else
                {
                    RSTCFG |= 0x10;
                    printf("RST管脚用做复位脚.\r\n");
                }
			}
		}
	}
	else
	{
		Key2_cnt = 0;
		Key2_Flag = 0;
	}

	if(!P35)
	{
		if(!Key3_Flag)
		{
			Key3_cnt++;
			if(Key3_cnt >= 50)		//50ms防抖
			{
				Key3_Flag = 1;			//设置按键状态，防止重复触发

                temp = (RSTCFG & 0x03);
                temp++;
                RSTCFG &= ~0x03;
                RSTCFG |= (temp & 0x03);
                printf("设置LVDS门槛电压级别：%bd.\r\n",(temp & 0x03));
			}
		}
	}
	else
	{
		Key3_cnt = 0;
		Key3_Flag = 0;
	}
}

//========================================================================
// 函数: void timer0_int(void)
// 描述: Timer0 1ms中断函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-6-11
// 备注: 
//========================================================================
void timer0_int(void) interrupt 1  //1ms 中断函数
{
    KeyResetScan();   //P3.2口按键触发软件复位，进入USB下载模式，不需要此功能可删除本行代码
}

//========================================================================
// 函数: void SetMCLK(u8 clk)
// 描述: 设置主频
// 参数: clk: 主频序号
// 返回: none.
// 版本: VER1.0
// 日期: 2022-10-14
// 备注: 
//========================================================================
void SetMCLK(u8 clk) //设置主频
{
    if(clk > IRC_64M) return;	//Error
    switch(clk)
    {
    case IRC_500K:
        //选择500KHz
        CLKDIV = 0x04;
        IRTRIM = T24M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 48;
        MAIN_Fosc =	500000L;	//定义主时钟
        break;

    case IRC_3M:
        //选择3MHz
        CLKDIV = 0x04;
        IRTRIM = T24M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 8;
        MAIN_Fosc =	3000000L;	//定义主时钟
        break;

    case IRC_5M:
        //选择5.5296MHz
        CLKDIV = 0x04;
        IRTRIM = T22M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 4;
        MAIN_Fosc =	5529600L;	//定义主时钟
        break;

    case IRC_6M:
        //选择6MHz
        CLKDIV = 0x04;
        IRTRIM = T24M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 4;
        MAIN_Fosc =	6000000L;	//定义主时钟
        break;

    case IRC_11M:
        //选择11.0592MHz
        CLKDIV = 0x04;
        IRTRIM = T22M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 2;
        MAIN_Fosc =	11059200L;	//定义主时钟
        break;

    case IRC_12M:
        //选择12MHz
        CLKDIV = 0x04;
        IRTRIM = T24M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 0;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 2;
        MAIN_Fosc =	12000000L;	//定义主时钟
        break;

    case IRC_22M:
        //选择22.1184MHz
        CLKDIV = 0x04;
        IRTRIM = T22M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	22118400L;	//定义主时钟
        break;

    case IRC_24M:
        //选择24MHz
        CLKDIV = 0x04;
        IRTRIM = T24M_ADDR;
        VRTRIM = VRT24M_ADDR;
        IRCBAND = IRCBAND_24M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	24000000L;	//定义主时钟
        break;

    case IRC_30M:
        //选择30MHz
        CLKDIV = 0x04;
        IRTRIM = T60M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 2;
        MAIN_Fosc =	30000000L;	//定义主时钟
        break;

    case IRC_32M:
        //选择32MHz
        CLKDIV = 0x04;
        IRTRIM = T64M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 2;
        MAIN_Fosc =	32000000L;	//定义主时钟
        break;

    case IRC_40M:
        //选择40MHz
        CLKDIV = 0x04;
        IRTRIM = T40M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	40000000L;	//定义主时钟
        break;

    case IRC_45M:
        //选择45.1584MHz
        CLKDIV = 0x04;
        IRTRIM = T45M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	45158400L;	//定义主时钟
        break;

    case IRC_48M:
        //选择48MHz
        CLKDIV = 0x04;
        IRTRIM = T48M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	48000000L;	//定义主时钟
        break;

    case IRC_50M:
        //选择50.8032MHz
        CLKDIV = 0x04;
        IRTRIM = T50M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	50803200L;	//定义主时钟
        break;

    case IRC_52M:
        //选择52MHz
        CLKDIV = 0x04;
        IRTRIM = T52M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	52000000L;	//定义主时钟
        break;

    case IRC_56M:
        //选择56MHz
        CLKDIV = 0x04;
        IRTRIM = T56M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 1;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	56000000L;	//定义主时钟
        break;

//    case IRC_60M:
//        //选择60MHz
//        CLKDIV = 0x04;
//        IRTRIM = T60M_ADDR;
//        VRTRIM = VRT44M_ADDR;
//        IRCBAND = IRCBAND_44M;
//        WTST = 2;       //设置程序读取等待控制寄存器
//        _nop_();
//        _nop_();
//        _nop_();
//        _nop_();
//        CLKDIV = 1;
//        MAIN_Fosc =	60000000L;	//定义主时钟
//        break;

    case IRC_64M:
        //选择64MHz
        CLKDIV = 0x04;
        IRTRIM = T64M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 2;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	64000000L;	//定义主时钟
        break;

    default:
        //选择60MHz
        CLKDIV = 0x04;
        IRTRIM = T60M_ADDR;
        VRTRIM = VRT44M_ADDR;
        IRCBAND = IRCBAND_44M;
        WTST = 2;       //设置程序读取等待控制寄存器
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        CLKDIV = 1;
        MAIN_Fosc =	60000000L;	//定义主时钟
        break;
    }
    Timer0_Init(MAIN_Fosc/1000);
    UartInit();
}
