我們前面分析過,不論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開了一個定時器,最后是取消了還是溢出了?