移植项目使用的是正点原子战舰V3开发板,网卡采用DM9000,DM9000属于链路层(PHY层+MAC层);LWIP属于互联层和传输层;应用层就是用户自己根据自己想要的功能实现,例如MQTT协议、HTTP协议等。
运行LWIP需要十几KB的RAM和40K左右的ROM
移植第一步:将LwIP添加到裸机工程
打开lwip-2.1.2 LWIP官网下载:lwIP - A Lightweight TCP/IP stack - Summary [Savannah]http://savannah.nongnu.org/projects/lwip/
将LWIP的src移植到工程文件中
src重命名LWIP
添加api core netif文件夹到工程文件中
添加图中.C文件到API中
添加图中.C文件到core中
添加图中.C文件到netif中
在工程添加头文件
移植第二步:移植头文件
在工程添加头文件 lwipopts.h、cc.h、pref.h
打开LwIP的contrib包
在contrib-2.1.0\examples\example_app 找到lwipopts.h
contrib-2.1.0\ports\unix\port\include\arch找到cc.h、pref.h
把3个头文件添加到arch文件夹里
打开lwipopts.h,并在文件内部写入修改以下配置
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
/**
* SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
* critical regions during buffer allocation, deallocation and memory
* allocation and deallocation.
*/
#define SYS_LIGHTWEIGHT_PROT 0 //* SYS_LIGHTWEIGHT_PROT==1:如果你想要确定的跨任务保护 *在缓冲区分配,回收和内存的关键区域 *分配和回收。
/**
* NO_SYS==1: Provides VERY minimal functionality. Otherwise,
* use lwIP facilities.
*/
#define NO_SYS 1 //NO_SYS表示无操作系统模拟层,这个宏非常重要, 因为无操作系统与有操作系统的移植和编写是完全不一样的,
//我们现在是无操作系统移植,所以将这个宏定义为1。
/**
* NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
* Mainly for compatibility to old versions.
*/
#define NO_SYS_NO_TIMERS 0 // NO_SYS_NO_TIMERS==1:当NO_SYS==1时,取消对sys_timeout的支持 *主要是为了兼容旧版本。
/* ---------- Memory options ---------- */
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
byte alignment -> define MEM_ALIGNMENT to 2. */
#define MEM_ALIGNMENT 4 //使用4字节对齐模式
/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE (10*512) //堆内存的大小。如果应用程序将发送很多需要复制的数据应该设置得大一点。
/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
sends a lot of data out of ROM (or other static memory), this
should be set high. */
#define MEMP_NUM_PBUF 50 // MEMP_NUM_PBUF: memp结构pbuf的数量。如果应用程序从ROM(或其他静态内存)发送大量数据,这应该设得高一些。
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
per active UDP "connection". */
#define MEMP_NUM_UDP_PCB 6 //MEMP_NUM_UDP_PCB: UDP协议控制块的数量。一个每个活跃的UDP“连接”。
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
connections. */
#define MEMP_NUM_TCP_PCB 10 //TCP协议控制块的数量。一个每个活跃的tcp“连接”。
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 6 //正在监听的TCP数量连接。
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
segments. */
#define MEMP_NUM_TCP_SEG 12 //同时排队的TCP段数。
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
timeouts. */
#define MEMP_NUM_SYS_TIMEOUT 10 //同时活动的超时次数。
/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE 10 //PBUF_POOL内存池中内存块数量。
/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE 500 //PBUF_POOL内存池中每个内存块大小。
/* ---------- TCP options ---------- */
#define LWIP_TCP 1 //开启TCP
#define TCP_TTL 255 //TCP生存时间
/* Controls if TCP should queue segments that arrive out of
order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ 0 //控制TCP是否应该对到达的段进行排队秩序。如果设备内存不足,则定义为0。
/* TCP Maximum segment size. */
#define TCP_MSS (1500 - 40) //TCP协议报文最大长度。
/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF (4*TCP_MSS) //允许TCP协议使用的最大发送缓冲区空间(字节)。
/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
#define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) //TCP发送端缓冲区空间(pbufs)。这一定是至少最多为(2 * TCP_SND_BUF/TCP_MSS)。
/* TCP receive window. */
#define TCP_WND (2*TCP_MSS) //TCP接收窗口大小。
/* ---------- ICMP options ---------- */
#define LWIP_ICMP 1
/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
turning this on does currently not work. */
#define LWIP_DHCP 1
/* ---------- UDP options ---------- */
#define LWIP_UDP 1
#define UDP_TTL 255
/* ---------- Statistics options ---------- */ //统计功能
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
/* ---------- link callback options ---------- */
/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
* whenever the link changes (i.e., link down)
*/
#define LWIP_NETIF_LINK_CALLBACK 0 //支持来自接口的回调函数当链接改变时(即链接向下)
/*
--------------------------------------
---------- Checksum options ----------
--------------------------------------
*/
/*The STM32F4x7 allows comput
ing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
- To use this feature let the following define uncommented.
- To disable it and process by CPU comment the the checksum.
*/
// #define CHECKSUM_BY_HARDWARE
#ifdef CHECKSUM_BY_HARDWARE //硬件效验
/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 0
/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 0
/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 0
/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 0
/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 0
/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 0
/*CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 0
#else //软件效验
/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP 1
/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP 1
/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP 1
/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP 1
/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP 1
/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP 1
/*CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP 1
#endif
/*
----------------------------------------------
---------- Sequential layer options ----------
----------------------------------------------
*/
/**
* LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
*/
#define LWIP_NETCONN 0 //开启/关闭netconn功能
/*
------------------------------------
---------- Socket options ----------
------------------------------------
*/
/**
* LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
*/
#define LWIP_SOCKET 0 //开启/关闭socket功能
/*
----------------------------------------
---------- Lwip Debug options ----------
----------------------------------------
*/
//#define LWIP_DEBUG 1
#endif /* __LWIPOPTS_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
打开cc.h,并在文件内部写入修改以下配置
#ifndef __CC_H__
#define __CC_H__
#include "stdio.h"
#include "main.h"
#define LWIP_NO_STDINT_H 1
typedef unsigned char u8_t;
typedef signed char s8_t;
typedef unsigned short u16_t;
typedef signed short s16_t;
typedef unsigned long u32_t;
typedef signed long s32_t;
typedef u32_t mem_ptr_t;
typedef int sys_prot_t;
#define U16_F "hu"
#define S16_F "d"
#define X16_F "hx"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define SZT_F "uz"
/* 选择小端模式 */
#define BYTE_ORDER LITTLE_ENDIAN
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
#define LWIP_PLATFORM_ASSERT(x) do {printf(x);}while(0)
extern u32_t sys_now(void);
#endif /* __CC_H__ */
打开perf.h,并在文件内部写入修改以下配置
#ifndef __PERF_H__
#define __PERF_H__
#define PERF_START /* null definition */
#define PERF_STOP(x) /* null definition */
#endif /* __PERF_H__ */
移植第三步:移植网卡驱动
打开ethernetif.c移植网卡驱动 low_level_output和low_level_input 这里是采用DM9000网卡的收发驱动,根据不同网卡的收发驱动直接换就可以用。
#include "ethernetif.h"
#include "dm9000.h"
#include "lwip_comm.h"
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "ethernetif.h"
#include <string.h>
#include "lwip/def.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "netif/ppp/pppoe.h"
#define IFNAME0 'e'
#define IFNAME1 'n'
struct ethernetif {
struct eth_addr *ethaddr;
/* Add whatever per-interface state that is needed here. */
};
//由ethernetif_init()调用用于初始化硬件
//netif:网卡结构体指针
//返回值:ERR_OK,正常
// 其他,失败
static void low_level_init(struct netif *netif)
{
netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置MAC地址长度,为6个字节
//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
netif->hwaddr[0]=lwipdev.mac[0];
netif->hwaddr[1]=lwipdev.mac[1];
netif->hwaddr[2]=lwipdev.mac[2];
netif->hwaddr[3]=lwipdev.mac[3];
netif->hwaddr[4]=lwipdev.mac[4];
netif->hwaddr[5]=lwipdev.mac[5];
netif->mtu=1500; //最大允许传输单元,允许该网卡广播和ARP功能
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;
// mymacinit(lwipdev.mac); // 调用自己实际的网卡初始化函数
}
//用于发送数据包的最底层函数(lwip通过netif->linkoutput指向该函数)
//netif:网卡结构体指针
//p:pbuf数据结构体指针
//返回值:ERR_OK,发送正常
// ERR_MEM,发送失败
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
DM9000_SendPacket(p);
return ERR_OK;
}
///用于接收数据包的最底层函数
//neitif:网卡结构体指针
//返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p;
p=DM9000_Receive_Packet();
return p;
}
//网卡接收数据(lwip直接调用)
//netif:网卡结构体指针
//返回值:ERR_OK,发送正常
// ERR_MEM,发送失败
void ethernetif_input(struct netif *netif)
{
err_t err;
struct pbuf *p;
p=low_level_input(netif); //调用low_level_input函数接收数据
if(p!=NULL)
{
err=netif->input(p, netif); //调用netif结构体中的input字段(一个函数)来处理数据包
if(err!=ERR_OK)
{
printf("1111\r\n");
LWIP_DEBUGF(NETIF_DEBUG,("ethernetif_input: IP input error\n"));
pbuf_free(p);
p = NULL;
}
}
}
//使用low_level_init()函数来初始化网络
//netif:网卡结构体指针
//返回值:ERR_OK,正常
// 其他,失败
err_t ethernetif_init(struct netif *netif)
{
struct ethernetif *ethernetif;
LWIP_ASSERT("netif != NULL", (netif != NULL));
ethernetif = mem_malloc(sizeof(struct ethernetif));
if (ethernetif == NULL) {
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
printf("ethernetif_init: out of memory\r\n");
return ERR_MEM;
}
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
// MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->state = ethernetif;
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
netif->output = etharp_output;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = low_level_output;
ethernetif->ethaddr = (struct eth_addr *) & (netif->hwaddr[0]);
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
我们还需要一个ethernetif.h文件,主要是对函数的一些声明
#ifndef __ETHERNETIF_H__
#define __ETHERNETIF_H__
#include "lwip/err.h"
#include "lwip/netif.h"
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);
void ethernetif_update_config(struct netif *netif);
void ethernetif_notify_conn_changed(struct netif *netif);
#endif
移植第四步:LWIP时基
一般我们在STM32中,一般采用SysTick作为LwIP的时基定时器,将SysTick产生中断的频率设置为1000HZ,也就是1ms触发一次中断,每次产生中断的时候,系统变量就会加1。
而我是使用(10ms定时器3) 每10ms加10,sys_now()函数中直接返回lwip_localtime值
//为LWIP提供计时
extern uint32_t lwip_localtime;//lwip本地时间计数器,单位:ms
u32_t sys_now(void)
{
return lwip_localtime;
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
lwip_localtime +=10; //加10
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
移植第五步:协议栈初始化
#include "lwip_comm.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/init.h"
#include "ethernetif.h"
//#include "lwip/timers.h"
//#include "lwip/tcp_impl.h"
//#include "lwip/ip_frag.h"
#include "lwip/tcpip.h"
#include "delay.h"
#include "usart.h"
#include <stdio.h>
#include "lwip/ip_addr.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK 战舰开发板 V3
//lwip通用驱动 代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/3/15
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//*******************************************************************************
//修改信息
//无
//
__lwip_dev lwipdev; //lwip控制结构体
struct netif lwip_netif; //定义一个全局的网络接口
extern u32 memp_get_memorysize(void); //在memp.c里面定义
extern u8_t *memp_memory; //在memp.c里面定义.
extern u8_t *ram_heap; //在mem.c里面定义.
u32 TCPTimer=0; //TCP查询计时器
u32 ARPTimer=0; //ARP查询计时器
u32 lwip_localtime; //lwip本地时间计数器,单位:ms
//lwip中mem和memp的内存申请
//返回值:0,成功;
// 其他,失败
//lwip 默认IP设置
//lwipx:lwip控制结构体指针
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
//默认远端IP为:192.168.1.100
lwipx->remoteip[0]=192;
lwipx->remoteip[1]=168;
lwipx->remoteip[2]=1;
lwipx->remoteip[3]=100;
//MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)
lwipx->mac[0]=dm9000cfg.mac_addr[0];
lwipx->mac[1]=dm9000cfg.mac_addr[1];
lwipx->mac[2]=dm9000cfg.mac_addr[2];
lwipx->mac[3]=dm9000cfg.mac_addr[3];
lwipx->mac[4]=dm9000cfg.mac_addr[4];
lwipx->mac[5]=dm9000cfg.mac_addr[5];
//默认本地IP为:192.168.1.30
lwipx->ip[0]=192;
lwipx->ip[1]=168;
lwipx->ip[2]=1;
lwipx->ip[3]=30;
//默认子网掩码:255.255.255.0
lwipx->netmask[0]=255;
lwipx->netmask[1]=255;
lwipx->netmask[2]=255;
lwipx->netmask[3]=0;
//默认网关:192.168.1.1
lwipx->gateway[0]=192;
lwipx->gateway[1]=168;
lwipx->gateway[2]=1;
lwipx->gateway[3]=1;
lwipx->dhcpstatus=0;//没有DHCP
}
//LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
// 1,内存错误
// 2,DM9000初始化失败
// 3,网卡添加失败.
u8 lwip_comm_init(void)
{
struct netif *Netif_Init_Flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功
ip4_addr_t ipaddr; //ip地址
ip4_addr_t netmask; //子网掩码
ip4_addr_t gw; //默认网关
lwip_init(); //初始化LWIP内核
lwip_comm_default_ip_set(&lwipdev); //设置默认IP等信息
IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);
printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);
printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input);//向网卡列表中添加一个网口
if(Netif_Init_Flag==NULL)return 3;//网卡添加失败
else//网口添加成功后,设置netif为默认值,并且打开netif网口
{
netif_set_default(&lwip_netif); //设置netif为默认网口
if (netif_is_link_up(&lwip_netif))
{
/*When the netif is fully configured this function must be called */
netif_set_up(&lwip_netif);
printf("1\r\n");
}
else
{
/* When the netif link is down this function must be called */
netif_set_down(&lwip_netif);
printf("2\r\n");
}
}
return 0;//操作OK.
}
//当接收到数据后调用
void lwip_pkt_handle(void)
{
//从网络缓冲区中读取接收到的数据包并将其发送给LWIP处理
ethernetif_input(&lwip_netif);
}
移植第六步:ping获取数据包
最后一步设置主函数 因为没有操作系统 死循环轮询lwip_pkt_handle()
int main(void)
{
u32 i,b;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
LED_Init(); //LED端口初始化
uart_init(115200); //串口1波特率默认为115200
KEY_Init(); //初始化按键
TIM3_Int_Init(1000,719);//定时器3频率为100hz 10ms
// TIM4_Int_Init(3000,7199); //定时器4 默认频率为3000hz 300ms TCP分包时间
DM9000_Init();
b=lwip_comm_init();
printf("b:%d\r\n",b);
while(1)
{
lwip_pkt_handle();
i++;
if(i==50000)
{
LED0=~LED0;
printf("OK\r\n");
i=0;
}
}
}