ST 适配的 LoRaWAN 协议栈
由于是 ST 适配过,所以和 Semtech 开源的 LoRaMac-node 协议栈还是有差别的
一、OTAA#
1. 参数#
OTAA 主要参数如下:
- DevAddr
- AppSKey
- NwkSKey
- NetworkActivation
2. 入网保存参数的流程#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| /* Mac/LoRaMac.c */
static void ProcessRadioRxDone( void )
{
...
switch( macHdr.Bits.MType )
{
case FRAME_TYPE_JOIN_ACCEPT:
{
...
SecureElementGetJoinEui( joinEui );
/* 解密 */
macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, joinEui, &macMsgJoinAccept );
...
...
/**
* 更新设备地址 DevAddr
* 1. Nvm.MacGroup2.DevAddr
* 2. Nvm.SecureElement.SeNvmDevJoinKey.DevAddrOTAA
*/
Nvm.MacGroup2.DevAddr = macMsgJoinAccept.DevAddr;
SecureElementSetDevAddr( ACTIVATION_TYPE_OTAA, Nvm.MacGroup2.DevAddr );
...
/* 更新激活状态 NetworkActivation */
Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_OTAA;
...
...
break;
}
...
}
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| /* Mac/LoRaMacCrypto.c */
LoRaMacCryptoStatus_t LoRaMacCryptoHandleJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEUI, LoRaMacMessageJoinAccept_t* macMsg )
{
...
{
...
/**
* 生成密钥 AppSKey
* 1. Nvm.SecureElement.KeyList[], 对应的 key id 是 APP_S_KEY
*/
retval = DeriveSessionKey10x( APP_S_KEY, currentJoinNonce, netID, nonce );
...
#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01010100 ))
...
#else
/**
* 生成密钥 NwKSKey
* 1. Nvm.SecureElement.KeyList[], 对应的 key id 是 NWK_S_KEY
*/
retval = DeriveSessionKey10x( NWK_S_KEY, currentJoinNonce, netID, nonce );
#endif
...
}
...
/**
* FCnt 相关统计清零
* CryptoNvm 指向的是 Nvm.Crypto, 实际是
* 1. Nvm.Crypto.FCntList.FCntUp
* 2. Nvm.Crypto.FCntList.FCntDown
* 3. Nvm.Crypto.FCntList.NFCntDown
* 4. Nvm.Crypto.FCntList.AFCntDown
*/
CryptoNvm->FCntList.FCntUp = 0;
CryptoNvm->FCntList.FCntDown = FCNT_DOWN_INITIAL_VALUE;
CryptoNvm->FCntList.NFCntDown = FCNT_DOWN_INITIAL_VALUE;
CryptoNvm->FCntList.AFCntDown = FCNT_DOWN_INITIAL_VALUE;
return LORAMAC_CRYPTO_SUCCESS;
}
|
二、ABP#
1. 参数#
ABP 要预先配置的参数如下:
- DevAddr
- AppKey
- AppSKey
- NwkSKey
2. 参数的配置#
DevAddr:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /* LmHandler/LmHandler.c */
LmHandlerErrorStatus_t LmHandlerSetDevAddr( uint32_t devAddr )
{
MibRequestConfirm_t mibReq;
if( LmHandlerJoinStatus() != LORAMAC_HANDLER_SET )
{
mibReq.Type = MIB_DEV_ADDR;
mibReq.Param.DevAddr = devAddr;
if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK )
{
return LORAMAC_HANDLER_ERROR;
}
return LORAMAC_HANDLER_SUCCESS;
}
else
{
return LORAMAC_HANDLER_ERROR;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /* Mac/LoRaMac.c */
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
{
...
switch( mibSet->Type )
{
...
case MIB_DEV_ADDR:
{
if(SecureElementSetDevAddr( Nvm.MacGroup2.NetworkActivation, mibSet->Param.DevAddr ) != SECURE_ELEMENT_SUCCESS )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
else
{
Nvm.MacGroup2.DevAddr = mibSet->Param.DevAddr;
}
break;
}
...
}
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| /* Crypto/soft-se.c */
SecureElementStatus_t SecureElementSetDevAddr( ActivationType_t mode, uint32_t devAddr )
{
#if (LORAWAN_KMS == 0)
if( mode == ACTIVATION_TYPE_OTAA )
{
SeNvm->SeNvmDevJoinKey.DevAddrOTAA = devAddr;
}
else
{
/**
* ABP 走这里
* 实际是 Nvm.SecureElement.SeNvmDevJoinKey.DevAddrABP
*/
SeNvm->SeNvmDevJoinKey.DevAddrABP = devAddr;
}
return SECURE_ELEMENT_SUCCESS;
#else
...
#endif /* LORAWAN_KMS */
}
|
AppKey / AppSKey / NwkSKey:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| /* LmHandler/LmHandler.c */
LmHandlerErrorStatus_t LmHandlerSetKey( KeyIdentifier_t keyID, uint8_t *key )
{
if( LmHandlerJoinStatus( ) != LORAMAC_HANDLER_SET )
{
if( keyID == APP_KEY )
{
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_APP_KEY;
mibReq.Param.AppKey = key;
if( LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK )
{
return LORAMAC_HANDLER_ERROR;
}
return LORAMAC_HANDLER_SUCCESS;
}
else if( SECURE_ELEMENT_SUCCESS != SecureElementSetKey( keyID, key ) )
{
return LORAMAC_HANDLER_ERROR;
}
}
else
{
return LORAMAC_HANDLER_ERROR;
}
return LORAMAC_HANDLER_SUCCESS;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| /* Mac/LoRaMac.c */
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
{
...
switch( mibSet->Type )
{
...
case MIB_APP_KEY:
{
if( mibSet->Param.AppKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
...
}
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| /* Mac/LoRaMacCrypto.c */
LoRaMacCryptoStatus_t LoRaMacCryptoSetKey( KeyIdentifier_t keyID, uint8_t* key )
{
if( SecureElementSetKey( keyID, key ) != SECURE_ELEMENT_SUCCESS )
{
return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
}
if( keyID == APP_KEY )
{
if( LoRaMacCryptoDeriveLifeTimeKey( CryptoNvm->LrWanVersion.Fields.Minor, MC_ROOT_KEY ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
}
if( LoRaMacCryptoDeriveLifeTimeKey( 0, MC_KE_KEY ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
}
if( LoRaMacCryptoDeriveLifeTimeKey( 0, DATABLOCK_INT_KEY ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_CRYPTO_ERROR_SECURE_ELEMENT_FUNC;
}
}
return LORAMAC_CRYPTO_SUCCESS;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| /* Crypto/soft-se.c */
SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t *key )
{
if( key == NULL )
{
return SECURE_ELEMENT_ERROR_NPE;
}
#if (LORAWAN_KMS == 0)
for( uint8_t i = 0; i < NUM_OF_KEYS; i++ )
{
if( SeNvm->KeyList[i].KeyID == keyID )
{
#if ( LORAMAC_MAX_MC_CTX == 1 )
if( keyID == MC_KEY_0 )
#else /* LORAMAC_MAX_MC_CTX > 1 */
...
#endif /* LORAMAC_MAX_MC_CTX */
{
SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
uint8_t decryptedKey[SE_KEY_SIZE] = { 0 };
retval = SecureElementAesEncrypt( key, SE_KEY_SIZE, MC_KE_KEY, decryptedKey );
memcpy1( SeNvm->KeyList[i].KeyValue, decryptedKey, SE_KEY_SIZE );
return retval;
}
else
{
memcpy1( SeNvm->KeyList[i].KeyValue, key, SE_KEY_SIZE );
return SECURE_ELEMENT_SUCCESS;
}
}
}
return SECURE_ELEMENT_ERROR_INVALID_KEY_ID;
#else /* LORAWAN_KMS == 1 */
...
#endif /* LORAWAN_KMS */
}
|
3. 入网保存参数的流程#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| /* LmHandler/LmHandler.c */
void LmHandlerJoin( ActivationType_t mode, bool forceRejoin )
{
MlmeReq_t mlmeReq;
mlmeReq.Type = MLME_JOIN;
mlmeReq.Req.Join.Datarate = LmHandlerParams.TxDatarate;
mlmeReq.Req.Join.TxPower = LmHandlerParams.TxPower;
if( mode == ACTIVATION_TYPE_OTAA )
{
...
}
else
{
...
JoinParams.Mode = ACTIVATION_TYPE_ABP;
...
if( CtxRestoreDone == false )
{
/* 如果没有保存过入网参数, 也就是未入网的设备上电后进行入网 */
...
}
LoRaMacStart();
/* Nvm.MacGroup2.NetworkActivation 赋值为 ACTIVATION_TYPE_ABP */
mibReq.Type = MIB_NETWORK_ACTIVATION;
mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP;
LoRaMacMibSetRequestConfirm( &mibReq );
...
}
#if (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
if( ( CtxRestoreDone == false ) || ( forceRejoin == true ) )
{
/* 强制重新入网 或 未入网的设备上电后进行入网 */
LoRaMacMlmeRequest( &mlmeReq );
}
DutyCycleWaitTime = mlmeReq.ReqReturn.DutyCycleWaitTime;
#endif /* LORAMAC_VERSION */
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| /* Mac/LoRaMac.c */
LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest )
{
...
switch( mlmeRequest->Type )
{
case MLME_JOIN:
{
...
#if (defined( LORAMAC_VERSION ) && ( LORAMAC_VERSION == 0x01000300 ))
...
#elif (defined( LORAMAC_VERSION ) && (( LORAMAC_VERSION == 0x01000400 ) || ( LORAMAC_VERSION == 0x01010100 )))
if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_OTAA )
{
...
}
else if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_ABP )
{
...
Nvm.MacGroup2.NetworkActivation = mlmeRequest->Req.Join.NetworkActivation;
...
}
#endif /* LORAMAC_VERSION */
break;
}
...
}
...
}
|
三、FCnt#
1. 帧计数的使用#
调用流程:
1
2
3
4
5
6
7
8
9
| # Mac/LoRaMac.c
Send
-> PrepareFrame
-> LoRaMacCryptoGetFCntUp
-> ScheduleTx
-> SendFrameOnChannel
-> SecureFrame
-> LoRaMacCryptoGetFCntUp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| /* Mac/LoRaMacCrypto.c */
LoRaMacCryptoStatus_t LoRaMacCryptoGetFCntUp( uint32_t* currentUp )
{
if( currentUp == NULL )
{
return LORAMAC_CRYPTO_ERROR_NPE;
}
/* CryptoNvm 指向的是 Nvm.Crypto, 实际是 Nvm.Crypto.FCntList.FCntUp */
*currentUp = CryptoNvm->FCntList.FCntUp + 1;
return LORAMAC_CRYPTO_SUCCESS;
}
|
2. 帧计数的更新#
调用流程:
1
2
3
4
| ScheduleTx
-> SendFrameOnChannel
-> SecureFrame
-> LoRaMacCryptoSecureMessage
|
1
2
3
4
5
6
7
8
9
10
| /* Mac/LoRaMacCrypto.c */
LoRaMacCryptoStatus_t LoRaMacCryptoSecureMessage( uint32_t fCntUp, uint8_t txDr, uint8_t txCh, LoRaMacMessageData_t* macMsg )
{
...
/* CryptoNvm 指向的是 Nvm.Crypto, 实际是 Nvm.Crypto.FCntList.FCntUp */
CryptoNvm->FCntList.FCntUp = fCntUp;
return LORAMAC_CRYPTO_SUCCESS;
}
|
四、NVM#
1. 类型和定义#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| /* Mac/LoRaMacInterfaces.h */
/*!
* LoRaMAC data structure for non-volatile memory (NVM).
* This structure contains data which must be stored in NVM.
*/
typedef struct sLoRaMacNvmData
{
/*!
* Parameters related to the crypto layer. Change with every TX/RX
* procedure.
*/
LoRaMacCryptoNvmData_t Crypto;
/*!
* Parameters related to the MAC which change with high probability after
* every TX/RX procedure.
*/
LoRaMacNvmDataGroup1_t MacGroup1;
/*!
* Parameters related to the MAC which do not change very likely with every
* TX/RX procedure.
*/
LoRaMacNvmDataGroup2_t MacGroup2;
/*!
* Parameters related to the secure-element.
*/
SecureElementNvmData_t SecureElement;
/*!
* Parameters related to the regional implementation which change with high
* probability after every TX/RX procedure.
*/
RegionNvmDataGroup1_t RegionGroup1;
/*!
* Parameters related to the regional implementation which do not change
* very likely with every TX/RX procedure.
*/
RegionNvmDataGroup2_t RegionGroup2;
/*!
* Parameters related to class b.
*/
LoRaMacClassBNvmData_t ClassB;
}LoRaMacNvmData_t;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| /* Mac/LoRaMac.c */
/**
* 在 LoRaWAN 1.0.4 及以后的版本, 都需要在 lorawan_conf.h 头文件中定义 CONTEXT_MANAGEMENT_ENABLED
* 下面是整理后的代码, 方便查看
*/
#if (defined( CONTEXT_MANAGEMENT_ENABLED ) && ( CONTEXT_MANAGEMENT_ENABLED == 1 ))
#if defined(__ICCARM__)
__NO_INIT __ROOT static LoRaMacNvmData_t Nvm @ ".LW_NVM_RAM";
#elif defined(__GNUC__)
__attribute__((section(".bss.LW_NVM_RAM")))
__NO_INIT __ROOT static LoRaMacNvmData_t Nvm;
#else
#warning NVM RAM placement not defined
__NO_INIT __ROOT static LoRaMacNvmData_t Nvm;
#endif
#if defined(__ICCARM__)
__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup @ ".LW_NVM_BACKUP_RAM";
#elif defined(__GNUC__)
__attribute__((section(".bss.LW_NVM_BACKUP_RAM")))
__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup;
#else
#warning NVM RAM placement not defined
__NO_INIT __ROOT static LoRaMacNvmData_t NvmBackup;
#endif
#else
static LoRaMacNvmData_t Nvm;
#endif
|
2. 保存#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| /* LmHandler/LmHandler.c */
LmHandlerErrorStatus_t LmHandlerNvmDataStore( void )
{
#if (defined( CONTEXT_MANAGEMENT_ENABLED ) && ( CONTEXT_MANAGEMENT_ENABLED == 1 ))
LoRaMacNvmData_t *nvm;
uint32_t nvm_size;
LmHandlerErrorStatus_t lmhStatus = LORAMAC_HANDLER_SUCCESS;
int32_t status = NVM_DATA_OK;
lmhStatus = LmHandlerHalt();
if( lmhStatus == LORAMAC_HANDLER_SUCCESS )
{
status = NvmDataMgmtStoreBegin();
if( status == NVM_DATA_NO_UPDATED_DATA )
{
lmhStatus = LORAMAC_HANDLER_NVM_DATA_UP_TO_DATE;
}
else if( ( status != NVM_DATA_OK ) || ( LmHandlerCallbacks->OnStoreContextRequest == NULL ) )
{
lmhStatus = LORAMAC_HANDLER_ERROR;
}
else
{
MibRequestConfirm_t mibReq;
mibReq.Type = MIB_NVM_CTXS;
LoRaMacMibGetRequestConfirm( &mibReq );
/* 获取到在 LoRaMac.c 里面定义的静态全局变量 static LoRaMacNvmData_t Nvm 的指针 */
nvm = ( LoRaMacNvmData_t * )mibReq.Param.Contexts;
/* 向上进行 8 取整, 因为 flash 操作的内存大小要求是 8 字节对齐 */
nvm_size = ( ( sizeof( LoRaMacNvmData_t ) + 7 ) & ~0x07 );
/**
* 调用回调函数保存 nvm
* 这个回调函数是调用 LmHandlerInit 初始化时提供的
*/
LmHandlerCallbacks->OnStoreContextRequest( nvm, nvm_size );
}
if( NvmDataMgmtStoreEnd() != NVM_DATA_OK )
{
lmhStatus = LORAMAC_HANDLER_ERROR;
}
}
if( ( lmhStatus == LORAMAC_HANDLER_SUCCESS ) && ( LmHandlerCallbacks->OnNvmDataChange != NULL ) )
{
LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_STORE );
}
return lmhStatus;
#else
return LORAMAC_HANDLER_ERROR;
#endif /* CONTEXT_MANAGEMENT_ENABLED */
}
|
假设回调函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #include "flash_if.h"
#define LORAWAN_NVM_BASE_ADDRESS ((void *)0x0803F000UL)
static void OnStoreContextRequest(void *nvm, uint32_t nvm_size)
{
/**
* 调用 ST 提供的 FLASH 操作函数进行保存操作
* 将整个 nvm (LoRaMacNvmData_t) 保存到 FLASH 里面
*/
if (FLASH_IF_Erase(LORAWAN_NVM_BASE_ADDRESS, FLASH_PAGE_SIZE) == FLASH_IF_OK) {
FLASH_IF_Write(LORAWAN_NVM_BASE_ADDRESS, (const void *)nvm, nvm_size);
}
}
|
3. 上电后读取#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
| /* LmHandler/LmHandler.c */
LmHandlerErrorStatus_t LmHandlerConfigure( LmHandlerParams_t *handlerParams )
{
...
/**
* 对于 LoRaMac.c 里面定义的静态全局变量 static LoRaMacNvmData_t Nvm 来说
* 1. 使用 memset1 将其置 0
* 2. 配置默认值
* 3. 调用 SecureElementInit 函数对 Nvm.SecureElement 进行初始化
* 4. 调用 LoRaMacCryptoInit 函数对 Nvm.Crypto 进行清零和初始化
*/
if( LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks, LmHandlerParams.ActiveRegion ) != LORAMAC_STATUS_OK )
{
return LORAMAC_HANDLER_ERROR;
}
#if (defined( CONTEXT_MANAGEMENT_ENABLED ) && ( CONTEXT_MANAGEMENT_ENABLED == 1 ))
/**
* 调用 RestoreNvmData 函数
* 1. 对 NvmBackup 进行 CRC32 校验
* 2. 调用 memcpy1 函数将 NvmBackup 赋值给 Nvm
* 3. 调用 memset1 函数将 NvmBackup 清零
*/
mibReq.Type = MIB_NVM_CTXS;
if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
{
CtxRestoreDone = true;
}
else
{
/* 由于是重新上电, 所以是走这里 */
mibReq.Type = MIB_NVM_BKP_CTXS;
if( LmHandlerCallbacks->OnRestoreContextRequest != NULL )
{
/**
* 获取 LoRaMac.c 里面定义的静态全局变量 static LoRaMacNvmData_t NvmBackup
* 读取 FLASH 保存的数据, 并将其赋值给 NvmBackup
*/
LoRaMacMibGetRequestConfirm( &mibReq );
LmHandlerCallbacks->OnRestoreContextRequest( mibReq.Param.BackupContexts, sizeof( LoRaMacNvmData_t ) );
}
/* 重新调用 RestoreNvmData 函数 */
mibReq.Type = MIB_NVM_CTXS;
if( LoRaMacMibSetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK )
{
mibReq.Type = MIB_NETWORK_ACTIVATION;
LoRaMacMibGetRequestConfirm( &mibReq );
/* 检测设备是否已经入网, 如果已经入网, 则需要恢复上下文 */
if( mibReq.Param.NetworkActivation != ACTIVATION_TYPE_NONE )
{
CtxRestoreDone = true;
}
}
}
/* 恢复上下文 */
if( CtxRestoreDone == true )
{
if( LmHandlerCallbacks->OnNvmDataChange != NULL )
{
LmHandlerCallbacks->OnNvmDataChange( LORAMAC_HANDLER_NVM_RESTORE );
}
if(( LmHandlerJoinStatus() == LORAMAC_HANDLER_SET) && LoRaMacIsStopped())
{
/**
* 由于设备上一次运行期间是已入网的, 所以需要在这里调用 LoRaMacStart 函数启动 LoRaMac
* 如果是未入网的情况, 则是通过 LmHandlerJoin 函数来调用 LoRaMacStart 函数
*/
LoRaMacStart();
}
/* 获取 LoRaMac.c 里面定义的静态全局变量 static LoRaMacNvmData_t Nvm */
mibReq.Type = MIB_NVM_CTXS;
LoRaMacMibGetRequestConfirm( &mibReq );
LoRaMacNvmData_t *current_nvm = mibReq.Param.Contexts;
LmHandlerParams.ActiveRegion = current_nvm->MacGroup2.Region;
LmHandlerParams.DefaultClass = current_nvm->MacGroup2.DeviceClass;
LmHandlerParams.AdrEnable = current_nvm->MacGroup2.AdrCtrlOn;
}
else
#endif /* CONTEXT_MANAGEMENT_ENABLED == 1 */
}
|
假设回调函数如下:
1
2
3
4
5
6
7
8
| #include "flash_if.h"
#define LORAWAN_NVM_BASE_ADDRESS ((void *)0x0803F000UL)
static void OnRestoreContextRequest(void *nvm, uint32_t nvm_size)
{
FLASH_IF_Read(nvm, LORAWAN_NVM_BASE_ADDRESS, nvm_size);
}
|
五、总结#
设备 OTAA 入网后,会将服务器下发的关键参数更新到 Nvm 变量里面。
设备 ABP 入网前,也会手动将关键参数更新到 Nvm 变量里面。
设备发送一笔数据帧,也会将 FCnt 更新到 Nvm 变量里面。
我们只需要将 Nvm 变量保存到 Flash 里面,上电后从 Flash 重新加载到 Nvm 变量,那么设备就不用再次入网,而是直接发送数据了。