节点与PC的通信TestSerial
一、实验目的
通过本实验的课程教学,主要是让用户了解以下内容:
1.通过本实验了解串口的基本概念;
2.通过本实验了解TinyOS执行模行。
3.通过本实验掌握TinyOS中串口的收发操作。
二、知识介绍
1.串行通信概念
随着计算机网络化和微机分级分布式应用系统的发展,通信的功能越来越重要。通信是指计算机与外界的信息传输,既包括计算机与计算机之间的传输,也包括计算机与外部设备,如终端、打印机和磁盘等设备之间的传输。在通信领域内,数据通信中按每次传送的数据位数,通信方式可分为:并行通信和串行通信。
串行通信是指计算机主机与外设之间以及主机系统与主机系统之间数据的串行传送。使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。
串口通信时,发送和接收到的每一个字符实际上都是一次一位的传送的,每一位为1或者为0。如图6-1所示。
图1-1串行通信示意图
2.分类
同步通信
同步通信是一种连续串行传送数据的通信方式,一次通信只传送一帧信息。这里的信息帧与异步通信中的字符帧不同,通常含有若干个数据字符。它们均由同步字符、数据字符和校验字符(CRC)组成。其中同步字符位于帧开头,用于确认数据字符的开始。数据字符在同步字符之后,个数没有限制,由所需传输的数据块长度来决定;校验字符有1到2个,用于接收端对接收到的字符序列进行正确性的校验。同步通信的缺点是要求发送时钟和接收时钟保持严格的同步。
异步通信
异步通信中,在异步通信中有两个比较重要的指标:字符帧格式和波特率。数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。接收端检测到传输线上发送过来的低电平逻辑“0”(即字符帧起始位)时,确定发送端已开始发送数据,每当接收端收到字符帧中的停止位时,就知道一帧字符已经发送完毕。
3.特点
数据在单条一位宽的传输线上,一比特接一比特地按顺序传送的方式称为串行通信。在并行通信中,一个字节(8位)数据是在8条并行传输线上同时由源传到目的地;而在串行通信方式中,数据是在单条1位宽的传输线上一位接一位地顺序传送。这样一个字节的数据要分8次由低位到高位按顺序一位位地传送。由此可见,串行通信的特点如下:
1、节省传输线,这是显而易见的。尤其是在远程通信时,此特点尤为重要。这也是串行通信的主要优点。
2、数据传送效率低。与并行通信比,这也这是显而易见的,这也是串行通信的主要缺点。
例如:传送一个字节,并行通信只需要1T的时间,而串行通信至少需要8T的时间。由此可见,串行通信适合于远距离传送,可以从几米到数千公里。对于长距离、低速率的通信,串行通信往往是唯一的选择。并行通信适合于短距离、高速率的数据传送,通常传输距离小于30米。特别值得一提的是,现成的公共电话网是通用的长距离通信介质,它虽然是为传输声音信号设计的,但利用调制解调技术,可使现成的公共电话网系统为串行数据通信提供方便、实用的通信线路。
三、实验步骤
1.上传编译代码
点击浏览按钮上传TestSerial代码,点击编译按钮编译
图1-2 实验代码上传
图1-3上传编译界面
2.烧录代码
首先需要申请SmartNode设备,申请步骤参考Blink实验,选择0号点烧录TestSerial代码,烧录提示信息如下:
图1-4 选择烧录按钮
图1-5 烧录提示信息
3.工具使用
选中0号节点(选中点后,点的数字颜色会变),点击收数按钮,收数成功会在右方提示信息里显示。
图1-6 收数操作
图1-7 操作提示信息
4.实验结果显示
点击下一步查看实验结果。如果想停止收数,回到Step2选中节点点击停止按钮即可。
图1-8 实验结果展示
四、实验分析
1.TinyOS编程模式分析
TinyOS的编程方式采用nesc语言,这是一种类C语言,nesc语言有几个最重要概念:组件,接口,模块。如下图TestSerialC程序组件图:
图1-9 TestSerialC组件图
从上图中,箭头的名字表示组件与组件之间访问使用的接口。下面我们结合程序来看下组件、接口、模块的概念。TestSerial目录下面包含Makefile,TestSerialAppC.nc,TestSerialC.nc三个文件。
TestSerialAppC.nc
源码
/*------代码截取于TestSerial/TestSerialAppC.nc-------*/
#include "TestSerial.h"
configuration TestSerialAppC { } //表示这是一个名为TestSerialAppC的配置
implementation {
//组件申明
components TestSerialC as App, LedsC, MainC;
components SerialActiveMessageC as AM;
components new TimerMilliC();
//组件间使用关系定义
App.Boot -> MainC.Boot;
App.Control -> AM;
App.Receive -> AM.Receive[AM_TEST_SERIAL_MSG];
App.AMSend -> AM.AMSend[AM_TEST_SERIAL_MSG];
App.Leds -> LedsC;
App.MilliTimer -> TimerMilliC;
App.Packet -> AM;
}
implementation关键字后面括号内是配置的具体实现。components关键字后面表明了这个配置文件所引用的组件,在这里分别是MainC, TestSerialC,Leds, SerialActiveMessageC以及一个TimerMilliC组件。最后七行表明了各组件间的provider和user的关系。A->B表示了一种关系,其中A为使用方(user),而B为提供方(provider)。命令(command)就是接口提供方已经实现的函数。事件(event)就是需要接口使用方实现的函数。
TestSerialC.nc
源码
/*------代码截取于TestSerial/TestSerialC.nc-------*/
#include "Timer.h"
#include "TestSerial.h"
module TestSerialC {
uses{
//声明了使用到的接口名称
interface SplitControl as Control;
interface Leds;
interface Boot;
interface Receive;
interface AMSend;
interface Timer< TMilli> as MilliTimer;
interface Packet;
}
}
implementation {
message_t packet;
bool locked = FALSE;
uint16_t counter = 0;
event void Boot.booted() {
call Control.start(); //节点上电后触发Boot接口事件,在该事件中开启串口
}
event void MilliTimer.fired() {
counter++;
if (locked) {
return;
}
else {
test_serial_msg_t* rcm = (test_serial_msg_t*)call Packet.getPayload
(&packet, sizeof(test_serial_msg_t));
if (rcm == NULL) { return; }
if (call Packet.maxPayloadLength() < sizeof(test_serial_msg_t)){
return;
}
rcm->counter = counter;
//串口发送函数
if (call AMSend.send(AM_BROADCAST_ADDR, &packet,
sizeof(test_serial_msg_t)) == SUCCESS) {
locked = TRUE;
}
}
}
//串口中断接收事件
event message_t* Receive.receive(message_t* bufPtr, void* payload, uint8_t len) {
if (len != sizeof(test_serial_msg_t)) {return bufPtr;}
else{
test_serial_msg_t* rcm = (test_serial_msg_t*)payload;
if (rcm->counter & 0x1) { call Leds.led0On(); }
else { call Leds.led0Off(); }
if (rcm->counter & 0x2) { call Leds.led1On(); }
else { call Leds.led1Off(); }
if (rcm->counter & 0x4) { call Leds.led2On(); }
else { call Leds.led2Off(); }
return bufPtr;
}
}
//串口发送完成触发该事件
event void AMSend.sendDone(message_t* bufPtr, error_t error) {
if (&packet == bufPtr) { locked = FALSE;}
}
event void Control.startDone(error_t err) {
if (err == SUCCESS) {
call MilliTimer.startPeriodic(1000); // 串口打开成功,开启定时器
}
}
event void Control.stopDone(error_t err) { }
}
节点每秒发送信息包到电脑的串口,当节点通过串口接收到来自电脑的信息包时,则把信息包的顺序号显示在LED灯上。
//Makefile
COMPONENT=TestSerialAppC
BUILD_EXTRA_DEPS += TestSerial.class
CLEAN_EXTRA = *.class TestSerialMsg.java
CFLAGS += -I$(TOSDIR)/lib/T2Hack
TestSerial.class: $(wildcard *.java) TestSerialMsg.java
javac -target 1.4 -source 1.4 *.java
TestSerialMsg.java:
mig java -target=null $(CFLAGS) -java-classname=TestSerialMsg TestSerial.h test_serial_msg -o $@
include $(MAKERULES)
//TestSerial.h
#ifndef TEST_SERIAL_H
#define TEST_SERIAL_H
typedef nx_struct test_serial_msg {
nx_uint16_t counter;
} test_serial_msg_t;
enum {
AM_TEST_SERIAL_MSG = 0x89,
};
#endif
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)