【DWM1000】 code 解密6一TAG 状态机第一步
我们前面分析过,不论ANCHOR 还是TAG,前面变量的初始化基本都是一样的,只是状态机必须明确区分不同的设备类型。我们从开始看TAG。由于初始化TAG的 testAppState一样初始化为TA_INIT。
INST_STATES testAppState ; int instance_init_s(int mode) TA_INIT |
case TA_INIT : // printf("TA_INIT") ; switch (inst->mode) { case TAG: { int mode = 0; dwt_enableframefilter(DWT_FF_DATA_EN | DWT_FF_ACK_EN); //allow data, ACK frames; inst->frameFilteringEnabled = 1 ; dwt_setpanid(inst->panid); dwt_seteui(inst->eui64); #if (USING_64BIT_ADDR==0) //the short address is assigned by the anchor #else //set source address into the message structure memcpy(&inst->msg.sourceAddr[0], inst->eui64, ADDR_BYTE_SIZE_L); #endif //change to next state - send a Poll message to 1st anchor in the list inst->mode = TAG_TDOA ; inst->testAppState = TA_TXBLINK_WAIT_SEND; memcpy(inst->blinkmsg.tagID, inst->eui64, ADDR_BYTE_SIZE_L); mode = (DWT_LOADUCODE|DWT_PRESRV_SLEEP|DWT_CONFIG|DWT_TANDV); if((dwt_getldotune() != 0)) //if we need to use LDO tune value from OTP kick it after sleep { mode |= DWT_LOADLDO; } if(inst->configData.txPreambLength == DWT_PLEN_64) //if using 64 length preamble then use the corresponding OPSet { mode |= DWT_LOADOPSET; } #if (DEEP_SLEEP == 1) if (inst->sleep_en) dwt_configuresleep(mode, DWT_WAKE_WK|DWT_WAKE_CS|DWT_SLP_EN); //configure the on wake parameters (upload the IC config settings) #endif } break; |
dwt_enableframefilter(DWT_FF_DATA_EN | DWT_FF_ACK_EN); //allow data, ACK frames; inst->frameFilteringEnabled = 1 ; |
控制滤波器,只接受DATA 和ACK数据,并且将frameFilteringEnabled 设置为1. 主要这个不TAG的frameFilteringEnabled,与前面的ANCHOR的frameFilteringEnabled 是一个东西,但是赋值分为两个,因为是两份代码分别跑在两个模块中。
dwt_setpanid(inst->panid); dwt_seteui(inst->eui64); |
Panid 和 64位地址设定,与ANCHOR一样。
#if (USING_64BIT_ADDR==0) //the short address is assigned by the anchor #else //set source address into the message structure memcpy(&inst->msg.sourceAddr[0], inst->eui64, ADDR_BYTE_SIZE_L); #endif |
由于短地址还没有,所以msg.sourceAddr目前只能是64位长地址。
inst->mode = TAG_TDOA ; |
这个mode之前没有用过,我们先记录下,以后肯定会用到TAG_TDOA
inst->testAppState = TA_TXBLINK_WAIT_SEND; |
这个testAppState记录上,下次进去testapprun_s 找作案现场用
memcpy(inst->blinkmsg.tagID, inst->eui64, ADDR_BYTE_SIZE_L); |
又一个变量被设置,我们先记录,后面用的时候查看, 这里保存了TAG的长地址
mode = (DWT_LOADUCODE|DWT_PRESRV_SLEEP|DWT_CONFIG|DWT_TANDV); if((dwt_getldotune() != 0)) //if we need to use LDO tune value from OTP kick it after sleep { mode |= DWT_LOADLDO; } if(inst->configData.txPreambLength == DWT_PLEN_64) //if using 64 length preamble then use the corresponding OPSet { mode |= DWT_LOADOPSET; } |
mode是个变量,给这个mode 赋了很多值
#if (DEEP_SLEEP == 1) if (inst->sleep_en) dwt_configuresleep(mode, DWT_WAKE_WK|DWT_WAKE_CS|DWT_SLP_EN); //configure the on wake parameters (upload the IC config settings) #endif } break; |
如果我们没有使用sleep,前面mode都感觉没有用了,mode主要作用是告诉chip醒来以后需要恢复那些量,我们先不具体分析了。现在break了,和ANCHOR一样,返回值为0,符合while条件会再次跳入到testapprun_s。
我们根据上面的testAppState = TA_TXBLINK_WAIT_SEND;再次找作案现场
case TA_TXBLINK_WAIT_SEND : { int flength = (BLINK_FRAME_CRTL_AND_ADDRESS + FRAME_CRC); //blink frames with IEEE EUI-64 tag ID inst->blinkmsg.frameCtrl = 0xC5 ; inst->blinkmsg.seqNum = inst->frame_sn++; dwt_writetxdata(flength, (uint8 *) (&inst->blinkmsg), 0) ; // write the frame data dwt_writetxfctrl(flength, 0); //using wait for response to do delayed receive inst->wait4ack = DWT_RESPONSE_EXPECTED; dwt_setrxtimeout((uint16)inst->fwtoTimeB_sy); //units are symbols //set the delayed rx on time (the ranging init will be sent after this delay) dwt_setrxaftertxdelay((uint32)inst->rnginitW4Rdelay_sy); //units are 1.0256us - wait for wait4respTIM before RX on (delay RX) dwt_starttx(DWT_START_TX_IMMEDIATE | inst->wait4ack); //always using immediate TX and enable dealyed RX inst->instToSleep = 1; //go to Sleep after this blink inst->testAppState = TA_TX_WAIT_CONF ; // wait confirmation inst->previousState = TA_TXBLINK_WAIT_SEND ; inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; //will use RX FWTO to time out (set below) } break ; // end case TA_TXBLINK_WAIT_SEND |
其中如下几步是DWM1000 代码中发送数据的基本流程
dwt_writetxdata(flength, (uint8 *) (&inst->blinkmsg), 0) ; dwt_writetxfctrl(flength, 0); dwt_setrxtimeout((uint16)inst->fwtoTimeB_sy); //units are symbols dwt_setrxaftertxdelay((uint32)inst->rnginitW4Rdelay_sy); dwt_starttx(DWT_START_TX_IMMEDIATE | inst->wait4ack); |
发送了一个数据包,数据包的内容为
inst->blinkmsg.frameCtrl = 0xC5 ; inst->blinkmsg.seqNum = inst->frame_sn++; |
其中还求必须有回应
//using wait for response to do delayed receive inst->wait4ack = DWT_RESPONSE_EXPECTED; |
发送完延时打开接收器(设定rx接收timeout以及打开接收器的时间)
dwt_setrxtimeout((uint16)inst->fwtoTimeB_sy); //units are symbols dwt_setrxaftertxdelay((uint32)inst->rnginitW4Rdelay_sy); |
设定了一些变量,有些还是很重要的。
inst->instToSleep = 1; //go to Sleep after this blink inst->testAppState = TA_TX_WAIT_CONF ; // wait confirmation inst->previousState = TA_TXBLINK_WAIT_SEND ; inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; //will use RX FWTO to time out (set below) |
同样根据inst->testAppState = TA_TX_WAIT_CONF ; 找下次进去testapprun_s 的作案现场。
到现在为止,我们分析代码发现,ANCHOR在等TAG发数据,现在TAG给它发送了,ANCHOR应该会收到数据了,而TAG依然会接着执行。 我们先看TAG,然后在看ANCHOR。 记住目前的状态是ANCHOR准备接收数据了。
接着看TAG,退出testapprun_s时,done为INST_DONE_WAIT_FOR_NEXT_EVENT;
if(done == INST_DONE_WAIT_FOR_NEXT_EVENT_TO) //we are in RX and need to timeout (Tag needs to send another poll if no Rx frame) { if(instance_data[instance].mode == TAG_TDOA) { instance_data[instance].instancetimer += instance_data[instance].tagBlinkSleepTime_ms; //set timeout time instance_data[instance].instancetimer_en = 1; //start timer } instance_data[instance].stoptimer = 0 ; //clear the flag - timer can run if instancetimer_en set (set above) instance_data[instance].done = INST_NOT_DONE_YET; |
看后面的注释,目前TAG发送一笔Blink信号后,确实有个延时打开接收器的动作,所以说确实现在是RX状态。
if(done == INST_DONE_WAIT_FOR_NEXT_EVENT_TO) //we are in RX and need to timeout |
我们再看看INST_DONE_WAIT_FOR_NEXT_EVENT_TO 这个宏定义,可以看出需要等待一个timeout,就知道if判断里面是开启定时器,等待一段时间了
#define INST_DONE_WAIT_FOR_NEXT_EVENT_TO 2 //this signifies that the current event has been processed and that instance is waiting for next one with a timeout //which will trigger if no event coming in specified time |
我们再来分析定时器代码,因为我们前面分析,TAG现在的mode是TAG_TDOA,所以摘录出其对应的代码
if(instance_data[instance].mode == TAG_TDOA) { instance_data[instance].instancetimer += instance_data[instance].tagBlinkSleepTime_ms; //set timeout time instance_data[instance].instancetimer_en = 1; //start timer } instance_data[instance].stoptimer = 0 ; //clear the flag - timer can run if instancetimer_en set (set above) instance_data[instance].done = INST_NOT_DONE_YET; |
我们标注颜色的两个变量,我们搜索一下之前是否有初始化
uint32 instancetimer; // e.g. this timer is used to timeout Tag when in deep sleep so it can send the next poll message int tagBlinkSleepTime_ms; instancesettagsleepdelay 1000 |
我们没有看到instancetimer的初始化,我们暂时考虑它为0,但是tagBlinkSleepTime_ms 是1000,所以通过第一句赋值语句instancetimer 等于1000了。
其它几个变量instancetimer_en和stoptimer 立马会用到,注意一下done此时被赋值为INST_NOT_DONE_YET,这里也记录一下,后面看怎么走了。
接着看代码
if((instance_data[instance].instancetimer_en == 1) && (instance_data[instance].stoptimer == 0)) |
这个if里面两个变量判定与刚才完全一样,所以立马用到了它们,而且满足条件,接着看if里面的内容
if(instance_data[instance].instancetimer < portGetTickCount()) { event_data_t dw_event; instance_data[instance].instancetimer_en = 0; dw_event.rxLength = 0; dw_event.type = DWT_SIG_RX_TIMEOUT; dw_event.type2 = 0x80 | DWT_SIG_RX_TIMEOUT; //printf("PC timeout DWT_SIG_RX_TIMEOUT\n"); instance_putevent(dw_event); } |
If条件里变量instancetimer 是我们刚刚计算赋值的,而portGetTickCount()函数我们之前没有遇到过,简单看下
#define portGetTickCount() portGetTickCnt() unsigned long portGetTickCnt(void) { return time32_incr; } |
我们看到这里,它返回一个time32_incr. 看到这里绝对这个变量没有初始化,假定是0,那就错误了。 这里肯定是有个定时器了,time32_incr是一个全局变量,在timer中断里增加。 所以portGetTickCnt() 返回一个实时时间量,再看我们之前的instancetimer认为是
instance_data[instance].instancetimer += instance_data[instance].tagBlinkSleepTime_ms; 现在感觉instance_data[instance].instancetimer应该也是一个在定时器累加的全部量,不然两者没有可比性,没法有相对延时的概念。
简单看下time32_incr 一些像代码。
void SysTick_Handler(void) { time32_incr++; #ifdef FILESYSTEM_ENABLE fsd_service(); #endif } |
通过下面的语句判断定时时间是否到了
if(instance_data[instance].instancetimer < portGetTickCount()) |
里面的语句,就是产生一个事件,事件type为DWT_SIG_RX_TIMEOUT,通过instance_putevent(dw_event)加入到事件列表,我们之前是通过peek查看是否有事件。
这几个event 相关的函数暂时不会影响我们分析大局,先暂时不看它们了。
instancetimer_en 设置为0,标志着停止计时。
instance_data[instance].instancetimer_en = 0 |
虽然我们把TAG代码中的instance_run中的代码全部分析完了,但是其实TAG实际跑代码不是这样的。正确的顺序应该是,接着我们刚才设定定时器à 查看定时器是否到期(没有到期)à返回Main 函数(我们分析过ANCHOR,除了打印信息,没有实质内容)à重新 instance_runà……定时器到期,设定event事件。
其中……可能重复了很多次,我们需要再看看里面怎么执行的,是否还会增加定时器等等,我们逐一再看看,这段时间是TAG发送完blink后打开接收器等待ANCHOR应答的时间段,虽然实际上时间可能1s不到,但是我们分析代码可能需要几个小时甚至更长。 接着看第二次进入instance_run。
int done = INST_NOT_DONE_YET; int message = instance_peekevent(); //get any of the received events from ISR while(done == INST_NOT_DONE_YET) { //int state = instance_data[instance].testAppState; done = instance_localdata[instance].testapprun_fn(&instance_data[instance], message) ; // run the communications application //we've processed message message = 0; } |
注意和这里,因为我们假定是时间没有到,所以peekevent应该还是啥也没有,所以message返回的还是0. 所以我们会再次进入testapprun_s。 我们需要找上次testAppState,
inst->testAppState = TA_TX_WAIT_CONF ; // wait confirmation inst->previousState = TA_TXBLINK_WAIT_SEND ; |
东西记不住1是返回去看代码,而是用笔记录,我们之前分析有记录。直接在testapprun_s 找相应的case
case TA_TX_WAIT_CONF : //after tx,waif for comfirm |
这里代码不少,我们需要根据if删减一下,这里事件TA_TX_WAIT_CONF,其实就是等待是否有应答,因为TAG 在发送blink信号的时候明确提出需要应答
{ event_data_t* dw_event = instance_getevent(11); //get and clear this event //NOTE: Can get the ACK before the TX confirm event for the frame requesting the ACK //this happens because if polling the ISR the RX event will be processed 1st and then the TX event //thus the reception of the ACK will be processed before the TX confirmation of the frame that requested it. if(dw_event->type != DWT_SIG_TX_DONE) //wait for TX done confirmation { if(dw_event->type == DWT_SIG_RX_TIMEOUT) //got RX timeout - i.e. did not get the response (e.g. ACK) { //printf("RX timeout in TA_TX_WAIT_CONF (%d)\n", inst->previousState); //we need to wait for SIG_TX_DONE and then process the timeout and re-send the frame if needed inst->gotTO = 1; } inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; break; } inst->done = INST_NOT_DONE_YET; if(inst->previousState == TA_TXFINAL_WAIT_SEND)// 不满足 { …… } else if (inst->gotTO) //timeout { //printf("got TO in TA_TX_WAIT_CONF\n"); inst_processrxtimeout(inst); inst->gotTO = 0; inst->wait4ack = 0 ; //clear this break; } else { inst->txu.txTimeStamp = dw_event->timeStamp; if(inst->previousState == TA_TXPOLL_WAIT_SEND) // 不满足 { …… } inst->testAppState = TA_RXE_WAIT ; // After sending, tag expects response/report, anchor waits to receive a final/new poll //fall into the next case (turn on the RX) message = 0; } } |
我们先一部分一部分的分析
if(dw_event->type != DWT_SIG_TX_DONE) //wait for TX done confirmation { if(dw_event->type == DWT_SIG_RX_TIMEOUT) //got RX timeout - i.e. did not get the response (e.g. ACK) { //printf("RX timeout in TA_TX_WAIT_CONF (%d)\n", inst->previousState); //we need to wait for SIG_TX_DONE and then process the timeout and re-send the frame if needed inst->gotTO = 1; } inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; break; } |
DWT_SIG_TX_DONE 这个事件应该是发送一帧数据后,DWM1000 中断里产生的,我们刚才在TAG发送了一帧数据,所以如果DWM1000 处理完了,应该会put event,事件type是DWT_SIG_TX_DONE。 好,那我们假设,下发数据后,DWM1000 还没有发送出去,我们代码已经执行到这里了,确实满足这个判断。 继续执行里面的
if(dw_event->type == DWT_SIG_RX_TIMEOUT) |
DWT_SIG_RX_TIMEOUT, 这个事件我们之前见过,是定时器溢出触发的的,我们刚才假定DWM1000 很快执行到这里,所以没有溢出,不满足条件。直接执行了。
inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; break; |
和TAG 上次退出时一样。一样分别是有如下几个关键变量没有修改
inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; inst->testAppState = TA_TX_WAIT_CONF ; // wait confirmation inst->previousState = TA_TXBLINK_WAIT_SEND ; |
因为这个时候定时器已经开启了,我们看看TAG此时退出到run里会执行那些。
if(done == INST_DONE_WAIT_FOR_NEXT_EVENT_TO) //we are in RX and need to timeout (Tag needs to send another poll if no Rx frame) { if(instance_data[instance].mode == TAG) //Tag (is either in RX or sleeping) { …… } if(instance_data[instance].mode == TAG_TDOA) { instance_data[instance].instancetimer += instance_data[instance].tagBlinkSleepTime_ms; //set timeout time instance_data[instance].instancetimer_en = 1; //start timer } instance_data[instance].stoptimer = 0 ; //clear the flag - timer can run if instancetimer_en set (set above) instance_data[instance].done = INST_NOT_DONE_YET; } |
再次给instancetimer它赋值,我们之前所instancetimer 是个动态量,分析有误。。。。。
###############》 其实定时时长没有变,所以还是和原来等待时间一样,后面持续在看是否溢出。==è重新分析instancetimer。
好,这是一种假设,另一种假设,依然针对下面这几行代码。
if(dw_event->type != DWT_SIG_TX_DONE) //wait for TX done confirmation { if(dw_event->type == DWT_SIG_RX_TIMEOUT) //got RX timeout - i.e. did not get the response (e.g. ACK) { //printf("RX timeout in TA_TX_WAIT_CONF (%d)\n", inst->previousState); //we need to wait for SIG_TX_DONE and then process the timeout and re-send the frame if needed inst->gotTO = 1; } inst->done = INST_DONE_WAIT_FOR_NEXT_EVENT; break; } |
我们分析代码可以知道,当没有发送完一直在等待事件DWT_SIG_TX_DONE,加入一种情况,DWM1000 坏掉了或者中断配置有问题,这个事件一直等不到,那么,上面的循环一直执行到定时器溢出。会满足后面的if(dw_event->type == DWT_SIG_RX_TIMEOUT),判断里面讲inst->gotTO = 1。 这个分支代码也很长了,因为我们可能需要在instance_run获取些信息了。 暂时不考虑怎么极端的情况。先预留一个DWM1000配置有误或者损坏TX后执行情况代码分析。
好了,我们假定DWM1000 一切正常,所以过了一段时间,收到了DWT_SIG_TX_DONE,那就不满足如下if条件,直接往后看吧。
if(dw_event->type != DWT_SIG_TX_DONE) //wait for TX done confirmation |
inst->done = INST_NOT_DONE_YET; if(inst->previousState == TA_TXFINAL_WAIT_SEND) { …… } else if (inst->gotTO) //timeout { …… } else { inst->txu.txTimeStamp = dw_event->timeStamp; if(inst->previousState == TA_TXPOLL_WAIT_SEND) { …… } inst->testAppState = TA_RXE_WAIT ; // After sending, tag expects response/report, anchor waits to receive a final/new poll //fall into the next case (turn on the RX) message = 0; } } //break ; // end case TA_TX_WAIT_CONF |
把不满足条件的代码注释掉,发现实际上当TAG发送完blink后这里只是简单的给几个重要的变量赋值,好好记录一下,根据经验这个很重要
inst->done = INST_NOT_DONE_YET; inst->testAppState = TA_RXE_WAIT ; |
一个全局变量txu.txTimeStamp赋值,这个初始化的时候没有动过,所以是0. 其中dw_event->timestamp 这个应该是DWM1000 在发送数据的时候MARK的时间,因为需要知道接收发送数据的时间换算距离。 其实发送blink 的时间应该没有什么意义,我们暂时不考虑,用到在回来看。
后面有个比较诡异的地方,没有break,直接会执行后面的case
//break ; // end case TA_TX_WAIT_CONF |
后面的case是TA_RXE_WAIT,我们在ANCHOR分析过,它是打开接收器,在TA_RXE_WAIT后面重要变量被修改了
inst->testAppState = TA_RX_WAIT_DATA; |
我们根据上面代码分析经验,我们简要瞅一眼TA_RX_WAIT_DATA。
case TA_RX_WAIT_DATA : //already recive a message // Wait RX data //printf("TA_RX_WAIT_DATA %d", message) ; switch (message) { |
在TA_RX_WAIT_DATA 会根据message执行不同的代码,我们前面分析了,如果没有event或者一些没有message 的event产生时,TA_RX_WAIT_DATA其实啥都不执行。也就是只有就收到无线信号时,message里面才是一个真实的数据。
分析到这里,TAG也在等ANCHOR的数据了。
我们回顾一下,ANCHOR启动后直接开始接收器等到TAG发送数据,分析TAG,TAG先发送一笔blink给ANCHOR,AHCHOR怎么回复blink我们还没有分析,TAG此时正在等ANCHOR的回复了,卡在这里了,我们下一节的内容主要分析ANCHOR接收blink以及回复blink。
遗留问题:TAG开了一个定时器,最后是取消了还是溢出了?
【DWM1000】 code 解密6一TAG 状态机第一步的更多相关文章
- 【DWM1000】 code 解密10 一 TAG 发送最后一个消息
更上面ANCHOR发送信息时的RTLS_DEMO_MSG_ANCH_RESP, 我们很快就可以找到如下代码 case RTLS_DEMO_MSG_ANCH_RESP: { 这里面一部分是设置重要变量, ...
- 【DWM1000】 code 解密8一 TAG接收blink response 信号
在分析这个部分前,目前我看到DWM1000 的资料,data可以分为blink和一般无线数据,后面有内容我们再扩充, 上面我们已经看到接收到blink触发的事件为 case SIG_RX_BLINK ...
- 【DWM1000】 code 解密3一ANCHOR RUN起来
int done = INST_NOT_DONE_YET; #define INST_DONE_WAIT_FOR_NEXT_EVENT 1 //this signifies that the curr ...
- UE4蓝图编程的第一步
认识UE4蓝图中颜色与变量类型: UE4中各个颜色对应着不同的变量,连接点和连线的颜色都在表示此处是什么类型的变量.对于初学者来说一开始看到那么多连接点, 可能会很茫然,搞不清还怎么连,如果知道了颜色 ...
- java微信开发API第一步 服务器接入
I如何接入服务器,下面就为大家进行介绍 一.说明 * 本示例根据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/2016 5:34 ...
- 零代码第一步,做个添加数据的服务先。node.js + mysql
node.js + mysql 实现数据添加的功能.万事基于服务! 增删改查之添加数据. 优点:只需要设置一个json文件,就可以实现基本的添加功能,可以视为是零代码. 添加数据的服务实现的功能: 1 ...
- 使用CocoaPods创建自己的私有库-iOS组件化第一步
目前iOS组件化常用的解决方案是Pod+路由+持续集成,通常架构设计完成后第一步就是将原来工程里的模块按照架构图分解为一个个独立的pod工程(组件),今天我们就来看看如何创建一个Pod私有库. 新建: ...
- 开发thinkphp的第一步就是给Application目录(不包括其下的文件)777权限, 关闭selinux
开发thinkphp的时候, 总是会出现各种个样 的奇怪的毛病, 比如: 说什么Application目录不可写, 比如: 说什么 _STORAGE_WRITE_ERROR, 不能生成 Runtime ...
- ElasticSearch第一步-环境配置
ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense ElasticSearch第三步-中文分词 ElasticSearch第四步-查询详解 Elasti ...
随机推荐
- SSM 三大框架---事务处理
SSM 三大框架---事务处理 原创 2016年05月12日 20:57:03 标签: spring / J2EE / java / 框架 / 事务 7010 在学习三大框架的时候,老师说事务处理是最 ...
- gitlab使用--汉化及修改端口
汉化思路:去gitlab汉化社区下载对应的汉化版本,这个文件和当前版本对比形成一个补丁,打入到当前配置文件中 1.查看当前gitlab版本 head -1(数字) /opt/gitlab/ve ...
- unicode解码
var newStr = System.Text.RegularExpressions.Regex.Unescape(str);
- war的创建
- Lua中assert( )函数的使用
当Lua遇到不期望的情况时就会抛出错误,比如:两个非数字进行相加:调用一个非函数的变量:访问表中不存在的值等.你也可以通过调用error函数显示的抛出错误,error的参数是要抛出的错误信息. ass ...
- JAVA 代码中使用中文的办法
在编译代码中插入 -encoding UTF-8 示例: javac -encoding UTF-8 *.java
- [转]笔记本怎么设置WIfi热点
https://jingyan.baidu.com/article/335530da4f774019cb41c3eb.html 随着手机的发展,流量的消耗也是大大地增加.虽然很多手机支持wifi,但是 ...
- Ubuntu下使用Evernote
Ubuntu下安装使用Evernote http://blog.csdn.net/yangzhuoluo/article/details/8118996 http://blog.csdn.net/ya ...
- JS如何监听动画结束
场景描述 在使用JS控制动画时一般需要在动画结束后执行回调去进行DOM的相关操作,所以需要监听动画结束进行回调.JS提供了以下事件用于监听动画的结束,简单总结学习下. CSS3动画监听事件 trans ...
- 【Android】Android EditText 去除边框
[Android]Android EditText 去除边框 将EditText属性设置修改 android:background="@null" //////////////// ...