STM32固件库(标准外设库)入门学习 第六章TIM定时器(一)


文章目录

  • STM32固件库(标准外设库)入门学习 第六章TIM定时器(一)
  • 前言
  • 一、定时器类型
    • 1 基本定时器
    • 2 通用定时器
    • 3 高级定时器
  • 二、定时器定时中断
    • 1 定时中断基本结构
    • 2 预分频器时序
    • 3 计数器时序
    • 4 计数器有/无预装时序
    • 5 RCC时钟树
      • 5.1 时钟产生电路
      • 5.2 时钟分配电路
  • 总结

前言

本学习教程,参考B站江科大自化协STM32视频,型号为STM32F103C8T6。

TIM(Timer)是STM功能最强大、结构最复杂的一个外设。定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。定时器就是一个计数器,当计数器的输入是一个准确可靠的基准时钟的时候,在对这个基准时钟进行计数的过程,实际上就是一个计时的过程。在STM32中,定时器的基准时钟一般都是主频72MHz,对这72MHz计72个数,那就是1MHz也就是1us的时间;若计72000个数,那就是1KHz也就是1ms的时间。

STM32拥有16位计数器、预分频器、自动重装寄存器的时基单元。计数器就是用来执行计数定时的一个寄存器,每来一个时钟,计数器加1;预分频器可以对计数器的时钟进行分频。让这个计数更加灵活;自动重装寄存器就是计数的目标值,就是想要计多少时钟去申请中断。这些计数器构成定时器最核心的部分,我们把这一块电路称为时基单元。

这些时基单元里的计数器等都是16位的,2的16次方为65536,也就是若预分频器设置最大,自动重装也设置最大,那在72MHz计数时钟下,定时器可以实现最大59.65s的定时,接近1分钟,计算方法:72M/65536/65536,得到中断频率,然后取倒数,就是59.65多。若觉得定时时间不够长,STM32还支持级联的模式,也就是一个定时器的输出,当做另一个定时器的输入,这样加一起,最大时间就是59.656553665536,时间大概为8000多年。若还嫌短,还可以再级联一个定时器,定时时间还会再延长6553665536倍,时间可达34亿万年。

定时器不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。由于定时器的基本结构是通用的,很多模块电路都能用到,所以STM32定时器上扩展了非常多的功能。


一、定时器类型

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型,复杂程度逐渐降低。我们主要学通用定时器。

同一个芯片会有很多定时器,所以TIM后会跟一个数字,数字不同定时器的类型也不同,除了1到8,在库函数中还出现了TIM9、10、11等,这些一般用不到,只需要知道TIM1到8是什么定时器就好了。不同类型定时器所连接的总线也不一样。

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4。

1 基本定时器

基本定时器拥有定时中断、主模式触发DAC数模转换器的功能。

预分频器、计数器、自动重装载寄存器构成了最基本的计数计时电路,所以这一块电路就做时基单元。

基本定时器只能选择内部时钟,预分频器之前,连接的时基准计数时钟的输入,最终来到输入端的内部时钟(CK_INT),此时钟来源为RCC的TIMxCLK,这个频率值一般都是系统的主频72MHz。

预分频器,对72MHz的计数时钟进行预分频,预分频写0就是1分频,输出72MHz;写1就是2分频,输出36MHz,以此类推,即实际分频系数=预分频器的值+1,分频器最大16位,所以可以写65535,即65536分频。

计数器,对预分频后的计数时钟进行计数操作,计数时钟每来一个上升沿,计数器的值就+1,计数器也是16位,所以里面的值可以从0一直加到65535,再加就会从0开始。计数器的值在计时过程中会不断的自增运行,当自增运行到目标值时,产生中断,完成定时的任务。

自动重装载寄存器,用来存储产生中断的目标值,也是16位,存的就是写入的计数目标,在运行过程中,计数值不断自增,自动重装值是固定的目标,当计数值=自动重装值时,即计时时间到,产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时。

向上的折线箭头,代表这里会产生一个中断信号,计数值等于自动重装值产生的中断,叫“更新中断”,在之后就会通向NVIC,再配置NVIC的定时器通道,那定时器的更新中断就能得到CPU的响应。

向下的折线箭头,代表会产生一个事件,对应的事件就叫“更新事件”,不会触发中断,但会触发内部其他电路的工作。

主模式触发DAC功能的功能。主从触发模式时定时器的特色,他能让硬件在不受程序的控制下实现自动运行,通过这种方式在某些情境下将会极大地减轻CPU的负担。在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发一次DAC,让他输出下一个电压点。若是采用正常的思路,就是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出,这样会使主程序处于频繁被中断的状态,影响主程序的运行和其他中断的响应,所以定时器就设计了一个主模式,使用此主模式可以把定时器的更新事件,映射到这个触发输出TRGO(Trigger OUT)的位置,再接到DAC的触发转换引脚上,这样定时器的更新就不需要再通过中断来触发DAC了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了。整个过程无需软件参与,实现硬件的自动化,这就是主模式的作用,当然除了主模式外,还有更多硬件自动化的设计。

2 通用定时器

中间红框是最核心的时基单元,与基本定时器一样**。但是计数模式不仅是向上计数一种,通用定时器和高级定时器还支持向下计数模式和中央对齐模式**。

向下计数模式就是从重装值开始,向下自减,到0后回到重装值并申请中断,然后继续下一轮,依此循环。

中央对齐模式是从0开始,先向上自增,到重装值并申请中断,然后再向下自减,减到0,再申请中断,然后继续下一轮,依次循环。

时基单元上面的结构是内外时钟源选择和主从触发模式的结构。

先看内外时钟源选择**。对于基本定时器而言,定时只能选择内部时钟,也就是系统频率72MHz,到了通用定时器里,时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟**。

外部时钟来源有TIMx_ETR(External)引脚的配置,可以参考一下引脚定义表。

例如在TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置内部的极性选择、边缘检测和预分频器电路,再配置输入滤波电路,这两块电路可以对外部时钟进行一定的整形,因外这是外部引脚的时钟,所以难免会有毛刺。滤波后的信号,兵分两路,上路ETRF进入触发控制器,紧跟就可以选择作为时基单元的时钟。如果想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当做计数器来用的话,那么就可以配置这一路的电路,在STM32中,这一路也叫做“外部时钟模式2”。

TRGI(Trigger IN)也可以提供时钟,从名字上看,主要用作触发输入来使用,这个触发输入可以触发定时器的从模式,后面会有。在这将触发输入作为外部时钟使用的情况,此时这一路就叫做“外部时钟模式1”。通过这一路的外部时钟有以下几路:
(1)ETR引脚的信号。向左看,第一个是ETR引脚的信号,ETR可以走两路进来当作时钟,对于时钟输入而言是等价的,只不过下面这一路的输入会占用触发输入的通道;
(2)ITR信号,这一部份的时钟信号是来自其他定时器的,从右边可以看到主模式的TRGO可以通向其他定时器,通向其他定时器的时候,就接到了其他定时器的ITR引脚上了,ITR0~3分别来自其他4个定时器的TRGO输出。根据参考手册14.4.3 从模式控制寄存器(TIMx_SMCR)可以看到ITR和定时器的连接关系,通过这一路就可以实现定时器级联的功能。

(3)TI1F_ED,这里连接的是输入捕获单元的CH1引脚边沿获得时钟,ED即Edge,就是边沿的意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效。输入捕获和测频率会用到。
(4)TI1FP1和TI2FP2获得。TI1FP1是连接CH1引脚获得时钟,TI2FP2是连接CH2引脚获得时钟。输入捕获和测频率会用到。

内部时钟72MHz这种方式最常用,外部时钟首选TIMx_ETR(External)引脚这一路。

通用定时器主模式输出。可以把定时器内部电路的一些事件,映射到TRGO引脚上,用来触发其他定时器、DAC或ADC,触发输出的范围比基本定时器更广一些。

时基单元下面一部分主要包含右边的输出比较电路,左边的捕获电路。

输出比较电路总共有四个通道,分别对应CH1到CH4的引脚,可用于输出PWM波形,驱动电机。左边这一块是输入捕获电路,也有四个通道,对应也是CH1到CH4的引脚,可用来测量输入方波的频率等,中间的寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的。

3 高级定时器

对比通用定时器的结构,红框内的部分没什么变化,改动的部分有:

(1)申请中断的地方,增加一个重复次数计数器,有了这个计数器后,就可以实现每隔几个计数周期,才发生一次更新事件和更新中断,相当于输出的更新信号又做了一次分频。对于高级定时器,之前计算的最大定时时间59秒多,在这里需要再×65536,又提升了很多定时时间,这就是重复计数器的工作流程。

(2)其他改动的地方,就是高级定时器对输出比较模块的升级。DTG(Dead Time Generate)死区生成电路,右边的输出引脚由原来的一个变为两个互补的PWM波,这些电路是为了驱动三相无刷电机的。三相无刷电机还是比较常用的,四轴飞行器、电动车的后轮、电钻等里面都会有这个三相无刷电机,因为三相无刷电机的驱动电路一般需要3个桥臂,每个桥臂2个大功率开关管来控制,总共需要6个大功率开关管来控制,所以输出PWM引脚的前三路变为互补的输出,第四路没有变化,因为三相电机只需要三路即可。另外为了防止互补输出的PWM驱动桥臂时,在开关切换的瞬间,由于器件的不理想,造成短暂的直通现象,所以前面加了DTG死区生成电路,在开关切换的瞬间,产生一定时长的死区,让桥臂的上下管全都关断,防止直通现象。最后一部分,就是刹车输入的功能,这个是为了给电机驱动提供安全保障的,若外部引脚BKIN(Break IN)产生了刹车信号,或者内部时钟失效,产生了故障,那么控制电路就会自动切断电机的输出,防止意外的发生。

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;    //脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //使能输出比较状态
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能  互补 输出状态
TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;  //脉冲 值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出比较极性低
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;//互补 输出极性高
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;   //MOE=0 设置 TIM1输出比较空闲状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;//MOE=0 重置 TIM1输出比较空闲状态
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //设定好的参数 初始化TIM

二、定时器定时中断

定一个时间,让定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段程序的目的。比如要做一个时钟、秒表,或者使用一些程序算法的时候,都需要用到定时中断这一功能。

1 定时中断基本结构

时基单元的三个寄存器最重要。

运行控制就是控制寄存器的一些位,比如启动停止、向上或向下计数等,操作这些寄存器就能控制时基单元的运行。

左边是为时基单元提供时钟的部分。

右边就是计时时间到,产生更新中断后的信号去向。在这里如果是高级定时器,还会多一个重复计数器。中断信号会现在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断。定时器模块有很多地方都要申请中断,这些中断都要经过中断输出控制,如果需要这个中断,那就允许,如果不需要,那就禁止,简单来说,中断输出控制就是一个中断输出的允许位。如果需要某个中断,就记得允许一下。

2 预分频器时序



(1)CK_PSC:预分频器的输入时钟,内部时钟一般位72MHz。

(2)CNT_EN:计数器使能,高电平计数器正常运行,低电平计数器停止。

(3)CK_CNT:计数器时钟,既是预分频器的时钟输出,也是计数器的时钟输入。开始时,计数器未使能,计数器时钟不运行,前半段预分频器系数为1,计数器时钟=预分频器前的时钟;后半段预分频器系数变为2,计数器的时钟=预分频器前时钟的一半。

(4)在计数器时钟的驱动下,计数器寄存器也跟随时钟的上升沿不断自增。在中间这个位置FC之后,计数值变为0。可推断出ARR自动重装值就是FC,当计数值计到和重装值相等,并且下一个时钟来临时,计数值才清零,同时下面产生一个更新事件。

(5)更新事件。

(6)预分频寄存器的一种缓冲机制。预分频寄存器实际上是有两个,一个是预分频控制寄存器,供读写使用,并不直接决定分频系数;另外还有缓冲寄存器,或者叫影子寄存器,缓冲寄存器才是真正起作用的寄存器,比如在某个时刻,把预分频寄存器由0改成1,如果在此时立刻改变时钟的分频系数,那么就会导致在一个计数周期内,前半部分和后半部分的频率不一样,计数计到一半,计数频率突然就会改变。STM32设计了这个缓冲寄存器,当计数计到一半时改变分频值,这个变化并不会立刻生效,而是会等到本次计数周期结束,产生了更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面,才会生效。所以在图中看到,即使在计数器中途改变了预分频值,计数频率仍然为原先频率,直到本轮计数完成,在下一轮计数时,改变后的预分频值才起作用。

(7)预分频器的内部实际上也是靠计数来分频的,当预分频值为0时,计数器就一直为0,直接输出原频率,当预分频值为1时,计数器就0、1、0、1、0、1、0、1、0、1这样计数。在回到0的时候输出一个脉冲,这样输出频率就是输入频率的2分频。预分频器的值和实际的分频系数之间有一个数的偏移,计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)。

3 计数器时序


(1)CK_PSC:预分频器的输入时钟,内部时钟一般位72MHz。

(2)CNT_EN:计数器使能,高电平计数器正常运行,低电平计数器停止。

(3)CK_CNT:计数器时钟,既是预分频器的时钟输出,也是计数器的时钟输入。开始时,计数器未使能,计数器时钟不运行。计数器使能后,预分频器系数变为2,计数器的时钟=预分频器前时钟的一半。所以计数器在这个时钟每个上升沿自增,增到0036时,发生溢出,计到0036之后再来一个上升沿,计数器清零,产生一个更新事件脉冲,还会置一个更新中断标志位UIF,此标志位只要置1,就会去申请中断,中断响应后,需要在中断程序中手动清零,这就是计数器的工作流程。

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
溢出时间:1/ CK_CNT_OV。

预分频器为了防止计数中途更改数值造成错误,设计了缓冲寄存器,计数器也有。如下图所示,带有黑色阴影的的寄存器都有缓冲机制。所以计数器的ARR自动重装载寄存器,也是有一个缓冲寄存器的,是否使用缓冲寄存器,是可以自己设置的。

4 计数器有/无预装时序

图105即无缓冲寄存器的情况,图106有预装时序,就是有缓冲寄存器的情况,通过设置ARP1位,就可以选择是否使用预装功能。

无预装情况:计数寄存器正在进行自增计数,突然更改了自动加载寄存器,就是自动重装寄存器,由FF改为36,那么此时计数值的目标值就由FF变为36,所以计数器寄存器计到36之后,直接更新,开始下一轮的计数。

有预装的情况:在计数中途突然把计数目标从F5改为36,影子寄存器才是真正起作用的,他还是F5,所以现在的计数目标还是计到F5产生更新事件,同时要更改的36才被传递到影子寄存器,在下一个计数周期,这个更改的36才有效。引入影子计数器的目的实际上是为了同步,即让值得变化和更新事件同步发生,防止在运行途中更改造成错误。如果此时去掉影子寄存器,F5改为36立刻生效,但此时计数值已经到了F1,超过36了,F1只能增加,但目标值36比F1还小,最后F1只能加到FFFF,再回到0,再加到36,才能产生更新,这就会造成一些小问题。

5 RCC时钟树

时钟树就是STM32中用来产生和配置时钟,并且把配置好得时钟发送到各个外设系统。时钟是所有外设运行得基础,时钟也是最先需要配置的东西。前文提到,程序中主函数之前还会执行一个System_Init函数,此函数就是用来配置时钟树。ST公司已经写好配置这个时钟树的System_Init函数。

从AHB左侧画一个界限,左边是时钟的产生电路,右边是时钟的分配电路。

5.1 时钟产生电路

中间的SYSCLK就是系统的时钟72MHz。

内部8MHz高速RC振荡器(HSI RC)。

外部4-16MHz高速石英晶体振荡器(HSE OSC),也就是晶振,一般都是接8MHz。

外部32.768kHz低速晶振(LSE OSC),一般给RTC提供时钟。

内部40 kHz低速RC晶振(LSI RC),给看门狗提供时钟。

内外两个高速晶振是用来提供系统时钟,AHB、APB2、APB1的时钟都是源于这两个高速晶振,内部外部8MHz的晶振,都是可以用的,只不过外部的石英振荡器比内部RC振荡器更加稳定,一般都用外部晶振,当系统很简单而且不需要精确的时钟,也可以使用内部RC振荡器,这样可以省下外部晶振的电路。

System_Init函数首先会启动内部时钟,选择内部8MHz为系统时钟,暂时以此时钟运行,再启动外部时钟,HSE OSC经过PLLXTPRE、PLLSRC选择,进入PLLMUL(PLL锁相环)进行倍频,8*9得到72MHz,锁相环输出稳定后,选择锁相环输出为系统时钟。这样就把系统时钟由8MHz改为72MHz。如果外部时钟出问题了,可能会导致一个现象,程序的时钟慢了大概10倍,比如用定时器定一个1s的时间,结果过了大概10s才进入中断。外部时钟出问题,系统无法切换到72MHz,就会以内部8MHz运行,所以慢了就接近10倍。

CSS(Clock Security System),这个是时钟安全系统,负责切换时钟,可监测外时钟的运行状态,一旦外部时钟失效,就会自动把外部时钟切换回内部时钟,保证系统时钟的运行,防止程序卡死造成事故。在高级定时器里,也有CSS的身影,在刹车输入里,一旦CSS检测到外部时钟失效,这里通过或门就会立刻反应到输出比较这里,让这个输出控制的电机立刻停止,防止意外,这就是STM32的一些安全保障措施。

5.2 时钟分配电路

首先72MHz系统时钟进入AHB总线。AHB总线有预分频器,在System_Init函数里配置的分配系数为1,AHB时钟就是72MHz;进入APB1总线,配置的分配系数为2,APB1总线时钟为36MHz。

通用定时器和基本定时都是接在APB1上的,而APB1的时钟是36MHz,按理来说他们的时钟也应是36MHz,但是在前文一直说所有的定时器的时钟都是72MHz。原因在于下面的支路,如果APB1预分频系数=1,则频率不变,否则频率*2。预分频系数给了2,频率要再乘2,所以通向定时器2~7的时钟,就又回到了72MHz,这就是之前一直说的所有的定时器的时钟都是72MHz。

APB2的预分频系数为1,所以APB2的时钟和AHB一样,都是72MHz,这里接到APB2上的高级定时器也单开了一路,确保1和8的时钟72MHz。

在时钟输出,都有与门进行输出控制,控制位写的是外部时钟使能,这就是我们程序中写RCC_APB2/1PeriphClockCmd作用的地方,打开时钟就是在外设时钟使能写1,让左边时钟通过与门输出给外设。


总结

本篇主要讲TIM定时器的基础知识,下一节将从代码层面来看

STM32固件库(标准外设库)入门学习 第六章TIM定时器(一)相关推荐

  1. hal库选择滴答时钟函数_STM32入门 : HAL库、标准外设库、LL库

      国内使用STM32 单片机的人很多,ST 为开发者提供了非常方便的开发库:有标准外设库(SPL 库).HAL 库.LL 库 三种.前者是ST的老库,后两者是ST现在主推的开发库,其中 LL 库是 ...

  2. STM32 之一 HAL库、标准外设库、LL库(STM32 Embedded Software)

    2020.2.4: 更新内容 STM32 Embedded Software   工作以来一直使用 ST 的 STM32 系列芯片,ST 为开发者提供了非常方便的开发库.到目前为止,有标准外设库(SP ...

  3. 官网下载stm32f407固件库(标准外设)详细步骤

    可通过此处找到STM32的各个标准外设库 STM32 ARM Cortex 32位微控制器 - STMicroelectronics 下载STM32F407的标准外设库,可直接点击下面链接 STM32 ...

  4. STM32固件库(标准外设库)入门学习 第四章OLED屏幕使用

    STM32固件库(标准外设库)入门学习 第四章OLED屏幕使用 本学习教程,参考B站江科大自化协STM32视频,型号为STM32F103C8T6. 文章目录 STM32固件库(标准外设库)入门学习 第 ...

  5. stm32固件库(STM32F10x标准外设库)V3.5简介

    STM32F固件库是根据CMSIS(ARM Cortex微控制器软件接口标准)而设计的.CMSIS标准由ARM和芯片生产商共同提出,让不同的芯片公司生产的Cortex M3微控制器能在软件上基本兼容. ...

  6. keil stm32标准库放在哪里_使用Keil MDK以及标准外设库创建STM32工程

    应部分网友要求,最新加入固件库以及开发环境使用入门视频教程,同时提供例程模板,个人录制,欢迎指正.下载地址:http://dl.dbank.com/c0w0ehqynd 2013.3补充在线视频教程 ...

  7. keil stm32标准库放在哪里_STM32(1)——使用Keil MDK以及标准外设库创建STM32工程...

    转载来自:http://emouse.cnblogs.com 1.1 开发工具与开发环境 1. 软件版本 本节所使用Keil MDK 为目前的最新版V4.21.其他版本差别不大,读者可以根据自己使用的 ...

  8. STM32 HAL库、标准外设库、LL库(STM32 Embedded Software)

    STM32 Embedded Software 工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库.到目前为止,有标准外设库(STD库).HAL库.LL库 三种.前两者都是常 ...

  9. 使用Keil MDK以及标准外设库创建STM32工程

    应部分网友要求,最新加入固件库以及开发环境使用入门视频教程,同时提供例程模板,个人录制,欢迎指正.下载地址:http://dl.dbank.com/c0w0ehqynd 2013.3补充在线视频教程 ...

最新文章

  1. go语言实现图片合成
  2. Python应用实战-从pandas的角度来对比MySQL,教你如何更快更好的学习sql
  3. cc压力测试_斯坦福大学鲍哲南院士AFM综述:教你如何设计压力传感器的微结构...
  4. 字符串类型的日期如何存储到数据库Date类型的字段中
  5. leetCode题解之Reshape the Matrix
  6. Python中math模块的使用
  7. list清空的函数java,6-1 jmu-Java-05集合-List中指定元素的删除 (20分)
  8. 如何一次为 Safari 中的所有标签添加书签?
  9. matlab傅里叶光学仿真,关于微透镜阵列的傅里叶光学分析
  10. libiconv android编译,linux环境下libiconv库基于Android NDK的编译方法
  11. 用android编写计数器前后台源代码,在Android中实现计数器
  12. RequestError Error connect ETIMEDOUT 59.24.3.174443
  13. 怎么才能获取企业工商数据和企业联系方式?
  14. Java发送QQ邮件问题Could not connect to SMTP host: smtp.qq.com, port: 465(内附完整代码)
  15. 罗克韦尔协议转换网关WTGNet-AB
  16. 发现中国地铁名字的秘密
  17. 美团招聘不要黄泛区及东北人_吃瓜群众愤慨怒怼!
  18. muduo---C++网络编程库
  19. [转]如何降低二手烟的危害
  20. 计算机网络攻防演练,网络安全攻防演练

热门文章

  1. mysql delimiter $$_mysql delimiter的说明
  2. md5sum校验文件
  3. mysql varbinary charindex_SqlServer中SubString与CharIndex函数的使用SubString和CharIndex结合使用...
  4. 惨遭面试官吊打高并发系统设计,回来学习 2400 小时后成功复仇
  5. 【修真院Java小课堂】Annotation注解
  6. Qt on Android: http下载与Json解析
  7. import cannot be resolved问题-解决方法
  8. 让电脑变成守时的小闹钟
  9. 财大气粗的节奏!腾讯应用宝4.0功能点评
  10. mysql新增BTREE索引_mysql如何添加多个btree索引