应用举例 现在来举例说明上述几个模块的使用方法。 硬件环境描述: 为了控制一盏灯,需要单片机提供一个做控制功能的开关量,这里不描述外部接口电路,只说明当单片机的P10脚为高电平时,灯灭,当P10脚为低电平时,灯亮。 可以通过计算机由串口发送命令来控制,或通过一个按键(push button不是自锁式的按键)来手动控制(按键接在P11脚上,当键没有按下时,P11电平为高,键按下时,引脚电平被接低),当使用按键手动控制的时候,需要给计算机发送通知。 设定串口通讯指令如下: 数据包由0xff做包头,4个字节长,第二个字节为命令代码,第三个字节为数据,最后一个字节为校验位。 命令和数据代码有如下组合: (计算机发给单片机) 0x10 0x01: 计算机控制灯亮。(数据位是非零值即可) 0x10 0x00: 计算机控制灯灭。 (单片机发给计算机) 0x11 0x01:单片机正常执行控制指令,返回。(数据位是非零值即可) 0x11 0x00: 单片机不能够正常执行控制指令,或控制指令错(不明含义的数据包或校验错等)。 0x12 0x01:手动控制灯亮。(数据位是非零值即可) 0x12 0x00: 手动控制灯灭。 建立工程: 在硬盘上建立文件夹Projects,在Projects下建立Common文件夹及Example文件夹。将各模块的头文件及实现文件拷贝到Common 文件夹下(推荐使用这样的文件组织结构,其它工程也可以建立在Projects下,各工程共享Common文件夹中的代码)。 启动KeilC的IDE,在Example下建立新工程,将各模块的实现文件包含进工程。 在Example文件夹下建立Output文件夹,更改工程设置,将Output作为输出文件和List文件的输出文件夹(推荐使用这样的结构,当保存工程文件时,可以简单的删除Output文件夹中的内容而不会误删有用的工程文件)。 建立工程配置头文件Config.h及工程主文件Example.c,并将Exmaple.c文件加入工程。 输入代码: 代码的具体编写过程略。下面是最后的Config.h文件及Example.c文件。 // // File: Config.h // #ifndef _CONFIG_H_ #define _CONFIG_H_ #include <Atmel/At89x52.h> // 使用AT89C52做控制 #include “../Common/Common.h” // 使用自定义的数据类型 #define TIMER_RELOAD 922 // 11.0592MHz晶振,1ms中断周期 #define TIMER_KBSCANDELAY 40 // 40ms重检测按键状态,即40ms消抖 #define SCOMM_AsyncInterface // 使用异步通讯服务 #define IsPackageHeader(x) ((x) == 0xff) // 判断包头是不是0xff #define IsPackageTailer(x, y, z) ((y) <= (z)) // 判断包的长度是不是足够 #endif // _CONFIG_H_ // // File: Example.c // #include <Atmail/At89x52.h> #include “../Common/Common.h” #include “../Common/Timer.h” #include “../Common/Scomm.h” #include “../Common/KBScan.h” BIT gbitLampState = 1; // 灯的状态,缺省为off static void Initialize() { InitTimerModule(); // 初始化时钟模块 InitSCommModule(0xfd, TRUE); // 初始化通讯模块,11.0592MHz晶振, // 波特率为19200 EA = 1; // 开中断 } void main() { Initialize(); // 初始化 while(TRUE) // 主循环 { ImpTimerService(); // 实现时钟中断服务,如键盘扫描 AsyncRecePackage(4); // 接收4个字节长的数据包 } } // 在中断外部响应时钟中断事件 void OnTimerEvent() { // do nothing } // 控制外部灯 static void TriggerLamp(BIT bEnable) { P10 = ~bEnable; // 需要反相控制 } // 键扫描回调函数 BYTE KBScan() { BIT b; P11 = 1; // 读之前拉高引脚电平 b = P11; // 读入引脚状态 return ~b; // 数据反相做扫描码 } // 计算校验和 static BYTE CalcCheckSum(BYTE* pbyBuf, BYTE byLen) { BYTE by, bySum = 0; for(by = 0; by < byLen; by++) bySum += pbyBuf[by]; return 0 – bySum; } // 接收到键盘消息回调函数 void OnKeyPressed(BYTE byvalue, BYTE byState) { BYTE by[4]; if(byState == 0) { switch(byvalue) { case 0x01: gbitLampState = ~g bitLampState; // 灯状态取反 TriggerLamp(gbitLampState); // 执行控制 by[0] = 0xff; // 构造数据包 by[1] = 0x12; by[2] = (BYTE)gbitLampState; by[3] = CalcCheckSum(by, 3); // 求校验和 SendPackage(by, 4); // 发送数据包 break; // 处理其它扫描码 default: break; } } // 接收到数据包回调函数 void OnRecePackage(BYTE* pbyBuf, BYTE byBufLen) { BYTE by[4]; by[0] = 0xff; by[1] = 0x11; if(byBufLen != 4  pbyBuf[3] != CalcCheckSum(pbyBuf, 3)) { by[2] = 0; by[3] = CalcCheckSum(by, 3); SendPackage(by, 4); // 处理长度或校验和不正确 } switch(pbyBuf[1]) { case 0x10: gbitLampState = (BIT)pbyBuf[2]; TriggerLamp(gbitLampState); by[2] = 1; by[3] = CalcCheckSum(by, 3); SendPackage(by, 4); // 发送成功执行通知 break; default: // 不知道的命令 by[2] = 0; by[3] = CalcCheckSum(by, 3); SendPackage(by, 4); // 发送没有成功执行通知 break; } } |