STM32F407ZG+UART1(DMA,115200)+ETH(DP83848)+LWIP:网口串口透传

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言


提示:以下是本篇文章正文内容,下面案例可供参考

一、UART,DMA收发数据

在数据发送缓冲区内放好要发送的数据(此数据缓冲区的首地址必须要在DMA初始化时写入到DMA配置中去)
将数据缓冲区内要发送的数据字节数传给DMA通道(串口发送和接收不是同一个通道)
开启DMA,一旦开启,则DMA开始发送数据,
等待数据发送完成标志!
判断数据发送完成:
启动DMA并发送完成后,产生DMA发送完成中断,在DMA中断服务函数中执行以下操作:
清DMA发送完成标志
关闭串口发送DMA通道
给前台(应用)程序设置一个软件标志位,说明数据发送完成。
DMA接收数据
串口接收DMA在初始化时就处于开启状态,一直等待数据的到来,串口中断IDLE在串口一直没有数据时,是不会产生的,产生的条件是:当清除IDLE标志位后,必须有接收到第一个数据后才触发,一旦接收的数据断流,即产生IDLE中断。
这里判断接收数据完成是通过串口的空闲方式实现,即当串口的数据流停止后,就会产生IDLE中断,在中断里做

关闭串口接收的DMA通道。一是防止又有数据接收到,产生干扰;二是便于DMA重新配置赋值。
清除DMA中断标志位
从DMA寄存器中获取接收到的数据字节数(对于上层应用很有必要)
重新设置DMA下次需要接收到的数据字节数(必须大于预期的接收值长度,否则计数减到0时又会复位,覆盖接收缓冲区中的数据,导致数据丢失!)
开启DMA通道,等待下一次的数据接收
可以设置信号量,通知应用程序数据接收完成,传递接收的数据长度,便于应用程序对数据的处理

二、LwIP

1.LwIP

LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。 [1] lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。为了简化处理过程和内存要求,lwIP对API进行了裁减,可以不需要复制一些数据。

2.模式

lwip提供三种API:1)RAW API 2)lwip API 3)BSD API。 RAW API把协议栈和应用程序放到一个进程里边,该接口基于函数回调技术,使用该接口的应用程序可以不用进行连续操作。不过,这会使应用程序编写难度加大且代码不易被理解。为了接收数据,应用程序会向协议栈注册一个回调函数。该回调函数与特定的连接相关联,当该关联的连接到达一个信息包,该回调函数就会被协议栈调用。这既有优点也有缺点。优点是既然应用程序和TCP/IP协议栈驻留在同一个进程中,那么发送和接收数据就不再产生进程切换。主要缺点是应用程序不能使自己陷入长期的连续运算中,这样会导致通讯性能下降,原因是TCP/IP处理与连续运算是不能并行发生的。这个缺点可以通过把应用程序分为两部分来克服,一部分处理通讯,一部分处理运算。 lwip API把接收与处理放在一个线程里面。这样只要处理流程稍微被延迟,接收就会被阻塞,直接造成频繁丢包、响应不及时等严重问题。因此,接收与协议处理必须分开。LwIP的作者显然已经考虑到了这一点,他为我们提供了 tcpip_input() 函数来处理这个问题, 虽然他并没有在 rawapi 一文中说明。 讲到这里,读者应该知道tcpip_input()函数投递的消息从哪里来的答案了吧,没错,它们来自于由底层网络驱动组成的接收线程。我们在编写网络驱动时, 其接收部分以任务的形式创建。 数据包到达后, 去掉以太网包头得到IP包, 然后直接调用tcpip_input()函数将其投递到mbox邮箱。投递结束,接收任务继续下一个数据包的接收,而被投递得IP包将由TCPIP线程继续处理。这样,即使某个IP包的处理时间过长也不会造成频繁丢包现象的发生。这就是lwip API。 BSD API提供了基于open-read-write-close模型的UNIX标准API,它的最大特点是使应用程序移植到其它系统时比较容易,但用在嵌入式系统中效率比较低,占用资源多。这对于我们的嵌入式应用有时是不能容忍的

3.特性

其主要特性如下: [1]
(1)支持多网络接口下的IP转发;
(2)支持ICMP协议;
(3)包括实验性扩展的UDP(用户数据报协议);
(4)包括阻塞控制、RTT 估算、快速恢复和快速转发的TCP(传输控制协议);
(5)提供专门的内部回调接口(Raw API),用于提高应用程序性能;
(6)可选择的Berkeley接口API (在多线程情况下使用) 。
(7)在最新的版本中支持ppp
(8) 新版本中增加了的IP fragment的支持.
(9) 支持DHCP协议,动态分配ip地址.

4.移植

为了移植到μC/OS系统中,需要进行以下调整。
(1) 信号量
LwIP中需要使用信号量进行通信,所以在sys_arch中应实现相应的信号量结构体struct sys_semt和处理函数sys_sem_new() 、sys_sem_free() 、sys_sem_signal ( ) 和sys_arch_sem_wait ( ) 。由于μC/OS已经实现了信号量OSEVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把μC/OS的函数重新包装成上面的函数,就可直接使用。
(2) 消息队列
LwIP 使用消息队列来缓冲、传递数据报文,因此要实现消息队列结构sys_mbox_t ,以及相应的操作函数:sys_mbox_new() 、sys_mbox_free () 、sys_mbox _post () 和sys_arch_mbox_fetch() 。μC/OS实现了消息队列结构及其操作,但是μC/OS没有对消息队列中的消息进行管理,因此不能直接使用,必须在μC/OS的基础上重新实现。具体实现时,对队列本身的管理利用μC/OS自己的OSQ操作完成,然后使用μC/OS中的内存管理模块实现对消息的创建、使用、删除和回收,两部分综合起来形成了LwIP的消息队列功能。
(3) 定时器函数
LwIP中每个和TCP/IP相关的任务的一系列定时事件组成一个单向链表,每个链表的起始指针存在lwip_timeouts 的对应表项中。移植时需要实现struct sys_timeouts * sys_arch_timeouts (void) 函数,该函数返回正处于运行态的线程所对应的timeout 队列指针。
(4) 创建新线程函数
在μC/OS 中,没有线程(thread) 的概念,只有任务(Task) 。它提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是LwIP中的thread并没有μC/OS 中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。

三.实现(STM32F407ZG+DP83848+UART1+LWIP)

本次例程使用uart1,进行串口收发。

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);                  //GPIOA9¸USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);                 //GPIOA10¸USART1

uart1DMA配置

void USER_DMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{ 
	DMA_InitTypeDef  DMA_InitStructure;
	
	if((u32)DMA_Streamx>(u32)DMA2)
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
		
	}else 
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);
	}
  DMA_DeInit(DMA_Streamx);
	
	while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}

  DMA_InitStructure.DMA_Channel = chx; 
  DMA_InitStructure.DMA_PeripheralBaseAddr = par;
  DMA_InitStructure.DMA_Memory0BaseAddr = mar;
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  DMA_InitStructure.DMA_BufferSize = ndtr;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA_Streamx, &DMA_InitStructure);
} 

ETH(DP83848),和STM32F407ZG使用RMII连接方式,MAC与PHY采用RMII接口.

STM32F407ZG+UART1(DMA,115200)+ETH(DP83848)+LWIP:网口串口透传
STM32F407ZG使用RMII连接引脚定义.。
IP为192.168.1.240:2040

STM32F407ZG+UART1(DMA,115200)+ETH(DP83848)+LWIP:网口串口透传

本次例程使用KEIL开发环境,UART1DMA实现不定长度数据收发,同时转发至网口,IP:192.168.1.240:2040,同样 网口,IP:192.168.1.240:2040可转发不定数据至UART1串口。
uart1波特率115200。

上一篇:「考试」noip模拟4


下一篇:41.骑士周游算法