分享一个强大的定时器吧。使用灵活

2019-07-20 10:21发布

STM32定时器比较多,使用起来也比较混乱。 windows下做过编程的人一定对SetTimer有印象,不过该定时器也不那么好用,因为id要自己维护,并且保证不跟程序中的其他id冲突。
手头上的项目要用到定时器,又不想每次都跑到寄存器里详细设定参数,所以就写了个类似SetTimer的定时器。
注:请在STM32F407上使用该定时器,其他内核需要修改,如M3内核是没有32位定时器的
使用方法: u8 SetTimer(u32 unTime, u32 unCount, TIMERFUN pHandler,u32 ucPara) 使用SetTimer去向系统申请一个定时器,如果成功,返回定时器ID,失败返回0.为了使定时器ID更有意义,这里设定返回值为定时器编号 这里根据计时时间自动分配定时器。比如你需要一个比较长时间的定时,那么SetTimer会给你分配一个32位定时器(M4内核) 参数: unTime:计时时间(ms,如需ns请自行修改或者使用delay) unCount :计时器运行次数,0为无限运行,非零的话,运行unCount次后,该定时器关闭。 pHandler:定时器定时后执行的函数,该处如果不需要执行,可填0(什么都不需要做,干嘛要开启定时器?),当然这里可以自己定义函数指针类型(比如传2个参数或者变参:呃,变参太变态) ucPara:函数参数
/*关闭定时器
tmrId:定时器ID
*/
void KillTimer(TIMER tmrId);


例如: [mw_shl_code=c,true] //每隔0.5s反转4bit灯 tmrIrb = SetTimer(500, 0, IndRvsBit, 4); printf("IndRvsBit Timer Id: %d ",tmrIrb); if(0 != tmrIrb) { ucCnt = SetTimer(10 * 1000, 1, KillBlink, tmrIrb); //10s后关闭位4反转 printf("KillBlink Timer Id: %d ",ucCnt); }[/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true][/mw_shl_code]



这里要提前定义好参数里的函数指针 [mw_shl_code=c,true]void KillBlink(u8 ucOpt) { // RTC_ShutWakeUp(); KillTimer(ucOpt); printf("KillBlink(%d) ", ucOpt); } void AdPickTime(u8 ucOpt) { PushCmd(0xF2,0); } void IndRvsBit(u8 ucOpt) { SubBoardIndSingle(4, acCurInd[4] ^ (1 << ucOpt)); }[/mw_shl_code]



header:
[mw_shl_code=c,true]#ifndef __TIMER_H #define __TIMER_H #include "board.h" #include "zkekglobe.h" ////////////////////////////////////////////////////////////////////////////////// //All rights reserved ////////////////////////////////////////////////////////////////////////////////// /*使能基本计时器 ucTimerIdx:计时器编号 范围:2~5 9~14 unCount 自动重装值。 uwPsc 计数器分频 2-65535 计时时间 = unCount * uwPsc / 84M(168M if ucTimerIdx in{9, 10, 11}) pHandler 计数完成后执行函数 ucPara 执行函数的参数 返回值: 0:成功 1:ucOpt unRunningFreqency unCount参数错误 2:定时器编号参数错误 3:定时器已经被占用 4:16位定时器传入的计数参数超阈值*/ u8 TimerEnable(u8 ucTimerIdx, u32 unCount, u16 uwPsc,TIMERFUN pHandler,u8 ucPara); /* 获取空闲计时器 unTime:要计时的时间 返回值:timerId or 0:failed */ u8 GetAFreeTimer(u32 unTime); /*设置定时器,最大计时2147483647ms(24.8天),最小1ms,其中unTime值不能超过该值 unTime: 计时时间,单位ms unCount: 0:连续计时;其它:计时次数,到达次数后计时器停止工作 pHandler 计数完成后执行函数 ucPara 执行函数的参数 返回值: TimerID or 0:失败 */ u8 SetTimer(u32 unTime, u32 unCount, TIMERFUN pHandler,u32 ucPara); /*关闭定时器 tmrId:定时器ID */ void KillTimer(TIMER tmrId); /*停止基本计时器 使能位置0,停止其时钟 ucTimerIdx:计时器编号 范围:2~5 ucOpt 可选参数 保留不用 */ void TimerStop(u8 ucTimerIdx,u8 ucOpt); #endif [/mw_shl_code]



















c: [mw_shl_code=c,true]#include "timer.h" ////////////////////////////////////////////////////////////////////////////////// ////All rights reserved ////////////////////////////////////////////////////////////////////////////////// TIMERFUN Timer2Fun = 0; TIMERFUN Timer3Fun = 0; TIMERFUN Timer4Fun = 0; TIMERFUN Timer5Fun = 0; TIMERFUN Timer9Fun = 0; TIMERFUN Timer10Fun = 0; TIMERFUN Timer11Fun = 0; TIMERFUN Timer12Fun = 0; TIMERFUN Timer13Fun = 0; TIMERFUN Timer14Fun = 0; u8 ucTimer2Para = 0; u8 ucTimer3Para = 0; u8 ucTimer4Para = 0; u8 ucTimer5Para = 0; u8 ucTimer9Para = 0; u8 ucTimer10Para = 0; u8 ucTimer11Para = 0; u8 ucTimer12Para = 0; u8 ucTimer13Para = 0; u8 ucTimer14Para = 0; u8 ucTmr2Efct = 0; u8 ucTmr3Efct = 0; u8 ucTmr4Efct = 0; u8 ucTmr5Efct = 0; u8 ucTmr9Efct = 0; u8 ucTmr10Efct = 0; u8 ucTmr11Efct = 0; u8 ucTmr12Efct = 0; u8 ucTmr13Efct = 0; u8 ucTmr14Efct = 0; u8 acTimerId[10] = { 0 }; u8 acTmrOrder[10] = {9, 10, 11, 3, 4, 12, 13, 14, 2, 5}; //定时器按定时时间长短查找顺序 u32 anTimingCnt[10] = { 0 }; u8 GetAFreeTimer(u32 unTime) { u8 ucTmrSearchWith = 0; u8 ucTimerIndex = 0; //find an idle timer if(unTime < 16384)//小于16.383s { ucTmrSearchWith = 1; } else if(unTime > 16383 && unTime < 32768) { ucTmrSearchWith = 3; } else if(unTime > 32767 && unTime < 2147483647) { ucTmrSearchWith = 8; } for(; ucTmrSearchWith < 10; ucTmrSearchWith++) { if(0 == acTimerId[ucTmrSearchWith]) { ucTimerIndex = acTmrOrder[ucTmrSearchWith]; } } return ucTimerIndex; } /*设置定时器,最大计时2147483647ms(24.8天),最小1ms,其中unTime值不能超过该值 unTime: 计时时间,单位ms unCount: 0:连续计时;其它:计时次数,到达次数后计时器停止工作 pHandler 计数完成后执行函数 ucPara 执行函数的参数 返回值:TimerID or 0:失败 */ u8 SetTimer(u32 unTime, u32 unCount, TIMERFUN pHandler,u32 ucPara) { u8 ucTimerIndex = 0; u8 ucRslt = 0; u8 ucTmrSearchWith = 0; u32 unTimeUse = 0; //find an idle timer if(unTime < 16384)//小于16.383s { ucTmrSearchWith = 0; } else if(unTime > 16383 && unTime < 32768) { ucTmrSearchWith = 3; } else if(unTime > 32767 && unTime < 2147483647) { ucTmrSearchWith = 8; } else { return 0; } for(; ucTmrSearchWith < 10; ucTmrSearchWith++) { if(0 == acTimerId[ucTmrSearchWith]) { ucTimerIndex = acTmrOrder[ucTmrSearchWith]; acTimerId[ucTmrSearchWith] = ucTimerIndex; ucRslt = ucTimerIndex; anTimingCnt[ucTmrSearchWith] = unCount; unTimeUse = unTime; if(ucTmrSearchWith < 3) { unTimeUse *= 2; } if( 0 != TimerEnable(ucTimerIndex, unTimeUse * 2, 42000, pHandler, ucPara)) { acTimerId[ucTmrSearchWith] = 0; ucRslt = 0; continue; } break; } } return ucRslt; } /*关闭定时器 tmrId:定时器ID */ void KillTimer(TIMER tmrId) { TimerStop(tmrId,0); switch(tmrId) { case 2: acTimerId[8] = 0; break; case 3: acTimerId[3] = 0; break; case 4: acTimerId[4] = 0; break; case 5: acTimerId[9] = 0; break; case 9: acTimerId[0] = 0; break; case 10: acTimerId[1] = 0; break; case 11: acTimerId[2] = 0; break; case 12: acTimerId[5] = 0; break; case 13: acTimerId[6] = 0; break; case 14: acTimerId[7] = 0; break; default: break; } } //定时器2中断服务程序 void TIM2_IRQHandler(void) { if(TIM2->SR&0X0001)//溢出中断 { if(ucTmr2Efct) { if(Timer2Fun) { (*Timer2Fun)(ucTimer2Para); } if(anTimingCnt[8] > 1) //判断计时次数 { anTimingCnt[8]--; } else if(1 == anTimingCnt[8]) //计时次数完成 { KillTimer(2); } } else { ucTmr2Efct = 1; } } TIM2->SR&=~(1<<0);//清除中断标志位 } //定时器3中断服务程序 void TIM3_IRQHandler(void) { if(TIM3->SR&0X0001)//溢出中断 { if(1 == ucTmr3Efct) { if(Timer3Fun) { (*Timer3Fun)(ucTimer3Para); } if(anTimingCnt[3] > 1) //判断计时次数 { anTimingCnt[3] --; } else if(1 == anTimingCnt[3]) //计时次数完成 { KillTimer(3); } } else { ucTmr3Efct = 1; } } TIM3->SR&=~(1<<0);//清除中断标志位 } //定时器4中断服务程序 void TIM4_IRQHandler(void) { if(TIM4->SR&0X0001)//溢出中断 { if(1 == ucTmr4Efct) { if(Timer4Fun) { (*Timer4Fun)(ucTimer4Para); } if(anTimingCnt[4] > 1) //判断计时次数 { anTimingCnt[4] --; } else if(1 == anTimingCnt[4]) //计时次数完成 { KillTimer(4); } } else { ucTmr4Efct = 1; } } TIM4->SR&=~(1<<0);//清除中断标志位 } //定时器5中断服务程序 void TIM5_IRQHandler(void) { if(TIM5->SR&0X0001)//溢出中断 { if(1 == ucTmr5Efct) { if(Timer5Fun) { (*Timer5Fun)(ucTimer5Para); } if(anTimingCnt[9] > 1) //判断计时次数 { anTimingCnt[9] --; } else if(1 == anTimingCnt[9]) //计时次数完成 { KillTimer(5); } } else { ucTmr5Efct = 1; } } TIM5->SR&=~(1<<0);//清除中断标志位 } //定时器9中断服务程序 void TIM1_BRK_TIM9_IRQHandler(void) { if(TIM9->SR&0X0001)//溢出中断 { if(1 == ucTmr9Efct) { if(Timer9Fun) { (*Timer9Fun)(ucTimer9Para); } if(anTimingCnt[0] > 1) //判断计时次数 { anTimingCnt[0]--; } else if(1 == anTimingCnt[0]) //计时次数完成 { KillTimer(9); } } else { ucTmr9Efct = 1; } TIM9->SR&=~(1<<0);//清除中断标志位 } } //定时器10中断服务程序 void TIM1_UP_TIM10_IRQHandler(void) { if(TIM10->SR&0X0001)//溢出中断 { if(1 == ucTmr10Efct) { if(Timer10Fun) { (*Timer10Fun)(ucTimer10Para); } if(anTimingCnt[1] > 1) //判断计时次数 { anTimingCnt[1] --; } else if(1 == anTimingCnt[1]) //计时次数完成 { KillTimer(10); } } else { ucTmr10Efct = 1; } TIM10->SR&=~(1<<0);//清除中断标志位 } } //定时器11中断服务程序 void TIM1_TRG_COM_TIM11_IRQHandler(void) { if(TIM11->SR&0X0001)//溢出中断 { if(1 == ucTmr11Efct) { if(Timer11Fun) { (*Timer11Fun)(ucTimer11Para); } if(anTimingCnt[2] > 1) //判断计时次数 { anTimingCnt[2] --; } else if(1 == anTimingCnt[2]) //计时次数完成 { KillTimer(11); } } else { ucTmr11Efct = 1; } TIM11->SR&=~(1<<0);//清除中断标志位 } } //定时器12中断服务程序 void TIM8_BRK_TIM12_IRQHandler(void) { if(TIM12->SR&0X0001)//溢出中断 { if(1 == ucTmr12Efct) { if(Timer12Fun) { (*Timer12Fun)(ucTimer12Para); } if(anTimingCnt[5] > 1) //判断计时次数 { anTimingCnt[5] --; } else if(1 == anTimingCnt[5]) //计时次数完成 { KillTimer(12); } } else { ucTmr12Efct = 1; } TIM12->SR&=~(1<<0);//清除中断标志位 } } //定时器13中断服务程序 void TIM8_UP_TIM13_IRQHandler(void) { if(TIM13->SR&0X0001)//溢出中断 { if(1 == ucTmr13Efct) { if(Timer13Fun) { (*Timer13Fun)(ucTimer13Para); } if(anTimingCnt[6] > 1) //判断计时次数 { anTimingCnt[6] --; } else if(1 == anTimingCnt[6]) //计时次数完成 { KillTimer(13); } } else { ucTmr13Efct = 1; } TIM13->SR&=~(1<<0);//清除中断标志位 } } //定时器14中断服务程序 void TIM8_TRG_COM_TIM14_IRQHandler(void) { if(TIM14->SR&0X0001)//溢出中断 { if(1 == ucTmr14Efct) { if(Timer14Fun) { (*Timer14Fun)(ucTimer14Para); } if(anTimingCnt[7] > 1) //判断计时次数 { anTimingCnt[7] --; } else if(1 == anTimingCnt[7]) //计时次数完成 { KillTimer(14); } } else { ucTmr14Efct = 1; } TIM14->SR&=~(1<<0);//清除中断标志位 } } /*使能基本计时器 ucTimerIdx:计时器编号 范围:2~5 9~14 unCount 自动重装值。 uwPsc 计数器分频 2-65535 计时时间 = unCount * uwPsc / 84M(168M if ucTimerIdx in{9, 10, 11}) pHandler 计数完成后执行函数 ucPara 执行函数的参数 返回值: 0:成功 1:ucOpt unRunningFreqency unCount参数错误 2:定时器编号参数错误 3:定时器已经被占用 4:16位定时器传入的计数参数超阈值*/ u8 TimerEnable(u8 ucTimerIdx, u32 unCount, u16 uwPsc,TIMERFUN pHandler,u8 ucPara) { //参数检查 if(unCount < 2 || uwPsc < 2 || uwPsc > 65535) //参数错误:重装值、分频数设置错误 { return 1; } if(ucTimerIdx < 2 || ucTimerIdx > 14 || (ucTimerIdx > 5 && ucTimerIdx < 9) ) //参数错误:不是2-5 9-14定时器 { return 2; } switch(ucTimerIdx) { case 2: if(0x0001 & TIM2 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1; TIM2 ->ARR = unCount - 1; TIM2 ->SC = uwPsc - 1; TIM2 ->DIER |= 1; Timer2Fun = pHandler; ucTimer2Para = ucPara; MY_NVIC_Init(2,0,TIM2_IRQn,2); TIM2->SR = 0;//清除中断标志位 TIM2 ->CR1 |= 0x01; break; case 3: if(unCount > 0x0000FFFF) { return 4; } if(0x0001 & TIM3 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 1; //使能定时器时钟 TIM3 ->ARR = unCount - 1; TIM3 ->SC = uwPsc - 1; TIM3 ->DIER |= 1; Timer3Fun = pHandler; ucTimer3Para = ucPara; MY_NVIC_Init(0,3,TIM3_IRQn,2); TIM3->SR = 0;//清除中断标志位 TIM3 ->CR1 |= 0x01; break; case 4: if(unCount > 0x0000FFFF) { return 4; } if(0x0001 & TIM4 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 2; //使能定时器时钟 TIM4 ->ARR = unCount - 1; TIM4 ->SC = uwPsc - 1; TIM4 ->DIER |= 1; Timer4Fun = pHandler; ucTimer4Para = ucPara; MY_NVIC_Init(1, 0 , TIM4_IRQn, 2); TIM4->SR = 0;//清除中断标志位 TIM4 ->CR1 |= 0x01; break; case 5: if(0x0001 & TIM5 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 3; //使能定时器时钟 TIM5 ->ARR = unCount - 1; TIM5 ->SC = uwPsc - 1; TIM5 ->DIER |= 1; Timer5Fun = pHandler; ucTimer5Para = ucPara; MY_NVIC_Init(2,1,TIM5_IRQn,2); TIM5->SR = 0;//清除中断标志位 TIM5 ->CR1 |= 0x01; break; case 9: if(0x0001 & TIM9 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB2ENR |= 1 << 16; //使能定时器时钟 TIM9 ->ARR = unCount - 1; TIM9 ->SC = uwPsc - 1; TIM9 ->DIER |= 1; Timer9Fun = pHandler; ucTimer9Para = ucPara; MY_NVIC_Init(0, 0 ,TIM1_BRK_TIM9_IRQn,2); TIM9->SR = 0;//清除中断标志位 TIM9 ->CR1 |= 0x01; break; case 10: if(0x0001 & TIM10 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB2ENR |= 1 << 17; //使能定时器时钟 TIM10 ->ARR = unCount - 1; TIM10 ->SC = uwPsc - 1; TIM10 ->DIER |= 1; Timer10Fun = pHandler; ucTimer10Para = ucPara; MY_NVIC_Init(0, 1 ,TIM1_UP_TIM10_IRQn,2); TIM10->SR = 0;//清除中断标志位 TIM10 ->CR1 |= 0x01; break; case 11: if(0x0001 & TIM11 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB2ENR |= 1 << 18; //使能定时器时钟 TIM11 ->ARR = unCount - 1; TIM11 ->SC = uwPsc - 1; TIM11 ->DIER |= 1; Timer11Fun = pHandler; ucTimer11Para = ucPara; MY_NVIC_Init(0, 2 ,TIM1_TRG_COM_TIM11_IRQn,2); TIM11->SR = 0;//清除中断标志位 TIM11 ->CR1 |= 0x01; break; case 12: if(0x0001 & TIM12 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 6; //使能定时器时钟 TIM12 ->ARR = unCount - 1; TIM12 ->SC = uwPsc - 1; TIM12 ->DIER |= 1; Timer12Fun = pHandler; ucTimer12Para = ucPara; MY_NVIC_Init(1, 1 ,TIM8_BRK_TIM12_IRQn,2); TIM12->SR = 0;//清除中断标志位 TIM12 ->CR1 |= 0x01; break; case 13: if(0x0001 & TIM13 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 7; //使能定时器时钟 TIM13 ->ARR = unCount - 1; TIM13 ->SC = uwPsc - 1; TIM13 ->DIER |= 1; Timer13Fun = pHandler; ucTimer13Para = ucPara; MY_NVIC_Init(1, 2 ,TIM8_UP_TIM13_IRQn,2); TIM13->SR = 0;//清除中断标志位 TIM13 ->CR1 |= 0x01; break; case 14: if(0x0001 & TIM14 ->CR1) //定时器正在被使用 { return 3; } RCC ->APB1ENR |= 1 << 8; //使能定时器时钟 TIM14 ->ARR = unCount - 1; TIM14 ->SC = uwPsc - 1; TIM14 ->DIER |= 1; Timer14Fun = pHandler; ucTimer14Para = ucPara; MY_NVIC_Init(1, 3 ,TIM8_TRG_COM_TIM14_IRQn,2); TIM14->SR = 0;//清除中断标志位 TIM14 ->CR1 |= 0x01; break; default: break; } return 0; //返回成功 } /*停止基本计时器 使能位置0,停止其时钟 ucTimerIdx:计时器编号 范围:2~5 ucOpt 可选参数 保留不用 */ void TimerStop(u8 ucTimerIdx,u8 ucOpt) { switch(ucTimerIdx) { case 2: TIM2 ->CR1 &= ~(1 << 0); //关闭使能 Timer2Fun = 0; ucTimer2Para = 0; ucTmr2Efct = 0; break; case 3: TIM3 ->CR1 &= ~(1 << 0); //关闭使能 Timer3Fun = 0; ucTimer3Para = 0; ucTmr3Efct = 0; break; case 4: TIM4 ->CR1 &= ~(1 << 0); //关闭使能 Timer4Fun = 0; ucTimer4Para = 0; ucTmr4Efct = 0; break; case 5: TIM5 ->CR1 &= ~(1 << 0); //关闭使能 Timer5Fun = 0; ucTimer5Para = 0; ucTmr5Efct = 0; break; case 9: TIM9 ->CR1 &= ~(1 << 0); //关闭使能 Timer9Fun = 0; ucTimer9Para = 0; ucTmr9Efct = 0; break; case 10: TIM10 ->CR1 &= ~(1 << 0); //关闭使能 Timer10Fun = 0; ucTimer10Para = 0; ucTmr10Efct = 0; break; case 11: TIM11 ->CR1 &= ~(1 << 0); //关闭使能 Timer11Fun = 0; ucTimer11Para = 0; ucTmr11Efct = 0; break; case 12: TIM12 ->CR1 &= ~(1 << 0); //关闭使能 Timer12Fun = 0; ucTimer12Para = 0; ucTmr12Efct = 0; break; case 13: TIM13 ->CR1 &= ~(1 << 0); //关闭使能 Timer13Fun = 0; ucTimer13Para = 0; ucTmr13Efct = 0; break; case 14: TIM14 ->CR1 &= ~(1 << 0); //关闭使能 Timer14Fun = 0; ucTimer14Para = 0; ucTmr14Efct = 0; break; default: break; } } [/mw_shl_code]



防止小白copy代码而不仔细阅读说明,我把函数类型定义写在这里,大牛请自行添加或修改. 另外,SetTimer那里挖了个坑,摔死一个算一个。
typedef void (*TIMERFUN)(u8);
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
10条回答
mack13013
1楼-- · 2019-07-21 09:52
FortuneDi 发表于 2017-6-28 16:42
为什么我在while 循环里调用这个函数会出现死机的现象

我记得我在设计这个的时候,没有一个地方是阻塞设计的,也就是不存在类似while(P)这样的设计,因为有可能P的条件受外部限制,而使的while循环一直无法通过,其他的可能阻塞的设计也都是避免了的。

刚刚我又重新检查了一遍整个设计,还是没有找到能都导致阻塞的循环、等待之类的代码。


然后我有想了一下,发现TIMERFUN这个由用户设计的回调函数是不受我控制的,而且也没有像OS那样设计时间片轮询,强行剥离TIMERFUN执行,这个地方如果用户编写的回调函数是阻塞的话,是有可能会出现死机的现象的。

会不会是你自己的回调函数TIMERFUN阻塞了?

在定时器调用、中断处理、关闭定时器以及回调函数这里多打几个断点,看一下哪些断点执行了,哪些不执行,观察一下究竟卡在哪里吧
Widow
2楼-- · 2019-07-21 15:46
 精彩回答 2  元偷偷看……
l6931639
3楼-- · 2019-07-21 17:46
 精彩回答 2  元偷偷看……
mack13013
4楼-- · 2019-07-21 23:40
l6931639 发表于 2017-12-22 21:37
虽然简化了设置,但是优先级是个坑

设计思路:短延时申请计时器得到16位定时器,长延时申请计时器得到32位定时器。

中断级别和分组没提到,是因为使用了系统默认的中断分配。

如果你有什么其他的设计思路,欢迎给出解决方案。

你说的优先级是个坑不知道具体是说哪方面。

一周热门 更多>