基于STC8G芯片的时钟显示系统

基于STC8G芯片的时钟显示系统

一、电路原理图设计

1.1EDA__立创EDA

1.1.1 主要器件BOM–仅供参考
IDNameFootprintSupplierPrice
1STC8G2K64S4LQFP-32C915671¥8
211.0592MHZHC-49USC358645¥0.7869
3LEDLED-SMDC375449¥0.1071
4KEYKEY-TH_4PC2762975¥0.6092
5AMS1117-3.3SOT-223C6186¥0.9204
6电容C0603
7电阻R0603
8MAX7129网络购买数码管¥3.42
9DHT11网络购买温湿度¥4.00
10OLED 0.91网络购买OLED¥7.40
11HC05网络购买蓝牙¥18
12BC95网络购买NB¥89
13DC电源插头插座网络购买电源¥0.4
1.1.2主要电路原理图设计
①电源原理图

U8—DC直流电源插头插座: 用于5V适配器供电;
网络购买封装自己添加
在这里插入图片描述

U4–AMS1117-3.3 :用于5V转3.3V;
在这里插入图片描述

② 数码管原理图

U10/U3—MAX7129: 用于显示数字;
网络购买封装自己添加在这里插入图片描述
实物参考
在这里插入图片描述
两个MAX7129电路设计为串联
在这里插入图片描述

③ STC8G原理图

STC8G主芯片网络标号连接
在这里插入图片描述

④ 下载口原理图

串口 1引出:用于下载
在这里插入图片描述
下载驱动
在这里插入图片描述

⑤ 按键原理图

在这里插入图片描述

⑥ 无线通信模块原理图

无线通信模块–兼容两种无线通信
NB模块-BC-95
4G模块-WH-LTE-7S1
封装自己按照尺寸画的;
在这里插入图片描述
WH-LTE-7S1介绍
在这里插入图片描述
WH-LTE-7S1_hareware_V1.0.2文档获取以下信息
①供电----DC5-16V

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
②串口通信----TTL
在这里插入图片描述
在这里插入图片描述
③模块复位----RESET
在这里插入图片描述(2)串口调试助手测试
测试工具在这里插入图片描述
在这里插入图片描述
查看USR-Cat-1_AT_ V1.0.5文档
按照AT指令的标准进行“问”与“答”

⑥ 辅助模块原理图

电源指示灯 --用于电源指示
程序状态灯 --用于程先生状态显示
晶振电路 --用于时钟系统
复位电路 --用于复位
在这里插入图片描述

⑥ 外设模块原理图

串口3 ----用于蓝牙模块透传
DHT11 ----用于温湿度获取
OLED ----用于数据显示在这里插入图片描述

1.2 PCB

1.2.1顶层

在这里插入图片描述

1.2.2底层

在这里插入图片描述

1.2.3 2D

在这里插入图片描述

1.2.4 3D

在这里插入图片描述

二、程序设计

2.1 主函数程序

// 主逻辑函数 默认上电日期2023-03-17- 08:00:00
   系统自加时间;
   系统记忆亮度等级为5;
struct ctm {
    char issync;
    char isleap;
    char tm_year;  /* years since 1900 */
    char tm_mon;   /* months since January, 0 to 11 */
    char tm_mday;  /* day of the month, 1 to 31 */
    char tm_hour;  /* hours since midnight, 0 to 23 */
    char tm_min;   /* minutes after the hour, 0 to 59 */
    char tm_sec;   /* seconds after the minute, 0 to 60*/
};
struct ctm c_lct;

#define JAN_2014_1970    	0x52c35a80  /* 3187296000 2001 - 1900 in seconds */
//初始化时间
void Init_value(void)
{
    t_utc=JAN_2014_1970;//计算日期(设备上电请求时间)必须大于等于2003年,否则NTP计算将会溢出(long类型)

    c_lct.tm_sec=0;
    c_lct.tm_min=0;
    c_lct.tm_hour=08;
    c_lct.tm_mday=17;
    c_lct.tm_mon=03;
    c_lct.tm_year=23;            /* years since 2000 */   
}
// 主程序 ---------------------
void main()
{
    Init_value();
    Init_MSP();  
	ReadCfg();	
    EA_On();                 //使能总中断
	Init_Cat();
    SendSyncEn=P21;
	SetWriteFlag(0);
    while (1)
    {
        Wdt_fresh(); //清看门狗,否则系统复位
        Task_WDT();  //清外部看门狗813,否则系统复位
		Task_Max7219(TASKS_LED);
#if defined(CAT)				
		Task_Cat(TASKS_CAT);  
#elif defined(NB)
		Task_Cat(TASKS_NB);  
#endif					
    }
}

2.2 系统初始化程序

// 系统初始化函数
   初始化时钟
   初始化IO
   初始化串口
   初始化看门狗
   初始化定时器0     
   初始化定时器2
void IO_Init(void)
{
    P0M1 = 0;
	P0M0 = 0;      //设置为准双向口,M1M0:00=准双向口,01=推挽,10=高阻,11=开漏
    P1M1 = 0;
    P1M0 = 0;      //设置为准双向口,M1M0:00=准双向口,01=推挽,10=高阻,11=开漏
    P2M1 = 0;
    P2M0 = 0;      //设置为准双向口,M1M0:00=准双向口,01=推挽,10=高阻,11=开漏
    P3M1 = 0;
    P3M0 = 0;      //设置为准双向口,M1M0:00=准双向口,01=推挽,10=高阻,11=开漏
	P1M0 |= 0x10;  //设置P14为推挽输出	
	P2M0 |= 0x0E;  //设置P21,P22,P23为推挽输出
}
void EA_On(void)
{
    EA = 1;       //使能总中断
}

void Init_MSP(void)
{
    SystemClock_Config();      //初始化时钟
    IO_Init();
    Wdt_Config();              //初始化看门狗
    TM0_Init(3);               //初始化定时器0,频率200HZ,优先级3。
    TM2_Baud_Init();           //初始化定时器2做串口1,2,3,4通用波特率发生器,优先级固定为最低优先级0
     
#ifdef USE_UART1	
    UART1_Init(0,UART1_BPR);   //初始化串口1
#endif	
#ifdef USE_UART2	
    UART2_Init();              //初始化串口2
#endif	
#ifdef USE_UART3	
    UART3_Init(1, UART3_BPR);  //初始化串口3
#endif	
#ifdef USE_UART4	
    UART4_Init(1, UART4_BPR);  //初始化串口4,
#endif	

}

2.3 串口驱动程序

// 串口1/2/3/4驱动函数
#ifdef USE_UART1
void UART1_Init(unsigned char mode, unsigned int baud) //串口1、2可设优先级,串口3、4固定为最低优先级0,mode=0,使用默认定时器波特率
{
    SCON = 0x50;                                                        //串口1控制寄存器SCON(可位访问)选用串口方式1(可变8位,0x40)    ,允许串口接收数据(0x10)
    PCON &= (~(SMOD | SMOD0));                              //电源管理寄存器PCON(不可位访问),串口1波特率不加倍,无帧错检测功能
    AUXR &= (~(UART_M0x6 | S1ST2));                 //辅助寄存器1 AUXR(不可位访问),串口1方式0波特率不加6倍,定时器1做串口1波特率发生器
    if(mode > 0) //定时器1做串口1波特率发生器   //串口1可用定时器2和定时器1做波特率发生器
    {
        TMOD &= 0x0f;              //定时器0/1模式寄存器TMOD(不可位访问),定时器1,模式0(16位自动重载模式)

        AUXR |= T1x12;                //辅助寄存器1 AUXR(不可位访问),定时器1以系统不分频
        INTCLKO &= (~T1CLKO);                //中断与时钟输出控制寄存器INTCLKO(不可位访问),关闭定时器1时钟输出
        TH1 = (-SYSCLK / baud / 4) >> 8;
        TL1 = (-SYSCLK / baud / 4);             //65536-11059200/115200/4=0FFE8H
        TR1 = 1;                   //定时器0/1、外部中断0/1控制寄存器TCON(可位访问),启动定时器1
    }
    else
    {
        AUXR |= S1ST2;         //辅助寄存器1 AUXR(不可位访问),定时器2做串口1波特率发生器
    }

    ES = 1;      //中断使能寄存器IE(可位访问) 使能串口1中断
}
void send_uart1( unsigned char *_ucaBuf, int _usLen)
{
	if(_usLen>0)
	{
		tx_num1=_usLen;
		tx_cnt1=1;
		pMsg1= _ucaBuf;
		SBUF = *pMsg1++;
	}
}
int get_uart1( unsigned char *_ucaBuf)
{
		int temp = 0;  
		if(rx_t1>=UOUT_TIME)
		{
			if(rx_cnt1>0)
			{
				memcpy (_ucaBuf, rxBuf1, rx_cnt1);
				temp=rx_cnt1;
				rx_cnt1=0;//重新开始接收,接收完置位,表明停止接收了
			}
		}	
		return temp;
}

#endif

#ifdef USE_UART2
void UART2_Init(void)//串口1、2可设优先级,串口3、4固定为最低优先级0,mode=0,使用默认定时2器波特率
{
    //串口2只能用定时器2作为波特率发生器
    S2CON = S2REN;          //串口2控制寄存器S2CON(不可位访问),选用串口方式0(可变8位)    ,允许串口接收数据(0x10) ,
    IE2 |= ES2;            //中断使能寄存器2 IE2(不可位访问)使能串口2中断
}
void send_uart2( unsigned char *_ucaBuf, int _usLen)
{
	if(_usLen>0)
	{
		tx_num2=_usLen;
		tx_cnt2=1;
		pMsg2=(unsigned char xdata *)_ucaBuf;
		S2BUF = *pMsg2++;
	}
}
int get_uart2( unsigned char *_ucaBuf)
{
		int temp = 0;  
		if(rx_t2>=UOUT_TIME)
		{
			if(rx_cnt2>0)
			{
				memcpy (_ucaBuf, rxBuf2, rx_cnt2);
				temp=rx_cnt2;
				rx_cnt2=0;//重新开始接收,接收完置位,表明停止接收了
			}
		}	
		return temp;
}
#endif

#ifdef USE_UART3
void UART3_Init(unsigned char mode, unsigned int baud) //串口1、2可设优先级,串口3、4固定为最低优先级0,mode=0,使用默认定时器2波特率
{
    if(mode > 0) //定时器3做串口3波特率发生器           //串口3可用定时器2和定时器3做波特率发生器
    {
        S3CON = S3ST3 | S3REN;                                          //串口3控制寄存器S3CON(不可位访问)串口方式0(可变8位),允许串口接收数据(0x10)  ,选用定时器3作为波特率发生器
        //TM3PS                                                                     //定时器3的8位预分频寄存器(TM3PS),默认为0  定时器2时钟=系统时钟SYSCLK/(TM3PS+1)
        T3H = (-SYSCLK / baud / 4) >> 8;
        T3L = (-SYSCLK / baud / 4);               //65536-11059200/115200/4=0FFE8H
        T4T3M   &=  0xf0;
        T4T3M |= (T3R | T3x12);                                         //启动定时器3,定时器以系统不分频

    }
    else//定时器2做串口3波特率发生器,注意与其它串口参数的影响
    {
        S3CON = S3REN;                                                      //串口3控制寄存器S3CON(不可位访问)串口方式0(可变8位),允许串口接收数据(0x10)  ,默认选用定时器2作为波特率发生器
    }
    IE2 |= ES3;                                 //中断使能寄存器2 IE2(不可位访问)使能串口3中断
}
void send_uart3( unsigned char *_ucaBuf, int _usLen)
{
	if(_usLen>0 )	
	{
		tx_num3=_usLen;
		tx_cnt3=1;
		pMsg3=(unsigned char xdata *)_ucaBuf;
		S3BUF = *pMsg3++;
	}

#endif

#ifdef USE_UART4
void UART4_Init(unsigned char mode, unsigned int baud) //串口1、2可设优先级,串口3、4固定为最低优先级0,mode=0,使用默认定时器2波特率
{
    if(mode > 0) //定时器4做串口4波特率发生器           //串口4可用定时器2和定时器4做波特率发生器
    {
        S4CON = S4ST4 | S4REN;      //串口4控制寄存器S4CON(不可位访问)串口方式0(可变8位),允许串口接收数据(0x10)  ,选用定时器4作为波特率发生器
        //TM4PS                                                                     //定时器4的8位预分频寄存器(TM4PS),默认为0  定时器2时钟=系统时钟SYSCLK/(TM4PS+1)
        T4H = (-SYSCLK / baud / 4) >> 8;
        T4L = (-SYSCLK / baud / 4);               //65536-11059200/115200/4=0FFE8H
        T4T3M   &=  0x0f;
        T4T3M |= (T4R | T4x12);    //启动定时器4,定时器以系统不分频
    }
    else//定时器2做串口4波特率发生器,注意与其它串口参数的影响
    {
        S4CON = S4REN;      //串口4控制寄存器S4CON(不可位访问)串口方式0(可变8位),允许串口接收数据(0x10)  ,默认选用定时器2作为波特率发生器
    }
    IE2 |= ES4;      //中断使能寄存器2 IE2(不可位访问)使能串口4中断
}
void send_uart4( unsigned char *_ucaBuf, int _usLen)
{
	if(_usLen>0)	
	{
		tx_num4=_usLen;
		tx_cnt4=1;
		pMsg4=(unsigned char xdata *)_ucaBuf;
		S4BUF = *pMsg4++;
	}
}
int get_uart4( unsigned char *_ucaBuf)
{
		int temp = 0;  
		if(rx_t4>=UOUT_TIME)
		{
			if(rx_cnt4>0)
			{
				memcpy (_ucaBuf, rxBuf4, rx_cnt4);
				temp=rx_cnt4;
				rx_cnt4=0;//重新开始接收,接收完置位,表明停止接收了
			}
		}
		
		return temp;
}
unsigned char get_uart4_tx_idle(void)
{
	if(tx_cnt4==tx_num4)return 1;
	else return 0;
}
void set_uart4_tx_clr(void)
{
	tx_num4=0;
	tx_cnt4=0;
}
#endif

2.4 看门狗驱动程序

// 看门狗驱动函数
   看门狗喂狗函数
void Wdt_Config(void)
{
    WDT_CONTR = 0x27;                           //使能看门狗,溢出时间约为8s
}
void Wdt_fresh(void)
{
    WDT_CONTR |= CLR_WDT;                       //清看门狗,否则系统复位
}
void delay_ms(int t)
{
	static int lms=0;
	int mt=t;
	while(mt>0)
	{
		if(lms!=mscnt)//5ms一次
		{
			lms=mscnt;
			mt-=MS_TICK;
		}
		Wdt_fresh(); //清看门狗,否则系统复位
        Task_WDT();  //清外部看门狗813,否则系统复位
	}
	
}

2.5 掉电记忆程序

// 掉电记忆写函数
   掉电记忆读函数

void IapIdle()
{
    IAP_CONTR = 0;                              //关闭IAP功能
    IAP_CMD = 0;                                //清除命令寄存器
    IAP_TRIG = 0;                               //清除触发寄存器
    IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}

unsigned char IapRead(unsigned int addr)
{
    unsigned char dat;

    IAP_CONTR = 0x80;                         	//使能IAP
	IAP_TPS = 12;                         			//设置擦除等待参数12MHZ
    IAP_CMD = 1;                                //设置IAP读命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    dat = IAP_DATA;                             //读IAP数据
    IapIdle();                                  //关闭IAP功能

    return dat;
}

void IapProgram(unsigned int addr, unsigned char dat)
{
    IAP_CONTR = 0x80;                         	//使能IAP
	IAP_TPS = 12;                         			//设置擦除等待参数12MHZ
    IAP_CMD = 2;                                //设置IAP写命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_DATA = dat;                             //写IAP数据
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    IapIdle();                                  //关闭IAP功能
}

void IapErase(unsigned int addr)
{
    IAP_CONTR = 0x80;                         	//使能IAP
	IAP_TPS = 12;                         			//设置擦除等待参数12MHZ
    IAP_CMD = 3;                                //设置IAP擦除命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();                                    //
    IapIdle();                                  //关闭IAP功能
}
void WirteCfg(void)
{
	IapErase(0x00);//擦除从0开始的一个扇区,512个字节	
	IapProgram(0x01, CfgData[1]);//写入亮度
	IapProgram(0x02, CfgData[2]);//写入通讯时长高
	IapProgram(0x03, CfgData[3]);//写入通讯时长低
	CfgData[4]=(CfgData[1]+CfgData[2]+CfgData[3])&0xff;
	IapProgram(0x04, CfgData[4]);//写入校验
	IapProgram(0x00, 0xa5);//写入标识
}
#define UPDATE_INTERVAL				0xFF
void ReadCfg(void)
{
	unsigned char i=0;
	CfgData[0]=IapRead(0x00);   //标识  0xa5
	if(CfgData[0]==0xa5)
	{
	  for (i=1;i<91;i++){	    //读多个数据
		  CfgData[i]=IapRead(i);  
		}
	}
	//使用默认参数
	CfgData[1]=0x05;             //亮度
	CfgData[2]=UPDATE_INTERVAL>>8;
	CfgData[3]=UPDATE_INTERVAL&0xff;//校时周期	
}

2.6 时钟、定时器驱动程序

// 时钟底层驱动函数
   定时器0底层驱动函数
   定时器2底层驱动函数
// 底层配置程序 ---------------------------------------------------------------------------------------------------------------
void SystemClock_Config(void)
{
    P_SW2 = 0x80;
    XOSCCR = 0xc0;                              //启动外部晶振
    while (!(XOSCCR & 1));                      //等待时钟稳定
    CLKDIV = 0x00;                              //时钟不分频
    CKSEL = 0x01;                               //选择外部晶振
    P_SW2 = 0x00;
}
void TM2_Baud_Init(void)//定时器2做串口1,2,3,4通用波特率发生器,优先级固定为最低优先级0
{
    //固定定时器2做串口1,2,3,4通用波特率发生器,优先级固定为最低优先级0
    AUXR &= (~(T2R | T2x12));
    AUXR |= (T2R | T2x12);            //启动定时器2,定时器2以系统不分频,定时器2做串口1波特率发生器
    INTCLKO &= (~T2CLKO);                                           //中断与时钟输出控制寄存器INTCLKO(不可位访问),关闭定时器2时钟输出
    //TM2PS                                                                     //定时器2的8位预分频寄存器(TM2PS),默认为0  定时器2时钟=系统时钟SYSCLK/(TM2PS+1)

    T2H = (-SYSCLK / UART2_BPR / 4) >> 8;
    T2L = (-SYSCLK / UART2_BPR / 4);               //65536-11059200/115200/4=0FFE8H

}
void TM0_Init(unsigned char pri) //定时器0、1可设优先级,定时器2、3、4固定为最低优先级0,最高为3
{
                            //定时器0 初始化  系统时钟12分频,模式0自动重载的16位定时器
    TMOD &= 0xf0;           //定时器0/1模式寄存器TMOD(不可位访问),定时器0,模式0(16位自动重载模式)

    AUXR &= (~T0x12);      //辅助寄存器1 AUXR(不可位访问),定时器0系统12分频
    AUXR |=T0x12;          //辅助寄存器1 AUXR(不可位访问),定时器0系统不分频
    INTCLKO &= (~T0CLKO);  //中断与时钟输出控制寄存器INTCLKO(不可位访问),关闭定时器0时钟输出
    TH0 = 0x28;           //11.0592M/200hz=55296;//65536-55296   =10240(0x2800)
    TL0 = 0x02;

    PT0 = pri & 0x01;     //定时器中断优先级PT0H,PT0:PT0=1   11=3 最高优先级
    IPH &= (~PT0H);       //无法按位操作
    IPH |= (pri & PT0H);  //PT0H=1

    TR0 = 1;              //定时器0/1、外部中断0/1控制寄存器TCON(可位访问),启动定时器0 不能修改TH0和TH1的值,会改变定时器周期
    ET0 = 1;              //中断使能寄存器IE(可位访问) 使能定时器0中断
}

2.7 定时器跑秒程序

// 定时器跑秒函数
   获取秒函数
   获取毫秒函数
   获取微秒函数
#define MS_TICK                 5     //最大间隔5ms(200HZ),否则需调整12分频,避免溢出
#define cpu_on()            P26=0
#define cpu_off()           P26=1
static volatile unsigned long idata t_utc;//从1970年开始的时间秒数
static volatile unsigned int  idata mscnt=0;//使用volatile,不优化,每次都更新寄存器值

void Time_Update(void)
{
  systimer+=MS_TICK; //= SYSTEMTICK_PERIOD_MS;
}
// 中断回调程序 -------------------------
void Time_Update_Callback(void)
{
    mscnt+=MS_TICK;
    if(mscnt>=1000)                                                     //跑秒
    {
        mscnt=0;   
        t_utc++;
        cpu_on();
    }
    else if(mscnt>=500)                                         //使能读QCCLK
    {
        cpu_off();
    }
	Time_Update();		
}

unsigned long GetUtcTime(void)//获取绝对标准UTC时间的总秒数,从1970开始
{
    return t_utc;
}
unsigned long GetMsTime(void)//获取当前毫秒数
{
    return mscnt;
}
unsigned long  GetUsTime(void)//获取当前微秒数
{
	unsigned long xdata t;
	t=TH0;
	t<<=8;
	t+=TL0;	
	t=65536-t;//读取定时器值
	t=(t*5000)/4608;//11.0592M/200hz/12=4608;//65536-4608   =60928(0xee00)
	t+= ((unsigned long)mscnt*1000);
	return t;
}

2.8 MAX7129驱动程序

// 数码管驱动函数
//------------------------------------------------------------------------------------
//-- DESCRIPTION   :   BIN to seven segments converter  
//--                   segment encoding
//--                        e
//--                      +---+ 
//--                    f |   | d
//--                      +---+  <- g
//--                    a |   | c
//--                      +---+
//--                        b
//--                  Outputs (data_out) active         : high
//--									.,a,b,c,d,e,f,g -->(bit7~bit0)
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
//-- DESCRIPTION   :   BIN to seven segments converter  HS210801K-16 
//--                   segment encoding
//--                        a
//--                      +---+ 
//--                    f |   | b
//--                      +---+  <- g
//--                    e |   | c
//--                      +---+
//--                        d
//--                  Outputs (data_out) active         : low
//--									.,g,f,e,d,c,b,a -->(bit7~bit0)
//------------------------------------------------------------------
//--                  Outputs (data_out) active         : high MAX7219 共阴,高有效
//--									.,a,b,c,d,e,f,g -->(bit7~bit0)
//----------------------------------------------------------------------------------
//MAX7219 	--VCC GND CLK CS 	DATA  对应SZZ40-SFM-NB1.0
#define CLK_Hi()    		P21 = 1				//高,CLK上升沿数据被移入内部移位寄存器,下降沿从DOUT移出 一次称一位
#define CLK_Lo()    		P21 = 0				//低,CLK下降沿数据从DOUT移出 一次称一位
#define CS_Hi()    			P22 = 1				//高,CS上升沿锁存以上移位进输入的最后16位数据 				MAX7219为LOAD	MAX7221为CS,只有在CS为低选通时,CLK输入才有效
#define CS_Lo()    			P22 = 0				//低,CS置低选通 MAX7219,串行数据载入移位寄存器				MAX7219为LOAD	MAX7221为CS,只有在CS为低选通时,CLK输入才有效
#define DIN_Hi()    		P23 = 1				//高,DIN,数据位输入高
#define DIN_Lo()    		P23 = 0				//低,DIN,数据位输入低

#define Delay()        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
/*定义MAX7219寄存器 ******************************************************************/ 
#define REG_NO_OP 0x00  				// 定义空操作 register
#define DIG_1 0x01        			// 定义数码管1 register 
#define DIG_2 0x02        			// 定义数码管2 register 
#define DIG_3 0x03        			// 定义数码管3 register 
#define DIG_4 0x04        			// 定义数码管4 register 
#define DIG_5 0x05        			// 定义数码管5 register 
#define DIG_6 0x06        			// 定义数码管6 register 
#define DIG_7 0x07        			// 定义数码管7 register 
#define DIG_8 0x08        			// 定义数码管8 register 
#define REG_DECODE 0x09       	// 定义解码控制 register 
#define REG_INTENSITY 0x0a     	// 定义显示亮度 register 
#define REG_SCAN_LIMIT 0x0b    	// 定义扫描限制 register 
max7219共阴极 对应显示段表  ([0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [A], [b], [C], [d], [E], [F], blank) ***********************************************************/ 
unsigned char code code_table[17]= {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b,0x77,0x1f,0x4e,0x3d,0x4f,0x47,0x00};   /*采用数组*/ 
/****************************************************************** * MAX7219_Send()描述: 向MAX7219传送一字节数据
Arguments : dataout = data to send 
Returns : none ******************************************************************/ 
void send (unsigned char datain) 
{
	unsigned char i,temp; 
	for (i=8; i>0; i--) 
	{ 
		CLK_Lo();                     // CLK 置低 
		temp=datain&0x80;
		if (temp==0x80)              	// 判断并输出一位 
			DIN_Hi();                		// 输出"1" 
		else                        	// 或 
			DIN_Lo();                		// 输出"0" 
		datain<<=1;                 	//datain左移位,以便再次与0x80按位与
		CLK_Hi();                     // CLK 置高 
	}
}
 /**************************************************************** * MAX7219_Write()/MAX7219_Write_1()描述: 向 MAX7219 写命令 
Arguments : reg_number = register to write to 
dataout = data to write to MAX7219 
Returns : none ************************************************************** */ 
void MAX7219_Write (unsigned char add1, unsigned char dat1)  //向第一片MAX7219写数据
{ 
	CS_Lo();                    		// CS置低选通 MAX7219
	send(add1);               			// 写register number 到 MAX7219 
	send(dat1);                			// 写 data 到 MAX7219 
	CS_Hi();                    		// 利用CS上升沿锁存以上移位进输入的16位数据 
} 
void MAX7219_Write_12 (unsigned char add1, unsigned char dat1,unsigned char add2, unsigned char dat2)  //向两片MAX7219写数据
{ 
	CS_Lo();                    		// CS置低选通 MAX7219
	send(add2);               			// 写register number 到 第一片MAX7219 
	send(dat2);                			// 写 data 到 第一片MAX7219 
	send(add1);               			// 写register number 到 第一片MAX7219 
	send(dat1);                			// 写 data 到 第一片MAX7219 
	CS_Hi();                    		// 利用CS上升沿锁存以上移位进输入的16位数据  
} 
/******************************************************************** MAX7219_DisplayChar()描述: 使某一位显示一个数字
Arguments : digit = digit number (0-7) 
character = character to display (0-9, A-Z) 
dp = dp to display (0, 0x80) 
Returns : none ****************************************************************/
void MAX7219_DisplayChar (unsigned char digit,unsigned char character,unsigned char dp) 
{ 
	MAX7219_Write((digit+1), code_table[character&0x0f]|dp); 
} 
//PS:这个函数可以不要,直接调用写数据函数就可以了(原版)
/******************************************************************** MAX7219_Clear()/MAX7219_clear_1()描述: 清除所有位的显示
Arguments : none 
Returns : none *****************************************************************/ 
void MAX7219_Clear (void) 
{ 
	unsigned char i; 
	for (i=0; i < 8; i++)MAX7219_Write(i, 0x00);    // 清除第一片MAX7219所有位的显示 
} 
/********************************************************************
MAX7219_SetBrightness()描述: 设置数码管显示亮度
Arguments : brightness (0-15)
Returns : none *****************************************************************/ 
void MAX7219_SetBrightness (unsigned char brightness)
{ 
	brightness &= 0x0f;                             	// 屏蔽高位字节 
	MAX7219_Write(REG_INTENSITY, brightness);   		// 设置数码管显示亮度 
} 
/******************************************************************** MAX7219_DisplayTestStop()描述: 退出 test 模式
Arguments : none
Returns : none ***************************************************************/ 
void MAX7219_DisplayTestStop (void) 
{ 
	MAX7219_Write(REG_DISPLAY_TEST, 0);   			//置 MAX7219为正常显示模式 
}
/********************************************************************
MAX7219_ShutdownStop()描述: 退出 shutdown 模式
Arguments : none
Returns : none ****************************************************************/ 
void MAX7219_ShutdownStop (void) 
{ 
	MAX7219_Write(REG_SHUTDOWN, 1);    		//置MAX7219为正常显示模式 
} 
/******************************************************************** MAX7219_Init() 描述: MAX7219初始化模块,应该先于其他MAX7219函数而被调用 
Arguments : none
Returns : none *****************************************************************/ 
void Max7219_Reset (void) 
{   
	//设置为全显示0~7 DIG 减小扫描位数,增加扫描率,亮度功率增加,显示小于3位时,要设置ISET电阻来限制最大电流,避免单个DIG电流过大。
	MAX7219_Write(REG_SCAN_LIMIT, 7);
	MAX7219_Write(REG_DECODE, 0x00); 			//所有位设置为非解码方式 
	MAX7219_ShutdownStop();						//置MAX7219为(非关断)正常显示模式 
	MAX7219_DisplayTestStop();					//置MAX7219为(非test)正常显示模式  
	MAX7219_SetBrightness((CfgData[1]*3)&0x0f);	//设置数码管显示亮度 
}

2.9 MAX7129显示程序

// 数码管显示函数
void MAX7219_ClearChar (void) 
{ 
	unsigned int  i=0;
	for(i=0;i<8;i++)
	{
		MAX7219_Write(i+1,CODE_BLANK);
	}
} 
/******************************************************************** MAX7219_DisplayTestStart()描述: 进入 test 模式
Arguments : none
Returns : none ****************************************************************/ 
void MAX7219_DisplayTestStart (void) 
{ 
	MAX7219_Write(REG_DISPLAY_TEST, 1);   								//置MAX7219为 test 模式
} 
void MAX7219_DisplayTime (struct ctm t) 
{ 
    //根据显示实际调整第一个参数的数值
	MAX7219_DisplayChar(8,2,0);
	MAX7219_DisplayChar(9,0,0);
	MAX7219_DisplayChar(10,(t.tm_year%100)/10,0);
	MAX7219_DisplayChar(11,t.tm_year %10,0);
	
	MAX7219_DisplayChar(12,(t.tm_mon+1)/10,0);
	MAX7219_DisplayChar(13,(t.tm_mon+1) %10,0);
	
	MAX7219_DisplayChar(14,t.tm_mday/10,0);
	MAX7219_DisplayChar(15,t.tm_mday %10,0);
	MAX7219_DisplayChar(0,t.tm_hour/10,0);
	MAX7219_DisplayChar(1,t.tm_hour %10,0x80);
	MAX7219_DisplayChar(2,t.tm_min/10,0);
	MAX7219_DisplayChar(3,t.tm_min %10,0x80);
	MAX7219_DisplayChar(4,t.tm_sec/10,0);
	MAX7219_DisplayChar(5,t.tm_sec %10,0);
}
#define TASKS_HOLD              100
void Task_Max7219(unsigned int tk)
{	
	static unsigned char index=0;
	static unsigned long xdata lt = 0;
	unsigned long xdata ut = 0;	
	ut = GetMsTime(); //一毫秒一次循环
	if(ut >= tk && ut < (tk + TASKS_HOLD)) //按ms的时序开始,tk 为0则立即更新
	{
			ut = GetUtcTime();//一秒一次循环
			if(ut != lt) //每秒更新一次
			{
				lt = ut;		
				switch (index)
				{
					case 0:
							Max7219_Reset();
							MAX7219_DisplayTestStart();
						break;
					case 1:
					case 2:
						break;
					case 3:
							MAX7219_DisplayTestStop(); //置MAX7219为正常显示模式 
							MAX7219_Clear();           //清除所有位的显示 
							MAX7219_DisplayVer();
						break;
					default:
						MAX7219_DisplayTime(c_lct);
				}
				if(index<100)index++;
			}
	}
}

2.10 无线通信模块程序

// 函数
#define GET 						get_uart2
#define PUT 						send_uart2

//有人WH-LTE-7S1-------------------------------
unsigned char code ATE0[]  		= "usr.cn#AT+E=OFF\r\n";
unsigned char code AT_CSQ[]  	= "usr.cn#AT+CSQ\r\n";
unsigned char code AT_IMEI[] 	= "usr.cn#AT+IMEI?\r\n";
unsigned char code AT_ICCID[] 	= "usr.cn#AT+ICCID?\r\n";
unsigned char code AT_LBS[] 		= "usr.cn#AT+LBS?\r\n";
unsigned char code AT_LBS2[] 	= "usr.cn#AT+LBS=2\r\n";
unsigned char code AT_CCLK[] 	= "usr.cn#AT+CCLK?\r\n";
unsigned char code AT_SOCKA_R[] 	= "usr.cn#AT+SOCKA?\r\n";
unsigned char code AT_SOCKA_W[] 	= "usr.cn#AT+SOCKA=";
unsigned char code CFG_SOCKA[] 		= "UDP,ntp.(获取时间网站).net,123\r\n";
unsigned char code AT_SOCKAEN_R[]	= "usr.cn#AT+SOCKAEN?\r\n";
unsigned char code AT_SOCKAEN_W[]	= "usr.cn#AT+SOCKAEN=ON\r\n";
unsigned char code AT_SHORTATM_W[]	= "usr.cn#AT+SHORTATM=120\r\n";
unsigned char code AT_SOCKB_R[] 	= "usr.cn#AT+SOCKB?\r\n";
unsigned char code AT_SOCKB_W[] 	= "usr.cn#AT+SOCKB=";
unsigned char code CFG_SOCKB[] 		= "UDP,iot.(获取时间网站).net,端口号\r\n";
unsigned char code AT_SOCKBEN_R[]	= "usr.cn#AT+SOCKBEN?\r\n";
unsigned char code AT_SOCKBEN_W[]	= "usr.cn#AT+SOCKBEN=ON\r\n";
unsigned char code AT_SHORTBTM_W[]	= "usr.cn#AT+SHORTBTM=120\r\n";
unsigned char code AT_HEARTEN_R[] 	= "usr.cn#AT+HEARTEN?\r\n";
unsigned char code AT_HEARTEN_W[] 	= "usr.cn#AT+HEARTEN=OFF\r\n";
unsigned char code AT_STMSG_R[] 	= "usr.cn#AT+STMSG?\r\n";
unsigned char code AT_STMSG_W[] 	= "usr.cn#AT+STMSG=chijiu_iot21_001\r\n";
unsigned char code AT_CACHEN_R[] 	= "usr.cn#AT+CACHEN?\r\n";
unsigned char code AT_CACHEN_W[] 	= "usr.cn#AT+CACHEN=OFF\r\n";
unsigned char code AT_SDPEN_R[] 	= "usr.cn#AT+SDPEN?\r\n";
unsigned char code AT_SDPEN_W[] 	= "usr.cn#AT+SDPEN=ON\r\n";
unsigned char code AT_S[] 	= "usr.cn#AT+S\r\n";
static int p_write(unsigned char* buf, int len)//用于统一发送数据格式,其中buf不能是ptx发送缓存
{	
	if(len>0)
	{
		memset(ptx, 0, TX_LEN);//清空发送缓存
		memcpy (ptx, buf, len);//通一发送数据类型和指针类型
		PUT(ptx,len);
	}
	return len;
}

static int p_read(unsigned char* buf)
{
	int idata  len=0;

	len=GET(buf);
	return len;
}
static void get_sdpen(void)
{
	p_write((unsigned char*)AT_SDPEN_R,strlen(AT_SDPEN_R));
}
static void get_sig(void)
{
	p_write((unsigned char*)AT_CSQ,strlen(AT_CSQ));
}
static void get_imei(void)
{
	p_write((unsigned char*)AT_IMEI,strlen(AT_IMEI));
}
static void get_iccid(void)
{
	p_write((unsigned char*)AT_ICCID,strlen(AT_ICCID));
}
static void get_lbs(void)
{
	p_write((unsigned char*)AT_LBS,strlen(AT_LBS));
}
static void get_lbs2(void)
{
	p_write((unsigned char*)AT_LBS2,strlen(AT_LBS2));
}
static void cat_config(void)
{
	int len0,len1=0;
	p_write((unsigned char*)ATE0,strlen(ATE0));//关回显
	delay_ms(100);
	memset(ptx, 0, TX_LEN);//清空发送缓存
	len0=strlen(AT_SOCKA_W);
	memcpy (ptx, (unsigned char*)AT_SOCKA_W, len0);//通一发送数据类型和指针类型
	len1=strlen(CFG_SOCKA);
	memcpy (ptx+len0, (unsigned char*)CFG_SOCKA, len1);//通一发送数据类型和指针类型
	PUT(ptx,len0+len1);
	delay_ms(100);
	p_write((unsigned char*)AT_SOCKAEN_W,strlen(AT_SOCKAEN_W));//设置SOCKA启用
	delay_ms(100);
	p_write((unsigned char*)AT_SHORTATM_W,strlen(AT_SHORTATM_W));//设置SOCKA短链接时长
	delay_ms(100);
	memset(ptx, 0, TX_LEN);//清空发送缓存
	len0=strlen(AT_SOCKB_W);
	memcpy (ptx, (unsigned char*)AT_SOCKB_W, len0);//通一发送数据类型和指针类型
	len1=strlen(CFG_SOCKB);
	memcpy (ptx+len0, (unsigned char*)CFG_SOCKB, len1);//通一发送数据类型和指针类型
	PUT(ptx,len0+len1);
	delay_ms(100);
	p_write((unsigned char*)AT_SOCKBEN_W,strlen(AT_SOCKBEN_W));//设置SOCKB启用
	delay_ms(100);
	p_write((unsigned char*)AT_SHORTBTM_W,strlen(AT_SHORTBTM_W));//设置SOCKB短链接时长
	delay_ms(100);
	p_write((unsigned char*)AT_HEARTEN_W,strlen(AT_HEARTEN_W));//设置心跳关闭
	delay_ms(100);
	p_write((unsigned char*)AT_CACHEN_W,strlen(AT_CACHEN_W));//设置串口缓存关闭
	delay_ms(100);	
	p_write((unsigned char*)AT_SDPEN_W,strlen(AT_SDPEN_W));//设置套节字分发开启
	delay_ms(100);	
	p_write((unsigned char*)AT_S,strlen(AT_S));

}

static void socka( char* buf)
{
	char *q;
	char *p;
	q=buf;
	//p=strstr(q,"SOCKA:UDP,ntp.zhonglianwang.net,123\r\n");//设置SOCKA地址 //如果修改,上边设置参数判读时也必须修改
	p=strstr(q,CFG_SOCKA);
	if(p==NULL)
	{
		initialized=2;//复位重置
	}
}
static void sockb( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,CFG_SOCKB);
	if(p==NULL)
	{
		initialized=2;//复位重置
	}
}

static void sockaen( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,"SOCKAEN:OFF\r\n");//设置SOCKA启用
	if(p!=NULL)
	{
		initialized=2;//复位重置
	}
}
static void sockben( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,"SOCKBEN:OFF\r\n");//设置SOCKB启用
	if(p!=NULL)
	{
		initialized=2;//复位重置
	}
}
static void hearten( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,"HEARTEN:ON\r\n");//设置心跳关闭
	if(p!=NULL)
	{
		initialized=2;//复位重置
	}
}
static void cachen( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,"CACHEN:ON\r\n");//设置串口缓存关闭
	if(p!=NULL)
	{
		initialized=2;//复位重置
	}
}
static void sdpen( char* buf)
{
	char *q;
	char *p;
	q=buf;
	p=strstr(q,"SDPEN:ON\r\n");
	if(p!=NULL)
	{
		if(initialized!=2)
		{
			initialized=1;//初始化成功	
			ltdl=0;	
		}
	}
	else
	{
		p=strstr(q,"SDPEN:OFF\r\n");//设置套节字分发开启
		if(p!=NULL)
		{
			initialized=2;//复位重置
		}
	}
}

void socket_send(unsigned char *buf,unsigned int len,unsigned char ch)//0~3
{
//套接字协议
}

void socket_receive(unsigned char *buf,unsigned int len)//0~3
{
//套接字协议
}

static void rec_pro(unsigned char* buf,int len)
{
	char *q;
	q=strstr((char*)buf,"+CSQ:");//\r\n+CSQ: 31, 99\r\n\r\nOK\r\n
	if(q!=NULL)csq(q);//+CSQ: 31, 99\r\n\r\nOK\r\n
	
//	q=strstr((char*)buf,"+CCLK:");//\r\n+CCLK: “20/06/19,20:05:19+32”\r\n\r\nOK\r\n
//	if(q!=NULL)cclk(q);
		
	q=strstr((char*)buf,"chijiu");//\r\n+IMEI:864333040712457\r\n\r\nOK\r\n
	if(q!=NULL)chijiu(q);
	
	q=strstr((char*)buf,"+ICCID:");//\r\n+ICCID:89860621240067311755\r\n\r\nOK\r\n
	if(q!=NULL)iccid(q);	
	
	q=strstr((char*)buf,"+IMEI:");//\r\n+IMEI:864333040712457\r\n\r\nOK\r\n
	if(q!=NULL)imei(q);
	
	q=strstr((char*)buf,"+LBS:");//\r\n+LBS: LNG = 114.43350220, LAT = 30.49159431, TIME = 2020-12-11 15:45:39, ADDINFO: 武汉市高新大道426号\r\n\r\nOK\r\n
	if(q!=NULL)lbs(q);
	
	q=strstr((char*)buf,"+SOCKA:");//\r\n+SOCKA:<TCP,UDP>,<1~100 bytes>,<1~65535>
	if(q!=NULL)socka(q);
	
	q=strstr((char*)buf,"+SOCKAEN:");//\r\n+SOCKAEN:ON\r\n\r\nOK\r\n
	if(q!=NULL)sockaen(q);
	
	q=strstr((char*)buf,"+SOCKB:");//\r\n+SOCKB:<TCP,UDP>,<1~100 bytes>,<1~65535>
	if(q!=NULL)sockb(q);
	
	q=strstr((char*)buf,"+SOCKBEN:");//\r\n+SOCKBEN:ON\r\n\r\nOK\r\n
	if(q!=NULL)sockben(q);	
	
	q=strstr((char*)buf,"+HEARTEN:");//\r\n+HEARTEN:OFF\r\n\r\nOK\r\n
	if(q!=NULL)hearten(q);	
	
	q=strstr((char*)buf,"+CACHEN:");//\r\n+CACHEN:OFF\r\n\r\nOK\r\n
	if(q!=NULL)cachen(q);	
	
	q=strstr((char*)buf,"+SDPEN:");//\r\n+SDPEN:ON\r\n\r\nOK\r\n
	if(q!=NULL)sdpen(q);

	socket_receive(buf,len);
}

#define cat1_on()            P14=1
#define cat1_off()           P14=0
void Init_Cat(void)
{
	cat1_on();	
}
void Task_Cat(unsigned int tk)
{	
	static unsigned long idata lt = 0;
	static unsigned char idata lm = 0;
	unsigned long idata ut = 0;
	unsigned int idata usCRC=0;
	int idata len=0;
	len=p_read(prx);

	if(len>0)
	{
		rec_pro(prx,len);

		if(cmd_en==1)
		{
			cmd_en=0;
			ut=clk_packet(upBuf);第一个客体包(1时间)
			ut=clk_packet_add(upBuf,ut,prx,len,18);//ADDR
			socket_send(upBuf,ut,1);
		}
	}
	
	ut = GetMsTime();
	if(ut >= (tk + dl_index) && ut < (tk + TASKS_HOLD + dl_index)) //按ms的时序开始,tk 为0则立即更新,
	{
			ut = GetUtcTime();
			if(ut != lt) //每秒更新一次
			{
				lt = ut;
				//执行TASK---------------------------
				if(ltdl<(((unsigned int)CfgData[2]<<8)|CfgData[3]))ltdl++;
				else 
				{
					ltdl=0;
					if(lm<48)lm++;//每(48*校时周期)后,上传输一次地理信息
					else lm=0;
				}

					if(initialized==2 || initialized==0)
					{
						if((ltdl&0x1f)==0x10)get_imei();
						else if((ltdl&0x1f)==0x11)p_write((unsigned char*)AT_STMSG_R,strlen(AT_STMSG_R));//查询启动信息
						else if((ltdl&0x1f)==0x12)p_write((unsigned char*)AT_SOCKA_R,strlen(AT_SOCKA_R));//查询SOCKA地址
						else if((ltdl&0x1f)==0x13)p_write((unsigned char*)AT_SOCKAEN_R,strlen(AT_SOCKAEN_R));//查询SOCKA启用
						else if((ltdl&0x1f)==0x14)p_write((unsigned char*)AT_SOCKB_R,strlen(AT_SOCKB_R));//查询SOCKB地址
						else if((ltdl&0x1f)==0x15)p_write((unsigned char*)AT_SOCKBEN_R,strlen(AT_SOCKBEN_R));//查询SOCKB启用
						else if((ltdl&0x1f)==0x16)p_write((unsigned char*)AT_HEARTEN_R,strlen(AT_HEARTEN_R));//查询心跳关闭
						else if((ltdl&0x1f)==0x17)p_write((unsigned char*)AT_CACHEN_R,strlen(AT_CACHEN_R));//查询串口缓存关闭
						else if((ltdl&0x1f)==0x18)get_iccid();
						else if((ltdl&0x1f)==0x19)get_sdpen();//查询套节字分发开启
						else if((ltdl&0x1f)==0x1a)
						{
							if(initialized==2)
							{
								initialized=0;  //0:未初始化,1:初始化成功,2;复位重置,3;校時成功
								cat_config();
								ltdl=0;
							}
						}
					}
					else
					{
						ut=GetNtpSync();
						//数据上传时刻----
						if((ltdl&0xffff)==0x12)get_lbs();
						else if((ltdl&0xffff)==0x13)get_sig();		//一天1M数据,一分钟不超694字节(往返),32秒一次上传数据(数据最好不超过108字节)(20+8+77),64秒一次NTP(20ip头+8UDP头+48数据=76,76*2=152字节)
						else if((ltdl&0xffff)==0x2C)//UPDATA
						{
							len=clk_packet(upBuf);第一个客体包(1时间)		
							len=clk_packet_add(upBuf,len,prx,5,16);//客体16--子钟--驱动(状态)	
							socket_send(upBuf,len,1);
							if(GetSyncOK()==0x01)
							{
								if(initialized==1)
								{
									ltdl=dl_index;//初始化校时成功后,根据IMEI号差开时序
									initialized=3;
								}
							}
						}
						else if((ltdl&0xff)==0x00)//NTP 同步周期固定为48~256秒//数据NTP同步时刻-首次链接,延时大------
						{
							if(GetSyncOK()!=1){  //第一次NTP同步本地时间后将ntp_en一直为1
							  ntp_en=0;
							}else{
							  ntp_en=1;
							}
							construct_packet(upBuf);//数据包长48
							socket_send(upBuf,48,0);
						}
						else if((ltdl&0xff)==0x07 || (ut!=0x01 && (ltdl&0x07)==0x07) && (ltdl&0xff)<=SYNCPRE_MIN)//NTP 同步周期固定为48~256秒  次数大于NTP的SYNC_MAX
						{
							ntp_en=1;
							construct_packet(upBuf);//数据包长48
							socket_send(upBuf,48,0);
							check_clk();
						}
						else if((ltdl&0xffff)==0x0011)
						{
							if(lm==2)get_lbs2();//数据获取地理位置时刻--
						}
					}
			}
	}
	
}

总结

上述程序中并未完全给出,但是思路没问题。

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃突兔兔突秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值