RL_TCPnet也算是一个比较有名的小型协议栈,相比于LwIP,它支持非常多的应用协议。并且这是ARM自家出的中间件,专门针对自家内核做过优化,性能强劲。所以学一下它非常有必要。这次搞以太网算是第二次了,去年开始尝试玩了一下,自己画了一套F107以太网开发板,但是最后是没调出来。后来发现是硬件电路画的有问题,就没接下去弄了。这次又拿起来搞,总结上次电路失败的经验,我又重新设计了一块电路板。嗯,这次是一次成功啊_!所以在学习网络应用之前,得首先确保你的硬件是可以用的啊,不然车底盘不稳,开的再快迟早是要翻车的呀!先上一份我做好的成品。最终学习目地是做一个串口服务器:)
嵌入式以太网接口一般有以下几种模式:
MAC即媒体访问控制,处于链路层的下方。MAC的作用是使设备在多节点应用中,不会引起访问冲突。
PHY即端口物理层,是OSI参考模型中的最底层。常用的PHY基本寄存器都一样,不一样的是各个厂家提供的扩展寄存器和专门设置的寄存器。所以驱动成功立一种PHY,驱动另一种PHY也就很简单。
该接口有16根线,使用25MHz外部晶振驱动时钟系统。支持10Mbps和100Mbps的运行速率。
RMII将标准减少到7根线,支持10Mbps和100Mbps运行速率;时钟信号需提高到50MHz;MAC和PHY需要使用同样的时钟源;使用2位宽的数据收发。
我所使用的接口就是RMII,使用该接口就没有MII那么多线看着头晕的麻烦。具体电路如下,DP83848其它的引脚功能需要自行查阅数据手册了
在确保硬件可以使用的情况下,接下来就要开始创建工程来驱动这个PHY。自keil5发布以来,创建和使用中间件就非常方便,特别是在使用中间件的时候,省去了很多移植的花费的时间。你可能会看到我就是不停的勾选,勾选勾选,一个工程例子代码就创建好了。。。(我使用的版本是5.23,组件都是最新版本)
新建一个工程,创建工程名,选择芯片F107RC(具体芯片具体选择),接下来就跳到“Manage Run-Time Environment”窗口。这是重点,选好依赖整个创建过程就很快。驱动选择如下:
这里说明一下,在勾选时,选项卡变黄是因为缺少相应的依赖,依赖齐全后,就变成绿色。因为RL_TCPnet这个版本需要使用RTX,所以RTOS必选。因为我使用的是MAC+PHY,所以MAC驱动必选,PHY按所选用的芯片来选择。像其它的PHY芯片,因为基本寄存器都一样,所以可以在keil5提供的一种PHY驱动文件中来更改驱动文件,以适应自己的PHY。选择UASRT驱动是用来Debug输出时使用的。
图中的DMA和GPIO是keil提供的驱动文件,目的是与CMSIS Driver相互依赖。使用STM32的标准库的GPIO是用来初始化PHY的复位引脚的。
使能STDOUT后,在程序中就可以使用printf函数。keil为F1提供相应的串口驱动,所以我们不必编写相应的串口驱动程序。
具体的选择如图所示,内核选择Debug STDIO调试版本,正式发布后可选择发行版本。接口使用的ETH,序号1表示有一个MAC物理IP.其它2个是供串行接口使用。现在这个例子不使用服务,我们使用UDP做测试。
以上工程依赖配置好后,就要添加相应的文件,配置文件了。
上图中提示出了2个错误,分别是ETH和USART找不到。那需要配置“RTE_Device.h”文件,进入此文件的配置视图模式。具体配置如下:
ETH接口有使用重映射的方式,要具体选择。系统的时钟一定不要配置错误。选择好后,再编译一次,还有一个错误 ,如下图:
这个提示是stdout_usart使用的串口号没有选择,配置如下:
因为我使用的是串口1,所以编号要选择对应的,波特率选择115200,打印速度多少会快点。
再编译一次就没啥错误了。你以为这就完了?其实还有很多。接下来配置RTX:
RTX系统配置要注意以下几点:1.RTOS kernel Timer要和系统时钟频率相同。2.RL_TCPnet在初始化时会创建2个线程,每个线程给它分配1k的栈空间,"Number of threads whith usr-provied stack size "设置为2;"Total stack size for threads whith user-provied stack size "设置2048,总共2K。待会儿调试窗口看到线程的运行状况。3.定时器线程运行栈设置为512。
RTX配置好后,接下来就是配置网络。
NetConfig.c配置基本上不用改。“Local Host Name”可以改成自定义。作用可以不使用IP地址就行访问。如:ping my_host 指令
Net_Config_ETH_0.h的配置:改IP地址和网关,不开启DHCP,将该线程栈设置1024。IPV6可以关掉,其它默认。
Net_Config_UDP.h配置使用默认配置5个socket
Net_Debug.c配置开启相应的服务报警,该报警有3个级别,我选择全部显示,当然也可以只选择errors 级别
现在大体上基本上配置完了,接下来配置一下复位脚,向udp_socket.c添加代码。注意stdio需要初始化,外部声明stdout_init(),然后在main函数进行初始化。
#include "rl_net.h"
#include "GPIO_STM32F10x.h"//需要手动添加
#include //需要手动添加
int32_t udp_sock; // UDP socket handle
extern int stdout_init (void);//需要手动添加
void send_udp_data (void);
static void delay_ms (int ms) {
ms *= (SystemCoreClock/10000);
while (ms--) { __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); }
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void PHY_Reset(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
delay_ms(100);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
}
更改一下例子代码,这个例子代码的作用是本地UDP接收到0x01 0xAA指令就向目标UDP回显数据。目标IP和端口是192.168.1.220:77,当然这可以自己随意改动
// Notify the user application about UDP socket events.
uint32_t udp_cb_func (int32_t socket, const NET_ADDR *addr, const uint8_t *buf, uint32_t len) {
// Data received
if ((buf[0] == 0x01) && (len == 2)) {
// Switch LEDs on and off
// LED_out (buf[1]);
send_udp_data();
}
return (0);
}
// Send UDP data to destination client.
void send_udp_data (void) {
if (udp_sock > 0) {
// IPv4 address: 192.168.0.1
NET_ADDR addr = { NET_ADDR_IP4, 77, 192, 168, 1, 220 };//可以根据自己的IP地址来
// IPv6 address: [fe80::1c30:6cff:fea2:455e]
// NET_ADDR addr = { NET_ADDR_IP6, 2000,
// 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x1c, 0x30, 0x6c, 0xff, 0xfe, 0xa2, 0x45, 0x5e };
uint8_t *sendbuf;
sendbuf = netUDP_GetBuffer (2);
sendbuf[0] = 0x01;
sendbuf[1] = 0xAA;
netUDP_Send (udp_sock, &addr, sendbuf, 2);
}
}
主函数添加复位引脚初始化代码
int main (void) {
stdout_init();
GPIO_Configuration();
PHY_Reset();
printf("PHY_RESET!\r\n");
netInitialize ();
// Initialize UDP socket and open port 2000
udp_sock = netUDP_GetSocket (udp_cb_func);
if (udp_sock > 0) {
netUDP_Open (udp_sock, 2000);
}
}
编译一下没有错误,配置一下工程,生成Hex文件就可以测试啦!
进入Debug调试模式,打开线程窗口可以看到系统创建的2个线程。如果使能其它服务,将会创建其它线程,对应的RTX的Number of Threads的相关配置要更改设置。
连接好串口,查看串口调试助手打印的信息,可以很层次的看见系统运行的情况。可以很清晰的看到,数据通信是按模型层次来一步步传递的。