本帖最后由 sujingliang 于 2025-5-15 20:19 编辑
起源
在去年STM32H7S78-DK开发板评测中,随套件而来的有一个WI-FI Module (EMW3080-BP) MB1400。
不同常见的WI-FI串口模块,这个模块是基于SPI接口通信。当时就想把它驱动了,但是找遍了例程和application都没有相关资料。倒是凭着ST官方论坛上的互动的只言片语中发现STM32H573I-DK有这款WI-FI模块驱动例程,位置大概在:
C:\Users\用户名\STM32Cube\Repository\STM32Cube_FW_H5_V1.3.0\Projects\STM32H573I-DK\Applications\NetXDuo\Nx_Network_Basics_wifi
由于当时时间紧张,并且能力有限,没有在评测期间在STM32H7S78-DK上的移植。于是一直搁置了,而想要实现对这个WI-FI SPI接口模块驱动的想法从来没有改变
SPI接口的WI-FI模块
本来想着
今年有幸入围STM32N6570-DK的评测,正好都具有STMOD+CONNECTOR,所以就想着不如在STM32N6570-DK实现对这款模块的驱动,也算了却一个心愿。
于是仿照STM32Cube_FW_H5_V1.3.0的Nx_Network_Basics_wifi例程开始搭建N6下的应用。开始并不顺利,由于对ThreadX、NetXDuo不了解,而且似乎串口重定向和程序有冲突也耽误了一些时间。最后程序调试通了,因为日志已经打印出来了,可以肉眼可见几个线程都跑起来了,似乎一切都向好的方向发展。但接上WI-FI模块,还是报错了无法连接,后来查看了一下STM32N6570-DK原理图,发现开发板上的STMOD+CONNECTOR引出的是串口,而非SPI,乌龙了。无法在STM32N6570-DK中实现驱动这个模块了。
偶然发现
尽管很乌龙,但是经过之前的磨难基本把STM32cubeMX配置和在KEIL中的编码流程搞清楚了,再到STM32H7S78-DK搭建应该是不成问题了。所以又回到原点,在STM32H7S78-DK中驱动这个模块。
照例打开STM32cubeMX,STM32H7RS软件包升级到v1.2.0,选择STM32H7S78-DK。突发奇想为什么每次都费时费力从空模板开始,为什么不从Start My project from Example开始一个工程,也许能解决一些时间。于是就发现虽然软件包的目录中没有WI-FI例程,但是MX例程中是存在WI-FI模块的例程。这难道就是众里寻他千百度,那人却在灯火阑珊处吗?我已顾不上各种复杂的心情,快速生成了工程,在经过漫长的编译,烧录后,验证一切功能正常,可以驱动了。
简单分析一下
这是生成的工程:
ThreadX:ThreadX是Express Logic公司开发的一款高性能实时操作系统(RTOS),现在由Microsoft维护。
NetXDuo:NetX Duo 是 Azure RTOS(ThreadX)提供的 双协议栈(IPv4/IPv6)网络协议栈,专为嵌入式设备设计,支持 TCP/IP、UDP、DHCP、DNS 等协议,具有低内存占用和高实时性特点。
mx_wifi:WIFI模块驱动,其中包括spi接口的EMW3080。
FileX:在这里用来生成网页。
1、main.c主要部分
int main(void)
{
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_SPI4_Init();
MX_UART4_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
MX_ThreadX_Init();
while (1)
{
}
}
MX_ThreadX_Init();//ThreadX初始化
2、app_threadx.c
void MX_ThreadX_Init(void)
{
/* USER CODE BEGIN Before_Kernel_Start */
/* USER CODE END Before_Kernel_Start */
tx_kernel_enter();
/* USER CODE BEGIN Kernel_Start_Error */
/* USER CODE END Kernel_Start_Error */
}
tx_kernel_enter();//ThreadX入口
3、app_azure_rtos.c
VOID tx_application_define(VOID *first_unused_memory)
中调用了:
status = MX_FileX_Init(memory_ptr);//处理网页读取
status = MX_NetXDuo_Init(memory_ptr);//处理网络相关
4、app_netxduo.c
UINT MX_NetXDuo_Init(VOID *memory_ptr)
中执行了:
ret = tx_thread_create(&AppMainThread, AppMainThreadName, App_Main_Thread_Entry,
(ULONG)byte_pool, stack_ptr, stack_size,
MAIN_THREAD_PRIORITY, MAIN_THREAD_PRIORITY, TX_NO_TIME_SLICE, TX_AUTO_START);
ret = tx_thread_create(&AppMain2Thread, AppMain2ThreadName, App_Main2_Thread_Entry,
(ULONG)byte_pool, stack_ptr, stack_size,
MAIN2_THREAD_PRIORITY, MAIN2_THREAD_PRIORITY, TX_NO_TIME_SLICE, TX_AUTO_START);
ret = tx_thread_create(&AppIperfThread, AppIperfThreadName, App_Iperf_Thread_Entry,
(ULONG)byte_pool, stack_ptr, stack_size,
APP_IPERF_THREAD_PRIORITY, APP_IPERF_THREAD_PRIORITY, TX_NO_TIME_SLICE, TX_DONT_START);
生成了几个线程,其中App_Iperf_Thread_Entry生成了一个Iperf网络测试界面供浏览器访问。
static VOID App_Iperf_Thread_Entry(ULONG thread_input)
{
TX_BYTE_POOL *const byte_pool = (TX_BYTE_POOL *) thread_input;
MSG_DEBUG("[%06" PRIu32 "]>\n", HAL_GetTick());
{
MSG_INFO(" - Device Name : %s.\n", wifi_obj_get()->SysInfo.Product_Name);
MSG_INFO(" - Device ID : %s.\n", wifi_obj_get()->SysInfo.Product_ID);
MSG_INFO(" - Device Version : %s.\n", wifi_obj_get()->SysInfo.FW_Rev);
MSG_INFO(" - MAC address : %02X.%02X.%02X.%02X.%02X.%02X\n",
wifi_obj_get()->SysInfo.MAC[0], wifi_obj_get()->SysInfo.MAC[1],
wifi_obj_get()->SysInfo.MAC[2], wifi_obj_get()->SysInfo.MAC[3],
wifi_obj_get()->SysInfo.MAC[4], wifi_obj_get()->SysInfo.MAC[5]);
}
/* The network is correctly initialized, start the Iperf utility. */
/* Allocate the memory for HTTP and Iperf stack */
{
const ULONG http_stack_size = NX_IPERF_HTTP_STACK_SIZE;
UCHAR *http_stack;
const ULONG iperf_stack_size = NX_IPERF_STACK_SIZE;
UCHAR *iperf_stack;
if (tx_byte_allocate(byte_pool, (VOID **)&http_stack, http_stack_size, TX_NO_WAIT) != TX_SUCCESS)
{
MSG_ERROR("Allocation failed!\n");
Error_Handler();
}
if (tx_byte_allocate(byte_pool, (VOID **)&iperf_stack, iperf_stack_size, TX_NO_WAIT) != TX_SUCCESS)
{
MSG_ERROR("Allocation failed!\n");
Error_Handler();
}
MSG_INFO("\n##### Please open a browser window with the Target board's IP address\n\n");
/* Application body. */
nx_iperf_entry(&IperfPacketPool, &IpInstance, http_stack, http_stack_size, iperf_stack, iperf_stack_size);
}
}
程序运行起来打印的日志:
浏览器访问返回:
5、与模块相关的配置:
mx_wifi_conf.h
#define MX_WIFI_USE_SPI (1)
#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
6、网页内容定义
nx_iperf.h
#define htmlresponse "HTTP/1.0 200 \r\nContent-Type: text/html\r\n\r\n"
#define htmltag "<HTML>"
#define htmlendtag "</HTML>"
#define titleline "<HEAD><TITLE>NetX IPerf Demonstration</TITLE></HEAD>\r\n"
#define bodytag "<body bgcolor=\"#000000\">\r\n"
#define bodyendtag "</body>\r\n"
#define **_area \
"<table border=0 align=center width=90%><tr>" \
"<td width=30%><img align=left src=ms**.jpg>" \
"</td><td width=33%></td><td width=33%><img align=right src=nx**.png></td></tr></table>"
#define hrline "<HR SIZE=6 WIDTH=\"90%\" NOSHADE COLOR=\"#FFFF00\">"
#define h1line1 " <H1><font face=arial color=\"#FFFFFF\">NetX IP Address: "
#define h1line2 "</font></H1><br>\r\n"
#define tabletag "<table height=50%>"
最后
由于个人能力有限,而且例程篇幅过长不做一一解读了,如果有兴趣可以通过MX生成代码解读。
SPI接口WIFI模块不常见,而且ST在实现上运用了ThreadX、NetXDuo等技术,而且封装了和模块通信的协议,再加上防御性编程,感觉远比驱动一般串口WFI模块复杂,甚至可以说不比直接通过WIFI模块二次开发简单。
终于跑通了,再无心魔,念头通达,修为又可以提升了。
|
|