Github
https://github.com/HaHaHaHaHaGe/OpenThreadUDP
简介
功能描述
完成后可实现两个NUCLEO-WB55RG开发板节点相互收发数据
使用的协议栈是OpenThread
发送数据的方式使用UDP广播方式
所需环境
下面的环境不是必须的,只是我在进行试验时所用到的环境,具体可根据自己的需求相应修改,但是实验步骤和结果不能保证一致。
软件部分:
STM32CubeMX 6.1.0
STM32CubeIDE 1.5.0
STM32CubeProgrammer 2.5.0
硬件部分:
NUCLEO-WB55RG
固件烧写
由于STM32WB55的RF部分是由M0核控制的,协议栈也是在M0里的,所以若想使用某个协议栈,就需要先去烧写对应的协议栈。
下载地址:https://github.com/STMicroelectronics/STM32CubeWB
固件目录:STM32CubeWB-master\STM32CubeWB-master\Projects\STM32WB_Copro_Wireless_Binaries\STM32WB5x
详细步骤:STM32CubeWB-master/STM32CubeWB-master/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/Release_Notes.html
画箭头的地方是我们需要用到的文件以及文件的烧写地址
在烧写相应的协议栈之前,首先要将FUS升级到v1.1.0
(也就是需要烧写stm32wb5x_FUS_fw.bin)
但是不能直接烧写1.1.0,需要烧写1.0.2然后再烧写1.1.0版本
烧写玩FUS后,就可以烧写stm32wb5x_Thread_FTD_fw.bin协议栈了
PS:我在dongle与开发板测试发现,开发板无法使用usb模式只能使用stlink,只有dongle可以使用usb,可能是因为原厂没有为开发板加入dfu?
注意:如果使用stlink方式烧写固件,那么Read FUS State等按钮是灰色的。
如:
固件烧写后,就可以开始下一步工作了
项目配置
创建好项目后,我们的主要目的是使能THREAD协议栈
但是想使用它,需要一些先决条件
启用THREAD的先决条件
1.System Core -> HSEM
2.System Core -> RCC 并且 HSE = 32Mhz
3.Timers -> RTC
4.Connectivity -> RF
5.Connectivity -> LPUART1
5.Middleware -> STM32_WPAN
详细配置
取消串口的static选项很重要,否则会导致编译不过
然后为了方便看到现象,可以开启PB0与PB1,查看当前发射与接受状态
代码修改
main.c
/* USER CODE BEGIN Includes */
#include "stm32_lpm.h"
#include "stm32_seq.h"
#include "dbg_trace.h"
#include "hw_conf.h"
#include "otp.h"
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
uint32_t child_notif = 0;
/* USER CODE END PV */
/* USER CODE BEGIN PFP */
void PeriphClock_Config(void);
static void Reset_Device( void );
static void Reset_IPCC( void );
static void Reset_BackupDomain( void );
static void Init_Exti( void );
static void Config_HSE(void);
/* USER CODE END PFP */
int main(void)
{
/* USER CODE BEGIN 1 */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
Reset_Device();
Config_HSE();
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
PeriphClock_Config();
Init_Exti();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RF_Init();
MX_RTC_Init();
MX_LPUART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Init code for STM32_WPAN */
APPE_Init();
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(child_notif == 1)
UTIL_SEQ_SetTask(TASK_UDP_SEND, CFG_SCH_PRIO_1);
UTIL_SEQ_Run( UTIL_SEQ_DEFAULT );
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void PeriphClock_Config(void)
{
#if (CFG_USB_INTERFACE_ENABLE != 0)
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
RCC_CRSInitTypeDef RCC_CRSInitStruct = { 0 };
/**
* This prevents the CPU2 to disable the HSI48 oscillator when
* it does not use anymore the RNG IP
*/
LL_HSEM_1StepLock( HSEM, 5 );
LL_RCC_HSI48_Enable();
while(!LL_RCC_HSI48_IsReady());
/* Select HSI48 as USB clock source */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
/*Configure the clock recovery system (CRS)**********************************/
/* Enable CRS Clock */
__HAL_RCC_CRS_CLK_ENABLE();
/* Default Synchro Signal division factor (not divided) */
RCC_CRSInitStruct.Prescaler = RCC_CRS_SYNC_DIV1;
/* Set the SYNCSRC[1:0] bits according to CRS_Source value */
RCC_CRSInitStruct.Source = RCC_CRS_SYNC_SOURCE_USB;
/* HSI48 is synchronized with USB SOF at 1KHz rate */
RCC_CRSInitStruct.ReloadValue = RCC_CRS_RELOADVALUE_DEFAULT;
RCC_CRSInitStruct.ErrorLimitValue = RCC_CRS_ERRORLIMIT_DEFAULT;
RCC_CRSInitStruct.Polarity = RCC_CRS_SYNC_POLARITY_RISING;
/* Set the TRIM[5:0] to the default value*/
RCC_CRSInitStruct.HSI48CalibrationValue = RCC_CRS_HSI48CALIBRATION_DEFAULT;
/* Start automatic synchronization */
HAL_RCCEx_CRSConfig(&RCC_CRSInitStruct);
#endif
return;
}
static void Config_HSE(void)
{
OTP_ID0_t * p_otp;
/**
* Read HSE_Tuning from OTP
*/
p_otp = (OTP_ID0_t *) OTP_Read(0);
if (p_otp)
{
LL_RCC_HSE_SetCapacitorTuning(p_otp->hse_tuning);
}
return;
}
static void Reset_Device( void )
{
#if ( CFG_HW_RESET_BY_FW == 1 )
Reset_BackupDomain();
Reset_IPCC();
#endif
return;
}
static void Reset_IPCC( void )
{
LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC);
LL_C1_IPCC_ClearFlag_CHx(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
LL_C2_IPCC_ClearFlag_CHx(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
LL_C1_IPCC_DisableTransmitChannel(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
LL_C2_IPCC_DisableTransmitChannel(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
LL_C1_IPCC_DisableReceiveChannel(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
LL_C2_IPCC_DisableReceiveChannel(
IPCC,
LL_IPCC_CHANNEL_1 | LL_IPCC_CHANNEL_2 | LL_IPCC_CHANNEL_3 | LL_IPCC_CHANNEL_4
| LL_IPCC_CHANNEL_5 | LL_IPCC_CHANNEL_6);
return;
}
static void Reset_BackupDomain( void )
{
if ((LL_RCC_IsActiveFlag_PINRST() != FALSE) && (LL_RCC_IsActiveFlag_SFTRST() == FALSE))
{
HAL_PWR_EnableBkUpAccess(); /**< Enable access to the RTC registers */
/**
* Write twice the value to flush the APB-AHB bridge
* This bit shall be written in the register before writing the next one
*/
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_BACKUPRESET_FORCE();
__HAL_RCC_BACKUPRESET_RELEASE();
}
return;
}
static void Init_Exti( void )
{
/**< Disable all wakeup interrupt on CPU1 except IPCC(36), HSEM(38) */
LL_EXTI_DisableIT_0_31(~0);
LL_EXTI_DisableIT_32_63( (~0) & (~(LL_EXTI_LINE_36 | LL_EXTI_LINE_38)) );
return;
}
/* USER CODE END 4 */
app_conf.h
typedef enum
{
CFG_TASK_MSG_FROM_M0_TO_M4,
CFG_TASK_SEND_CLI_TO_M0,
CFG_TASK_SYSTEM_HCI_ASYNCH_EVT,
#if (CFG_USB_INTERFACE_ENABLE != 0)
CFG_TASK_VCP_SEND_DATA,
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */
/* USER CODE BEGIN CFG_IdleTask_Id_t */
CFG_TASK_UDP_SEND,
/* USER CODE END CFG_IdleTask_Id_t */
CFG_TASK_NBR /**< Shall be last in the list */
} CFG_IdleTask_Id_t;
/* Scheduler types and defines */
/*------------------------------------*/
#define TASK_MSG_FROM_M0_TO_M4 (1U << CFG_TASK_MSG_FROM_M0_TO_M4)
/* USER CODE BEGIN DEFINE_TASK */
#define TASK_UDP_SEND (1U << CFG_TASK_UDP_SEND)
/* USER CODE END DEFINE_TASK */
app_thread.c
/* USER CODE BEGIN PM */
#define SuccessOrExit(aStatus) \
do \
{ \
if ((aStatus) != 0) \
{ \
goto exit; \
} \
} while (false)
#define VerifyOrExit(aCondition, ...) \
do \
{ \
if (!(aCondition)) \
{ \
__VA_ARGS__; \
goto exit; \
} \
} while (false)
/* USER CODE END PM */
/* USER CODE BEGIN PFP */
static otError UdpBind(uint16_t aPort);
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
static void APP_THREAD_UdpSend(void);
static otError UdpSend(void);
/* USER CODE END PFP */
/* USER CODE BEGIN PV */
otUdpSocket mSocket;
uint8_t udpBufffer[256] = "HELLO UDP!";
uint16_t udpPort = 1234;
extern uint32_t child_notif;
/* USER CODE END PV */
/* Functions Definition ------------------------------------------------------*/
void APP_THREAD_Init( void )
{
/* USER CODE BEGIN APP_THREAD_INIT_1 */
/* USER CODE END APP_THREAD_INIT_1 */
SHCI_CmdStatus_t ThreadInitStatus;
/* Check the compatibility with the Coprocessor Wireless Firmware loaded */
APP_THREAD_CheckWirelessFirmwareInfo();
#if (CFG_USB_INTERFACE_ENABLE != 0)
VCP_Init(&VcpTxBuffer[0], &VcpRxBuffer[0]);
#endif /* (CFG_USB_INTERFACE_ENABLE != 0) */
/* Register cmdbuffer */
APP_THREAD_RegisterCmdBuffer(&ThreadOtCmdBuffer);
/**
* Do not allow standby in the application
*/
UTIL_LPM_SetOffMode(1 << CFG_LPM_APP_THREAD, UTIL_LPM_DISABLE);
/* Init config buffer and call TL_THREAD_Init */
APP_THREAD_TL_THREAD_INIT();
/* Configure UART for sending CLI command from M4 */
APP_THREAD_Init_UART_CLI();
/* Send Thread start system cmd to M0 */
ThreadInitStatus = SHCI_C2_THREAD_Init();
/* Prevent unused argument(s) compilation warning */
UNUSED(ThreadInitStatus);
/* Register task */
/* Create the different tasks */
UTIL_SEQ_RegTask( 1<<(uint32_t)CFG_TASK_MSG_FROM_M0_TO_M4, UTIL_SEQ_RFU, APP_THREAD_ProcessMsgM0ToM4);
/* USER CODE BEGIN INIT TASKS */
UTIL_SEQ_RegTask( 1<<(uint32_t)CFG_TASK_UDP_SEND, UTIL_SEQ_RFU, APP_THREAD_UdpSend);
/* USER CODE END INIT TASKS */
/* Initialize and configure the Thread device*/
APP_THREAD_DeviceConfig();
/* USER CODE BEGIN APP_THREAD_INIT_2 */
/* USER CODE END APP_THREAD_INIT_2 */
}
static void APP_THREAD_DeviceConfig(void)
{
otError error;
error = otInstanceErasePersistentInfo(NULL);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_ERASE_PERSISTENT_INFO,error);
}
otInstanceFinalize(NULL);
otInstanceInitSingle();
error = otSetStateChangedCallback(NULL, APP_THREAD_StateNotif, NULL);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_SET_STATE_CB,error);
}
error = otLinkSetChannel(NULL, C_CHANNEL_NB);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_SET_CHANNEL,error);
}
error = otLinkSetPanId(NULL, C_PANID);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_SET_PANID,error);
}
error = otIp6SetEnabled(NULL, true);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_IPV6_ENABLE,error);
}
error = otThreadSetEnabled(NULL, true);
if (error != OT_ERROR_NONE)
{
APP_THREAD_Error(ERR_THREAD_START,error);
}
/* USER CODE BEGIN DEVICECONFIG */
/* Initialiaze socket */
memset(&mSocket, 0, sizeof(mSocket));
/* Open socket */
otUdpOpen(NULL, &mSocket, HandleUdpReceive, NULL);
UdpBind(udpPort);
/* USER CODE END DEVICECONFIG */
}
/**
* @brief Thread notification when the state changes.
* @param aFlags : Define the item that has been modified
* aContext: Context
*
* @retval None
*/
static void APP_THREAD_StateNotif(uint32_t NotifFlags, void *pContext)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(pContext);
/* USER CODE BEGIN APP_THREAD_STATENOTIF */
/* USER CODE END APP_THREAD_STATENOTIF */
if ((NotifFlags & (uint32_t)OT_CHANGED_THREAD_ROLE) == (uint32_t)OT_CHANGED_THREAD_ROLE)
{
switch (otThreadGetDeviceRole(NULL))
{
case OT_DEVICE_ROLE_DISABLED:
/* USER CODE BEGIN OT_DEVICE_ROLE_DISABLED */
/* USER CODE END OT_DEVICE_ROLE_DISABLED */
break;
case OT_DEVICE_ROLE_DETACHED:
/* USER CODE BEGIN OT_DEVICE_ROLE_DETACHED */
child_notif = 0U;
/* USER CODE END OT_DEVICE_ROLE_DETACHED */
break;
case OT_DEVICE_ROLE_CHILD:
/* USER CODE BEGIN OT_DEVICE_ROLE_CHILD */
// if (child_notif == 0)
// {
// HAL_Delay(3000U);
// UTIL_SEQ_SetTask(TASK_UDP_SEND, CFG_SCH_PRIO_1);
// }
child_notif = 1U;
/* USER CODE END OT_DEVICE_ROLE_CHILD */
break;
case OT_DEVICE_ROLE_ROUTER :
/* USER CODE BEGIN OT_DEVICE_ROLE_ROUTER */
/* USER CODE END OT_DEVICE_ROLE_ROUTER */
break;
case OT_DEVICE_ROLE_LEADER :
/* USER CODE BEGIN OT_DEVICE_ROLE_LEADER */
/* USER CODE END OT_DEVICE_ROLE_LEADER */
break;
default:
/* USER CODE BEGIN DEFAULT */
/* USER CODE END DEFAULT */
break;
}
}
}
/* USER CODE BEGIN FD_LOCAL_FUNCTIONS */
/**
* @brief This function initiates the APP_THREAD_UdpSend procedure
*
* @param None
* @retval None
*/
static void APP_THREAD_UdpSend(void)
{
HAL_Delay(100U);
/* Send Udp request */
UdpSend();
}
static otError UdpSend(void)
{
otError error;
otMessageInfo messageInfo;
otMessage * message = NULL;
memset(&messageInfo, 0, sizeof(messageInfo));
error = otIp6AddressFromString("ff02::1", &messageInfo.mPeerAddr);
SuccessOrExit(error);
messageInfo.mPeerPort = udpPort;
messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD;
message = otUdpNewMessage(NULL, true);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
error = otMessageAppend(message, udpBufffer, (uint16_t)strlen((const char*)udpBufffer));
SuccessOrExit(error);
APP_DBG("Sending UDP message %s", udpBufffer);
error = otUdpSend(&mSocket, message, &messageInfo);
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
APP_DBG("UdpSend failed with error : %s", error);
otMessageFree(message);
}
return error;
}
static uint16_t Swap16(uint16_t v)
{
return (((v & 0x00ffU) << 8) & 0xff00) | (((v & 0xff00U) >> 8) & 0x00ff);
}
static uint16_t HostSwap16(uint16_t v)
{
return Swap16(v);
}
static otError UdpBind(uint16_t aPort)
{
otError error;
otSockAddr sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
/* "::" specifies the IPv6 Unspecified Address */
error = otIp6AddressFromString("::", &sockaddr.mAddress);
SuccessOrExit(error);
sockaddr.mPort = aPort;
sockaddr.mScopeId = OT_NETIF_INTERFACE_ID_THREAD;
error = otUdpBind(&mSocket, &sockaddr);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
exit:
return error;
}
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
int length;
uint8_t udpBuffferReceived[256];
APP_DBG("Received %d bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage));
APP_DBG(
"%x:%x:%x:%x:%x:%x:%x:%x %d ", HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[0]),
HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[1]), HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[2]),
HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[3]), HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[4]),
HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[5]), HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[6]),
HostSwap16(aMessageInfo->mPeerAddr.mFields.m16[7]), aMessageInfo->mPeerPort);
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), udpBuffferReceived, sizeof(udpBuffferReceived) - 1);
udpBuffferReceived[length] = '\0';
APP_DBG("Received %s\r\n", udpBuffferReceived);
if(strcmp((char const*)udpBuffferReceived, (char const*)udpBufffer) == 0)
{
APP_DBG("Comparison OK!");
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
}
/* USER CODE END FD_LOCAL_FUNCTIONS */
stm32wbxx_it.c
/* USER CODE BEGIN PFP */
extern void HW_IPCC_Rx_Handler(void);
extern void HW_IPCC_Tx_Handler(void);
/* USER CODE END PFP */
/* USER CODE BEGIN 1 */
/**
* @brief This function handles IPCC RX occupied global interrupt request.
* @param None
* @retval None
*/
void IPCC_C1_RX_IRQHandler(void)
{
HW_IPCC_Rx_Handler();
}
/**
* @brief This function handles IPCC TX free global interrupt request.
* @param None
* @retval None
*/
void IPCC_C1_TX_IRQHandler(void)
{
HW_IPCC_Tx_Handler();
}
/* USER CODE END 1 */