赤兔开发板以太网模块使用教程
一、基本介绍
大家好,这里是Verimake,今天给大家介绍下赤兔以太网配件的使用方法。
开始之前我们先来看下赤兔开发板上这款主控CH32V307VCT6芯片手册,
从手册上我们可以看到,307内部有1G和10M的以太网,这里要注意下,1G是不含有物理层PHY的,如果需要使用是需要外部PHY芯片的,通过相应的接口来控制的,而10M是307内部就有PHY,我们将10M的接口引出连接网口就是我们的以太网模块。
通过数据手册我们可以看到如果需要使用1G则需要通过RGMII接口连接一款千兆的芯片,由于赤兔的开发板设计我们没有将RGMII接口引出,所以没有做这样的以太网设计。
接下来我们看下设备的连接:
模块通过FFC的软排线引出和赤兔开发板的摄像头接口相连接,这里只要注意排线的安装不要装反即可,正确方向如图所示。连接后模块不需要单独供电。通过RJ-45接头的网线可以将赤兔连接到电脑、交换机或者路由器上,在局域网中就可以访问到赤兔,可以接收赤兔发送的数据,也可以给赤兔下发数据或者控制,也可以通过网线进行赤兔和赤兔之间通信。下面我们先介绍PC和赤兔之间的通信。
二、样例演示
首先我们打开沁恒提供的EVT样例里面的ETH目录下有网口的相关例程,我们先打开TCPClient的例程,该例程是赤兔开发板作为客户端,登录网络中的TCPServer端,和服务端建立TCP连接后就可以将服务端发送的信息接收下来,然后再通过TCP发送给服务端。这个例程开始使用前需要将一些地方进行修改,主要是网口上的两个灯控制口需要更改为PB8和PB9。
修改如下:
void Ethernet_LED_Configuration(void)
{
GPIO_InitTypeDef GPIO={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //修改为B口
GPIO.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9; //修改为8和9口
GPIO.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO);
Ethernet_LED_LINKSET(1);
Ethernet_LED_DATASET(1);
}
void Ethernet_LED_LINKSET(u8 setbit)
{
if(setbit){
GPIO_SetBits(GPIOB, GPIO_Pin_8); //设置PB8为1
}
else {
GPIO_ResetBits(GPIOB, GPIO_Pin_8); //设置PB8为0
}
}
void Ethernet_LED_DATASET(u8 setbit)
{
if(setbit){
GPIO_SetBits(GPIOB, GPIO_Pin_9); //设置PB9为1
}
else {
GPIO_ResetBits(GPIOB, GPIO_Pin_9); //设置PB9为0
}
}
修改好之后我们编译先看看实验现象然后我们再详细学习下代码。编译好后下载代码到赤兔,通过网线连接好赤兔和PC(如果有的笔记本没有网口可以通过usb转网口的hub实现),设置PC的网口的IP地址为192.168.1.100,掩码255.255.255.0,网关192.168.1.1.连接完成后。我们在PC机上调试时候可以借助网络调试助手来测试网络通信,打开网络调试助手,设置协议类型为TCP Server,本地主机地址为192.168.1.100,如果地址下拉里面没有这个地址需要检查连接赤兔开发板的网口ip4地址设置是否是上面的要求,是否是先连接赤兔开发板再打开了软件。如果是第一种只需要按要求修改即可,如果是第二种只需要把软件关闭重新开就可以了。本地端口设置为1000.打开通信,此时网络调试助手就会收到一个连接。如图会有显示。并且可以看到连接的地址是192.168.1.10。此时点击发送,就会看见接收部分就会显示刚刚发送的信息。此外通过串口连接赤兔,还可以看到相关的连接信息。如下。
那么它是怎么工作的呢,我们接下来详细的看下它的运作过程。
三、样例解析
打开工程,我们先来看下本工程和其他工程的区别,在这个工程里多了一个NetLib的目录,里面存放了两个文件一个WCHNET.h和libwchnet.a。这是沁恒制作的网络库,WCHNET.h里面我们可以看到相关的宏定义的量以及网络控制函数。我们在使用时候就是利用这些函数即可。.a文件就是这些函数的静态库,源码这里是没有的。那么我们只需要知道如何使用就可以了。接下来我们看下main.c里面程序运行过程吧。
首先在main函数中:
int main(void)
{
u8 i;
Delay_Init();
USART_Printf_Init(115200); /*串口打印初始化*/
printf("TcpClient Test\r\n");
SET_MCO(); //设置时钟
TIM2_Init(); //初始化定时器2
WCH_GetMac(MACAddr); /*获取芯片Mac地址*/
i=WCHNET_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr); /*以太网库初始化*/
mStopIfError(i);
if(i==WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n");
while(!(WCHNET_GetPHYStatus()&PHY_LINK_SUCCESS)) /*等待PHY连接成功*/
{
Delay_Ms(100);
}
WCHNET_CreatTcpSocket(); /*创建Tcp socket*/
while(1)
{
WCHNET_MainTask(); /*以太网库主任务函数,需要循环调用*/
if(WCHNET_QueryGlobalInt()) /*查询以太网全局中断,如果有中断,调用全局中断处理函数*/
{
WCHNET_HandleGlobalInt();
}
}
}
从代码中我们可以看到,首先初始化赤兔的网络,如果网络有接入,则开始TCP连接。在while循环中以太网主任务必须要循环调用,通过查询以太网的全局中断可以判断是否有接收数据,是否还在连接中等状态,从而进行相关工作。例如例程中通过查询中断,如果有接收到数据,就会将数据发回。接下来我们看下接收到以太网全局中断函数WCHNET_HandleGlobalInt();
void WCHNET_HandleGlobalInt(void)
{
u8 initstat;
u16 i;
u8 socketinit;
initstat = WCHNET_GetGlobalInt(); /* 获取全局中断标志*/
if(initstat & GINT_STAT_UNREACH) /* 不可达中断 */
{
printf("GINT_STAT_UNREACH\r\n");
}
if(initstat & GINT_STAT_IP_CONFLI) /* IP冲突中断 */
{
printf("GINT_STAT_IP_CONFLI\r\n");
}
if(initstat & GINT_STAT_PHY_CHANGE) /* PHY状态变化中断 */
{
i = WCHNET_GetPHYStatus(); /* 获取PHY连接状态*/
if(i&PHY_Linked_Status)
printf("PHY Link Success\r\n");
}
if(initstat & GINT_STAT_SOCKET) /* scoket 产生中断 */
{
for(i = 0; i < WCHNET_MAX_SOCKET_NUM; i ++)
{
socketinit = WCHNET_GetSocketInt(i); /* 获取socket中断并清零 */
if(socketinit)WCHNET_HandleSockInt(i,socketinit); /* socket中断查询 */
}
}
}
在以太网全局中断中可以查询如连接状态,不可达,socket中断等。在接收到服务器的数据后就会产生socket中断,使用WCHNET_HandleSockInt函数就可以根据socket的不同中断进行不同的操作了。
void WCHNET_HandleSockInt(u8 sockeid,u8 initstat)
{
u32 len;
if(initstat & SINT_STAT_RECV) /* socket接收中断*/
{
len = WCHNET_SocketRecvLen(sockeid,NULL); /* 获取socket缓冲区数据长度 */
printf("WCHNET_SocketRecvLen %d \r\n",len);
WCHNET_SocketRecv(sockeid,MyBuf,&len); /* 获取socket缓冲区数据 */
WCHNET_SocketSend(sockeid,MyBuf,&len); /* 演示回传数据 */
}
if(initstat & SINT_STAT_CONNECT) /* socket连接成功中断*/
{
printf("TCP Connect Success\r\n");
}
if(initstat & SINT_STAT_DISCONNECT) /* socket连接断开中断*/
{
printf("TCP Disconnect\r\n");
}
if(initstat & SINT_STAT_TIM_OUT) /* socket连接超时中断*/
{
printf("TCP Timout\r\n"); /* 延时200ms,重连*/
Delay_Ms(200);
WCHNET_CreatTcpSocket();
}
}
这里我们就可以看到如果是接收中断,就会把接收的数据通过WCHNET_SocketSend(sockeid,MyBuf,&len); 再发出去。如果连接成功会打印信息,连接断开也会打印相应信息,连接超时除了会打印信息外还会重新连接。相应的全局中断和socket中断在WCHNET.h中都可以找到。如图:
那么在创建连接时候我们之前设置ip、端口等信息是在main.c开头设置的,
u8 IPAddr[4] = {192,168,1,10}; /*IP地址*/
u8 GWIPAddr[4] = {192,168,1,1}; /*网关*/
u8 IPMask[4] = {255,255,255,0}; /*子网掩码*/
u8 DESIP[4] = {192,168,1,100}; /*目的IP地址*/
u8 SocketId; /*socket id号*/
u8 SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN]; /*socket缓冲区*/
u8 MyBuf[RECE_BUF_LEN];
u16 desport=1000; /*目的端口号*/
u16 srcport=1000; /*源端口号*/
如果需要在局域网中有多个赤兔连接,那么各自的ip最后的值需要加以区分。
四、样例拓展
接下来我们做一点修改,尝试着不同的功能。例如将接收的内容在LCD上显示,如果接收到led on我们就将LED1打开,接收到led off我们就把LED1熄灭。
第一步:添加LCD的相关库到工程。拷贝lcd.c lcd.h font.h到User目录下。在main.c里面的main函数添加lcd初始化函数:
Delay_Init();
USART_Printf_Init(115200); /*串口打印初始化*/
printf("TcpClient Test\r\n");
lcd_init();
LCD_SetBrightness(60);
lcd_set_color(WHITE, BLUE);
lcd_fill(0, 0, 239, 239, WHITE);
SET_MCO();
这里需要注意LCD_SetBrightness(60);里面的值越大屏幕越暗,最大100。然后我们设置背景为白色,打印字符为蓝色。
接下来在接收数据那里添加把接收的数据在lcd上显示。
if(initstat & SINT_STAT_RECV) /* socket接收中断*/
{
len = WCHNET_SocketRecvLen(sockeid,NULL); /* 获取socket缓冲区数据长度 */
printf("WCHNET_SocketRecvLen %d \r\n",len);
WCHNET_SocketRecv(sockeid,MyBuf,&len); /* 获取socket缓冲区数据 */
lcd_show_string(3, 10, 16," ");
lcd_show_string(3, 10, 16, MyBuf);
WCHNET_SocketSend(sockeid,MyBuf,&len); /* 演示回传数据 */
}
编译后,下载,我们重新发送数据,观察屏幕可以看到相应的信息。
第二步:
添加LED控制,需要首先初始化led_gpio,编写函数led_gpio();
void led_gpio(){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); //设置PE11为输出管脚控制额LED1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
然后将这个函数在main函数中进行初始化。只需要添加led_gpio();在lcd初始化下面。
然后修改接收部分,添加led控制。
if(initstat & SINT_STAT_RECV) /* socket接收中断*/
{
len = WCHNET_SocketRecvLen(sockeid,NULL); /* 获取socket缓冲区数据长度 */
printf("WCHNET_SocketRecvLen %d \r\n",len);
WCHNET_SocketRecv(sockeid,MyBuf,&len); /* 获取socket缓冲区数据 */
if(strncmp(MyBuf,"led on",6)==0|strncmp(MyBuf,"led off",7)==0 )
{
if (strncmp(MyBuf,"led on",6)==0)
{
GPIO_ResetBits(GPIOE, GPIO_Pin_11);
}
else
{
GPIO_SetBits(GPIOE, GPIO_Pin_11);
}
}
else
{
lcd_show_string(3, 10, 32," ");
lcd_show_string(3, 10, 32, MyBuf);
}
WCHNET_SocketSend(sockeid,MyBuf,&len); /* 演示回传数据 */
}
这里需要注意,MyBuf每次接收完数据后是不会清空的,所以接收数据是有叠加的,所以字符比较函数要使用指定长度的strncmp();
编译下载后,我们可以测试下,观看下效果。
自此本次的例程演示就结束了。
拓展介绍
如果需要将多个赤兔组成局域网,需要使用下面的网络结构;
图里的pc机也可以使用赤兔来作为server的角色。
目前例程里使用的是网络调试助手的工具,如果需要自己设计上位机程序,这里推荐使用python进行开发,python socket开发非常简单,简单几句就可以建设TCP Server。加上Qt的界面开发做一个有界面的网络上位机就很简单了。
此外按照EVT里面以太网的例程,我们还可以将赤兔配置为TCP Server或者使用UDP通信,这些内容和本例程非常相似,有兴趣的可以按本例程进行测试。