传统意义上的计时器是指利用特定的原理来测量时间的装置, 在古代, 常用沙漏、点燃一炷香等方式进行粗略的计时, 在现代科技的带动下, 计时水平越来越高, 也越来越精确, 之所以需要进行计时是在很多情况下我们需要知道时间已经过去了多少, 举例说, 上课下课的打铃、 考试时的计时、车站按时间间隔进行发车等。 不仅在日常生活中会应用到计时, 在一些电子设备中计时的普遍存在, 如手机里的闹钟、电子秒表、电子设备的定时关机等, 这些计时的目的都是相同的, 当达到一定时间后执行某件事, 计时器相当于提醒作用, 当达到某个时间后提醒人们或者机器该做某件事了。
    
    在Windows系统中, 计时器作为一种输入设备存在于系统中, 当每到一个设定的时间间隔后它都会向应用程序发出一个 WM_TIMER 的消息, 以提醒程序规定的间隔时间已经过去了, 计时器在程序中的应用十分广泛, 举些我们容易想到的示例:
        1>. 游戏这控制物体的移动速度, 比如说某个物体每100毫秒移动某个单位距离;
        2>. 文件的自动保存, 当用户编辑某些文件时5分钟自动保存一次, 避免因意外情况造成编辑的成果全部丢失;
        3>. 实现程序的自动退出, 当程序达到某个设定的时间后程序自动退出;

一、使用计时器
    计时器的使用主要分为创建、处理、销毁三个部分。
        ①. 创建: 创建一个计时器并设定其定计时器的任务周期, 例如每5秒向程序发送一条 WM_TIMER 消息 ;
        ②. 处理: 根据接收到的 WM_TIMER 消息让程序作出响应的处理 ;
        ③. 销毁: Windows的计时器属于系统资源, 在使用完毕后应及时销毁。
        
    1>. 计时器的创建
        要创建一个计时器可以使用 SetTimer 函数, SetTimer函数的原型:

    UINT_PTR SetTimer(
    HWND hWnd, //窗口句柄
    UINT_PTR nIDEvent, //定时器的ID
    UINT uElapse, //间隔时间, 单位为毫秒
    TIMERPROC lpTimerFunc //所使用的回调函数
    );

  参数说明:
    参数一窗口句柄即为接收 WM_TIMER 消息的窗口句柄;
    参数二为设置该计时器的ID, 用于与其他的计时器进行区分; 
    参数三为计时器发送 WM_TIMER 消息的时间间隔, 单位为毫秒, 最大可设置的时间间隔为一个 unsigned long int 型所能容下的数据大小, 为 4 294 967 295 毫秒(约合49.7天), 当设定的时间间隔到了后Windows就会向应用程序的消息队列放入一个 WM_TIMER 消息 ;
    参数四为定时器所使用的回调函数, 当使用回调函数时, 所产生的 WM_TIMER 消息自动调用回调函数进行处理。
    
    其函数的返回值为成功创建的定时器的ID。
    
    你可以在任何时候创建一个新的计时器, 例如在接收到 WM_CREATE 消息时。
    
    创建计时器的三种方式:
        方式一: 不使用回调函数

            SetTimer( hwnd, nIDEvent, uiMsecInterval, NULL ) ;

创建举例:

                SetTimer( hwnd, 1, 100, NULL ) ;

这样我们就创建了一个ID为1, 消息频率为100毫秒, 没有使用回调函数的计时器, 每当程序运行100毫秒Windows就会向应用程序的消息队列里放入一个 WM_TIMER 消息。

        方式二: 使用回调函数

            SetTimer( hwnd, nIDEvent, uiMsecInterval, TimeProc ) ;

创建举例:

                SetTimer( hwnd, 1, 100, TimeProc ) ;

TimeProc即为该定时器所指定使用的回调函数, 它可以是你喜欢的任何名字, 但是函数声明时的类型必须为 CALLBACK型, 表示该函数为回调函数, 需要注意的时, 当为定时器使用回调函数时, 该定时器所发出的 WM_TIMER 消息将直接发送给回调函数进行处理并从消息队列里销毁该消息。

        方式三: 不使用窗口句柄

            iTimerID = SetTimer( NULL, 0, uiMsecInterval, TimeProc ) ;

当忽略窗口句柄时, 那么第二个参数计时器ID也应被忽略, 填充0, 由系统随机分配一个与其他定时器不重复的ID, 返回值即为分配到的ID, 如果返回值为0表示计时器创建失败, 如果要处理该定时器发出的消息需要配合回调函数使用。

    2>. 计时器消息的处理
        ①. 当不使用回调函数时
            当不使用回调函数时程序会收到 WM_TIMER 消息, 这时只要像处理普通消息一样处理 WM_TIMER 消息就行了, 如果有多个计时器, 可以从 wParam 参数中根据计时器的ID作不同的处理, 例如:

            case WM_TIMER:
switch(wParam)
{
case 1:
[处理ID为1的计时器]
break;
case 2:
[处理ID为2的计时器]
break ;
...
}
return 0 ;

        ②. 使用回调函数的计时器
            当计时器创建时指定好回调函数时, 回调函数可以像下面的写法进行:

            VOID CALLBACK TimerProc( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime )
{
[处理 WM_TIMER 消息]
}

当不同的计时器使用同一个回调函数时, 可以根据回调函数的 iTimerID 参数来区分不同的计时器, 形如:

           switch(iTimerID)
{
case 1: //处理ID为1的定时器
[...]
break;
case 2: //处理ID为2的定时器
[...]
break;
...
}

        3>. 销毁计时器
        在开始部分也已经说了, Windows的计时器属于系统资源, 在使用完毕后应及时销毁。销毁计时器的函数是 KillTimer, 他的函数原型如下:

        BOOL KillTimer(
HWND hWnd, //窗口句柄
UINT_PTR uIDEvent //计时器ID
);

要销毁一个计时器, 必须知道该计时器的ID, 所以保留计时器的ID也是十分重要的, 你可以在任何时候销毁一个已经创建的计时器, 包括在处理计时器消息时。 最好在程序退出之前销毁完所有的已创建的计时器, 一个不错的办法是在处理 WM_DESTROY 消息时对于那些没有销毁的全部进行销毁。
        需要注意的是, 当成功销毁一个计时器后, 该计时器所产生的 WM_TIMER 消息并不会从消息队列中移除, 如果消息队列中还有没有处理的 WM_TIMER 消息, 那么即使销毁了该计时器, 应用程序还是会有可能处理到没有处理完的 WM_TIMER 消息。

二、重置计时器
    在某些情况下我们可能需要改变一件事的处理时间间隔, 如果先销毁一个计时器再创建一个新的时间间隔的计时器未免有些麻烦, 当需要重新设定某个计时器的时间间隔时只需要再次调用 SetTimer 函数改变其中的 时间间隔 值即可, 当改变某个计时器的时间间隔时需要知道该计时器的ID, 举例:

 1     static int t = 1000 ;        //初始间隔为1000毫秒, 即1秒
2 switch(message)
3 {
4 case WM_CREATE:
5 SetTimer( hwnd, 1, t, NULL ) ; //创建一个ID为1, 时间间隔为t的计时器
6 return 0 ;
7
8 case WM_TIMER:
9 t += 1000 ; //每处理一次WM_TIMER消息将时间间隔增加1秒
10 SetTimer( hwnd, 1, t, NULL ) ; //重置ID为1计时器
11 MessageBox( hwnd, TEXT("时间间隔增加一秒!"), TEXT("计时器消息"), MB_OK ) ;
12 return 0 ;
13 }

  这段代码的作用就是首先将计时器的初始间隔时间设为1秒, 然后每处理一次 WM_TIMER 消息后都将消息间隔再增加一秒。

三、使用计时器需要知道的一些问题
    1>. 程序运行时会被 WM_TIMER 消息打断吗?
        或许当我们在执行一个很重要的任务时害怕已经被突然发送来的 WM_TIMER 消息打断而被迫去处理 WM_TIMER 消息, 实际上这种情况是不会发生的, WM_TIMER 和其他普通的消息一样, 当计时器发出该消息时Windows会把它放在该程序的消息队列中, 只有当 while( GetMessage(&msg, NULL, 0, 0) )从消息队列获取到该消息时程序才会进行处理。

    2>. 使用Windows计时器进行计时是否精确?
        使用Windows计时器进行计时并不精确, 主要有两方面的原因造成的:
            ①. 时钟周期的影响
                简单的说, Windows是通过获取底层的"时钟滴答"来进行计时的, 而 "时钟滴答" 是有一定的周期的, 举例来说, 假如这个滴答周期为55毫秒, 那么每滴答一次Windows就知道55毫秒过去了, 但是它没法知道10毫秒是什么时候过去的, 如果我们告诉Windows要进行一个100毫秒的计时, 那么Windows会拿100毫秒除以滴答周期55毫秒进行4四舍五入, 得到的结果为2, 然后当两个滴答过去后Windows才会通知你100毫秒已经过去了, 但实际上已经过去了110毫秒了。
                
                实际上, 在Windows98上, 55毫秒就是那时的计时器周期, 在Windows NT的Windows, 计时器的周期已经缩短到10毫秒左右, 也就是说误差已经缩小到10毫秒内。
                当向计时器设置间隔10毫秒以下的任务时, Windows只能以10毫秒计。
                
            ②. 消息处理的速度影响
                当 WM_TIMER 消息发送到消息队列后, 但前面已经积攒了大量的消息, 程序需要把前面的消息处理完才能处理 WM_TIMER 消息, WM_TIMER 消息是低优先级的, 只有当消息队列中没有其他消息时程序才能收到并处理他们, 也就是说当计时器发出 WM_TIMER 消息一直到当你收到 WM_TIMER 消息这个过程又将消耗一定的时间, 举个例子说, 当你计时器设定的时间频率是 100毫秒, 当这个时间过去后Windows向消息队列中放入一个 WM_TIMER 消息, 但是在该消息前面还有1个将会耗时1分钟才能处理完的消息, 那么从消息发出到程序处理实际上已经过去 1分钟再加上100毫秒了, 远大于我们期望的时间间隔。
                
            综上两个原因, 在Windows中, 用计时器进行精确计时是不准的, 因为他不够"专一"。

    3>. WM_TIMER消息在消息队列中会大量积存么?
    与 WM_PAINT 消息类似, WM_TIMER 消息在消息队列也同样不会大量存在, 当连续不断的产生多个 WM_TIMER 消息时, Windows会把这些连续存在的 WM_TIMER 消息合成一条, 其他的消息将会被抛弃销毁, 因此我们也同样不可以根据收到多少 WM_TIMER 消息来计算已经过去了多少时间, 因为我们无法知道 Windows 为我们丢弃了多少 WM_TIMER 消息。

四、计时器使用举例
    1>. 示例一: 定时退出程序
        该功能将创建一个计时器, 当程序运行10秒后自动退出程序, 窗口过程部分函数如下:

 1 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2 {
3 switch(message)
4 {
5 case WM_CREATE: //处理WM_CREATE消息时完成计时器的创建
6 SetTimer( hwnd, 1, 10000, NULL ) ; //设置一个ID为1, 时间间隔为10秒, 无回调函数的计时器
7 return 0 ;
8
9 case WM_TIMER: //处理WM_TIMER消息
10 KillTimer( hwnd, 1 ) ; //处理 WM_TIMER 消息时销毁计时器
11 PostQuitMessage( 0 ) ; //在消息队列中插入退出消息
12 return 0 ;
13 }
14 return DefWindowProc( hwnd, message, wParam, lParam ) ;
15 }

完整的示例代码:

VOID CALLBACK TimerProc( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime )
{ //定义计时器回调函数
MessageBox( hwnd, TEXT("我是负责弹出对话框的计时器! 间隔为5秒!"), TEXT("计时器消息"), MB_OK ) ;
} LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
HDC hdc ;
PAINTSTRUCT ps ;
static int iTimerID ; //记录计时器ID
static int y = ; //记录已输出行y坐标 switch(message)
{
case WM_CREATE: //处理WM_CREATE消息时完成计时器的创建
iTimerID = SetTimer( hwnd, , , TimerProc ) ; //设置一个ID随机分配、时间间隔为5秒, 有回调函数的计时器
SetTimer( hwnd, , , NULL ) ; //设置一个ID为2, 时间间隔为3秒, 无回调函数的计时器
return ; case WM_TIMER: //处理WM_TIMER消息
switch(wParam)
{
case : //处理ID为2的计时器消息
hdc = GetDC( hwnd ) ;
TextOut( hdc, , y, TEXT("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。"),
lstrlen("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。") ) ;
y += ; //向下移动20个像素, 模拟文字换行
ReleaseDC( hwnd, hdc ) ;
ValidateRect( hwnd, NULL ) ;
break ; /*
如果创建了更多的计时器, 这里继续case计时器的ID, 用来区分不同计时器发来的消息
*/
}
return ; case WM_DESTROY:
KillTimer( hwnd, iTimerID ) ; //销毁ID为随机分配的计时器
KillTimer( hwnd, ) ; //销毁ID为2的计时器
PostQuitMessage( ) ;
return ;
}
return DefWindowProc( hwnd, message, wParam, lParam ) ;
}

可以看到, 程序在处理 WM_CREATE 消息时完成了计时器的创建, 计时器的时间间隔为10秒, 当处理到 WM_TIMER 消息时首先销毁了定时器, 随后在程序的消息队列中插入了一个退出消息, 这样就简单的完成了程序的自动退出。

    2>. 示例二: 定时弹出对话框并定时在客户区绘制文字
        该功能需要实现的是创建两个计时器, 一个计时器负责提醒程序每间隔3秒在客户区输出一行文字, 另一个计时器间隔5秒, 负责弹出一个对话框, 告诉用户某些信息, 窗口过程函数部分的代码如下:

 1 VOID CALLBACK TimerProc( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime )
2 { //定义计时器回调函数
3 MessageBox( hwnd, TEXT("我是负责弹出对话框的计时器! 间隔为5秒!"), TEXT("计时器消息"), MB_OK ) ;
4 }
5
6 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
7 {
8 HDC hdc ;
9 PAINTSTRUCT ps ;
10 static int iTimerID ; //记录计时器ID
11 static int y = 10 ; //记录已输出行y坐标
12
13 switch(message)
14 {
15 case WM_CREATE: //处理WM_CREATE消息时完成计时器的创建
16 iTimerID = SetTimer( hwnd, 0, 5000, TimerProc ) ; //设置一个ID随机分配、时间间隔为5秒, 有回调函数的计时器
17 SetTimer( hwnd, 2, 3000, NULL ) ; //设置一个ID为2, 时间间隔为3秒, 无回调函数的计时器
18 return 0 ;
19
20 case WM_TIMER: //处理WM_TIMER消息
21 switch(wParam)
22 {
23 case 2: //处理ID为2的计时器消息
24 hdc = GetDC( hwnd ) ;
25 TextOut( hdc, 10, y, TEXT("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。"),
26 lstrlen("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。") ) ;
27 y += 20 ; //向下移动20个像素, 模拟文字换行
28 ReleaseDC( hwnd, hdc ) ;
29 ValidateRect( hwnd, NULL ) ;
30 break ;
31
32 /*
33 如果创建了更多的计时器, 这里继续case计时器的ID, 用来区分不同计时器发来的消息
34 */
35 }
36 return 0 ;
37
38 case WM_DESTROY:
39 KillTimer( hwnd, iTimerID ) ; //销毁ID为随机分配的计时器
40 KillTimer( hwnd, 2 ) ; //销毁ID为2的计时器
41 PostQuitMessage( 0 ) ;
42 return 0 ;
43 }
44 return DefWindowProc( hwnd, message, wParam, lParam ) ;
45 }

完整的示例代码:

#include<windows.h>

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;
VOID CALLBACK TimerProc( HWND, UINT, UINT, DWORD ) ; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
static TCHAR szAppName[] = TEXT("UseTimer") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ; wndclass.lpszClassName = szAppName ;
wndclass.hInstance = hInstance ;
wndclass.lpfnWndProc = WndProc ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ;
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ;
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.lpszMenuName = NULL ; if( !RegisterClass(&wndclass) )
{
MessageBox( NULL, TEXT("错误, 无法注册窗口类!"), szAppName, MB_OK | MB_ICONERROR ) ;
return ;
} hwnd = CreateWindow( szAppName, TEXT("UseTimer - Demo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
, ,
NULL, NULL, hInstance, NULL ) ; ShowWindow( hwnd, iCmdShow ) ;
UpdateWindow( hwnd ) ; while( GetMessage( &msg, NULL, , ) )
{
TranslateMessage( &msg ) ;
DispatchMessage( &msg ) ;
}
return msg.wParam ;
} LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
HDC hdc ;
PAINTSTRUCT ps ;
static int iTimerID ; //记录计时器ID
static int y = ; //记录已输出行y坐标 switch(message)
{
case WM_CREATE: //处理WM_CREATE消息时完成计时器的创建
iTimerID = SetTimer( hwnd, , , TimerProc ) ; //设置一个ID随机分配、时间间隔为5秒, 有回调函数的计时器
SetTimer( hwnd, , , NULL ) ; //设置一个ID为2, 时间间隔为3秒, 无回调函数的计时器
return ; case WM_TIMER: //处理WM_TIMER消息
switch(wParam)
{
case :
hdc = GetDC( hwnd ) ;
TextOut( hdc, , y, TEXT("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。"),
lstrlen("我是来自ID为2的计时器, 间隔为3秒, 我负责绘制文字。") ) ;
y += ; //向下移动20个像素, 模拟文字换行
ReleaseDC( hwnd, hdc ) ;
ValidateRect( hwnd, NULL ) ;
break ; /*
如果创建了更多的计时器, 这里继续case计时器的ID, 用来区分不同计时器发来的消息
*/
}
return ; case WM_DESTROY:
KillTimer( hwnd, iTimerID ) ; //销毁ID为随机分配的计时器
KillTimer( hwnd, ) ; //销毁ID为2的计时器
PostQuitMessage( ) ;
return ;
}
return DefWindowProc( hwnd, message, wParam, lParam ) ;
} VOID CALLBACK TimerProc( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime )
{ //定义计时器回调函数
MessageBox( hwnd, TEXT("我是负责弹出对话框的计时器! 间隔为5秒!"), TEXT("计时器消息"), MB_OK ) ;
}

效果:

同样, 程序在处理 WM_CREATE 消息时创建了两个计时器, 一个是使用不指定计时器ID方式创建的, 并且使用了计时器回调函数, 该计时器用来负责弹出对话框;
        另一个是使用指定计时器ID并且不使用计时器回调函数的方法创建的, 此方式将会把 WM_TIMER 消息发送到 hwnd 窗口, 当case到该消息时, 又使用了switch语句 进行了判断, 目的是根据不同ID的计时器作出不同的动作, 这里实际上是没有必要使用switch语句, 因为第一个计时器已经使用了计时器回调函数, 但如果创建的计时器有很多, 使用switch进行判断就很有必要了。

C语言Windows程序设计—— 使用计时器的更多相关文章

  1. windows程序设计笔记

    2014.05.06 新建一个visual C++ -- 常规 -- 空白 的项目,用.c后缀名指定这是一个用C语言来写的windows项目.和C语言的hellworld程序做了一个比较,按照wind ...

  2. 《Windows程序设计第5版》学习进度备忘

    书签:另外跳过的内容有待跟进 __________________学习资源: <Windows程序设计第5版珍藏版> __________________知识基础支持: _________ ...

  3. windows程序设计简介

    大家好,非常高兴和大家一起分享Windows开发心得,Windows已经诞生很多年了,一直因为它的简单易用而深受欢迎,相信很多人在使用Windows的时候,一定有这样一个想法:希望自己将来可以写一个很 ...

  4. 愉快的开始 - Windows程序设计(SDK)000

    愉快的开始 让编程改变世界 Change the world by program  参考教材 购买链接:Windows程序设计(第5版)(珍藏版)(附CD-ROM光盘1张)  学习环境 视频演示:W ...

  5. Windows程序设计:格式化对话框的设计

    刚开始学习Windows程序设计,磕磕碰碰,先做个小笔记缓缓神经,主要是将MessageBox这个Windows API函数的. MessageBox函数是许多人刚开始学习Windows程序设计或者是 ...

  6. Windows程序设计画图实现哆啦A梦

    在看雪论坛上看到的一个帖子,很喜欢,转载一下.原文地址:http://bbs.pediy.com/showthread.php?t=138630哆啦A梦是画出来的,不知道作者算这些坐标位置算了多久,真 ...

  7. windows程序设计01_utf8编码问题

    坚持与妥协 从学程序的第一天老师就给我们说源代码应该使用utf8保存.因为先入为主,"源代码应该使用utf8"的观念已经在"学院派"出身的程序员脑子里根深蒂固. ...

  8. Windows 程序设计(4) MFC 03 -系列学习

    本文整体目录和绝大部门内容来自 [鸡啄米网站]的MFC系列文章,欢迎支持原创 (一)VS2010/MFC编程入门之前言 VC++全称是Visual C++,是由微软提供的C++开发工具,它与C++的根 ...

  9. Windows 程序设计

    一.Win32 API /******************************************************************** created: 2014/04/1 ...

随机推荐

  1. PAT Advanced 1067 Sort with Swap(0,*) (25) [贪⼼算法]

    题目 Given any permutation of the numbers {0, 1, 2,-, N-1}, it is easy to sort them in increasing orde ...

  2. ES6 find()

    Array.prototype.find() 返回数组中满足提供测试函数的第一个元素的值,否则返回undefined let b = blogs.find(function(e) => { re ...

  3. 主流消息队列rocketMq,rabbitMq比对使用

    首先整理这个文章是因为我正好有机会实战了一下rocketmq,阿里巴巴的一个开源消息中间件.所以就与以往中rabbitmq进行小小的比较一下.这里主线的根据常见面试问题进行整理. 一.消息队列常用的场 ...

  4. LeetCode——542. 01 矩阵

    给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 0 0 ...

  5. Python文件基本操作及上下文管理

    文件基本操作 打开文件:f = open(fole_name,mode = 'r'),传入表示文件路径的字符串,会返回一个文件对象,mode是文件打开模式. 关闭文件:f.close(),调用给定文件 ...

  6. python学习——tuple

    tuple 上次谈到了列表,而这次所谈的元组其实和列表有许多相似的地方,故元组又叫"戴上了枷锁的列表".这是因为元组不能改动内部的元素,所以就不能使用上次谈到的append.ext ...

  7. Linux虚拟机添加硬盘

    任务:添加1块硬盘,并且分1个区,挂载到/bak 第一.插上硬盘 第二.分区 第三.格式化(定义:文件系统的类型)  FAT,FAT32,NTFS,ext1,ext2,ext3,ext4,.... 第 ...

  8. sin之舞---蓝桥杯练习

    问题描述 最近FJ为他的奶牛们开设了数学分析课,FJ知道若要学好这门课,必须有一个好的三角函数基本功.所以他准备和奶牛们做一个“Sine之舞”的游戏,寓教于乐,提高奶牛们的计算能力. 不妨设 An=s ...

  9. 迅为IMX6Q开发板提供原理图_底板PCB_驱动程序源码_芯片和LCD数据手册_开发板环境_使用手册

      迅为IMX6开发板: Android4.4/6.0系统  Linux + Qt5.7系统  Ubuntu12.04系统 部分案例:HMI:3D打印机:医疗设备:工控机:触控一体机:车载终端 核心板 ...

  10. IntelliJ的.iml文件及相关的Class Not Found 问题

    .iml 文件是IntelliJ IDEA 自动创建的模块文件,用于Java应用开发,存储一些模块开发相关的信息,比如一个Java组件, 插件组件,Maven组件等等, 还可能会存储一些模块路径信息, ...