嵌入式开发笔记


计算机系统:  cpu    内存(ram)  硬盘(rom)  输入/输出设备(io)


51单片机: 一个芯片具有CPU, 内存, ROM(flash),  IO口(用于接输入/输出设备)
SOC: system on chip(一个芯片就具有一个计算机系统的组成). 高级版本的单片机


单片机,SOC: cc2530这个芯片就是一个完整计算机系统: cpu, ram, rom, IO口


CC2530 == 51单片机 + 无线传输控制器


在计算机系统里,使用高电平(3.3v)表示二进制的1, 低电平(0v)表示二进制的0
IO: input  / output port
硬件驱动:主要的任务就是获取IO口的电平状态, 或者是设置相应IO口的电平
硬件里面会留下接口给软件开发人员调用,从面获取/设置IO口的电平


cc2530:  P0_0 ~ P0_7,  P1_0 ~ P1_7, P2_0 ~ P2_4
//////////////////////////////
PULLUP(上拉): 表示此IO口默认工作时的电平是处于高电平状态,但可以受别人的电平影响。
与直接接电源线不同。直接接电源线,此IO口的电平不会改变。


PULLDOWN(下拉):表示默认工作是低电平状态。
/////////////////////////////


io口是由IO controler来管理的. 我们是通过io控制器来配置io的输出电平或者获取io口的电平 


IO controler管理io口的作用(可给外围模块来控制,如uart的控制器, 或者用软件代码来控制);
还可以配置io口作输入或输出(输入就是获取io口的电平, 输出是要改变io的电平), 接不接上拉或下拉(上下拉就是设置IO口的默认电平就处在高低电平状态). 还有IO口作中断功能的控制.


//////////////
There are 21 digital input/output pins that can be configured as general-purpose digital I/O or as peripheral
I/O signals connected to the ADC, timers, or USART peripherals. The usage of the I/O ports is fully
configurable from user software through a set of configuration registers.
The I/O ports have the following key features:


• 21 digital input/output pins
• General-purpose I/O or peripheral I/O
• Pullup or pulldown capability on inputs
• External interrupt capabilit


IO口作通用IO使用或是作如ADC, timer, uart这些硬件模块来控制是由 寄存器 PxSEL来确定(每组IO都一个选择寄存器, P0SEL, P1SEL, P2SEL)
P0组有8个IO口, P0SEL这个寄存器里的值是8位的, 每一位的值对应对一个IO口,如第3位的值对应P0_3口
PxSEL(select), PxDIR(direction), PxINP(input pull)


输出: 表示需要改变外面IO口的电平,也可用于判断外面IO口的电平
输入: 表示只判断外面IO口的电平, 改变不了外面IO口的电平
作通用IO使用时,需通过寄存器PxDIR来设置是作输入或是输出


通过寄存器PxINP来设置是接上拉或下拉
寄存器P0,P1, P2是用于获取IO口的电平(作输入使用时),或是改变IO口的电平(作输出时).


P0_1 : 在电路里表示P0组IO口的第1个
       在代码里表示对应的IO口。  P0_1 = 1; // P0 |= 1<<1;


///////////////
烟雾传感器: 
可调电阻用于调节灵敏度
    AO: 模拟信号输出,输出的电压值越大表示烟雾浓度就越大(只能接ADC)
    DO: 数字输出,只有0/1, 表示有或没有烟雾探测到
    VCC:3.3v  ~ 5v
    GND:


人体感应:
两个可调电阻, 一个用于调节灵敏度, 一个调节间隔时间
   VCC: 3.3 ~ 5v
   GND:
   OUT: 探测到人输出高电平, 没人就输出低电平






光敏传感器:
   VCC:
   GND:
   DATA:  当有光或没有光时,这个脚会输出高/低电平

//////////////P61
时钟信号: 如10Hz, 表示在一秒钟内有10个周期信号(从一种边沿到下一个相同的边沿), 每个信号的周期持续100ms
硬件通过这些信号,即可知道什么时候需要执行下一步的工作.
         如CPU工作频率为12M, 一秒钟有12M个时钟周期, 每个周期时间执行一条指令.
 
时钟信号的源头由晶振(电子元件)产生


核心板上SOC上所接的晶振是32Mhz, 但是SOC复位时默认是使用16Mhz时钟




CLKCONCMD寄存器的OSC位来确定的(0: 32Mhz,  1:16Mhz(复位值))


当改时钟时,需确保 CLKCONSTA.OSC = CLKCONCMD.OSC
CLKCONCMD.CLKSPD 这里选择使用什么频率的时钟


//从16Mhz时钟改成32M时钟
CLKCONCMD &= ~(1<<6); //32Mhz source
while (CLKCONSTA & (1<<6))
;
CLKCONCMD &= ~7; //32Mhz
while (CLKCONSTA & 7)
;


////////////////////////////
///////////////////////////


通过一根导线,两个计算机通信。 发送数据端改变这根导线的电平(发1就改成高电平,发0改成低电平), 接收数据端只要判断导线上的电平即可得到发送端发出的二进制数据


两个设备收发数据归根到底就是改变电平/判断电平。一般情况下,设备都没有接真正的地线, 都是使用电源的负极作地线,但电源的负极对地线是电压差的,这样会引出两个设备间的参考电压值不一致。发送的3.3v有可能在接收端判断为2v.
要解决这个问题,必须保证通信双方共有一个参考标准才行,共地(将两个设备的地线连接起来)


单工: 数据传输的方向是固定的, 发送端---> 接收端
半双工: 数据传输的方向是可以双向的,但不能同一时刻收发.   
全双工: 可同时双向传输


  tx  
  gnd
  rx
////
如要发出二进制数据:  101110, 则发送端先改成高电平, 低电平,高电平, 高电平, 高电平, 低电平.每个数据信号电平的持续时间必须保持一致。如每位数据的信号需持续10ms, 则一秒钟可收发100位数据(波特率 100bit per second)
意味着, 收发双方需共用一个波特率才可以.
常用的波特率:  
gps, 蓝牙, gsm, 51单片机, 云台:  9600
arm开发板, zigbee, 3g/4g模块:   115200


uart的tx空闲时时高电平状态, 要发出数据时,先发出一个低电平信号(持续一位数据的时间,如100bps, 则10ms), 用于通知接收端准备收数据.这一位数据叫始启位(Start bit).


uart发出的数据是按帧为单位的:
一帧数据由: Start bit(1位低电平), Data bit(5/6/7/8位,用户的数据), 校验位(奇偶校验1位, 或不用校验), Stop bit(1,2两位高电平)




uart通常需配置的内容: 波特率, 数据位, 校验位, 停止位
    如果一个uart设备没有指定使用怎样的数据位, 校验位, 停止位, 则直接使用8N1(8位数据位, 没有校验位, 1位停止位)


三根线的uart设备基础接法:
A:                 B:
  tx  --->-----    rx
  GND <-------->   GND
  rx  ----<-----   tx


五根线的uart设备接法:
A:                 B:
  tx  --->-----    rx
  GND <-------->   GND
  rx  ----<-----   tx
  cts ---->-----   rts // cts, rts用于硬件流控使用, 
  rts ----<-----   cts


////
发出uart数据,根据要发出的二进制数据改变tx脚的电平, 持续时间为:1/波特率. 我们可以直接把IO作tx, 根据数据与波特率来改变此IO口的电平即可. 这样作是可行的,但比较麻烦, 所以一般CPU都会提供uart控制器帮我们根据需求控制IO口的电平, 发出符合uart的信号


使用uart控制器, 需要设置控制器使用的波特率, 数据位,校验位,停止位,硬件流控是否使用.
设置好后,即可把数据交给控制器发出和把控制器接收到的数据取回


ttl电平:  二进制1: 1.8v, 3.3v, 5v     二进制0: 0v
ttl电平如果传输距离远的话,导线的内阻就会变大而分一部分压降, 对接收端的判断电压有影响.
为了解决这个问题,引用RS232接口标准
RS232电平: 二进制1:  -3v ~ -25v    二进制0:  3v ~ 25v
uart的ttl电平由电平转换芯片(MAX232, SP323)转成RS232电平
uart的ttl电平可由芯片PL2303, CH342转成 usb接口电平


cc2530里提供两个usart控制器, 这两个控制器可选择作uart控制器或spi控制器使用


uart0的tx,rx口可以选择 P0_2,P0_3 也可以选择 P1_4, P1_5
通过寄存器PERCFG来确定使用哪两个IO口


U0CSR: 控制与状态寄存器
U0UCR: uart控制器相关的配置(8N1)
U0GCR[BAUD_E]: uart的波特率相关
U0DBUF: 用于把数据交给uart控制器,或者从uart控制器里把接收到的数据取回来
U0BAUD[BAUD_M]: uart的波特率相关


///////
     P0SEL |= (1<<2)|(1<<3); //P0_2, P0_3 as uart0_rx/tx
     PERCFG &= ~1; //uart0 alt1(p0_2, P0_3)
     
     U0CSR = (1<<7);  // uart mode
     U0UCR = 0; //8N1
     U0GCR = 11;
     U0BAD = 216;
     


注意minicom只显示接收到的内容, minicom上按enter键只会发'\r'过去


//////////////////中断


CC2530: 共有18个中断源(18个中断控制器的中断线)


中断控制器: 用于扩展中断源, 管理中断线的使能与关闭, 记录中断线的状态,并清除中断


In order to enable any of the interrupts, the following steps must be taken:
1. Clear interrupt flags


2. Set individual interrupt-enable bit in the peripherals SFR register, if any.(如果中断线的IO口是由其它控制器控制的话,则需要在控制器的配置寄存器里开启. 如usart的发送或接收中断,需要usart的配置寄存器里开启中断功能)


3. Set the corresponding individual interrupt-enable bit in the IEN0, IEN1, or IEN2 register to 1.


4. Enable global interrupt by setting the EA bit in IEN0 to 1.


5. Begin the interrupt service routine at the corresponding vector address of that interrupt. See Table 2-5 for addresses.


////////////
内部中断:中断线不会引出cpu芯片外面, 中断线已安排给固定的外围控制器使用的. (如usart, 定时器, ADC等中断)


外部中断: 中断线引出cpu芯片外面, 由IO口来复用的. 我们可用于接其它模块的功能.
外部中断需要在IO口控制器里配置作中断使用. 而且需指定此IO口在什么电平状态下触发中断


• P0IEN: P0 interrupt enables
• P1IEN: P1 interrupt enables
• P2IEN: P2 interrupt enables
• PICTL: P0, P1, and P2 edge configuration
• P0IFG: P0 interrupt flags
• P1IFG: P1 interrupt flags
• P2IFG: P2 interrupt flags


一个IO口的电平状态有: 高电平, 低电平, 下降沿, 上升沿, 双边沿
CC2530外部中断线只能捕捉:下降沿/上升沿
//////////按键中断的初始化


 // IEN1.P0IE: P0 interrupt enable     // IEN* 控制18种中断源的开启
  IEN1 |= 1<<5;


  //P0IEN: P0 interrupt enables
  P0IEN |= 1<<1; // P0_1 irq enable
    
  //PICTL: P0, P1, and P2 edge configuration
  PICTL |= 1; // P0 falling edge
  
  // P0IFG: P0 interrupt flags
  P0IFG = 0;
  
  // global irq enable
  //  IEN0 |= 1<<7;
  EA = 1;


//中断处理函数
#pragma vector = P0INT_VECTOR
__interrupt void btn1_irq(void)
{
  //清除中断
  P0IFG = 0; 
  // IRCON &= ~(1<<5)
  P0IF = 0;
}
////////////////////////////////
SDK: software development kit
BSP: board support package(板级支持包):硬件电路图,PCB. 相应的源码
API: Application Programming Interface  编程应用接口
CBC-MAC:  Cipher Block Chaining Message Authentication Code
CCM:   Counter with CBC-MAC (mode of operation)
CCM*:  Extension of CCM
FCS:   Frame Check Sequence
HAL:   Hardware Abstraction Layer  把同类型的设备封装成标准的统一的API
IO:    Input/Output
MIC:   Message Integrity Code
MPDU:  MAC Protocol Data Unit
PAN:   Personal Area Network
PER:   Packet Error Rate
RF:    Radio Frequency
RSSI:  Received Signal Strength Indicator
SFD:   Start of Frame Delimiter
////////////////////////////////




无线点灯:


工程目录:
  application(主程序, main函数的源码)


  basicRf(封装好的无线传输功能库)


 hal(hardware abstract layer, 芯片厂家把不同的芯片控制器封装成统一的接口函数, 便于开发人员快速熟识新的芯片)


  utilities(额外的功能库)
//////
typedef struct {
    uint16 myAddr;       //16位的短地址
    uint16 panId;        //节点的PAN ID
    uint8 channel;       //RF通道(必须在11-26之间) 表示不同的频段从(2405 – 2480)MHz
    uint8 ackRequest;    //目标确认为true
...
} basicRfCfg_t;


HAL层会把板上的硬件作成统一的接口,如按键就做成(btns = halButtonPushed(), btns == HAL_BUTTON_1)


   INSTRUCTIONS:
    Startup:
    1. Create a basicRfCfg_t structure, and initialize the members:
    2. Call basicRfInit() to initialize the packet protocol.


    Transmission:
    1. Create a buffer with the payload to send
    2. Call basicRfSendPacket()


    Reception:
    1. Check if a packet is ready to be received by highger layer with
    basicRfPacketIsReady()
    2. Call basicRfReceive() to receive the packet by higher layer


uint8 basicRfInit(basicRfCfg_t* pRfConfig);
uint8 basicRfSendPacket(uint16 destAddr, uint8* pPayload, uint8 length);
uint8 basicRfReceive(uint8* pRxData, uint8 len, int16* pRssi);


CCM - Counter with CBC-MAC (mode of operation)
HAL - Hardware Abstraction Layer (硬件抽象层)
PAN - Personal Area Network (个人局域网)
RF - Radio Frequency (射频)
RSSI - Received Signal Strength Indicator (接收信号强度指示)






/////////


halMcuWaitMs(ms);


halBoardInit(); //作时钟初始化, LED, 按键,手柄, lcd等初始化
hal_led.h:
void halLedInit(void);
void halLedSet(uint8 led_id);
void halLedClear(uint8 led_id);
void halLedToggle(uint8 led_id);
 
//改代码支持控制两个灯
//改代码支持两个不同的按键来控制作不同的事情






////////////////////////
ZigBee 的协议分为两部分,IEEE 802.15.4 定义了 PHY(物理层) MAC和 (介质访问层)技术规范;ZigBee联盟定义了 NWK(网络层)、APS(应用程序支持子层)、APL(应用层)技术规范。ZigBee
协议栈就是将各个层定义的协议都集合在一直,以函数的形式实现,并给用户提供 API(应用
层),用户可以直接调用.




物理层( PHY)
物理层定义了物理无线信道和 MAC 子层之间的接口,提供物理层数据服务和
物理层管理服务。
物理层内容:
1)ZigBee 的激活;
2)当前信道的能量检测;
3)接收链路服务质量信息;
4)ZigBee 信道接入方式;
5)信道频率选择;
6)数据传输和接收。


MAC层负责处理所有的物理无线信道访问,并产生网络信号、同步信号;支持PAN连接和分
离,提供两个对等MAC实体之间可靠的链路。
MAC层功能:
1)网络协调器产生信标;
2)与信标同步;
3)支持 PAN(个域网)链路的建立和断开;
4)为设备的安全性提供支持;
5)信道接入方式采用免冲突载波检测多址接入(CSMA-CA)机制;
6)处理和维护保护时隙(GTS)机制;
7)在两个对等的 MAC 实体之间提供一个可靠的通信链路


网络层(NWK)
ZigBee协议栈的核心部分在网络层。网络层主要实现节点加入或离开网络、接收或抛弃其他
节点、路由查找及传送数据等功能。
网络层功能:
1)网络发现;
2)网络形成;
3)允许设备连接;
4)路由器初始化;
5)设备同网络连接;
6)直接将设备同网络连接;
7)断开网络连接;
8)重新复位设备;
9)接收机同步;
10)信息库维护。


ZigBee应用层框架包括应用支持层(APS)、ZigBee设备对象(ZDO)和制造商所定义的应用对
象。
应用支持层的功能包括:维持绑定表、在绑定的设备之间传送消息。
ZigBee设备对象的功能包括:定义设备在网络中的角色(如ZigBee协调器和终端设备),发起
和响应绑定请求,在网络设备之间建立安全机制。 ZigBee设备对象还负责发现网络中的设备,
并且决定向他们提供何种应用服务。
ZigBee应用层除了提供一些必要函数以及为网络层提供合适的服务接口外,一个重要的功能
是应用者可在这层定义自己的应用对象


应用程序框架(AF):
运行在ZigBee协议栈上的应用程序实际上就是厂商自定义的应用对象,并且遵循规范
(profile)运行在端点1~ 240上。在ZigBee应用中,提供2种标准服务类型:键值对(KVP)
或报文(MSG)
设备对象(ZDO):
ZigBee设备对象(ZDO)的功能包括负责定义网络中设备的角色,如:协调器或者终端设备。
还包括对绑定请求的初始化或者响应,在网络设备之间建立安全联系等。实现这些功能, ZDO
使用APS层的APSDE-SAP和网络层的NLME-SAP。ZDO是特殊的应用对象,它在端点
(entire)0上实现。远程设备通过ZDO请求描述符信息,接收到这些请求时,ZDO会调用配臵
对象获取相应描述符值。


在ZigBee网络中存在三种逻辑设备类型:Coordinator(协调器),Router(路由器)和
End-Device(终端设备)。ZigBee网络由一个Coordinator以及多个Router和多个
End_Device组成


Coordinator(协调器)
协调器负责启动整个网络。它也是网络的第一个设备。协调器选择一个信道和一个网络ID(也
称之为PAN ID,即Personal Area Network ID),随后启动整个网络。协调器也可以用来协
助建立网络中安全层和应用层的绑定(bindings)。
注意,协调器的角色主要涉及网络的启动和配臵。一旦这些都完成后,协调器的工作就像一
个路由器(或者消失go away)。由于ZigBee网络本身的分布特性,因此接下来整个网络的操
作就不在依赖协调器是否存在。


Router(路由器)
路由器的功能主要是:允许其他设备加入网络,多跳路由和协助它自己的由电池供电的终端
设备的通讯。
通常,路由器希望是一直处于活动状态,因此它必须使用主电源供电。但是当使用树状网络
拓扑结构时,允许路由间隔一定的周期操作一次,这样就可以使用电池给其供电。




End-Device(终端设备)
终端设备没有特定的维持网络结构的责任,它可以睡眠或者唤醒,因此它可以可以是一个电
池供电设备。通常,终端设备对存储空间(特别是RAM的需要)比较小。
//////////
工程目录:
App:   应用层目录,这是用户创建各种不同工程的区域
HAL:  
MAC:  MAC层 ,含了MAC层的参数配置文件及其MAC的LIB库的函数接口文件
MT:   串口, 监控调试
NWK:  NWK网络层目录, 包含网络层配置参数文件网络层库的函数接口文件及APS层库的函数接口
OSAL:    协议栈的操作系统
Profile:  Application framework应用框架层目录,包含AF层处理函数文件。应用框架层是应用程序和APS层的无线数据接口
Security:  
Services: 提供地址处理函数目录,包括地址模式的定义及地址处理函数
Tools:    工程配置目录,包括空间划分及Z-Stack相关配置信息
ZDO:      
ZMAC:    MAC层目录,包括MAC层参数配置及MAC层LIB库函数回调处理函数
ZMAIN:   主函数目录,包括入口函数及硬件配置文件
Output:




//////////
应用程序:
void SampleApp_Init( uint8 task_id ); //作应用程序时初始化的操作
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ); //应用程序处理事件函数, 一次只能处理一个事件,并返未处理的事件返回
   
       注意:应用程序的函数是被动调用,注意它们的调用时机


//此数组装载每个任务的事件处理函数地址, 顺序与任务ID的次序有关, 每个任务的ID在函数osalInitTasks里初始化.
const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
  ...
  SampleApp_ProcessEvent
};


tasksCnt表示所有任务的个数.
uint16 *tasksEvents; 
  // 分配内存,返回指向缓冲区的指针
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  // uint16 tasksEvents[tasksCnt];  


  每个任务的事件由tasksEvents[taskId]来记录


//系统里使用tasksEvents[任务ID]来检查相应的任务是否有事件发生, 如果有事件发生,则通过tasksArr[任务ID]来调用相应的事件处理函数
//////////////////////


ZMain.c: main函数


main()
{
  ...
  // Initialize the operating system
  osal_init_system();
 // Initialize the system tasks.
   osalInitTasks();
//用户创建的任务
  SampleApp_Init( taskID );


  osal_start_system(); //main函数最后就是死循环
osal_run_system();
events = (tasksArr[idx])( idx, events );//根据作务id, 从数组里tasksArr里调用相应的事件处理函数


}
/////////////////////////////
HAL层的配置在“ZStack-2.5.1a\Components\hal\target\CC2530EB\hal_board_cfg.h"里


void MyDelayMs(int ms)
{
  int i, j;
  for (i = 0; i < ms; i++)
  {
    j = 1000;
    while(j--)
    {
      /* 32 NOPs == 1 usecs */
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop");
    }
  }
}


LED的配置 :
/* 1 - Green */
#define LED1_BV           BV(0)
#define LED1_SBIT         P1_0
#define LED1_DDR          P1DIR
#define LED1_POLARITY     ACTIVE_LOW


在协议栈里的按键:
#define PUSH2_BV          BV(5) //BV(0)
#define PUSH2_SBIT        P0_5  //P2_0
#define PUSH2_POLARITY    ACTIVE_LOW
/////////////////////////////////////////////////
按键的初始化过程:
main()
{
...
HalDriverInit(); 
                 HalKeyInit();


InitBoard( OB_READY );
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
 Hal_KeyIntEnable = interruptEnable; //全局变量记录是否用中断
 pHalKeyProcessFunction = cback; //全局变量记录按键处理函数地址


 ....
 osal_set_event(Hal_TaskID, HAL_KEY_EVENT); //直接给Hal层的任务设置事件
}


}


hal层的任务处理函数:
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
...
if (events & HAL_KEY_EVENT)
{
HalKeyPoll();
void HalKeyPoll (void)
{
if (HAL_PUSH_BUTTON1())
   keys |= HAL_KEY_SW_6;
if (keys && (pHalKeyProcessFunction))
{
  (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}  
}

osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); //设置定时器100ms后给Hal层任务发出按键的事件
}
}


void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
OnBoard_SendKeys( keys, shift );
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
if ( registeredKeysTaskID != NO_TASK_ID )
{
   msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
   if ( msgPtr )
   {
     msgPtr->hdr.event = KEY_CHANGE;
     msgPtr->state = state;
     msgPtr->keys = keys;


     osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
  }
}
}
}
//////////////




在任务初始化函数里:
RegisterForKeys(mytask_id); //当系统处理按键时,会把按键的事件发送到mytask_id任务去.
uint8 RegisterForKeys( uint8 task_id )
{
registeredKeysTaskID = task_id;
}




osal_msg_allocate(size); //相当于malloc();
osal_msg_deallocate(void *p); // free();


osal_msg_send( App_TaskID, (byte *)pMsg );  //给指定的App_TaskID的任务发出事件消息 
//相当于:  tasksEvents[App_TaskID] |= SYS_EVENT_MSG; 
//而且pMsg的内容放存放在系统的事件队列里


osal_msg_receive(mytaskID); //接收指定任务ID的事件内容


////////////////////////////////////////////////


在hal_uart.h里提供:
extern uint8 HalUARTOpen ( uint8 port, halUARTCfg_t *config );
extern uint16 HalUARTRead ( uint8 port, uint8 *pBuffer, uint16 length );
extern uint16 HalUARTWrite ( uint8 port, uint8 *pBuffer, uint16 length );
理论上我们可以自己调用HalUARTOpen函数进行uart初始化后就可以发送/接收数据了


// 在MT_UART.c里的函数MT_UartInit里有调用HalUARTOopen函数进行初始化. 
   我们只要调用MT_UartInit函数即可进行uart的初始化, 再调用HalUARTWrite()就可发出内容了


MT_UART.c的uart接收数据函数
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
   int len;
   
   len = Hal_UART_RxBufLen(port); //获取当前有多少字节数据可接收
   if (len <= 0)
     return;


   //动态分配空间,此空间大小为消息头+数据长度+1字节(用于标明接收数据的长度)   
   pMsg = (mtOSALSerialData_t *)osal_msg_allocate(sizeof(mtOSALSerialData_t )+len+1);


  //初始化消息头
   pMsg->hdr.event = CMD_SERIAL_MSG;
   pMsg->msg = (uint8*)(pMsg+1);//让消息的数据指针指向消息头后的空间(只是接收数据的缓冲区 + 用于标明数据长度的1字节)


   len = HalUARTRead(port, &pMsg->msg[1], len); //从uart控制器里接收uart数据
   pMsg->msg[0] = len; //标明接收数据字节数
   
   osal_msg_send( App_TaskID, (byte *)pMsg );  //给指定的App_TaskID的任务发出事件消息 


   // App_TaskID是用户任务初始化时调用函数指定的:MT_UartRegisterTaskID(用户任务ID);
}




工程项目设置(tools --> options --> C/c++ compiler --> preprocessor): 
xMT_TASK
xMT_SYS_FUNC
xMT_ZDO_FUNC


////////////////
增加定时器功能:  //只要系统定时给我们的任务发事件即可

uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value );
指定timeout_value毫秒后给taskID任务发出event_id类的事件。注意只有事件信号,没有数据




////////////////////////////////////////
endpoint: 每个接口都能接收(用于输入)或发送(用于输出)簇格式的数据。一共有二个特殊的端点,
即端点 0 和端点 255。端点 0 用于整个 ZigBee 设备的配置和管理。应用程序可以通过端点
0 与 ZigBee 堆栈的其它层通信,从而实现对这些层的初始化和配置。附属在端点 0 的对象
被称为 ZigBee 设备对象(ZD0)。端点 255 用于向所有端点的广播。端点 241 到 254 是保留
端点。
所有端点都使用应用支持子层(APS)提供的服务。APS 通过网络层和安全服务提供层与
端点相接,并为数据传送、安全和绑定提供服务,因此能够适配不同但兼容的设备,比如带
灯的开关。
APS 使用网络层(NWK)提供的服务。NWK 负责设备到设备的通信,并负责网络中设备
初始化所包含的活动、消息路由和网络发现。应用层可以通过 ZigBee 设备对象(ZD0)对网
络层参数进行配置和访问。


/////////
zstack协议栈里的数据发送函数AF_DataRequest


afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
     uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
                           uint8 options, uint8 radius )


///////////
typedef enum
{
  afAddrNotPresent = AddrNotPresent,
  afAddr16Bit      = Addr16Bit,      //表示目的地址是一个32位地址
  afAddr64Bit      = Addr64Bit,      //表示目的地址是一个64位地址
  afAddrGroup      = AddrGroup,      //表示目的地址是组播的
  afAddrBroadcast  = AddrBroadcast   //表示目的地址是广播的
} afAddrMode_t;


typedef struct
{
  union
  {
    uint16      shortAddr;  //16位地址
    ZLongAddr_t extAddr;    //64位地址
  } addr;
  afAddrMode_t addrMode;    //指定目的地址的类型
  uint8 endPoint;           //指定目的端点
  uint16 panId;  // used for the INTER_PAN feature
} afAddrType_t;
////广播地址


  afAddrType_t SampleApp_Periodic_DstAddr;


  SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播
  SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址


////组播地址
  SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //组寻址
  SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
  SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001


  //加入组
  aps_Group_t SampleApp_Group;
  SampleApp_Group.ID = 0x0001;//组号
  osal_memcpy( SampleApp_Group.name, "Group 1", 7  );//设定组名
  aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把该组登记添加到APS中


///P2P地址
  SampleApp_P2P_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; //点播 
  SampleApp_P2P_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; 
  SampleApp_P2P_DstAddr.addr.shortAddr = 0x0000;            //发给协调器


//////
typedef struct
{
  uint8 endPoint;   //指定本任务使用的端点
  uint8 *task_id;  // Pointer to location of the Application task ID. 本任务的ID
  SimpleDescriptionFormat_t *simpleDesc;
  afNetworkLatencyReq_t latencyReq;  
} endPointDesc_t;


typedef struct
{
  uint8          EndPoint;
  uint16         AppProfId;
  uint16         AppDeviceId;
  uint8          AppDevVer:4;
  uint8          Reserved:4;             // AF_V1_SUPPORT uses for AppFlags:4.
  uint8          AppNumInClusters;
  cId_t         *pAppInClusterList;
  uint8          AppNumOutClusters;
  cId_t         *pAppOutClusterList;
} SimpleDescriptionFormat_t;


const SimpleDescriptionFormat_t SampleApp_SimpleDesc =
{
  SAMPLEAPP_ENDPOINT,              //  int Endpoint;
  SAMPLEAPP_PROFID,                //  0x0F08, 从zigbee厂商获取
  SAMPLEAPP_DEVICEID,              //  0x0001, ....
  SAMPLEAPP_DEVICE_VERSION,        //  0,
  SAMPLEAPP_FLAGS,                 //  0,
  SAMPLEAPP_MAX_CLUSTERS,          //  输入集群号数组的元素个数
  (cId_t *)SampleApp_ClusterList,  //  输入集群号数组的首地址
  SAMPLEAPP_MAX_CLUSTERS,          //  输出集群号数组的元素个数
  (cId_t *)SampleApp_ClusterList   //  输出集群号数组的首地址
};


//////////////


endPointDesc_t SampleApp_epDesc;
 // Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符
  SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指任务所使用的端点号
  SampleApp_epDesc.task_id = &SampleApp_TaskID;   //SampleApp 描述符的任务ID
  SampleApp_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符
  SampleApp_epDesc.latencyReq = noLatencyReqs;    //延时策略


  // Register the endpoint description with the AF
  afRegister( &SampleApp_epDesc );    //向AF层登记描述符




/////////////////////////






typedef struct
{
osal_event_hdr_t hdr; /* OSAL Message header OSAL 消息头 */
uint16 groupId; /* Message's group ID - 0 if not set 消息组ID */
uint16 clusterId; /* Message's cluster ID 消息族ID */
afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP, it's an InterPAN message 源地址类型*/
uint16 macDestAddr;     /* MAC header destination short address MAC物理地址*/
uint8 endPoint;         /* destination endpoint MAC 目的端点*/
uint8 wasBroadcast;     /*广播地址*/
uint8 LinkQuality;      /*接收数据帧的链路质量*/
uint8 correlation; /*接收数据帧的未加工相关值*/
int8 rssi;   /* The received RF power in units dBm接收的射频功率*/
uint8 SecurityUse; /* deprecated 弃用*/
uint32 timestamp; /* receipt timestamp from MAC收到时间标记*/
afMSGCommandFormat_t cmd; /* Application Data 应用程序数据*/
} afIncomingMSGPacket_t;
//无线数据包格式结构体


typedef struct
{
byte TransSeqNumber;
uint16 DataLength; // Number of bytes in TransData
byte *Data;
} afMSGCommandFormat_t







 

猜你喜欢

转载自blog.csdn.net/qq_36449541/article/details/78131467