找回密码
 立即注册
查看: 3437|回复: 9

STC8H4K64TL主时钟停振省电模式唤醒:触摸按键唤醒 + 串口接收引脚唤醒 + 外部中断唤

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:415
  • 最近打卡:2025-04-30 09:58:34
已绑定手机

39

主题

2006

回帖

6841

积分

论坛元老

积分
6841
发表于 2023-10-12 19:01:10 | 显示全部楼层 |阅读模式
STC8H4K64TL主时钟停振省电模式唤醒
===触摸按键唤醒 + 串口接收引脚等触发休眠唤醒 + 外部中断唤醒 + 定时器外部时钟输入脚唤醒

截图202310121900284463.jpg

附件是参考例程


STC8H4K64Txx-触摸按键校验检测例程-低功耗-外部唤醒.zip

20.15 KB, 下载次数: 287

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:415
  • 最近打卡:2025-04-30 09:58:34
已绑定手机

39

主题

2006

回帖

6841

积分

论坛元老

积分
6841
发表于 2023-10-13 14:05:42 | 显示全部楼层
STC8H4K64TL【主时钟停振省电模式】唤醒
对比了部分不行的代码(启动触摸按键唤醒,外部中断唤醒却不行了)
===发现在进【主时钟停振省电模式】之前需要


进休眠之前关闭TSGO,休眠唤醒后再开启
截图202310131403334990.jpg
这样在使能触摸按键休眠唤醒功能情况下,
也可以支持串口接收引脚等触发休眠唤醒、外部中断唤醒、定时器外部时钟输入脚等其它脚位唤醒。
回复 支持 反对

使用道具 举报 送花

0

主题

3

回帖

28

积分

新手上路

积分
28
发表于 2023-10-13 15:40:52 | 显示全部楼层
说明书中,配置TSWAIT为0是错误的!如果配置为0,触摸可以唤醒,其他就唤醒不了!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:415
  • 最近打卡:2025-04-30 09:58:34
已绑定手机

39

主题

2006

回帖

6841

积分

论坛元老

积分
6841
发表于 2023-10-13 15:56:15 | 显示全部楼层
szja*** 发表于 2023-10-13 15:40
说明书中,配置TSWAIT为0是错误的!如果配置为0,触摸可以唤醒,其他就唤醒不了! ...

实测跟TSWAIT没有关系
只要做到进休眠之前关闭TSGO,休眠唤醒后再开启,TSWAIT不管设0还是1都可以。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15613
发表于 2023-10-13 18:44:49 | 显示全部楼层
STC8H系列数据手册更新,2023/10/13
截图202310131845423940.jpg
www.STCAI.com

截图202310131843406225.jpg

截图202310131844136253.jpg




回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:50
  • 最近打卡:2025-04-30 22:59:03

73

主题

5882

回帖

1万

积分

超级版主

积分
12073
发表于 2023-10-16 00:37:45 | 显示全部楼层
本帖最后由 梁工 于 2023-10-16 09:55 编辑

梁工的例子:
截图202310160728461767.jpg
截图202310160954238955.jpg

STC8H-RTC唤醒-触摸按键及唤醒-相关程序.rar

701.42 KB, 下载次数: 192

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15613
发表于 2023-10-16 07:31:13 | 显示全部楼层
截图202310160730099862.jpg



/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU Demo --------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.stcai.com ------------------------------------------*/
/* --- BBS: www.stcaimcu.com ---------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/


/*********************************************************/
#define MAIN_Fosc                11059200L        //定义主时钟

#include        "..\..\STC8Hxxx.h"


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

请先别修改程序, 直接下载"rtc.hex"测试, 主频选择11.0592MHZ. 测试正常后再修改移植.
由于使用的中断号超过了31,所以KEIL编译器要拓展Keil的C代码中断号, 运行一下拓展Keil的C代码中断号.exe即可。

RTC每秒唤醒一次,通过串口返回日期时间。
上位机通过串口发送数据给MCU也会唤醒MCU,并且连续运行5秒。由于唤醒需要点时间(80~100us),所以当波特率大约2400时,唤醒后收到的数据通常会错误,再发一次即可正确接收。
上位机通过串口设置RTC日期时间和闹铃时间。

MCU通过串口1返回: 年 月 日 时 分 秒:  21-6-19 12:34:56
通过串口发送设置值: 21-6-19 12:34:56
通过串口发送闹钟值: A12:34:56

默认参数:
串口1设置均为 1位起始位, 8位数据位, 1位停止位, 无校验.
串口1(P3.0 P3.1): 115200bps.

串口要发送数据2次(间隔4秒内), 第一次唤醒(唤醒需要时间, 第一次接收数据将会错误), 第二次数据才会正确接收.

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

/*************        本地常量声明        **************/
#define        RX1_Length        32                /* 接收缓冲长度 */


/*************        本地变量声明        **************/
u8        xdata        RX1_Buffer[RX1_Length];        //接收缓冲
u8        RX1_Cnt;                //串口接收计数.
u8        RX1_TimeOut;        //串口接收超时
bit        B_TX1_Busy;        // 发送忙标志

u8        year, month, day, hour, minute, second;        //RTC实时时间
bit        B_1S;        //秒中断, 秒有变化
bit        B_1ms;        //1ms标志
u16        OpTime;                // 此变量非0时, 不睡眠, 连续运行程序(本例串口唤醒后连续运行5秒, 以便正确接收串口数据)

u8        GetDataIndex;        //获取数据索引仅仅用于读取计数
u8        GetDataState;        //获取数据状态, 非0则错误

u8                ALARM_hour;                 // RTC闹钟的小时值
u8                ALARM_minute;                // RTC闹钟的分钟值
u8                ALARM_second;                // RTC闹钟的秒值
bit                B_ALARM;


/*************        本地函数声明        **************/
void        UART1_config(u32 brt, u8 timer, u8 io);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
void         UART1_TxByte(u8 dat);
void         UART1_PrintString(u8 *puts);
u8                SetRTC(void);                //设置RTC时间函数
void        RTC_config(void);        //RTC初始化函数
void        RTC_read(void);                //读RTC时间函数
void        ReturnRTC(void);        //返回时间信息
u8                GetData(void);                //获取数据, 设置时间命令: 21-6-19 12:34:56
void          delay_ms(u8 ms);
u8                Timer0_Config(u8 t, u32 reload);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.




//========================================================================
// 函数: void main(void)
// 描述: 主函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void main(void)
{
        P0M1 = 0;        P0M0 = 0;
        P1M1 = 0;        P1M0 = 0;
        P2M1 = 0;        P2M0 = 0;
        P3M1 = 0;        P3M0 = 0;
        P4M1 = 0;        P4M0 = 0;
        P5M1 = 0;        P5M0 = 0;
        P6M1 = 0;        P6M0 = 0;
        P7M1 = 0;        P7M0 = 0;

        UART1_config(115200UL, 1, 0);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
        Timer0_Config(0, MAIN_Fosc / 1000);        //t=0: reload值是主时钟周期数,  (中断频率, 1000次/秒)

        EA = 1;

        UART1_PrintString("STC8H RTC Test Prgramme!\r\n");

        OpTime    = 0;
        RTC_config();


        while (1)
        {
                if(B_1ms)        //1ms标志
                {
                        B_1ms = 0;

                        if(OpTime != 0)        OpTime--;        //连续运行时间

                        if(RX1_TimeOut != 0)
                        {
                                if(--RX1_TimeOut == 0)        //设置时间命令: 21-6-19 12:34:56
                                {
                                        if(RX1_Buffer[0] == 'A')        //设置闹铃
                                        {
                                                GetDataIndex = 1;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                ALARM_hour   = GetData();                //获取数据 时
                                                ALARM_minute = GetData();                //获取数据 分
                                                ALARM_second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        ALAHOUR = ALARM_hour;                 // RTC闹钟的小时值
                                                        ALAMIN  = ALARM_minute;                // RTC闹钟的分钟值
                                                        ALASEC  = ALARM_second;                // RTC闹钟的秒值
                                                        ALASSEC = 0;                                // RTC闹钟的1/128秒值
                                                        RTCIEN  |= 0x80;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
                                                        UART1_PrintString("已设置闹铃!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置闹铃数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        else        //设置时间
                                        {
                                                GetDataIndex = 0;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                year   = GetData();                //获取数据 年
                                                month  = GetData();                //获取数据 月
                                                day    = GetData();                //获取数据 日
                                                hour   = GetData();                //获取数据 时
                                                minute = GetData();                //获取数据 分
                                                second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        UART1_PrintString("获取设置时间: ");        ReturnRTC();
                                                        if(SetRTC() == 0)        UART1_PrintString("已设置时间!\r\n");
                                                        else                                UART1_PrintString("设置时间数据错误!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置时间数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        RX1_Cnt = 0;
                                }
                        }
                }

                if(B_ALARM)                //闹铃
                {
                        B_ALARM = 0;
                        UART1_PrintString("滴滴滴! 闹铃! \r\n");
                }

                if(B_1S)        //秒有变化
                {
                        B_1S = 0;
                        RTC_read();
                        ReturnRTC();                                //从串口返回时间信息
                }

                if(OpTime == 0)                // OpTime==0才进入掉电模式
                {
                        PCON |= 0x02;        //睡眠
                        NOP(5);
                }
        }
}


/********************** 获取串口数据 *****************************/
u8        GetData(void)
{
        u8 i,j,k;

        if(GetDataIndex >= RX1_Cnt)
        {
                GetDataState = 1;
                return 0;
        }
//        if(RX1_Cnt < 11)        //数据长度错误 设置时间
        if(RX1_Cnt < 6)                //数据长度错误 设置时间 设置闹铃
        {
                GetDataState = 2;
                return 0;
        }

        i = 0;
        k = 0;
        while(GetDataIndex <= RX1_Cnt)                //设置时间命令: 21-6-19 12:34:56
        {
                j = RX1_Buffer[GetDataIndex];
                if((j == '-') || (j == ' ') || (j == ':') || (GetDataIndex == RX1_Cnt))        //数据段结束
                {
                        GetDataIndex++;
                        if(i == 0)        //数据错误
                        {
                                GetDataState = 3;
                                return 0;
                        }
                        else        return k;        //数据正确
                }
                if((j < '0') || (j > '9'))        //数据合法性检测
                {
                        GetDataIndex++;
                        GetDataState = 4;
                        return 0;
                }
                k = k * 10 + j - '0';
                i++;
                GetDataIndex++;
        }

        GetDataState = 9;
        return 10;        //数据错误
}

/********************** 串口返回时间信息 *****************************/
void        ReturnRTC(void)
{
        UART1_TxByte(year/10+'0');        //年
        UART1_TxByte(year%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(month/10+'0');        //月
        UART1_TxByte(month%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(day/10+'0');        //日
        UART1_TxByte(day%10+'0');
        UART1_TxByte(' ');
        UART1_TxByte(hour/10+'0');        //时
        UART1_TxByte(hour%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(minute/10+'0');        //分
        UART1_TxByte(minute%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(second/10+'0');        //秒
        UART1_TxByte(second%10+'0');
        UART1_TxByte(0x0d);
        UART1_TxByte(0x0a);
}


/********************** 设置RTC时间 *****************************/
u8        SetRTC(void)
{
        P_SW2 |= 0x80;                //SFR enable
        if(year > 99)                                                return 1;
        if((month == 0) || (month > 12))        return 2;
        if((day == 0) || (day > 31))                return 3;
        if(hour > 23)                                        return 4;
        if(minute > 59)                                        return 5;
        if(second > 59)                                        return 6;
        INIYEAR  = year;
        INIMONTH = month;
        INIDAY   = day;
        INIHOUR  = hour;
        INIMIN   = minute;
        INISEC   = second;
        INISSEC  = 0;
        RTCCFG   = 0x01 | 0x00;        //设置RTC时间, |0x00:选择外部32K时钟, |0x02:选择内部32K时钟.
        while(RTCCFG & 0x01);        //等待初始化完成. 设置RTC时间需要32768Hz的1个周期时间,大约30.5us. 由于同步, 所以实际等待时间是0~30.5us.
                                                        //如果不等待设置完成就睡眠, 则RTC会由于设置没完成, 停止计数, 唤醒后才继续完成设置并继续计数.
        return 0;
}

/********************** RTC配置函数 *****************************/
void        RTC_config(void)        //RTC初始化函数
{
        P_SW2  |= 0x80;                //SFR enable
        P1n_pure_input(0xc0);                //P1.6 P1.7设置为高阻输入
        P1IE = ~0xc0;                                //P1.6 P1.7关闭数字输入功能
        X32KCR = 0x80 + 0x40;                //启动外部32K晶振, 低增益+0x00, 高增益+0x40.

        year    = 21;
        month   = 6;
        day     = 18;
        hour    = 12;
        minute  = 0;
        second  = 0;

        RTCCR   = 0x01;        //使能RTC, 并开始RTC计数
        SetRTC();                //设置RTC时间
        RTCIF   = 0;        //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        RTCIEN  = 0x08;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        ALAHOUR = 8;        //闹钟小时
        ALAMIN  = 30;        //闹钟分钟
        ALASEC  = 0;        //闹钟秒
        ALASSEC = 0;        //闹钟1/128秒
}

/********************** 读取RTC时间信息 *****************************/
void        RTC_read(void)
{
        P_SW2  |= 0x80;                //SFR enable
        year    = RTC_YEAR;                // RTC的年计数值
        month   = RTC_MONTH;        // RTC的月计数值
        day     = RTC_DAY;                // RTC的日计数值
        hour    = RTC_HOUR;                // RTC的时计数值
        minute  = RTC_MIN;                // RTC的分计数值
        second  = RTC_SEC;                // RTC的秒计数值
//        ssecond = RTC_SSEC;                // RTC的1/128秒计数值
}



//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        SetTimer2Baudraye(u16 dat)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
        AUXR &= ~(1<<4);        //Timer stop
        AUXR &= ~(1<<3);        //Timer2 set As Timer
        AUXR |=  (1<<2);        //Timer2 set as 1T mode
        TH2 = (u8)(dat >> 8);
        TL2 = (u8)dat;
        IE2  &= ~(1<<2);        //禁止中断
        AUXR |=  (1<<4);        //Timer run enable
}


//========================================================================
// 函数: void        UART1_config(u32 brt, u8 timer, u8 io)
// 描述: UART1初始化函数。
// 参数:   brt: 通信波特率.
//       timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
//          io: 串口1切换到的IO,  io=1: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        UART1_config(u32 brt, u8 timer, u8 io)        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7.
{
        brt = 65536UL - (MAIN_Fosc / 4) / brt;
        if(timer == 2)        //波特率使用定时器2
        {
                AUXR |= 0x01;                //S1 BRT Use Timer2;
                SetTimer2Baudraye((u16)brt);
        }

        else                //波特率使用定时器1
        {
                TR1 = 0;
                AUXR &= ~0x01;                //S1 BRT Use Timer1;
                AUXR |=  (1<<6);        //Timer1 set as 1T mode
                TMOD &= ~(1<<6);        //Timer1 set As Timer
                TMOD &= ~0x30;                //Timer1_16bitAutoReload;
                TH1 = (u8)(brt >> 8);
                TL1 = (u8)brt;
                ET1 = 0;                        // 禁止Timer1中断
                INT_CLKO &= ~0x02;        // Timer1不输出高速时钟
                TR1  = 1;                        // 运行Timer1
        }

                 if(io == 1)        {S1_USE_P36P37();        P3n_standard(0xc0);}        //切换到 P3.6 P3.7
        else if(io == 2)        {S1_USE_P16P17();        P1n_standard(0xc0);}        //切换到 P1.6 P1.7
        else                                {S1_USE_P30P31();        P3n_standard(0x03);}        //切换到 P3.0 P3.1

        SCON = (SCON & 0x3f) | (1<<6);        // 8位数据, 1位起始位, 1位停止位, 无校验
//        PS  = 1;        //高优先级中断
        ES  = 1;        //允许中断
        REN = 1;        //允许接收
}


//========================================================================
// 函数: void UART1_TxByte(u8 dat)
// 描述: 串口1发送一个字节数据函数
// 参数: dat: 要发送的一个字节数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_TxByte(u8 dat)
{
        B_TX1_Busy = 1;                //标志发送忙
        SBUF = dat;                        //发一个字节
        while(B_TX1_Busy);        //等待发送完成
}

//========================================================================
// 函数: void UART1_PrintString(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_PrintString(u8 *puts)
{
    for (; *puts != 0;        puts++)        UART1_TxByte(*puts);
}


//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: 串口1中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_ISR (void) interrupt UART1_VECTOR
{
        if(RI)
        {
                RI = 0;
                if(RX1_Cnt >= RX1_Length)        RX1_Cnt = 0;
                RX1_Buffer[RX1_Cnt++] = SBUF;
                RX1_TimeOut = 5;
                OpTime = 5000;        //串口一旦唤醒, 接着5秒不睡眠, 以便正确接收数据. 唤醒帧将不能正确接收(因为唤醒需要计时us时间), 除非用很慢的速度(比如2400).
        }

        if(TI)
        {
                TI = 0;
                B_TX1_Busy = 0;
        }
}


//========================================================================
// 函数:u8        Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-3-5
//========================================================================
u8        Timer0_Config(u8 t, u32 reload)        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{
        TR0 = 0;        //停止计数

        if(t != 0)        reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);        //重装的是时间(us), 计算所需要的系统时钟数.
        if(reload >= (65536UL * 12))        return 1;        //值过大, 返回错误
        if(reload < 65536UL)        AUXR |= 0x80;                //1T mode
        else
        {
                AUXR &= ~0x80;        //12T mode
                reload = reload / 12;
        }
        reload = 65536UL - reload;
        TH0 = (u8)(reload >> 8);
        TL0 = (u8)(reload);

        ET0 = 1;        //允许中断
//        PT0 = 1;        //高优先级中断
        TMOD = (TMOD & ~0x03) | 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
        TR0 = 1;                        //开始运行
        return 0;
}

//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2016-5-12
//========================================================================
void timer0_ISR (void) interrupt TIMER0_VECTOR
{
   B_1ms = 1;        //1ms标志
}


//========================================================================
// 函数: AUXR_ISR(void) interrupt 13
// 描述: 扩展中断函数(中断号>=32的中断)
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        RTC_ISR(void) interrupt 36
{
        u8        i;
        i = RTCIF;
        RTCIF = 0;                //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        if(i & 0x08)        //秒中断
        {
                B_1S = 1;        //秒标志
        }
        if(i & 0x80)        //闹铃中断
        {
                B_ALARM = 1;        //闹铃标志
        }
}


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15613
发表于 2023-10-16 07:33:23 | 显示全部楼层
截图202310160732032647.jpg



/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU Demo --------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.stcai.com ------------------------------------------*/
/* --- BBS: www.stcaimcu.com ---------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/


/*********************************************************/
#define MAIN_Fosc                11059200L        //定义主时钟

#include        "..\..\STC8Hxxx.h"


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

请先别修改程序, 直接下载"rtc.hex"测试, 主频选择11.0592MHZ. 测试正常后再修改移植.
由于使用的中断号超过了31,所以KEIL编译器要拓展Keil的C代码中断号, 运行一下拓展Keil的C代码中断号.exe即可。

RTC每秒唤醒一次,通过串口返回日期时间。
上位机通过串口发送数据给MCU也会唤醒MCU,并且连续运行5秒。由于唤醒需要点时间(80~100us),所以当波特率大约2400时,唤醒后收到的数据通常会错误,再发一次即可正确接收。
上位机通过串口设置RTC日期时间和闹铃时间。
同时传统的INT0~INT3均可唤醒MCU;

INT0唤醒后在中断服务函数里取反P2.0指示.
INT1唤醒后在中断服务函数里取反P2.1指示.
INT2唤醒后在中断服务函数里取反P2.2指示.
INT3唤醒后在中断服务函数里取反P2.3指示.
INT4唤醒后在中断服务函数里取反P2.4指示.

MCU通过串口1返回: 年 月 日 时 分 秒:  21-6-19 12:34:56
通过串口发送设置值: 21-6-19 12:34:56
通过串口发送闹钟值: A12:34:56

默认参数:
串口1设置均为 1位起始位, 8位数据位, 1位停止位, 无校验.
串口1(P3.0 P3.1): 115200bps.

串口要发送数据2次(间隔4秒内), 第一次唤醒(唤醒需要时间, 第一次接收数据将会错误), 第二次数据才会正确接收.

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

/*************        本地常量声明        **************/
#define        RX1_Length        32                /* 接收缓冲长度 */


/*************        本地变量声明        **************/
u8        xdata        RX1_Buffer[RX1_Length];        //接收缓冲

u8        RX1_Cnt;                //串口接收计数.
u8        RX1_TimeOut;        //串口接收超时

bit        B_TX1_Busy;        // 发送忙标志

u8        year, month, day, hour, minute, second;        //RTC实时时间
bit        B_1S;        //秒中断, 秒有变化
bit        B_1ms;        //1ms标志
u16        OpTime;                // 此变量非0时, 不睡眠, 连续运行程序(本例串口唤醒后连续运行5秒, 以便正确接收串口数据)

u8        GetDataIndex;        //获取数据索引仅仅用于读取计数
u8        GetDataState;        //获取数据状态, 非0则错误

u8        ALARM_hour;                 // RTC闹钟的小时值
u8        ALARM_minute;                // RTC闹钟的分钟值
u8        ALARM_second;                // RTC闹钟的秒值
bit        B_ALARM;


/*************        本地函数声明        **************/
void        UART1_config(u32 brt, u8 timer, u8 io);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
void         UART1_TxByte(u8 dat);
void         UART1_PrintString(u8 *puts);
u8                SetRTC(void);                //设置RTC时间函数
void        RTC_config(void);        //RTC初始化函数
void        RTC_read(void);                //读RTC时间函数
void        ReturnRTC(void);        //返回时间信息
u8                GetData(void);                //获取数据, 设置时间命令: 21-6-19 12:34:56
void          delay_ms(u8 ms);
u8                Timer0_Config(u8 t, u32 reload);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.




//========================================================================
// 函数: void main(void)
// 描述: 主函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void main(void)
{
        P0M1 = 0;        P0M0 = 0;
        P1M1 = 0;        P1M0 = 0;
        P2M1 = 0;        P2M0 = 0;
        P3M1 = 0;        P3M0 = 0;
        P4M1 = 0;        P4M0 = 0;
        P5M1 = 0;        P5M0 = 0;
        P6M1 = 0;        P6M0 = 0;
        P7M1 = 0;        P7M0 = 0;

        UART1_config(115200UL, 1, 0);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
        Timer0_Config(0, MAIN_Fosc / 1000);        //t=0: reload值是主时钟周期数,  (中断频率, 1000次/秒)

        EA = 1;

        UART1_PrintString("STC8H RTC Test Prgramme!\r\n");

        OpTime    = 0;
        RTC_config();


        while (1)
        {
                if(B_1ms)        //1ms标志
                {
                        B_1ms = 0;

                        if(OpTime != 0)        OpTime--;        //连续操作时间

                        if(RX1_TimeOut != 0)
                        {
                                if(--RX1_TimeOut == 0)        //设置时间命令: 21-6-19 12:34:56
                                {
                                        if(RX1_Buffer[0] == 'A')        //设置闹铃
                                        {
                                                GetDataIndex = 1;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                ALARM_hour   = GetData();                //获取数据 时
                                                ALARM_minute = GetData();                //获取数据 分
                                                ALARM_second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        ALAHOUR = ALARM_hour;                 // RTC闹钟的小时值
                                                        ALAMIN  = ALARM_minute;                // RTC闹钟的分钟值
                                                        ALASEC  = ALARM_second;                // RTC闹钟的秒值
                                                        ALASSEC = 0;                                // RTC闹钟的1/128秒值
                                                        RTCIEN  |= 0x80;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
                                                        UART1_PrintString("已设置闹铃!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置闹铃数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        else        //设置时间
                                        {
                                                GetDataIndex = 0;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                year   = GetData();                //获取数据 年
                                                month  = GetData();                //获取数据 月
                                                day    = GetData();                //获取数据 日
                                                hour   = GetData();                //获取数据 时
                                                minute = GetData();                //获取数据 分
                                                second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        UART1_PrintString("获取设置时间: ");        ReturnRTC();
                                                        if(SetRTC() == 0)        UART1_PrintString("已设置时间!\r\n");
                                                        else                                UART1_PrintString("设置时间数据错误!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置时间数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        RX1_Cnt = 0;
                                }
                        }
                }

                if(B_ALARM)        //闹铃
                {
                        B_ALARM = 0;
                        UART1_PrintString("滴滴滴! 闹铃! \r\n");
                }

                if(B_1S)        //秒有变化
                {
                        B_1S = 0;
                        RTC_read();
                        ReturnRTC();        //从串口返回时间信息
                }

                if(OpTime == 0)                // OpTime==0才进入掉电模式
                {
                        IT0 = 1;                                //INT0 下降沿中断
                        IT1 = 1;                                //INT1 下降沿中断
                        IE0  = 0;                                //清除外中断0标志位
                        IE1  = 0;                                //清除外中断1标志位
                        AUXINTIF &= ~INT2IF;        //清除外中断2标志位
                        AUXINTIF &= ~INT3IF;        //清除外中断3标志位
                        AUXINTIF &= ~INT4IF;        //清除外中断4标志位
                        NOP(3);
                        EX0 = 1;                                //INT0 允许中断
                        EX1 = 1;                                //INT1 允许中断
                        INT_CLKO |= (1 << 4);        //INT2 允许中断
                        INT_CLKO |= (1 << 5);        //INT3 允许中断
                        INT_CLKO |= (1 << 6);        //INT4 允许中断
                        NOP(3);

                        PCON |= 0x02;        //睡眠
                        NOP(5);

                        EX0 = 0;                                //INT0 禁止中断        (按用户程序需要)
                        EX1 = 0;                                //INT1 禁止中断        (按用户程序需要)
                        INT_CLKO &= ~(1 << 4);        //INT2 禁止中断        (按用户程序需要)
                        INT_CLKO &= ~(1 << 5);        //INT3 禁止中断        (按用户程序需要)
                        INT_CLKO &= ~(1 << 6);        //INT4 禁止中断        (按用户程序需要)
                }
        }
}

/********************* INT0中断函数 *************************/
void INT0_ISR (void) interrupt 0
{
        P20 = ~P20;                        //取反指示引脚
}

/********************* INT1中断函数 *************************/
void INT1_ISR (void) interrupt 2
{
        P21 = ~P21;                        //取反指示引脚
}

/********************* INT2中断函数 *************************/
void INT2_ISR (void) interrupt 10
{
        P22 = ~P22;                        //取反指示引脚
}

/********************* INT3中断函数 *************************/
void INT3_ISR (void) interrupt 11
{
        P23 = ~P23;                        //取反指示引脚
}

/********************* INT4中断函数 *************************/
void INT4_ISR (void) interrupt 16
{
        P24 = ~P24;                        //取反指示引脚
}


/********************** 获取串口数据 *****************************/
u8        GetData(void)
{
        u8 i,j,k;

        if(GetDataIndex >= RX1_Cnt)
        {
                GetDataState = 1;
                return 0;
        }
//        if(RX1_Cnt < 11)        //数据长度错误 设置时间
        if(RX1_Cnt < 6)                //数据长度错误 设置时间 设置闹铃
        {
                GetDataState = 2;
                return 0;
        }

        i = 0;
        k = 0;
        while(GetDataIndex <= RX1_Cnt)                //设置时间命令: 21-6-19 12:34:56
        {
                j = RX1_Buffer[GetDataIndex];
                if((j == '-') || (j == ' ') || (j == ':') || (GetDataIndex == RX1_Cnt))        //数据段结束
                {
                        GetDataIndex++;
                        if(i == 0)        //数据错误
                        {
                                GetDataState = 3;
                                return 0;
                        }
                        else        return k;        //数据正确
                }
                if((j < '0') || (j > '9'))        //数据合法性检测
                {
                        GetDataIndex++;
                        GetDataState = 4;
                        return 0;
                }
                k = k * 10 + j - '0';
                i++;
                GetDataIndex++;
        }

        GetDataState = 9;
        return 10;        //数据错误
}

/********************** 串口返回时间信息 *****************************/
void        ReturnRTC(void)
{
        UART1_TxByte(year/10+'0');        //年
        UART1_TxByte(year%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(month/10+'0');        //月
        UART1_TxByte(month%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(day/10+'0');        //日
        UART1_TxByte(day%10+'0');
        UART1_TxByte(' ');
        UART1_TxByte(hour/10+'0');        //时
        UART1_TxByte(hour%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(minute/10+'0');        //分
        UART1_TxByte(minute%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(second/10+'0');        //秒
        UART1_TxByte(second%10+'0');
        UART1_TxByte(0x0d);
        UART1_TxByte(0x0a);
}


/********************** 设置RTC时间 *****************************/
u8        SetRTC(void)
{
        P_SW2 |= 0x80;                //SFR enable
        if(year > 99)                                                return 1;
        if((month == 0) || (month > 12))        return 2;
        if((day == 0) || (day > 31))                return 3;
        if(hour > 23)                                        return 4;
        if(minute > 59)                                        return 5;
        if(second > 59)                                        return 6;
        INIYEAR  = year;
        INIMONTH = month;
        INIDAY   = day;
        INIHOUR  = hour;
        INIMIN   = minute;
        INISEC   = second;
        INISSEC  = 0;
        RTCCFG   = 0x01 | 0x00;        //设置RTC时间, |0x00:选择外部32K时钟, |0x02:选择内部32K时钟.
        while(RTCCFG & 0x01);        //等待初始化完成. 设置RTC时间需要32768Hz的1个周期时间,大约30.5us. 由于同步, 所以实际等待时间是0~30.5us.
                                                        //如果不等待设置完成就睡眠, 则RTC会由于设置没完成, 停止计数, 唤醒后才继续完成设置并继续计数.
        return 0;
}

/********************** RTC配置函数 *****************************/
void        RTC_config(void)        //RTC初始化函数
{
        P_SW2  |= 0x80;                //SFR enable
        P1n_pure_input(0xc0);                //P1.6 P1.7设置为高阻输入
        P1IE = ~0xc0;                                //P1.6 P1.7关闭数字输入功能
        X32KCR = 0x80 + 0x40;                //启动外部32K晶振, 低增益+0x00, 高增益+0x40.

        year    = 21;
        month   = 6;
        day     = 18;
        hour    = 12;
        minute  = 0;
        second  = 0;

        RTCCR   = 0x01;        //使能RTC, 并开始RTC计数
        SetRTC();                //设置RTC时间
        RTCIF   = 0;        //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        RTCIEN  = 0x08;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        ALAHOUR = 8;        //闹钟小时
        ALAMIN  = 30;        //闹钟分钟
        ALASEC  = 0;        //闹钟秒
        ALASSEC = 0;        //闹钟1/128秒
}

/********************** 读取RTC时间信息 *****************************/
void        RTC_read(void)
{
        P_SW2  |= 0x80;                //SFR enable
        year    = RTC_YEAR;                // RTC的年计数值
        month   = RTC_MONTH;        // RTC的月计数值
        day     = RTC_DAY;                // RTC的日计数值
        hour    = RTC_HOUR;                // RTC的时计数值
        minute  = RTC_MIN;                // RTC的分计数值
        second  = RTC_SEC;                // RTC的秒计数值
//        ssecond = RTC_SSEC;                // RTC的1/128秒计数值
}



//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        SetTimer2Baudraye(u16 dat)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
        AUXR &= ~(1<<4);        //Timer stop
        AUXR &= ~(1<<3);        //Timer2 set As Timer
        AUXR |=  (1<<2);        //Timer2 set as 1T mode
        TH2 = (u8)(dat >> 8);
        TL2 = (u8)dat;
        IE2  &= ~(1<<2);        //禁止中断
        AUXR |=  (1<<4);        //Timer run enable
}


//========================================================================
// 函数: void        UART1_config(u32 brt, u8 timer, u8 io)
// 描述: UART1初始化函数。
// 参数:   brt: 通信波特率.
//       timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
//          io: 串口1切换到的IO,  io=1: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        UART1_config(u32 brt, u8 timer, u8 io)        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7.
{
        brt = 65536UL - (MAIN_Fosc / 4) / brt;
        if(timer == 2)        //波特率使用定时器2
        {
                AUXR |= 0x01;                //S1 BRT Use Timer2;
                SetTimer2Baudraye((u16)brt);
        }

        else                //波特率使用定时器1
        {
                TR1 = 0;
                AUXR &= ~0x01;                //S1 BRT Use Timer1;
                AUXR |=  (1<<6);        //Timer1 set as 1T mode
                TMOD &= ~(1<<6);        //Timer1 set As Timer
                TMOD &= ~0x30;                //Timer1_16bitAutoReload;
                TH1 = (u8)(brt >> 8);
                TL1 = (u8)brt;
                ET1 = 0;                        // 禁止Timer1中断
                INT_CLKO &= ~0x02;        // Timer1不输出高速时钟
                TR1  = 1;                        // 运行Timer1
        }

                 if(io == 1)        {S1_USE_P36P37();        P3n_standard(0xc0);}        //切换到 P3.6 P3.7
        else if(io == 2)        {S1_USE_P16P17();        P1n_standard(0xc0);}        //切换到 P1.6 P1.7
        else                                {S1_USE_P30P31();        P3n_standard(0x03);}        //切换到 P3.0 P3.1

        SCON = (SCON & 0x3f) | (1<<6);        // 8位数据, 1位起始位, 1位停止位, 无校验
//        PS  = 1;        //高优先级中断
        ES  = 1;        //允许中断
        REN = 1;        //允许接收
}


//========================================================================
// 函数: void UART1_TxByte(u8 dat)
// 描述: 串口1发送一个字节数据函数
// 参数: dat: 要发送的一个字节数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_TxByte(u8 dat)
{
        B_TX1_Busy = 1;                //标志发送忙
        SBUF = dat;                        //发一个字节
        while(B_TX1_Busy);        //等待发送完成
}

//========================================================================
// 函数: void UART1_PrintString(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_PrintString(u8 *puts)
{
    for (; *puts != 0;        puts++)        UART1_TxByte(*puts);
}


//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: 串口1中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_ISR (void) interrupt UART1_VECTOR
{
        if(RI)
        {
                RI = 0;
                if(RX1_Cnt >= RX1_Length)        RX1_Cnt = 0;
                RX1_Buffer[RX1_Cnt++] = SBUF;
                RX1_TimeOut = 5;
                OpTime = 5000;        //串口一旦唤醒, 接着5秒不睡眠, 以便正确接收数据. 唤醒帧将不能正确接收(因为唤醒需要计时us时间), 除非用很慢的速度(比如2400).
        }

        if(TI)
        {
                TI = 0;
                B_TX1_Busy = 0;
        }
}


//========================================================================
// 函数:u8        Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-3-5
//========================================================================
u8        Timer0_Config(u8 t, u32 reload)        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{
        TR0 = 0;        //停止计数

        if(t != 0)        reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);        //重装的是时间(us), 计算所需要的系统时钟数.
        if(reload >= (65536UL * 12))        return 1;        //值过大, 返回错误
        if(reload < 65536UL)        AUXR |= 0x80;                //1T mode
        else
        {
                AUXR &= ~0x80;        //12T mode
                reload = reload / 12;
        }
        reload = 65536UL - reload;
        TH0 = (u8)(reload >> 8);
        TL0 = (u8)(reload);

        ET0 = 1;        //允许中断
//        PT0 = 1;        //高优先级中断
        TMOD = (TMOD & ~0x03) | 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
        TR0 = 1;                        //开始运行
        return 0;
}

//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2016-5-12
//========================================================================
void timer0_ISR (void) interrupt TIMER0_VECTOR
{
   B_1ms = 1;        //1ms标志
}


//========================================================================
// 函数: AUXR_ISR(void) interrupt 13
// 描述: 扩展中断函数(中断号>=32的中断)
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        RTC_ISR(void) interrupt 36
{
        u8        i;
        i = RTCIF;
        RTCIF = 0;                //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        if(i & 0x08)        //秒中断
        {
                B_1S = 1;        //秒标志
        }
        if(i & 0x80)        //闹铃中断
        {
                B_ALARM = 1;        //闹铃标志
        }
}


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15613
发表于 2023-10-16 07:34:57 | 显示全部楼层
截图202310160734028919.jpg



/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU Demo --------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.stcai.com ------------------------------------------*/
/* --- BBS: www.stcaimcu.com ---------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/


/*********************************************************/
#define MAIN_Fosc                11059200L        //定义主时钟

#include        "..\..\STC8Hxxx.h"


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

请先别修改程序, 直接下载"rtc.hex"测试, 主频选择11.0592MHZ. 测试正常后再修改移植.
由于使用的中断号超过了31,所以KEIL编译器要拓展Keil的C代码中断号, 运行一下拓展Keil的C代码中断号.exe即可。

RTC每秒唤醒一次,通过串口返回日期时间。
上位机通过串口发送数据给MCU也会唤醒MCU,并且连续运行5秒。由于唤醒需要点时间(80~100us),所以当波特率大约2400时,唤醒后收到的数据通常会错误,再发一次即可正确接收。
上位机通过串口设置RTC日期时间和闹铃时间。
P0~P7所有IO中断测试程序.
P0.0~P0.7中断对应取反P1.0~P1.7输出指示。
P1.0~P1.7中断对应取反P0.0~P0.7输出指示。
P2.0~P2.7中断对应取反P4.0~P4.7输出指示。
P3.0~P3.7中断对应取反P4.0~P4.7输出指示。
P4.0~P4.7中断对应取反P2.0~P2.7输出指示。
P5.0~P5.7中断对应取反P4.0~P4.7输出指示。
P6.0~P6.7中断对应取反P7.0~P7.7输出指示。
P7.0~P7.7中断对应取反P6.0~P6.7输出指示。

本例子只设置了P4.0~P4.7的中断, 中断函数分别取反P2.0~P2.7来指示进入中断.
用户可以根据例子随意设置自己需要的IO中断.

MCU通过串口1返回: 年 月 日 时 分 秒:  21-6-19 12:34:56
通过串口发送设置值: 21-6-19 12:34:56
通过串口发送闹钟值: A12:34:56

默认参数:
串口1设置均为 1位起始位, 8位数据位, 1位停止位, 无校验.
串口1(P3.0 P3.1): 115200bps.

串口要发送数据2次(间隔4秒内), 第一次唤醒(唤醒需要时间, 第一次接收数据将会错误), 第二次数据才会正确接收.

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

/*************        本地常量声明        **************/
#define        RX1_Length        32                /* 接收缓冲长度 */


/*************        本地变量声明        **************/
u8        xdata        RX1_Buffer[RX1_Length];        //接收缓冲
u8        RX1_Cnt;                //串口接收计数.
u8        RX1_TimeOut;        //串口接收超时
bit        B_TX1_Busy;        // 发送忙标志

u8        year, month, day, hour, minute, second;        //RTC实时时间
bit        B_1S;        //秒中断, 秒有变化
bit        B_1ms;        //1ms标志
u16        OpTime;                // 此变量非0时, 不睡眠, 连续运行程序(本例串口唤醒后连续运行5秒, 以便正确接收串口数据)

u8        GetDataIndex;        //获取数据索引仅仅用于读取计数
u8        GetDataState;        //获取数据状态, 非0则错误

u8                ALARM_hour;                 // RTC闹钟的小时值
u8                ALARM_minute;                // RTC闹钟的分钟值
u8                ALARM_second;                // RTC闹钟的秒值
bit                B_ALARM;


/*************        本地函数声明        **************/
void        UART1_config(u32 brt, u8 timer, u8 io);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
void         UART1_TxByte(u8 dat);
void         UART1_PrintString(u8 *puts);
u8                SetRTC(void);                //设置RTC时间函数
void        RTC_config(void);        //RTC初始化函数
void        RTC_read(void);                //读RTC时间函数
void        ReturnRTC(void);        //返回时间信息
u8                GetData(void);                //获取数据, 设置时间命令: 21-6-19 12:34:56
void          delay_ms(u8 ms);
u8                Timer0_Config(u8 t, u32 reload);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.

void        GPIO_INT_Config(u8 gpio, u8 mode, u8 pin);        //普通IO中断配置函数, 中断模式(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.



//========================================================================
// 函数: void main(void)
// 描述: 主函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void main(void)
{
        P_SW2 = 0x80;        //允许访问扩展寄存器

        P0M1 = 0;        P0M0 = 0;
        P1M1 = 0;        P1M0 = 0;
        P2M1 = 0;        P2M0 = 0;
        P3M1 = 0;        P3M0 = 0;
        P4M1 = 0;        P4M0 = 0;
        P5M1 = 0;        P5M0 = 0;
        P6M1 = 0;        P6M0 = 0;
        P7M1 = 0;        P7M0 = 0;

        UART1_config(115200UL, 1, 0);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
        Timer0_Config(0, MAIN_Fosc / 1000);        //t=0: reload值是主时钟周期数,  (中断频率, 1000次/秒)

        GPIO_INT_Config(GPIO_P4, INT_FALL, Pin0 | Pin1 | Pin2 | Pin3);        //普通IO中断配置函数, 中断模式(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.
        GPIO_INT_Config(GPIO_P4, INT_RISE, Pin4 | Pin5 | Pin6 | Pin7);        //普通IO中断配置函数, 中断模式(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.
        P4WKUE = 0xff;        //允许所有P4口中断唤醒.

        EA = 1;

        UART1_PrintString("STC8H RTC Test Prgramme!\r\n");

        OpTime    = 0;
        RTC_config();


        while (1)
        {
                if(B_1ms)
                {
                        B_1ms = 0;

                        if(OpTime != 0)        OpTime--;        //连续运行时间

                        if(RX1_TimeOut != 0)
                        {
                                if(--RX1_TimeOut == 0)        //设置时间命令: 21-6-19 12:34:56
                                {
                                        if(RX1_Buffer[0] == 'A')        //设置闹铃
                                        {
                                                GetDataIndex = 1;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                ALARM_hour   = GetData();                //获取数据 时
                                                ALARM_minute = GetData();                //获取数据 分
                                                ALARM_second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        ALAHOUR = ALARM_hour;                 // RTC闹钟的小时值
                                                        ALAMIN  = ALARM_minute;                // RTC闹钟的分钟值
                                                        ALASEC  = ALARM_second;                // RTC闹钟的秒值
                                                        ALASSEC = 0;                                // RTC闹钟的1/128秒值
                                                        RTCIEN  |= 0x80;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
                                                        UART1_PrintString("已设置闹铃!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置闹铃数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        else        //设置时间
                                        {
                                                GetDataIndex = 0;        //获取数据索引仅仅用于读取计数
                                                GetDataState = 0;        //获取数据状态, 非0则错误
                                                year   = GetData();                //获取数据 年
                                                month  = GetData();                //获取数据 月
                                                day    = GetData();                //获取数据 日
                                                hour   = GetData();                //获取数据 时
                                                minute = GetData();                //获取数据 分
                                                second = GetData();                //获取数据 秒
                                                if(GetDataState == 0)        //获取数据状态, 为0则正确, 非0则错误
                                                {
                                                        UART1_PrintString("获取设置时间: ");        ReturnRTC();
                                                        if(SetRTC() == 0)        UART1_PrintString("已设置时间!\r\n");
                                                        else                                UART1_PrintString("设置时间数据错误!\r\n");
                                                }
                                                else
                                                {
                                                        UART1_PrintString("设置时间数据错误!\r\n");
                                                        UART1_PrintString("错误代码: ");
                                                        UART1_TxByte(GetDataState+'0');
                                                        UART1_PrintString("\r\n");
                                                }
                                        }
                                        RX1_Cnt = 0;
                                }
                        }
                }

                if(B_ALARM)                //闹铃
                {
                        B_ALARM = 0;
                        UART1_PrintString("滴滴滴! 闹铃! \r\n");
                }

                if(B_1S)        //秒有变化
                {
                        B_1S = 0;
                        RTC_read();
                        ReturnRTC();                                //从串口返回时间信息
                }

                if(OpTime == 0)                // OpTime==0才进入掉电模式
                {
                        AUXINTIF &= ~INT4IF;        //清除外中断4标志位
                        NOP(3);
                        INT_CLKO |= (1 << 6);        //INT4 允许中断, 唤醒配合串口接收。
                        NOP(3);

                        PCON |= 0x02;        //睡眠
                        NOP(5);

                        INT_CLKO &= ~(1 << 6);        //INT4 禁止中断        (按用户程序需要)
                }
        }
}

/********************* INT4中断函数 *************************/
void INT4_ISR (void) interrupt 16
{
        OpTime = 5000;        //取反指示引脚
}


/********************** 获取串口数据 *****************************/
u8        GetData(void)
{
        u8 i,j,k;

        if(GetDataIndex >= RX1_Cnt)
        {
                GetDataState = 1;
                return 0;
        }
//        if(RX1_Cnt < 11)        //数据长度错误 设置时间
        if(RX1_Cnt < 6)                //数据长度错误 设置时间 设置闹铃
        {
                GetDataState = 2;
                return 0;
        }

        i = 0;
        k = 0;
        while(GetDataIndex <= RX1_Cnt)                //设置时间命令: 21-6-19 12:34:56
        {
                j = RX1_Buffer[GetDataIndex];
                if((j == '-') || (j == ' ') || (j == ':') || (GetDataIndex == RX1_Cnt))        //数据段结束
                {
                        GetDataIndex++;
                        if(i == 0)        //数据错误
                        {
                                GetDataState = 3;
                                return 0;
                        }
                        else        return k;        //数据正确
                }
                if((j < '0') || (j > '9'))        //数据合法性检测
                {
                        GetDataIndex++;
                        GetDataState = 4;
                        return 0;
                }
                k = k * 10 + j - '0';
                i++;
                GetDataIndex++;
        }

        GetDataState = 9;
        return 10;        //数据错误
}

/********************** 串口返回时间信息 *****************************/
void        ReturnRTC(void)
{
        UART1_TxByte(year/10+'0');        //年
        UART1_TxByte(year%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(month/10+'0');        //月
        UART1_TxByte(month%10+'0');
        UART1_TxByte('-');
        UART1_TxByte(day/10+'0');        //日
        UART1_TxByte(day%10+'0');
        UART1_TxByte(' ');
        UART1_TxByte(hour/10+'0');        //时
        UART1_TxByte(hour%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(minute/10+'0');        //分
        UART1_TxByte(minute%10+'0');
        UART1_TxByte(':');
        UART1_TxByte(second/10+'0');        //秒
        UART1_TxByte(second%10+'0');
        UART1_TxByte(0x0d);
        UART1_TxByte(0x0a);
}


/********************** 设置RTC时间 *****************************/
u8        SetRTC(void)
{
        P_SW2 |= 0x80;                //SFR enable
        if(year > 99)                                                return 1;
        if((month == 0) || (month > 12))        return 2;
        if((day == 0) || (day > 31))                return 3;
        if(hour > 23)                                        return 4;
        if(minute > 59)                                        return 5;
        if(second > 59)                                        return 6;
        INIYEAR  = year;
        INIMONTH = month;
        INIDAY   = day;
        INIHOUR  = hour;
        INIMIN   = minute;
        INISEC   = second;
        INISSEC  = 0;
        RTCCFG   = 0x01 | 0x00;        //设置RTC时间, |0x00:选择外部32K时钟, |0x02:选择内部32K时钟.
        while(RTCCFG & 0x01);        //等待初始化完成. 设置RTC时间需要32768Hz的1个周期时间,大约30.5us. 由于同步, 所以实际等待时间是0~30.5us.
                                                        //如果不等待设置完成就睡眠, 则RTC会由于设置没完成, 停止计数, 唤醒后才继续完成设置并继续计数.
        return 0;
}

/********************** RTC配置函数 *****************************/
void        RTC_config(void)        //RTC初始化函数
{
        P_SW2  |= 0x80;                //SFR enable
        P1n_pure_input(0xc0);                //P1.6 P1.7设置为高阻输入
        P1IE = ~0xc0;                                //P1.6 P1.7关闭数字输入功能
        X32KCR = 0x80 + 0x40;                //启动外部32K晶振, 低增益+0x00, 高增益+0x40.

        year    = 21;
        month   = 6;
        day     = 18;
        hour    = 12;
        minute  = 0;
        second  = 0;

        RTCCR   = 0x01;        //使能RTC, 并开始RTC计数
        SetRTC();                //设置RTC时间
        RTCIF   = 0;        //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
//        RTCIEN  = 0x08;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        RTCIEN  = 0x10;        //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
        ALAHOUR = 8;        //闹钟小时
        ALAMIN  = 30;        //闹钟分钟
        ALASEC  = 0;        //闹钟秒
        ALASSEC = 0;        //闹钟1/128秒
}

/********************** 读取RTC时间信息 *****************************/
void        RTC_read(void)
{
        P_SW2  |= 0x80;                //SFR enable
        year    = RTC_YEAR;                // RTC的年计数值
        month   = RTC_MONTH;        // RTC的月计数值
        day     = RTC_DAY;                // RTC的日计数值
        hour    = RTC_HOUR;                // RTC的时计数值
        minute  = RTC_MIN;                // RTC的分计数值
        second  = RTC_SEC;                // RTC的秒计数值
//        ssecond = RTC_SSEC;                // RTC的1/128秒计数值
}



//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        SetTimer2Baudraye(u16 dat)        // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
        AUXR &= ~(1<<4);        //Timer stop
        AUXR &= ~(1<<3);        //Timer2 set As Timer
        AUXR |=  (1<<2);        //Timer2 set as 1T mode
        TH2 = (u8)(dat >> 8);
        TL2 = (u8)dat;
        IE2  &= ~(1<<2);        //禁止中断
        AUXR |=  (1<<4);        //Timer run enable
}


//========================================================================
// 函数: void        UART1_config(u32 brt, u8 timer, u8 io)
// 描述: UART1初始化函数。
// 参数:   brt: 通信波特率.
//       timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
//          io: 串口1切换到的IO,  io=1: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        UART1_config(u32 brt, u8 timer, u8 io)        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7.
{
        brt = 65536UL - (MAIN_Fosc / 4) / brt;
        if(timer == 2)        //波特率使用定时器2
        {
                AUXR |= 0x01;                //S1 BRT Use Timer2;
                SetTimer2Baudraye((u16)brt);
        }

        else                //波特率使用定时器1
        {
                TR1 = 0;
                AUXR &= ~0x01;                //S1 BRT Use Timer1;
                AUXR |=  (1<<6);        //Timer1 set as 1T mode
                TMOD &= ~(1<<6);        //Timer1 set As Timer
                TMOD &= ~0x30;                //Timer1_16bitAutoReload;
                TH1 = (u8)(brt >> 8);
                TL1 = (u8)brt;
                ET1 = 0;                        // 禁止Timer1中断
                INT_CLKO &= ~0x02;        // Timer1不输出高速时钟
                TR1  = 1;                        // 运行Timer1
        }

                 if(io == 1)        {S1_USE_P36P37();        P3n_standard(0xc0);}        //切换到 P3.6 P3.7
        else if(io == 2)        {S1_USE_P16P17();        P1n_standard(0xc0);}        //切换到 P1.6 P1.7
        else                                {S1_USE_P30P31();        P3n_standard(0x03);}        //切换到 P3.0 P3.1

        SCON = (SCON & 0x3f) | (1<<6);        // 8位数据, 1位起始位, 1位停止位, 无校验
//        PS  = 1;        //高优先级中断
        ES  = 1;        //允许中断
        REN = 1;        //允许接收
}


//========================================================================
// 函数: void UART1_TxByte(u8 dat)
// 描述: 串口1发送一个字节数据函数
// 参数: dat: 要发送的一个字节数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_TxByte(u8 dat)
{
        B_TX1_Busy = 1;                //标志发送忙
        SBUF = dat;                        //发一个字节
        while(B_TX1_Busy);        //等待发送完成
}

//========================================================================
// 函数: void UART1_PrintString(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_PrintString(u8 *puts)
{
    for (; *puts != 0;        puts++)        UART1_TxByte(*puts);
}


//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: 串口1中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_ISR (void) interrupt UART1_VECTOR
{
        if(RI)
        {
                RI = 0;
                if(RX1_Cnt >= RX1_Length)        RX1_Cnt = 0;
                RX1_Buffer[RX1_Cnt++] = SBUF;
                RX1_TimeOut = 5;
                OpTime = 5000;        //串口一旦唤醒, 接着5秒不睡眠, 以便正确接收数据. 唤醒帧将不能正确接收(因为唤醒需要计时us时间), 除非用很慢的速度(比如2400).
        }

        if(TI)
        {
                TI = 0;
                B_TX1_Busy = 0;
        }
}


//========================================================================
// 函数:u8        Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-3-5
//========================================================================
u8        Timer0_Config(u8 t, u32 reload)        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{
        TR0 = 0;        //停止计数

        if(t != 0)        reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);        //重装的是时间(us), 计算所需要的系统时钟数.
        if(reload >= (65536UL * 12))        return 1;        //值过大, 返回错误
        if(reload < 65536UL)        AUXR |= 0x80;                //1T mode
        else
        {
                AUXR &= ~0x80;        //12T mode
                reload = reload / 12;
        }
        reload = 65536UL - reload;
        TH0 = (u8)(reload >> 8);
        TL0 = (u8)(reload);

        ET0 = 1;        //允许中断
//        PT0 = 1;        //高优先级中断
        TMOD = (TMOD & ~0x03) | 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
        TR0 = 1;                        //开始运行
        return 0;
}

//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2016-5-12
//========================================================================
void timer0_ISR (void) interrupt TIMER0_VECTOR
{
   B_1ms = 1;        //1ms标志
}


//========================================================================
// 函数: AUXR_ISR(void) interrupt 13
// 描述: 扩展中断函数(中断号>=32的中断)
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void        RTC_ISR(void) interrupt 36
{
        u8        i;
        i = RTCIF;
        RTCIF = 0;                //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
//        if(i & 0x08)        //秒中断
        if(i & 0x10)        //分中断
        {
                B_1S = 1;        //秒标志
        }
        if(i & 0x80)        //闹铃中断
        {
                B_ALARM = 1;        //闹铃标志
        }
}


//========================================================================
// 函数: void        GPIO_INT_Config(u8 gpio, u8 pin, u8 mode)
// 描述: IO中断初始化函数.
// 参数: gpio: 要初始化的IO组, 参数取值(只取其一): GPIO_P0, GPIO_P1, GPIO_P2, GPIO_P3, GPIO_P4, GPIO_P5, GPIO_P6, GPIO_P7, GPIO_P8, GPIO_P9.
//       mode: 中断模式,       参数取值(只取其一): INT_FALL, INT_RISE, INT_LOW, INT_HIGH. 分别是 上升沿 下降沿 低电平  高电平 中断.
//       pin:  要初始化的引脚, 参数取值(多个值时用或操作): Pin0, Pin1, Pin2, Pin3, Pin4, Pin5, Pin6, Pin7. PinAll.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        GPIO_INT_Config(u8 gpio, u8 mode, u8 pin)
{
        u8        xdata        *pt;
        u8        j;

        if(gpio >= 10)        return;        //IO口错误
        if(mode >= 4)        return;        //模式错误
        P_SW2 |= 0x80;                        //访问XFR

        pt  = 0xfd00 + gpio;
        *pt |= pin;                                //允许IO中断

        pt  = 0xfd30 + gpio;
        if(mode & 0x02)        j = 0xff & pin;        //设置模式
        else                        j = 0;
        *pt = (*pt & ~pin) | j;
        pt  = 0xfd20 + gpio;
        if(mode & 0x01)        j = 0xff & pin;
        else                        j = 0;
        *pt = (*pt & ~pin) | j;
}



//========================================================================
// 函数: void        P0INT_ISR(void) interrupt 37
// 描述: P0(P0.0~P0.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P0INT_ISR(void) interrupt 37        //P0.0~P0.7 IO中断处理代码
{
        u8        i;
        i = P0INTF;                //读取中断标志
        P0INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P0.0中断
        {
                P10 = ~P10;                        //取反P1.0指示
        }
        if((i & INTF_1) != 0)        //P0.1中断
        {
                P11 = ~P11;                        //取反P1.1指示
        }
        if((i & INTF_2) != 0)        //P0.2中断
        {
                P12 = ~P12;                        //取反P1.2指示
        }
        if((i & INTF_3) != 0)        //P0.3中断
        {
                P13 = ~P13;                        //取反P1.3指示
        }
        if((i & INTF_4) != 0)        //P0.4中断
        {
                P14 = ~P14;                        //取反P1.4指示
        }
        if((i & INTF_5) != 0)        //P0.5中断
        {
                P15 = ~P15;                        //取反P1.5指示
        }
        if((i & INTF_6) != 0)        //P0.6中断
        {
                P16 = ~P16;                        //取反P1.6指示
        }
        if((i & INTF_7) != 0)        //P0.7中断
        {
                P17 = ~P17;                        //取反P1.7指示
        }
}

//========================================================================
// 函数: void        P1INT_ISR(void) interrupt 38
// 描述: P1(P1.0~P1.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P1INT_ISR(void) interrupt 38                //P1.0~P1.7 IO中断处理代码
{
        u8        i;
        i = P1INTF;                //读取中断标志
        P1INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P1.0中断
        {
                P00 = ~P00;                        //取反P0.0指示
        }
        if((i & INTF_1) != 0)        //P1.1中断
        {
                P01 = ~P01;                        //取反P0.1指示
        }
        if((i & INTF_2) != 0)        //P1.2中断
        {
                P02 = ~P02;                        //取反P0.2指示
        }
        if((i & INTF_3) != 0)        //P1.3中断
        {
                P03 = ~P03;                        //取反P0.3指示
        }
        if((i & INTF_4) != 0)        //P1.4中断
        {
                P04 = ~P04;                        //取反P0.4指示
        }
        if((i & INTF_5) != 0)        //P1.5中断
        {
                P05 = ~P05;                        //取反P0.5指示
        }
        if((i & INTF_6) != 0)        //P1.6中断
        {
                P06 = ~P06;                        //取反P0.6指示
        }
        if((i & INTF_7) != 0)        //P1.7中断
        {
                P07 = ~P07;                        //取反P0.7指示
        }
}

//========================================================================
// 函数: void        P2INT_ISR(void) interrupt 39
// 描述: P2(P2.0~P2.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P2INT_ISR(void) interrupt 39                //P2.0~P2.7 IO中断处理代码
{
        u8        i;
        i = P2INTF;                //读取中断标志
        P2INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P2.0中断
        {
                P40 = ~P40;                        //取反P4.0指示
        }
        if((i & INTF_1) != 0)        //P2.1中断
        {
                P41 = ~P41;                        //取反P4.1指示
        }
        if((i & INTF_2) != 0)        //P2.2中断
        {
                P42 = ~P42;                        //取反P4.2指示
        }
        if((i & INTF_3) != 0)        //P2.3中断
        {
                P43 = ~P43;                        //取反P4.3指示
        }
        if((i & INTF_4) != 0)        //P2.4中断
        {
                P44 = ~P44;                        //取反P4.4指示
        }
        if((i & INTF_5) != 0)        //P2.5中断
        {
                P45 = ~P45;                        //取反P4.5指示
        }
        if((i & INTF_6) != 0)        //P2.6中断
        {
                P46 = ~P46;                        //取反P4.6指示
        }
        if((i & INTF_7) != 0)        //P2.7中断
        {
                P47 = ~P47;                        //取反P4.7指示
        }
}

//========================================================================
// 函数: void        P3INT_ISR(void) interrupt 40
// 描述: P3(P3.0~P3.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P3INT_ISR(void) interrupt 40                //P3.0~P3.7 IO中断处理代码
{
        u8        i;
        i = P3INTF;                //读取中断标志
        P3INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P3.0中断
        {
                P40 = ~P40;                        //取反P4.0指示
        }
        if((i & INTF_1) != 0)        //P3.1中断
        {
                P41 = ~P41;                        //取反P4.1指示
        }
        if((i & INTF_2) != 0)        //P3.2中断
        {
                P42 = ~P42;                        //取反P4.2指示
        }
        if((i & INTF_3) != 0)        //P3.3中断
        {
                P43 = ~P43;                        //取反P4.3指示
        }
        if((i & INTF_4) != 0)        //P3.4中断
        {
                P44 = ~P44;                        //取反P4.4指示
        }
        if((i & INTF_5) != 0)        //P3.5中断
        {
                P45 = ~P45;                        //取反P4.5指示
        }
        if((i & INTF_6) != 0)        //P3.6中断
        {
                P46 = ~P46;                        //取反P4.6指示
        }
        if((i & INTF_7) != 0)        //P3.7中断
        {
                P47 = ~P47;                        //取反P4.7指示
        }
}

//========================================================================
// 函数: void        P4INT_ISR(void) interrupt 41
// 描述: P4(P4.0~P4.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P4INT_ISR(void) interrupt 41                //P4.0~P4.7 IO中断处理代码
{
        u8        i;
        i = P4INTF;                //读取中断标志
        P4INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P4.0中断
        {
                P20 = ~P20;                        //取反P2.0指示
        }
        if((i & INTF_1) != 0)        //P4.1中断
        {
                P21 = ~P21;                        //取反P2.1指示
        }
        if((i & INTF_2) != 0)        //P4.2中断
        {
                P22 = ~P22;                        //取反P2.2指示
        }
        if((i & INTF_3) != 0)        //P4.3中断
        {
                P23 = ~P23;                        //取反P2.3指示
        }
        if((i & INTF_4) != 0)        //P4.4中断
        {
                P24 = ~P24;                        //取反P2.4指示
        }
        if((i & INTF_5) != 0)        //P4.5中断
        {
                P25 = ~P25;                        //取反P2.5指示
        }
        if((i & INTF_6) != 0)        //P4.6中断
        {
                P26 = ~P26;                        //取反P2.6指示
        }
        if((i & INTF_7) != 0)        //P4.7中断
        {
                P27 = ~P27;                        //取反P2.7指示
        }
}

//========================================================================
// 函数: void        P5INT_ISR(void) interrupt 42
// 描述: P5(P5.0~P5.4)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P5INT_ISR(void) interrupt 42                //P5.0~P5.4 IO中断处理代码
{
        u8        i;
        i = P5INTF;                //读取中断标志
        P5INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P5.0中断
        {
                P40 = ~P40;                        //取反P4.0指示
        }
        if((i & INTF_1) != 0)        //P5.1中断
        {
                P41 = ~P41;                        //取反P4.1指示
        }
        if((i & INTF_2) != 0)        //P5.2中断
        {
                P42 = ~P42;                        //取反P4.2指示
        }
        if((i & INTF_3) != 0)        //P5.3中断
        {
                P43 = ~P43;                        //取反P4.3指示
        }
        if((i & INTF_4) != 0)        //P5.4中断
        {
                P44 = ~P44;                        //取反P4.4指示
        }
        if((i & INTF_5) != 0)        //P5.5中断
        {
                P45 = ~P45;                        //取反P4.5指示
        }
}

//========================================================================
// 函数: void        P6INT_ISR(void) interrupt 43
// 描述: P6(P6.0~P6.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P6INT_ISR(void) interrupt 43                //P6.0~P6.7 IO中断处理代码
{
        u8        i;
        i = P6INTF;                //读取中断标志
        P6INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P6.0中断
        {
                P70 = ~P70;                        //取反P7.0指示
        }
        if((i & INTF_1) != 0)        //P6.1中断
        {
                P71 = ~P71;                        //取反P7.1指示
        }
        if((i & INTF_2) != 0)        //P6.2中断
        {
                P72 = ~P72;                        //取反P7.2指示
        }
        if((i & INTF_3) != 0)        //P6.3中断
        {
                P73 = ~P73;                        //取反P7.3指示
        }
        if((i & INTF_4) != 0)        //P6.4中断
        {
                P74 = ~P74;                        //取反P7.4指示
        }
        if((i & INTF_5) != 0)        //P6.5中断
        {
                P75 = ~P75;                        //取反P7.5指示
        }
        if((i & INTF_6) != 0)        //P6.6中断
        {
                P76 = ~P76;                        //取反P7.6指示
        }
        if((i & INTF_7) != 0)        //P6.7中断
        {
                P77 = ~P77;                        //取反P7.7指示
        }
}

//========================================================================
// 函数: void        P7INT_ISR(void) interrupt 44
// 描述: P0(P7.0~P7.7)中断处理函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2020-5-20
// 备注:
//========================================================================
void        P7INT_ISR(void) interrupt 44                //P7.0~P7.7 IO中断处理代码
{
        u8        i;
        i = P7INTF;                //读取中断标志
        P7INTF &= ~i;        //清除中断标志

        if((i & INTF_0) != 0)        //P7.0中断
        {
                P60 = ~P60;                        //取反P6.0指示
        }
        if((i & INTF_1) != 0)        //P7.1中断
        {
                P61 = ~P61;                        //取反P6.1指示
        }
        if((i & INTF_2) != 0)        //P7.2中断
        {
                P62 = ~P62;                        //取反P6.2指示
        }
        if((i & INTF_3) != 0)        //P7.3中断
        {
                P63 = ~P63;                        //取反P6.3指示
        }
        if((i & INTF_4) != 0)        //P7.4中断
        {
                P64 = ~P64;                        //取反P6.4指示
        }
        if((i & INTF_5) != 0)        //P7.5中断
        {
                P65 = ~P65;                        //取反P6.5指示
        }
        if((i & INTF_6) != 0)        //P7.6中断
        {
                P66 = ~P66;                        //取反P6.6指示
        }
        if((i & INTF_7) != 0)        //P7.7中断
        {
                P67 = ~P67;                        //取反P6.7指示
        }
}


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:9
  • 最近打卡:2024-12-04 16:35:53

0

主题

9

回帖

80

积分

注册会员

积分
80
发表于 2023-11-28 00:57:07 | 显示全部楼层
谢谢分享
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 02:22 , Processed in 0.197819 second(s), 114 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表