使用keil5中的RL_TCPNet中间件建立一个工程

前言

RL_TCPnet也算是一个比较有名的小型协议栈,相比于LwIP,它支持非常多的应用协议。并且这是ARM自家出的中间件,专门针对自家内核做过优化,性能强劲。所以学一下它非常有必要。这次搞以太网算是第二次了,去年开始尝试玩了一下,自己画了一套F107以太网开发板,但是最后是没调出来。后来发现是硬件电路画的有问题,就没接下去弄了。这次又拿起来搞,总结上次电路失败的经验,我又重新设计了一块电路板。嗯,这次是一次成功啊_!所以在学习网络应用之前,得首先确保你的硬件是可以用的啊,不然车底盘不稳,开的再快迟早是要翻车的呀!先上一份我做好的成品。最终学习目地是做一个串口服务器:)
这里写图片描述

硬件电路设计

嵌入式以太网接口一般有以下几种模式:

  • 1.MCU内部自带MAC+PHY,比如TI的LM3S6911,此款芯片是自带MAC和PHY,外部接上RJ45接口电路就可以使用了。
  • 2.MCU内部自带MAC,需要外接PHY。比如我使用的STM32F107,外接一个DP83848物理芯片和添加RJ45接口电路就可以使用了。当然PHY有很多LA8720、DM9162等都可以使用。
  • 3.MCU内部既不带MAC也不带MAC和PHY,这就是一般低端的单片机了,这种情况下可以使用SPI接口网络芯片,典型的就是enc28j60。像F103不带MAC的就可以使用SPI驱动该芯片,以实现网络应用。

MAC即媒体访问控制,处于链路层的下方。MAC的作用是使设备在多节点应用中,不会引起访问冲突。

PHY即端口物理层,是OSI参考模型中的最底层。常用的PHY基本寄存器都一样,不一样的是各个厂家提供的扩展寄存器和专门设置的寄存器。所以驱动成功立一种PHY,驱动另一种PHY也就很简单。

底层驱动接口

(1)独立于介质的接口:MII

这里写图片描述
该接口有16根线,使用25MHz外部晶振驱动时钟系统。支持10Mbps和100Mbps的运行速率。

(2)精简的独立于介质的接口:RMII

这里写图片描述
RMII将标准减少到7根线,支持10Mbps和100Mbps运行速率;时钟信号需提高到50MHz;MAC和PHY需要使用同样的时钟源;使用2位宽的数据收发。

(3)DP83848的RMII应用电路

我所使用的接口就是RMII,使用该接口就没有MII那么多线看着头晕的麻烦。具体电路如下,DP83848其它的引脚功能需要自行查阅数据手册了
这里写图片描述

创建工程

在确保硬件可以使用的情况下,接下来就要开始创建工程来驱动这个PHY。自keil5发布以来,创建和使用中间件就非常方便,特别是在使用中间件的时候,省去了很多移植的花费的时间。你可能会看到我就是不停的勾选,勾选勾选,一个工程例子代码就创建好了。。。(我使用的版本是5.23,组件都是最新版本)

(1)新建UDP例子工程

1.CMSIS驱动选择

新建一个工程,创建工程名,选择芯片F107RC(具体芯片具体选择),接下来就跳到“Manage Run-Time Environment”窗口。这是重点,选好依赖整个创建过程就很快。驱动选择如下:
这里写图片描述
这里说明一下,在勾选时,选项卡变黄是因为缺少相应的依赖,依赖齐全后,就变成绿色。因为RL_TCPnet这个版本需要使用RTX,所以RTOS必选。因为我使用的是MAC+PHY,所以MAC驱动必选,PHY按所选用的芯片来选择。像其它的PHY芯片,因为基本寄存器都一样,所以可以在keil5提供的一种PHY驱动文件中来更改驱动文件,以适应自己的PHY。选择UASRT驱动是用来Debug输出时使用的。

2.STM32驱动选择

图中的DMA和GPIO是keil提供的驱动文件,目的是与CMSIS Driver相互依赖。使用STM32的标准库的GPIO是用来初始化PHY的复位引脚的。
这里写图片描述

3.STDIO调试选择

使能STDOUT后,在程序中就可以使用printf函数。keil为F1提供相应的串口驱动,所以我们不必编写相应的串口驱动程序。
这里写图片描述

4.NetWork选择

具体的选择如图所示,内核选择Debug STDIO调试版本,正式发布后可选择发行版本。接口使用的ETH,序号1表示有一个MAC物理IP.其它2个是供串行接口使用。现在这个例子不使用服务,我们使用UDP做测试。
这里写图片描述

(2)文件配置

以上工程依赖配置好后,就要添加相应的文件,配置文件了。

1.添加模板代码

鼠标右键“Source Group1”向该组增加文件。
这里写图片描述

增加UDP模板代码文件
这里写图片描述

增添stoutio模板文件
这里写图片描述

去掉main函数注释
这里写图片描述

编译一下工程,会错误提示,然后按着错误来解决相应的问题。
这里写图片描述

上图中提示出了2个错误,分别是ETH和USART找不到。那需要配置“RTE_Device.h”文件,进入此文件的配置视图模式。具体配置如下:
这里写图片描述

ETH接口有使用重映射的方式,要具体选择。系统的时钟一定不要配置错误。选择好后,再编译一次,还有一个错误 ,如下图:
这里写图片描述

这个提示是stdout_usart使用的串口号没有选择,配置如下:
这里写图片描述

因为我使用的是串口1,所以编号要选择对应的,波特率选择115200,打印速度多少会快点。

2.配置RTX

再编译一次就没啥错误了。你以为这就完了?其实还有很多。接下来配置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。

中断服务程序使用堆栈介绍
这里写图片描述

RL_TCPnet在RTXv4中使用的堆栈需求
这里写图片描述

3.配置NetWork

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 级别
这里写图片描述

4.更改必要的代码

现在大体上基本上配置完了,接下来配置一下复位脚,向udp_socket.c添加代码。注意stdio需要初始化,外部声明stdout_init(),然后在main函数进行初始化。

#include "rl_net.h"
#include "GPIO_STM32F10x.h"//需要手动添加
#include <stdio.h>  //需要手动添加

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的相关配置要更改设置。
这里写图片描述

连接好串口,查看串口调试助手打印的信息,可以很层次的看见系统运行的情况。可以很清晰的看到,数据通信是按模型层次来一步步传递的。
这里写图片描述

打开网络调试助手,设置本地IP和远程目标IP。发送数据,就可以看到数据回显,表示通信成功
这里写图片描述

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值