写点什么

STM32 配合 W5500 网卡连接 MQTT 服务器

作者:DS小龙哥
  • 2023-06-17
    重庆
  • 本文字数:6131 字

    阅读完需:约 20 分钟

【1】W5500 网卡

W5500 是一种基于 TCP/IP 协议的网络通讯芯片,可以提供网络连接功能,相当于是一种嵌入式以太网控制器,具有低功耗、高速传输、易于集成等特点。W5500 芯片能够支持 TCP、UDP、IPv4、ARP、ICMP、IGMP 等协议,使得它变得非常适合用于嵌入式设备与互联网之间的通信需求,例如智能家居、工业控制、远程监控等场景。W5500 网卡还有一个特点是它支持硬件协议堆栈,这意味着它可以非常快地执行协议栈中的操作,从而大大提高了数据传输的效率。同时,W5500 还具有较低的功耗,因此非常适合嵌入式设备这种资源受限的场景。


W5500 芯片通过 SPI 总线与 MCU 进行通信,MCU 需要实现 SPI 总线协议来控制 W5500 进行数据交互。


【2】SPI 协议

SPI(Serial Peripheral Interface)协议是一种串行外设接口协议,是一种全双工、同步的接口技术,通常用于连接微控制器和外设,例如传感器、存储器、显示器等。SPI 协议传输效率高,使用简单,开销较小,因此被广泛应用于嵌入式系统中。


SPI 协议使用主从模式,主设备可以控制多个从设备,从设备不能主动向主设备发送数据或信息。SPI 协议具有以下几个重要的信号线:


  1. SCLK:时钟线,由主设备提供,用于同步主从设备之间的数据传输。

  2. MOSI(Master Out Slave In):主输出从输入线,由主设备提供,用于向从设备发送数据。

  3. MISO(Master In Slave Out):主输入从输出线,由从设备提供,用于向主设备发送数据。

  4. SS(Slave Select):从设备选择信号线,由主设备提供。当主设备需要与某个从设备通信时,将该线电平拉低,以选择需要通信的从设备。


SPI 协议的数据传输是基于数据字节的传输,主设备每次通过 MOSI 线发送一个字节,从设备通过 MISO 线接受该字节,并回传一个字节。数据的传输顺序可以根据时钟线(SCLK)的极性和相位配置为四种不同的模式。SPI 协议支持的模式受闪存、RAM、I/O 和模拟/数字转换器等外设和类型的限制。

【3】W5500 建立 TCP 协议通信

以下是 STM32 通过 W5500 建立 TCP 通信,并访问 TCP 服务器,完成数据收发的示例代码。


代码中使用了 STM32 HAL 库,W5500 的 IP 地址和端口号需要根据实际情况进行设置。


#include "main.h"#include "stdio.h"#include "stm32f1xx_hal.h"#include "wizchip_conf.h"#include "socket.h"#include "dhcp.h"
/* Private variables */SPI_HandleTypeDef hspi1;
/* Private function prototypes */void SystemClock_Config(void);static void MX_GPIO_Init(void);static void MX_SPI1_Init(void);void W5500_Init(void);uint8_t socket;uint8_t buf[1024];
int main(void){ /* MCU Configuration */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init();
/* W5500 Initialization */ W5500_Init();
/* Connect to TCP Server */ uint8_t server_ip[4] = {192, 168, 1, 100}; uint16_t server_port = 5000; uint8_t connected = 0;
while (!connected) { if (getSn_SR(socket) == SOCK_CLOSED) { socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket == 0xFF) { /* Error: Failed to create socket */ } else { /* Configure socket */ uint8_t dest_ip[4] = {192, 168, 1, 200}; uint16_t dest_port = 5000; uint8_t buf[4]; IINCHIP_WRITE(Sn_DIPR(socket), dest_ip); IINCHIP_WRITE(Sn_DPORT(socket), dest_port); IINCHIP_SOCKET_CONTROL(socket, Sn_CR_OPEN); HAL_Delay(10);
/* Try to connect to server */ IINCHIP_SOCKET_CONTROL(socket, Sn_CR_CONNECT); HAL_Delay(1000); if (getSn_SR(socket) == SOCK_ESTABLISHED) { connected = 1; } else { /* Connection failed */ IINCHIP_SOCKET_CONTROL(socket, Sn_CR_CLOSE); HAL_Delay(10); } } } }
/* Send Data to Server */ uint8_t tx_data[4] = {0x01, 0x02, 0x03, 0x04}; write(socket, tx_data, sizeof(tx_data));
/* Receive Data from Server */ int rx_len = 0; while (1) { rx_len = getSn_RX_RSR(socket); if (rx_len > 0) { read(socket, buf, rx_len); /* Data received from server, do something */ }= SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 10;HAL_SPI_Init(&hspi1);}/* GPIO Initialization */static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin : PA4 */GPIO_InitStruct.Pin = GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}/* System Clock Configuration */void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parametersin the RCC_OscInitTypeDef structure. */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler();}}
复制代码

【4】封装 MQTT 协议报文

下面使用 MQTT client library for Contiki 来连接 MQTT 服务器。这个库适用于不同的平台,包括 STM32。在使用前,需要根据需求进行一些配置,例如: 指定 MQTT 服务器的地址和端口号,配置 MQTT 客户端 ID 和主题等。


#include "contiki.h"#include "contiki-net.h"#include "mqtt.h"
#include "stm32f1xx_hal.h"#include "wizchip_conf.h"#include "w5500.h"
/* MQTT Configuration */#define SERVER_IP_ADDR "192.168.1.100"#define SERVER_PORT 1883#define MQTT_CLIENT_ID "mqtt_stm32"#define MQTT_TOPIC "example_topic"
/* Network Configuration */static wiz_NetInfo gWIZNETINFO = { .mac = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, .ip = {192, 168, 1, 200}, .sn = {255, 255, 255, 0}, .gw = {192, 168, 1, 1}, .dns = {8, 8, 8, 8}, .dhcp = NETINFO_STATIC };
/* W5500 Buffer */static uint8_t buf[2048];
/* Prototypes */static void MX_SPI1_Init(void);static void MX_GPIO_Init(void);void SystemClock_Config(void);void Error_Handler(void);void W5500_Select(void);void W5500_UnSelect(void);uint8_t W5500_WriteByte(uint8_t b);uint8_t W5500_ReadByte(void);void MQTT_Callback(struct mqtt_connection *m, void *userdata, mqtt_event_t event, mqtt_data_t *data);
/* MQTT Connection */static struct mqtt_connection mqtt_conn;static struct mqtt_message *msg_ptr = NULL;static uint8_t mqtt_connected = 0;
PROCESS(mqtt_process, "MQTT Process");
AUTOSTART_PROCESSES(&mqtt_process);
/* MQTT Process */PROCESS_THREAD(mqtt_process, ev, data){ PROCESS_BEGIN();
/* Initialize W5500 */ reg_wizchip_cs_cbfunc(W5500_Select, W5500_UnSelect); reg_wizchip_spi_cbfunc(W5500_ReadByte, W5500_WriteByte); wizchip_init(buf, buf);
/* Configure Network */ ctlnetwork(CN_SET_NETINFO, (void*)&(gWIZNETINFO));
/* DHCP Initialization */ uint8_t /* Enable DHCP */ dhcp_client_start();
/* Wait for DHCP to finish */while (gWIZNETINFO.dhcp == NETINFO_DHCP) { HAL_Delay(1000); // wait for DHCP to finish } /* Print IP Address */ printf("IP address: %d.%d.%d.%d\n", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3]); /* Configure MQTT Connection */ memset(&mqtt_conn, 0, sizeof(mqtt_conn)); mqtt_conn.state = MQTT_INIT; mqtt_conn.host = SERVER_IP_ADDR; mqtt_conn.port = SERVER_PORT; mqtt_conn.client_id = MQTT_CLIENT_ID; mqtt_conn.user_data = NULL; mqtt_conn.user_name = NULL; mqtt_conn.password = NULL; mqtt_conn.protocol_version = MQTT_VERSION_3_1_1; mqtt_conn.keep_alive = 60; /* Connect to MQTT Server */ mqtt_connect(&mqtt_conn); /* Wait for MQTT Connection to Finish */ while (!mqtt_connected) { PROCESS_PAUSE(); } /* Publish Message to MQTT Server */ static char msg[100] = "Hello from STM32 using MQTT protocol!"; msg_ptr = mqtt_msg_publish_init(msg, strlen(msg), MQTT_TOPIC, MQTT_QOS_LEVEL_0, MQTT_RETAIN_OFF); mqtt_publish(&mqtt_conn, msg_ptr); /* Wait for Message to be Sent */ while (mqtt_conn.out_buffer_sent == 0) { PROCESS_PAUSE(); } /* Subscribe to MQTT Topic */ mqtt_subscribe(&mqtt_conn, MQTT_TOPIC, MQTT_QOS_LEVEL_0); /* Loop Forever */ while (1) { PROCESS_PAUSE(); } PROCESS_END();}
/* SPI Initialization / static void MX_SPI1_Init(void) { / SPI1 parameter configuration*/hspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 10;HAL_SPI_Init(&hspi1);}/* GPIO Initialization */static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin : PA4 */GPIO_InitStruct.Pin = GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}/* System Clock Configuration */void SystemClock_Config(void) {RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parametersin the RCC_OscInitTypeDef structure. */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler();} }
/* Error Handler */void Error_Handler(void) {while (1) { // error } } /* W5500 Select */ void W5500_Select(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); } /* W5500 Unselect */ void W5500_UnSelect(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } /* W5500 Write Byte */ uint8_t W5500_Writebyte(uint8_t b) { uint8_t res; HAL_SPI_TransmitReceive(&hspi1, &b, &res, 1, HAL_MAX_DELAY); return res; } /* W5500 Readbyte */ uint8_t W5500_Readbyte(void) { uint8_t b = 0xff; HAL_SPI_TransmitReceive(&hspi1, &b, &b, 1, HAL_MAX_DELAY); return b;}/* MQTT Callback */void MQTT_Callback(struct mqtt_connection *m, void *userdata, mqtt_event_t event, mqtt_data_t *data) { switch (event) { case MQTT_EVENT_CONNECTED: printf("MQTT connected\n"); mqtt_connected = 1; break; case MQTT_EVENT_DISCONNECTED: printf("MQTT disconnected\n"); mqtt_connected = 0; break; case MQTT_EVENT_PUBLISHED: printf("MQTT message published\n"); break; case MQTT_EVENT_SUBACK: printf("MQTT subscribed to topic\n"); break; case MQTT_EVENT_UNSUBACK: printf("MQTT unsubscribed from topic\n"); break; case MQTT_EVENT_DATA: printf("MQTT received message\n"); printf("Topic: %.*s\n", data->topic_name_size, data->topic_name); printf("Message: %.*s\n", data->data_size, (char *)data->data); break; default: break; }}
复制代码


发布于: 2023-06-17阅读数: 23
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022-01-06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
STM32配合W5500网卡连接MQTT服务器_6 月优质更文活动_DS小龙哥_InfoQ写作社区