搜索
bottom↓
回复: 51

STM32F107+FreeRTOS+Lwip的源码以及一个socket client的例程

  [复制链接]
(271320978)

出0入0汤圆

发表于 2012-12-21 15:33:12 | 显示全部楼层 |阅读模式
本人之前在STM32F107上做过UCOS+LWIP,参照网络上一些资源修改了sys_arch.c/sys_arch.h,对tcpip_thread 的优先级、以及与之对应的lwip_timeouts、static struct sys_timeouts lwip_timeouts[LWIP_TIMEOUT_AMOUNT]的LWIP_TIMEOUT_AMOUNT理解不到位,另外网上流传的sys_mbox_t sys_mbox_new(int size)实现方法好像也是不是很好,我修改如下:

//*------------------------------------------------------------------------------------------------
//* 函数名称 : sys_mbox_new
//* 功能描述 : 建立一个空的邮箱
//* 入口参数 : 无
//* 出口参数 : - != SYS_MBOX_NULL : 邮箱申请成功,返回一个指向被申请邮箱的指针
//*          : - = SYS_MBOX_NULL  : 邮箱没有申请成功
//*------------------------------------------------------------------------------------------------
sys_mbox_t sys_mbox_new(int size)
{
    u8_t       ucErr;
    sys_mbox_t pQDesc;
   
    pQDesc = OSMemGet( pQueueMem, &ucErr );
    if( ucErr == OS_NO_ERR ) {
                //移植说明中有“邮箱用于消息传递,用户即可以将其实现为一个队列,允许多条消息投递到这个邮箱,
                //也可以每次只允许投递一个消息,这两种方式lwip都可以正常运行。不过,前者更有效。”
                //TCPIP_MBOX_SIZE,
                //DEFAULT_RAW_RECVMBOX_SIZE,
                //DEFAULT_UDP_RECVMBOX_SIZE,
                //DEFAULT_TCP_RECVMBOX_SIZE
                //DEFAULT_ACCEPTMBOX_SIZE 在opt.h 定义为0
                //可以 lwipopts.h 可以对上述宏重新定义,而在opt.h中有
                //* The queue size value itself is platform-dependent, but is passed to
        //* sys_mbox_new() when xxxxxxx is called.
                //因此把 size 固定 MAX_MSG_QUEUES
        //pQDesc->pQ_Mbox = OSQCreate( &(pQDesc->pvQ_msgQueue[0]), size );   //>>>
                pQDesc->pQ_Mbox = OSQCreate( &(pQDesc->pvQ_msgQueue[0]), MAX_MSG_QUEUES );        // <<<  
        if( pQDesc->pQ_Mbox != ((OS_EVENT *)0 ) ) {
            return pQDesc;
        }
    }
    return SYS_MBOX_NULL;
}

对struct sys_timeouts *sys_arch_timeouts(void)的实现,网上流传很多版本
我也请教过bernard,他这样回复:
/
//lwip的移植是这样的:
//通常lwip那边的线程需要使用sys_thread_new来创建,然后和网络相关的定时器都统一的放在一个列表上(基本上原来netconn、socket的API都仅能够应用于sys_thread_new创建的线程上)
//
//为了解除这个限制,所以和定时器相关的都仅应用于lwip自己的线程,而其他则不采用这种方式。当使用sys_thread_new创建线程时,会把定时器链表放到thread->user_data域上面。所以这也就是这个返回NULL的来由:
//仅有使用sys_thread_new创建的线程才能够获得这个定时器,在其他线程中调用sys_arch_timeouts()函数只能够返回NULL。
//
下列是我的修改的:
struct sys_timeouts *sys_arch_timeouts(void)
{
        OS_TCB curr_task_pcb;
        u8_t curr_prio;
        s16_t offset;
       
//        null_timeouts.next = NULL;
       
        OSTaskQuery(OS_PRIO_SELF,&curr_task_pcb);                 /* 获取当前线程的优先级 */
        curr_prio = curr_task_pcb.OSTCBPrio;
       
        offset = curr_prio - LWIP_START_PRIO;
       
        if(offset < 0 || offset >= LWIP_TIMEOUT_AMOUNT)      /*如果不是LwIP的线程,那么返回timeouts->NULL*/
        {
                //return &null_timeouts;
                return NULL;
        }
       
        return &lwip_timeouts[offset];
}

修改下来,测试效果还好,不过本人能力有限,对这些修改、理解,心里也是没有底。
刚好st官网上有STM32F2X7+FreeRTOS+Lwip的例程,所以就有参照STM32F2x7_ETH_LwIP_V1.1.0做修改的冲动,以适用STM32F107芯片。
附件是STM32F107+FreeRTOS+Lwip的源码以及一个socket client的例程源码。


对于socket编程,大多例程都是:

1、建立socket
2、连续socket
3、发送数据
4、关闭socket

这样做进行简单的测试是没有问题的。在一些数据采集设备,一般设备做为“客户”端,也是一直连接到主站服务器端,一是为定时上传数据,而是供主站服务器端随时召唤数据。这样的例程就不能满足了。

例程里“客户端”就是长连接,一直等到接收数据,本例程只是简单把接收的数据回送到服务器端。在实际使用中,可根据需要处理数据。
当然在建立另一个任务中,专门来处理发送数据。特别注意的是需要加互斥量做保护。

例程使用了lwip_select函数,网上找关于select说明感觉不错,摘录如下:

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non- block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下!

Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明):

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct tim *timeout);

先说明两个结构体:

第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO (fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。

第二,struct tim是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

具体解释select的参数:

int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

struct tim* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

返回值:

负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

例子:

main()

{

    int sock;

    FILE *fp;

    struct fd_set fds;

    struct tim timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0

    char buffer[256]={0}; //256字节的接收缓冲区

   

  while(1)
   {
        FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化

        FD_SET(sock,&fds); //添加描述符

        FD_SET(fp,&fds); //同上

        maxfdp=sock>fp?sock+1:fp+1;    //描述符最大值加1
           switch(select(maxfdp,&fds,&fds,NULL,&timeout))   //select使用
        {
            case -1: exit(-1);break; //select错误,退出程序
        case 0:break;  //再次轮询
        default:
                  if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
            {

                        recvfrom(sock,buffer,256,.....);//接受网络数据

                        if(FD_ISSET(fp,&fds)) //测试文件是否可写
                            fwrite(fp,buffer...);//写入文件buffer清空;
                   }// end if break;
          }// end switch
     }//end while
}//end main


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
(271318845)

出0入0汤圆

发表于 2012-12-21 16:08:45 | 显示全部楼层
顶一个 学习了
(271123796)

出0入0汤圆

发表于 2012-12-23 22:19:34 | 显示全部楼层
Thanks for sharing!
(271121341)

出0入0汤圆

发表于 2012-12-23 23:00:29 来自手机 | 显示全部楼层
学习了,收藏!!
来自:amoBBS 阿莫电子论坛 Windows Phone 7 客户端
(263040191)

出0入0汤圆

发表于 2013-3-27 11:46:19 | 显示全部楼层
感谢楼主分享,学习了...
(261970064)

出0入0汤圆

发表于 2013-4-8 21:01:46 | 显示全部楼层
顶一个 学习了
(261483632)

出0入0汤圆

发表于 2013-4-14 12:08:58 | 显示全部楼层
正在找SOCKET作为客户端的例程,多谢楼主
(261274232)

出0入0汤圆

发表于 2013-4-16 22:18:58 | 显示全部楼层
LwIP _Mark .   
(261062198)

出0入0汤圆

发表于 2013-4-19 09:12:52 | 显示全部楼层
lwip
(260410241)

出0入0汤圆

发表于 2013-4-26 22:18:49 | 显示全部楼层
有enc28j60的没?
(257856717)

出0入0汤圆

发表于 2013-5-26 11:37:33 | 显示全部楼层
感谢楼主分享!
回头要找找FreeRTOS STM32F2X LwIP的官方例子!
(251808417)

出0入0汤圆

发表于 2013-8-4 11:42:33 | 显示全部楼层
感谢楼主分享!
(250990007)

出0入0汤圆

发表于 2013-8-13 23:02:43 来自手机 | 显示全部楼层
mark……
顶一个…
(250951386)

出0入0汤圆

 楼主| 发表于 2013-8-14 09:46:24 | 显示全部楼层
socket client 编程例程:
typedef struct _XTCPCLIENTSOCK{
        int s;                                                /*socket 标识符 -1无效,>= 0 有效*/
        int bconnect;                                /*socket 是否连接成功,0 未连接,>= 1 连接*/
        xSemaphoreHandle        sxMutex;/*互斥量,socket不是线程安全的,为了实现socket
                                                                        在一个线程里接收数据,而在其他线程发送,
                                                                        建立互斥量来做互斥操作*/
}XTCPCLIENTSOCK;

XTCPCLIENTSOCK xClientSocket;

#define BUF_SIZE                32
char ClientRevBuf[BUF_SIZE];

void TCPClient(void *arg)
{
        struct sockaddr_in ServerAddr;
        int optval = 1;
        fd_set fdsr;
        struct timeval tv;
       
        (void)arg;
        xClientSocket.bconnect = 0;
        //建立互斥量
        xClientSocket.sxMutex = xSemaphoreCreateMutex();
       
        if(xClientSocket.sxMutex == NULL )
        {
                while(1);
        }
        while(1)
        {
                //创建客户端
                xClientSocket.s = lwip_socket( AF_INET,SOCK_STREAM,IPPROTO_TCP);
                if(xClientSocket.s == -1 ){
                        continue;
                }
                //创建成功
                optval = 1;
               
                lwip_setsockopt(xClientSocket.s,SOL_SOCKET,SO_KEEPALIVE,&optval,sizeof(optval));
                ServerAddr.sin_family = AF_INET;
                ServerAddr.sin_addr.s_addr = inet_addr("192.168.0.80");//
                ServerAddr.sin_port = htons(8080);
               
                xClientSocket.bconnect = 0;
                //连接服务器
                if( lwip_connect(xClientSocket.s,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr) ) == - 1)
                {
                        //connect error!
                        lwip_close(xClientSocket.s);
                        continue;
                }
                xClientSocket.bconnect = 1;
               
                //是否接收到数据
                while(1)
                {
                        FD_ZERO(&fdsr);
                        FD_SET(xClientSocket.s,&fdsr);
                        tv.tv_sec = 10;
                        tv.tv_usec = 0 ;

                        if( lwip_select(xClientSocket.s + 1,&fdsr,NULL,0,&tv) == 0 )
                                continue;
                       
                        if( FD_ISSET(xClientSocket.s,& fdsr) )
                        {
                                int datalen;
                                int ret;
                                //有数据,互斥操作
                                if( xSemaphoreTake( xClientSocket.sxMutex, portMAX_DELAY ) != pdTRUE ){
                                        while(1);
                                }

                                datalen = lwip_recv(xClientSocket.s,ClientRevBuf,BUF_SIZE,0);
                                if(datalen > 0)
                                {
                                        //接收的数据,测试,回送给服务器
                                        ret = lwip_send(xClientSocket.s,ClientRevBuf,datalen,0);
                                       
                                }else{
                                        //服务器关闭等异常
                                        xClientSocket.bconnect = 0 ;
                                        lwip_close(xClientSocket.s);
                                        xSemaphoreGive( xClientSocket.sxMutex );
                                        break;        //重新连接服务器       
                                }
                                xSemaphoreGive( xClientSocket.sxMutex );
                        }                       
                }       
        }
}
(250950798)

出0入0汤圆

发表于 2013-8-14 09:56:12 | 显示全部楼层
LwIP              
(246861092)

出0入0汤圆

发表于 2013-9-30 17:57:58 | 显示全部楼层
顶一个 学习了
(229266173)

出0入0汤圆

发表于 2014-4-22 09:26:37 | 显示全部楼层
收藏了,学习学习。
(224757519)

出0入0汤圆

发表于 2014-6-13 13:50:51 | 显示全部楼层
mark!
(222067859)

出0入0汤圆

发表于 2014-7-14 16:58:31 | 显示全部楼层
下来学习学习。
(221224797)

出0入0汤圆

发表于 2014-7-24 11:09:33 | 显示全部楼层
学习一下
(221138032)

出0入0汤圆

发表于 2014-7-25 11:15:38 | 显示全部楼层
楼主很强大啊!
(217748530)

出0入0汤圆

发表于 2014-9-2 16:47:20 | 显示全部楼层
辛苦了,谢谢喽!
(217652769)

出0入0汤圆

发表于 2014-9-3 19:23:21 | 显示全部楼层
刚开始弄网络方面的,学习下
(213233023)

出0入0汤圆

发表于 2014-10-24 23:05:47 | 显示全部楼层
好好学习,多谢分享
(212630843)

出0入0汤圆

发表于 2014-10-31 22:22:07 | 显示全部楼层
裸奔就了,来学习了,
LZ,比如lwip,FreeRTOS上面那个网络包感觉怎样?
(211356368)

出0入0汤圆

 楼主| 发表于 2014-11-15 16:23:22 | 显示全部楼层
LearningASM 发表于 2014-10-31 22:22
裸奔就了,来学习了,
LZ,比如lwip,FreeRTOS上面那个网络包感觉怎样?

不清楚你说的网络包是什么意思,我也没有裸奔过。
(211355043)

出0入0汤圆

发表于 2014-11-15 16:45:27 | 显示全部楼层
Yan_xp 发表于 2014-11-15 16:23
不清楚你说的网络包是什么意思,我也没有裸奔过。

FreeRTOS 有个基于STM32F107的网络例程,网络代码是他们自己写的,不知道你有没试过
(211353260)

出0入0汤圆

 楼主| 发表于 2014-11-15 17:15:10 | 显示全部楼层
LearningASM 发表于 2014-11-15 16:45
FreeRTOS 有个基于STM32F107的网络例程,网络代码是他们自己写的,不知道你有没试过 ...

freeRTOS官方包里面没有STM32+LWIP的例程吧,st有stm32f207+freertos+lwip的例程。
有os,lwip就可以像bsd socket一样方便编程!
(211152222)

出0入0汤圆

发表于 2014-11-18 01:05:48 | 显示全部楼层
非常好,找了很多地方才找到下载
(208185662)

出0入0汤圆

发表于 2014-12-22 09:08:28 | 显示全部楼层
mark,几乎都是作server的例子,但一般的通信模型应该是产品作客户端,公司作服务器吧。反过来哪来那么多公网IP
(208175446)

出0入0汤圆

 楼主| 发表于 2014-12-22 11:58:44 | 显示全部楼层
现在手机的一些app也是作为客户端的,与服务器建立常连接,定时地发一些心跳报文,维持tcp连接,这样服务器与客户端可以双向通信,服务器也就周期性推送广告。
(206632141)

出0入0汤圆

发表于 2015-1-9 08:40:29 | 显示全部楼层
IAR 是版本几? 我用7.3打开,很多错误,通过路径把头文件添加好之后,还是有些错识,90多个,
(206631308)

出0入0汤圆

发表于 2015-1-9 08:54:22 | 显示全部楼层
楼主板子原理图有没有,PHY 芯片是
(206518273)

出0入0汤圆

发表于 2015-1-10 16:18:17 | 显示全部楼层
xiou 发表于 2014-12-22 09:08
mark,几乎都是作server的例子,但一般的通信模型应该是产品作客户端,公司作服务器吧。反过来哪来那么多公 ...

有没有LWIP socket作为服务器的例子呢,任务部分就可以
(206353450)

出0入0汤圆

发表于 2015-1-12 14:05:20 | 显示全部楼层
Yan_xp 发表于 2013-8-14 09:46
socket client 编程例程:
typedef struct _XTCPCLIENTSOCK{
        int s;                                                /*socket 标识符 -1无效,>= 0  ...

有没有LWIP 用socket写的服务器 呢,?
(206172108)

出0入0汤圆

 楼主| 发表于 2015-1-14 16:27:42 | 显示全部楼层
黄晨0410 发表于 2015-1-12 14:05
有没有LWIP 用socket写的服务器 呢,?

使用iar6.3,phy以及原理图参考st官方的stm32f100c开发板。现在还没有做服务器端的例子。
(205568588)

出0入0汤圆

 楼主| 发表于 2015-1-21 16:06:22 | 显示全部楼层
有朋友在问:lwip做服务器的例子,刚好我也需要这样的需求。因此,也上网找到了资料。

      如果是按照,绑定地址--->监听--->接入客户端->等待客户端数据-->响应客户端--->关闭客户端--->监听..... 的方式,倒是很方便实现的:lwip的socket相关函数就是阻塞,按顺序编写就行了。
      有些服务器是一直处于监听状态,有客户端请求,则接入客户端,并随时与所有的客户端双向通信,也不主动关闭客户端。这样就需要同时处理监听,等待所有客户端的数据输入的问题。如果在linux下实现,使用fork倒也方便。freertos却没有相应的功能。不过,lwip还是有select函数,也能满足上述要求。
     具体实现,参考:http://blog.csdn.net/jnu_simba/article/details/9071445

http://blog.csdn.net/Simba888888/article/category/1426325/2博客有“ linux网络编程之socket”,大家还是从头到尾一一研读吧。
(205350462)

出0入0汤圆

发表于 2015-1-24 04:41:48 | 显示全部楼层
顶,mark
(204896563)

出0入0汤圆

发表于 2015-1-29 10:46:47 | 显示全部楼层
没有心跳包, 技网线,或服务器断电,怎样处理呢
(203589867)

出0入0汤圆

发表于 2015-2-13 13:45:03 | 显示全部楼层
学习了,多谢!!
(181111685)

出0入0汤圆

发表于 2015-10-31 17:41:25 | 显示全部楼层
顶一个
(174129060)

出0入0汤圆

发表于 2016-1-20 13:18:30 | 显示全部楼层
顶起!!!
(173451274)

出0入0汤圆

发表于 2016-1-28 09:34:56 | 显示全部楼层
感谢分享,再接再LI !
(170834866)

出0入0汤圆

发表于 2016-2-27 16:21:44 | 显示全部楼层
这几天正在搞这个东西,收藏了
(144941001)

出0入0汤圆

发表于 2016-12-23 09:06:09 | 显示全部楼层
学习学习,
(120832000)

出0入170汤圆

发表于 2017-9-28 10:02:50 | 显示全部楼层
学习了,非常感谢
(113054985)

出0入0汤圆

发表于 2017-12-27 10:19:45 | 显示全部楼层
好资料,学习学习
(109555835)

出0入0汤圆

发表于 2018-2-5 22:18:55 | 显示全部楼层
非常感谢   
(71733894)

出0入0汤圆

发表于 2019-4-19 16:24:36 | 显示全部楼层
看看可不可以学习一下
(71580826)

出0入0汤圆

发表于 2019-4-21 10:55:44 | 显示全部楼层
Thank you
(71494710)

出0入0汤圆

发表于 2019-4-22 10:51:00 | 显示全部楼层
非常感谢     
(8293568)

出0入0汤圆

发表于 2021-4-22 22:43:22 | 显示全部楼层
感谢老铁!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2021-7-27 22:29

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表