基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(四)-移植到STM32平台

zigbee 专栏收录该内容
6 篇文章 2 订阅

相关系列文章

基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(-)-Z3GatewayHost应用搭建

基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(二)-使用gateway-management-ui

基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(三)-移植到ESP32平台(1)

基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(三)-移植到ESP32平台(2)

基于芯科Host-NCP解决方案的Zigbee 3.0 Gateway技术研究(四)-移植到STM32平台

概要

其实并没成功地把Host应用移植到STM32平台,所以这里只是记录一下移植过程中,碰到的一些问题,以便以后作为参考。

整个移植过程,和前面移植到ESP32是大致一样的,基本都是按:代码适配、生成静态库、在STM32开发环境集成;这样一个思路来实现。

软硬件环境

STM32-EFR32面包板一套

Host:STM32F407VGT6 核心版 Flash:1MB  SRAM :192+4KB

NCP:EFR32MG21A020F768IM32

WIFI:ESP826612-f (AT固件版),用于网络链接,mqtt链接

STM32开发环境:Keil uVision5、STM32CubeMX

STM32交叉编译链:gcc version 10.2.1 20201103 (release) (GNU Arm Embedded Toolchain 10-2020-q4-major) (ARM官方下载地址)

代码适配

要调整的代码基本和前面ESP32类似,所以这里不再重复列出了,只列出与ESP32不一样的地方:

1.system-timer-stm32.c:因为STM32不支持gettimeofday方法,所以调整为如下,其中stm32HalCommonGetInt32uMillisecondTick将在应用中实现。

#include <sys/time.h>

#include PLATFORM_HEADER
//stm32方式读取程序运行后经过的毫秒数
extern uint32_t  stm32HalCommonGetInt32uMillisecondTick(void);
uint32_t halCommonGetInt32uMillisecondTick(void)
{
	/**
  struct timeval tv;
  uint32_t now;

  gettimeofday(&tv, NULL);
  now = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
  return now;
  **/
  
  return stm32HalCommonGetInt32uMillisecondTick();
}

uint16_t halCommonGetInt16uMillisecondTick(void)
{
  return (uint16_t)halCommonGetInt32uMillisecondTick();
}

uint16_t halCommonGetInt16uQuarterSecondTick(void)
{
  return (uint16_t)(halCommonGetInt32uMillisecondTick() >> 8);
}

2.删除ota相关部分功能。(因为stm32的flash空间不够,所以不考虑ota功能)

3.调整Makefile的编译工具链和编译参数,其中-mcpu=cortex-m3 -mthumb -D STM32F407xx -D VECT_TAB_FLASH 代表STM32开发板的cpu型号和芯片,如果是其他STM32系列,要一一对应。

COMPILER ?= arm-none-eabi-gcc
LINKER   ?= arm-none-eabi-ld
ARCHIVE  ?= ar
STD      ?= gnu99

CPPFLAGS= $(INCLUDES) $(DEFINES) $(COMPILER_FLAGS) $(DEPENDENCY_FLAGS) -g -mcpu=cortex-m3 -mthumb -D STM32F407xx -D VECT_TAB_FLASH

以上为和ESP32不同的地方,调整完后通过make命令生成stm32使用的静态库。

链接静态库

1.STM32的工程项目,最好是基于HAL库的。

一开始的时候,使用STM32标准库的项目,但是调试了很久,始终无法很好地解决通过串口传输数据的问题,包括STM32与EFR32间的串口、STM32与ESP8266间的串口;所以后来改为使用HAL库。

2.通过STM32CubeMX配置串口、时钟、freeRTOS等,然后生成Keil uVision5工程项目代码。

 

3.在Keil uVision5中打开工程,并且设置静态库链接

4.设置编译参数,包括使用“Use MicroLIB”、启用IRAM2、增加“--wchar32”编译项。

工程代码适配

1.修改堆栈大小,因为STM32默认使用的堆栈较小,所以要调大,如下图所示

2.适配时间获取方法,通过HAL_GetTick获取系统运行毫秒数(这个很关键,保证host-ncp间通讯同步;后来想移植到STM32H7系列,但发现H7的开发板,时间获取一直有问题,每每1秒误差60毫秒,所以直接放弃了)。

3.串口适配,主要使用了HAL库的方法,这里重点是保证串口不要阻塞

#include "usart.h"
#include "stm32f4xx_hal.h"
#include "stdbool.h"

// 串口2 gateway-host-ncp
UART_HandleTypeDef huart2;

// 串口Rx接收处理(每次接收1个字节数据)
static uint8_t aRxBuffer;
// 串口Rx接收缓存数组
static uint8_t Uart_RxBuff[1024];
// 串口接收数据长度
static uint8_t Uart_Rx_Cnt = 0;
// 溢出提示
static uint8_t cAlmStr[] = "数据溢出(超过1024)\r\n";

// 标识串口ncp数据是否接收完成
static bool ncpDataReving = false;

// 串口2 PA2 PA3 用于host-ncp通讯
void uart_gateway_host_init(void)
{
	// 使用USART2串口
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 115200;
	huart2.Init.WordLength = UART_WORDLENGTH_8B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;

	if (HAL_UART_Init(&huart2) != HAL_OK)
	{
		Usart_Error_Handler();
	}

	// 使能接收中断,开启接收,每次接收1个字节,存入aRxBuffer变量
	// 在接收完回调函数RxCpltCallback中再次开启中断
	HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1);
}

// zigbee npc 串口发送
int gateway_host_send_byte(char *src, int size)
{
	HAL_UART_Transmit(&huart2, src, size, 0xFFFF);
	return size;
}
// zigbee npc 串口读取
int gateway_host_read_byte(char *buf, int length)
{

	if (!ncpDataReving && Uart_Rx_Cnt > 1)
	{
		// 读取数据到buf
		memcpy(buf, Uart_RxBuff, sizeof(int) * Uart_Rx_Cnt);
		length = Uart_Rx_Cnt;
		Uart_Rx_Cnt = 0;
		memset(Uart_RxBuff, 0x00, sizeof(Uart_RxBuff));
	}
	else
	{
		length = 0;
	}
	return length;
}

void usart_gateway_RxCpltCallback(UART_HandleTypeDef *huart)

{
	if (huart->Instance == USART2)
	{

		ncpDataReving = true;
		if (Uart_Rx_Cnt >= 1024) //溢出判断
		{
			printf("%s\r\n", cAlmStr);
			Uart_Rx_Cnt = 0;
			memset(Uart_RxBuff, 0x00, sizeof(Uart_RxBuff));
		}
		else
		{

			Uart_RxBuff[Uart_Rx_Cnt++] = aRxBuffer;						   //接收数据转存
			if ((Uart_RxBuff[Uart_Rx_Cnt - 1] == 0x7e) && Uart_Rx_Cnt > 2) //判断结束位
			{
				// 数据接收完成
				ncpDataReving = false;
			}
		}
		//再开启接收中断,接收新数据
		HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1);
	}
}

4.因为STM32不具备wifi能力,所以只能增加esp8266模块实现。但这也是最麻烦的,最后始终没法彻底解决。

  主要有以下几个问题:

1)默认的esp8266固件不带mqtt功能,所以只能自己刷mqtt固件

2)乐鑫的mqtt固件,在发送数据的时候,数据长度不能超过512字节;

3)下载了乐鑫的esp8266工程,想通过menuconfig调整数据长度限制,但是发觉调整后,发送数据长度还是不能超过512字节;查看代码,但已经封装为静态库的方式,没法调整

所以基于上面的原因,在STM32的Host应用中,使用mqtt时非常不稳定。另外也由于STM32的内存和flash都较小,所以决定不再深入下去了。

以下为STM32 host中一些较大优化的空间

Z3GatewayHost.h 中的
EMBER_AF_PLUGIN_COMMAND_RELAY_RELAY_TABLE_SIZE默认是200,调整为50

device-table.h中的
EMBER_AF_PLUGIN_DEVICE_TABLE_DEVICE_TABLE_SIZE 默认是255,调整为32

ezsp-host-configuration-defaults.h中的
EZSP_HOST_RX_POOL_SIZE; 默认为20,调整为10

总结

从stm32生成的固件来看,stm32的编译优化能力很厉害,可以看到生成后的固件堆栈使用还是可以接受的。

==============================================================================


      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   

    272560      38770     117976       2080     156068    2414173   Grand Totals
    272560      38770     117976       1216     156068    2414173   ELF Image Totals (compressed)
    272560      38770     117976       1216          0          0   ROM Totals

==============================================================================

    Total RO  Size (Code + RO Data)               390536 ( 381.38kB)
    Total RW  Size (RW Data + ZI Data)            158148 ( 154.44kB)
    Total ROM Size (Code + RO Data + RW Data)     391752 ( 382.57kB)

==============================================================================

程序运行后,不使用mqtt的情况下,通过命令行来进行网络设置、开放网络、设备入网、设备属性读取、设备控制等都是能正常使用的;但始终由于f407的内存和flash有限,也不具备原生的互联网能力,所以个人觉得不太合适作为融合通讯网关。

  • 1
    点赞
  • 0
    评论
  • 4
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

lshddd

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值