1、打开STM32CubeMX,并选择好相应的芯片。文中的芯片为STM32F207VCT6,选择后如下图:
2、配置RCC时钟、ETH、PA8以及使能LWIP;
由于此处我们的开发板硬件上为RMII方式,因此选择ETH-RMII,若有同志的开发板为MII方式,请参考MII的配置方法,此处只针对RMII;
RCC选择外部时钟源,另外勾选MCO1,软件会自动将PA8配置为MCO1模式,该引脚对于RMII方式很重要,用于为PHY芯片提供50MHz时钟;
使能LWIP;
3、时钟树的相关配置,必须保证MCO1输出为50Mhz,如果这个频率不对会导致PHY芯片无法工作;
我这里因为芯片为207VCT6,为了使MCO1输出为50Mhz,做了PLL倍频参数的一些调整,总体如下:(同志们配置时可根据自己的芯片灵活配置,但需保证MCO1的输出为50Mhz)
4、ETH、LWIP、RCC相关参数设置;
至此,比较重要的都在前面了,但是还有一点仍需要注意,即PA8引脚输出速度,几次不成功都是因为这个引脚没注意。
后续的参数设置可以根据同志们自己的需求分别设置,这里给出我的设置供参考;
ETH参数保持默认,但中断勾选一下;
LWIP参数设置如下:(因为我这里是配置UDP服务器,IP选择静态分配)
5、生成工程,做最后的函数修改;
给生成的工程添加UDP服务器的初始化以及端口绑定等相关函数;
我这里直接将之前的官方例程中的UDP服务器文件加进来,如下:
之后将.c文件添加到用户程序,主函数添加Udp的.h头文件;如下:(udp文件的具体内容在后面给出)
6、主函数还需要添加一下几个函数,在这里不对函数作用及实现原理讲解,仅做添加说明。
附:udp_echoserver相关文件内容(该文件为官方的示例程序,版权归官方,此处做转载)
udp_echoserver.c的内容如下:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include
#include
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define UDP_SERVER_PORT 7 /* define the UDP local connection port */
#define UDP_CLIENT_PORT 7 /* define the UDP remote connection port */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void udp_echoserver_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initialize the server application.
* @param None
* @retval None
*/
void udp_echoserver_init(void)
{
struct udp_pcb *upcb
err_t err
/* Create a new UDP control block */
upcb = udp_new()
if (upcb)
{
/* Bind the upcb to the UDP_PORT port */
/* Using IP_ADDR_ANY allow the upcb to be used by any local interface */
err = udp_bind(upcb, IP_ADDR_ANY, UDP_SERVER_PORT)
if(err == ERR_OK)
{
/* Set a receive callback for the upcb */
udp_recv(upcb, udp_echoserver_receive_callback, NULL)
}
}
}
/**
* @brief This function is called when an UDP datagrm has been received on the port UDP_PORT.
* @param arg user supplied argument (udp_pcb.recv_arg)
* @param pcb the udp_pcb which received data
* @param p the packet buffer that was received
* @param addr the remote IP address from which the packet was received
* @param port the remote port from which the packet was received
* @retval None
*/
void udp_echoserver_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
/* Connect to the remote client */
udp_connect(upcb, addr, UDP_CLIENT_PORT)
/* Tell the client that we have accepted it */
udp_send(upcb, p)
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb)
/* Free the p buffer */
pbuf_free(p)
}
udp_echoserver.h的内容如下:
#ifndef __ECHO_H__
#define __ECHO_H__
void udp_echoserver_init(void)
#endif /* __MINIMAL_ECHO_H */
7、至此,所有的工作完成,编译工程,下载至开发板。由于udp_echoserver中绑定的端口号为7,这里我们通过测试工具测试网络的功能,
UDP是面向无连接的,使用起来比较简单,打开socke之后,指定目标端口,直接进行接收和发送:
socket本身提供了一些接口:
需要注意的是, boost.asio.buffer 是一种接口适配器,通过接口进行发送和接收,必须有对应的数据缓冲区提供数据或者存储空间。
同步接收同步发送的UDP服务器也比较简单,创建一个绑定到本地端口的socket,然后就是接收及发送动作:
同步操作是不需要运行IO服务的,以最常规的方式来进行发送和接收,注意接收时如果接收到全部消息,即EOF也是通过报错形式,错误码为 error::message_size 。
实现异步的UDP服务器就略显复杂,需要保证IO服务运行,发起异步操作时要注意数据缓冲区生命周期:
可以看到 do_recv 方法发起了一个异步接收操作,在操作完成回调中再次发起,构造服务器时率先调用了 do_recv ,从而保证IO服务一直运行。
do_recv 方法在发起异步操作前申请了一块内存,接收的内容被保存在这块内存之中,当 do_send 发起异步发送操作时被借用,直到发送完成才将这段内存释放掉。
在构造函数中启动了一个线程来执行IO服务,并detach掉线程,从而保证服务器不阻塞,在析构函数停止了IO服务。
需要注意到的是 remote_ep_ 在执行 do_send 时被 move 了,由于 remote_ep_ 标识了远程端口,而且被声明为成员变量,在接受操作中会被填充远程端口内容,如果多个远程主机同时发起,单个 remote_ep_ 是无法正常处理的,所以一旦内容被填充后,就会转移出去给发送操作使用[个人理解,没有实际测试和验证]。
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)