【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-终端源码

【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-终端源码

广东职业技术学院 欧浩源

一、关于硬件电路

关于这个综合实训案例,具体需求详见《【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-题目需求》。

我自己实在“全国职业院校技能大赛--物联网技术应用赛项”的Zigbee模块上实现的。该模块的电路应该和TI公司官方评估板的推荐电路差不多,我想现在市面上很多开发板也是参考这样的电路设计,只要您使用的开发板上有LED灯、按键输入、串口输出和一路A/D转换,都可以使用我的源码实现这个综合实训,硬件电路引脚连接如下图:

二、程序设计思路

整个测控终端的代码大约有370多行,在这里不能逐一的分析和讲述。各个基础功能模块的操作和《CC2530入门教材》中的案例没有太大差异,其核心代码为主要业务逻辑的处理。这里的主函数不想前面单一功能模块案例的那么简单了,实际上它是一个中断服务与前后台处理的程序结构。既要处理本地的各种功能,又要响应远程的各种命令,不但要管理各个基本任务,而且还要进行运行状态的切换。在功能比较复杂的系统中,可能还需要引入基于状态机思路的事件调度思想或者更高级的程序框架结构。
        串口数据接收是测控终端的第一个关键地方。在这里不是要接收一个字节或者一串字符,而是要完整正确接收完8个字节的一帧数据,在数据接收完成后通过一个标志F_DataRecv通知主函数一帧数据接收完成可以进行解析和执行了。命令帧的正确解析和顺利执行是测控终端运行的枢纽,这个部分没有难点,主要是根据通信规约来逐一解析。既然测控终端要采集光照数据和记录设备状态,那么ADC数据采集部分也是一个核心部分,但是没有太大的难点。在进行阈值比较,进行灯光自动开关的时候,不要忘记对照明设备状态的实时登记哦。

三、项目源代码

#include "ioCC2530.h"

#define D_SEC   P1_1
#define D_DAT   P1_0
#define D_AUTO  P1_4
#define D_LIGHT P1_3
#define K_LIGHT P1_2
#define K_ALARM P0_1

unsigned char F_Alarm = 0;
unsigned char F_Clear = 0;
unsigned char F_02Second = 0;
unsigned char F_RecvData = 0;
unsigned char F_SendInfo = 0;
unsigned char ID = 0x01;
unsigned char SendDat[8],RecvDat[8];
unsigned char count_t1,count_recv;

/*=======================简单的延时函数========================*/
void Delay(unsigned int t)
{
  while(t--);
}
/*======================端口初始化函数========================*/
void Init_Port()
{
  P1SEL &= ~0x1b;     //P1_0、P1_1、P1_3和P1_4作为通用I/O端口
  P1DIR |= 0x1b;      //P1_0、P1_1、P1_3和P1_4端口输出
  P1SEL &= ~0x04;     //P1_2作为通用I/O端口
  P1DIR &= ~0x04;     //P1_2端口输入
  P0SEL &= ~0x02;     //P0_1作为通用I/O端口
  P0DIR &= ~0x02;     //P0_1端口输入
  D_SEC = 0;          //秒闪灯关闭
  D_DAT = 0;          //数据灯关闭
  D_AUTO = 0;         //应急灯关闭
  D_LIGHT = 0;        //照明灯关闭
}
/*=======================灯光测试函数========================*/
void Check_Light()
{
  D_SEC = 1;
  Delay(60000);
  D_DAT = 1;
  Delay(60000);
  D_AUTO = 1;
  Delay(60000);
  D_LIGHT = 1;
  Delay(60000);
  D_SEC = 0;
  Delay(60000);
  D_DAT = 0;
  Delay(60000);
  D_AUTO = 0;
  Delay(60000);
  D_LIGHT = 0;
  Delay(60000);
}

/*========================打开照明灯=========================*/
void Open_Light()
{
  D_LIGHT = 1;            //打开照明灯
  SendDat[5] |= 0x01;     //设置照明灯为打开状态
}
/*========================关闭照明灯=========================*/
void Close_Light()
{
  D_LIGHT = 0;            //关闭照明灯
  SendDat[5] &= ~0x01;    //设置照明灯为关闭状态
}
/*======================手动控制照明灯=======================*/
void Control_Light()
{
  if(K_LIGHT == 0)              //判断按键的信号
  {
    Delay(200);                 //去抖动
    if(K_LIGHT == 0)            //确认按键按下
    {
      while(K_LIGHT == 0);      //等待按键松开
      if((SendDat[5]&0x01) == 0x00)   //判断灯光当前状态
      {
        Open_Light();
      }
      else
      {
       Close_Light();
      }
    }
  }
}
/*=======================定时器1初始化========================*/
void Init_Timer1()
{
  T1CC0L = 0xd4;      //设置最大计数值低8位
  T1CC0H = 0x30;      //设置最大计数值高8位
  T1IE = 1;
  EA = 1;
  T1CTL = 0x0f;       //分频系数是128,正计数/倒计数模式
}

/*====================定时器1服务函数========================*/
#pragma vector = T1_VECTOR
__interrupt void Timer1_int()
{
  T1STAT &= ~0x20;    //清除定时器1的溢出中断标志位
  count_t1++;         //定时器1溢出一次加1,溢出周期为0.2S

  F_02Second = 1;
  if(count_t1 == 5)   //定时1秒的时间间隔到。
  {
    D_SEC = ~D_SEC;     //切换秒闪灯
    count_t1 = 0;
  }
}
/*====================ADC0初始化函数========================*/
void Init_ADC0()
{
  P0SEL |= 0x01;      //P0_0端口设置为外设功能
  P0DIR &= ~0x01;     //P0_0端口设置为输入端口
  APCFG |= 0x01;      //P0_0作为模拟I/O使用
}
/*====================ADC0数据读取函数=====================*/
void Read_ADC0()
{
  ADCIF = 0;
  //参考电压选择AVDD5引脚,256抽取率,AIN0通道0
  ADCCON3 = (0x80 | 0x10 | 0x00);
  while(!ADCIF);      //等待A/D转换完成,
  SendDat[3] = ADCH;  //读取ADC数据高位寄存器
  SendDat[4] = ADCL;  //读取ADC数据低位寄存器
}
/*================判断光照度并执行应急照明===============*/
void CheckDataAndExceute()
{
  if(SendDat[3] < 0x06)     //光照阈值比较
  {
    D_AUTO = 1;             //自动点亮应急灯
    SendDat[5] |= 0x02;     //标志应急灯打开状态
  }
  else{
    D_AUTO = 0;             //自动关闭应急灯
    SendDat[5] &= ~0x02;    //标志应急灯关闭状态
  }
}
/*=====================串口0初始化函数====================*/
void Init_Uart0()
{
  PERCFG = 0x00;    //串口0的引脚映射到位置1,即P0_2和P0_3
  P0SEL = 0x0C;     //将P0_2和P0_3端口设置成外设功能
  U0BAUD = 59;      //16MHz的系统时钟产生9600BPS的波特率
  U0GCR = 9;
  U0UCR |= 0x80;    //禁止流控,8位数据,清除缓冲器
  U0CSR |= 0xC0;    //选择UART模式,使能接收器
  UTX0IF = 0;       //清除TX发送中断标志
  URX0IF = 0;       //清除RX接收中断标志
  URX0IE = 1;       //使能URAT0的接收中断
  EA = 1;           //使能总中断
}
/*===================串口0接收中断服务函数==================*/
#pragma vector = URX0_VECTOR
__interrupt void UR0_SendInt()
{
  unsigned char dat;
  URX0IF = 0;               //清除RX接收中断标志
  dat = U0DBUF;             //终端接收到一个字节数据
  if(dat == 0xaf)           //首先判断是不是0xaf
  {
    RecvDat[0] = dat;       //如果是0xaf则放到帧头的位置
  }
  else if(RecvDat[0] == 0xaf)
  {
    RecvDat[count_recv+1] = dat;
    count_recv++;           //接收完数据帧剩下的7个字节
    if(count_recv == 7)
    {
      count_recv = 0;       //帧计数清0
      F_RecvData = 1;       //标志一个8位的数据帧接收完成
    }
  }
}
/*====================清空接收缓存函数====================*/
void Clear_RecvData()
{
  unsigned char i;
  for(i = 0; i < 8; i++)
  {
    RecvDat[i] = 0;
  }
}
/*===================解析并执行上位机命令==================*/
void ExecuteCmd()
{
  if(RecvDat[0] == 0xaf && RecvDat[7] == 0xfa)
  {
    if(ID == RecvDat[1])        //首先判断命令帧的ID和本机ID是否一致
    {
      switch(RecvDat[2])        //然后解析命令域
      {
        case 0x01:              //启动数据采集功能
            F_SendInfo = 1;
            D_DAT = 1;
          break;
        case 0x02:              //关闭数据采集功能
            F_SendInfo = 0;
            D_DAT = 0;
          break;
        case 0x03:              //远程打开照明灯
          Open_Light();
          break;
        case 0x04:              //远程关闭照明灯
          Close_Light();
          break;
        case 0x0f:              //解除现场报警
          if(F_Alarm == 1)      //只有在已经触发报警的情况下
          {
            F_Clear = 1;        //才会解除现场报警
          }
          break;
      }
    }
  }
  Clear_RecvData();             //解析命令完成后清空接收数据缓存
}
/*====================发送单个字节的函数===================*/
void UR0SendByte(unsigned char dat)
{
  U0DBUF = dat;
  while(!UTX0IF);
  UTX0IF = 0;
}
/*=====================发送数据帧的函数====================*/
void UR0SendString(unsigned char *str, unsigned char num)
{
  unsigned char i;
  for(i = 0; i < num; i++)
  {
    UR0SendByte(str[i]);
  }
}
/*====================计算校验和的函数====================*/
void CheckSum()
{
  unsigned char i;
  SendDat[6] = 0;
  for(i = 0; i < 6; i++)
    SendDat[6] += SendDat[i];
}
/*===================初始化发送缓存函数===================*/
void Init_SendData()
{
  SendDat[0] = 0xaf;
  SendDat[1] = ID;
  SendDat[2] = 0xff;
  SendDat[3] = 0x00;
  SendDat[4] = 0x00;
  SendDat[5] = 0x00;
  SendDat[6] = 0x00;
  SendDat[7] = 0xfa;
}
/*===================终端上线命令帧函数===================*/
void SendSysOpenCmd()
{
  Init_SendData();
  SendDat[2] = 0x10;
  CheckSum();
  UR0SendString(SendDat, 8);
}
/*===================上传终端数据的函数===================*/
void Send_Infomation()
{
  SendDat[2] = 0x11;
  CheckSum();
  UR0SendString(SendDat, 8);
}
/*====================上传报警帧的函数====================*/
void Send_Alarm()
{
  SendDat[2] = 0x1f;
  CheckSum();
  UR0SendString(SendDat, 8);
}

/*====================秒时间到服务函数====================*/
void WorkFor02Second()
{
    Read_ADC0();        //每秒钟采样一次光照度传感器的电压
    if(F_SendInfo == 1) //上传测控终端的数据及设备状态
    {
      Send_Infomation();
    }
}
/*=======================外部中断初始化========================*/
void Init_INTP()
{
  P0IE = 1;           //端口0中断使能
  P0IEN |= 0x02;      //端口P0_1外部中断使能
  PICTL |= 0x01;      //端口P0_0到P0_7下降沿触发
  EA = 1;             //使能总中断
}
/*====================外部中断服务函数========================*/
#pragma vector = P0INT_VECTOR     //外部中断0的向量入口
__interrupt void Int0_Sevice()
{
  P0IFG &= ~ 0x02;    //软件清除P0_1端口的标志位
  P0IF = 0;           //软件清除P0端口的标志位
  F_Alarm = 1;        //设置报警标志位
  Send_Alarm();       //向上位机发送现场报警命令帧
}
/*=======================报警灯光函数========================*/
void Alarm_Light()
{
  D_SEC = 1;
  D_DAT = 1;
  D_AUTO = 1;
  D_LIGHT = 1;
  Delay(60000);
  Delay(60000);
  Delay(60000);
  D_SEC = 0;
  D_DAT = 0;
  D_AUTO = 0;
  D_LIGHT = 0;
  Delay(60000);
  Delay(60000);
  Delay(60000);
  if(F_Clear == 1)      //上位机解除现场报警
  {
    F_Clear = 0;        //恢复报警安全锁
    F_Alarm = 0;        //解除报警,恢复正常工作状态
    Check_Light();      //重新检查照明设备状态
    SendSysOpenCmd();   //重新发送终端上线命令帧
    T1CTL = 0x0f;       //重新打开定时器
  }
}
/*===================测控终端初始化函数====================*/
void Init_System()
{
  Init_Port();                //初始化通用I/O端口
  Init_INTP();                //初始化外部中断
  Init_Timer1();              //初始化定时器1
  Init_ADC0();                //初始化ADC0
  Init_Uart0();               //初始化串口0
  Init_SendData();            //初始化串口发送数据缓存
  Clear_RecvData();           //初始化串口接收数据缓存
  Check_Light();              //检测照明设备工作状态
  SendSysOpenCmd();           //向上位机发送终端上线命令帧
}
/*=======================主函数===========================*/
void main()
{
  Init_System();              //测控终端初始化
  while(1)
  {
    if(F_RecvData == 1)       //接收到一个完整的数据帧
    {
      ExecuteCmd();           //解析命令帧并执行功能
      F_RecvData = 0;         //清除命令帧接收完成标志
    }
    if(F_Alarm == 0)          //现场正常工作状态
    {
      Control_Light();        //手动控制照明灯
      CheckDataAndExceute();  //自动控制应急灯
      if(F_02Second == 1)     //定时0.2秒到
      {
        WorkFor02Second();    //执行0.2秒钟应该做的事情
        F_02Second = 0;       //清除定时0.2秒到的标志
      }
    }
    else                      //现场进入报警工作状态
    {
      T1CTL = 0x00;           //关闭定时器
      F_02Second = 0;         //清除定时标志
      F_SendInfo = 0;         //清除数据发送标志
      Alarm_Light();          //开启现场报警灯
    }
  }
}
时间: 2024-10-10 07:58:56

【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-终端源码的相关文章

【CC2530入门教程-03】CC2530的中断系统及外部中断应用

第3课  CC2530的中断系统及外部中断应用 广东职业技术学院  欧浩源 一.中断相关的基础概念  内核与外设之间的主要交互方式有两种:轮询和中断. 轮询的方式貌似公平,但实际工作效率很低,且不能及时响应紧急事件:中断系统使得内核具备了应对突发事件的能力. 在执行CPU当前程序时,由于系统中出现了某种急需处理的情况,CPU暂停正在执行的程序,转而去执行另外一段特殊程序来处理出现的紧急事务,处理结束后,CPU自动返回到原来暂停的程序中去继续执行. 这种程序在执行过程中由于外界的原因而被中间打断的

2013-2014-2学期,综合实训概况

1.选题 (1)学生管理信息系统 (2)宿舍管理信息系统 (3)仓库管理信息系统 (4)员工管理信息系统 (5)婚礼抽奖程序 (6)自选课题 2.分组 (1)郑超.周瑾玉.覃川川,题目:学生成绩管理系统设计: (2)施越佳.杨焕槟.徐家豪,题目:学生管理系统设计: (3)马晓翠.沈利.叶黎明.题目:宿舍管理系统设计: (4)赵瑛杰.陈云武.王凌雯.题目:学员培训管理系统设计: (5)张琳.袁金萍.吴剑,题目:宿舍管理系统设计; (6)周行.徐诗雨.连宁宁,题目:美食管理系统设计: (7)张朋.求

【CC2530入门教程-06】CC2530的ADC工作原理与应用

第6课  CC2530的ADC工作原理与应用 广东职业技术学院  欧浩源 一.A/D转换的基本工作原理 将时间上连续变化的模拟量转化为脉冲有无的数字量,这一过程就叫做数字化,实现数字化的关键设备是ADC. ADC:数模转换器,将时间和幅值连续的模拟量转化为时间和幅值离散的数字量,A/D转换一般要经过采样.保持.量化和编码4个过程. 二.CC2530的A/D转换模块 CC2530的ADC模块支持最高14位二进制的模拟数字转换,具有12位的有效数据位,它包括一个模拟多路转换器,具有8个各自可配置的通

【CC2530入门教程-05】CC2530的串行接口原理与应用

第5课  CC2530的串行接口原理与应用 广东职业技术学院  欧浩源 一.并行通信与串行通信 微控制器与外设之间的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信. 并行通信:指数据的各位同时发送或接收,每个数据位使用单独的一条导线.传输速度快.效率高,但需要的数据线较多,成本高. 串行通信:指数据一位接一位地顺 序发送或接收.需要的数据线少,成本低,但传输速度慢,效率低. 二.CC2530的串口通信模块 CC2530有两个串行通信接口USART0和USART1,它们能

Android自定义view教程06--Activity的绘制流程简单分析(基于android 4.0源码进行分析)

要明白这个流程,我们还得从第一部开始,大家都知道 在activity里面 setcontentview 调用结束以后 就可以看到程序加载好我们的布局文件了,从而让我们在手机上看到这个画面. 那么我们来看一下这个源码是如何实现的. 1 /** 2 * Set the activity content from a layout resource. The resource will be 3 * inflated, adding all top-level views to the activit

Unreal Engine 4(虚幻UE4)GameplayAbilities 插件入门教程(五)技能属性集(AttributeSet)

如果没有完成前面的教程,请前往学习.先上一段理论介绍(源于https://wiki.unrealengine.com/GameplayAbilities_and_You#GameplayTasks): [如果您没有耐心看完这些介绍,请调到MarkA处] AttributeSets are thankfully very simple to explain. They define float values (and ONLY float values. Right now only float

Unreal Engine 4(虚幻UE4)GameplayAbilities 插件入门教程(四)技能屏蔽和简单的Buff等

本节内容继续上一节教程的内容(如果没有看过前面的教程,请前往学习),不会讲太难的新东西,而是继续探究技能标签(Abiilty Tags)的内容.先来一道开胃菜. 第1.1步: 将上一次的召唤冰龙中的CancelAbilitiesWithTags清空,表示这个技能不会打断任何其他技能.在"阻塞技能列表"BlockAbilitiesWithTags中配置Magic.Fire. 第1.2步:冰龙的激活逻辑修改为下图所示,表示它也是耗时的技能. 第1.3步:运行,先释放烈焰之鸟后释放冰龙,从打

java综合实训第二次

第93课:SparkStreaming updateStateByKey 基本操作综合案例实战和内幕源码解密

Spark Streaming的DStream为我们提供了一个updateStateByKey方法,它的主要功能是可以随着时间的流逝在Spark Streaming中为每一个key维护一份state状态,通过更新函数对该key的状态不断更新.对每一个新的batch而言,Spark Streaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新(对每个新出现的key,会同样执行state的更新函数操作),但是如果通过更新函数对state更新后返回none