一步步移植uCOS-II and LwIP (一)
来源:互联网 发布:有双头蛇 知乎 编辑:程序博客网 时间:2024/05/16 23:52
STM32F103ZE下移植uCOS-II and LwIP 汇总
本文主要记录嵌入式实时操作系统uCOS-II(Ver 2.85)和轻量型TCP/IP协议栈LwIP(Ver 1.4.1)在32bit单片机STM32F103ZE上的移植过程,并列举几个simple examples 说明两者工作原理。本文的叙述原则是“用到什么知识点,就查阅相关资料”,对于其它延伸知识点不再概述。
所需物资:
- 硬件开发平台,本平台网卡为DM9000A
- uCOS-II(Ver 2.85)源码,可直接从Micrium官网下载uCOS在cortex-M3上的移植例程uCOSII-ST-STM32F103ZE-SK
- LwIP(Ver 1.4.1)源码,下载链接LwIP1.4.1
一、Lwip移植
TCP/IP协议分为网络链路层、网络层、传输层和应用层四个部分,网络链路层主要涉及底层硬件驱动的编写,另外三个上次协议一般采用协议栈的方式软件实现。要实现与其它网络设备通信,首当其冲的是要移植好底层网卡驱动。LwIP协议栈已经为我们提供了网络链路底层接口,我们要做到主要工作涉及到以下几个方面:
- 单片机与网卡DM9000芯片的通信;
- 完善LwIP协议栈文件ethernetif.c接口函数,该部分的难点在于实现LwIP规定的struct pbuf类型的数据包与网卡数据之间相互转换;
- 上层软件协议的simple explain;
1、网卡DM9000底层驱动的编写
首先查阅DM9000的Datasheet(建议直接从芯片官网上查找,一般会有该芯片的Application note or Demo)
本文主要是运用DM9000的16-bit mode,其总线形式类似与intel 8080总线,涉及读写指令和数据的控制引脚为CS#、IOW#、IOR#、CMD,数据总线引脚SD0~SD15,中断引脚INT。通常我们可以直接采用单片机I/O软件模拟时序来访问DM9000,但STM32内部自带FSMC控制器,可把DM9000当作SRAM器件使用地址访问,减少了不少功夫。关于STM32的FSMC,可详细参阅STM32的Reference Munual。DM9000数据手册介绍CMD引脚是控制读写数据和指令(寄存器)的开关标志,CMD=1,DATA port,CMD=0,INDEX port。所以可以将CMD接入FSMC的某一根地址线上(本文的硬件平台接入的是FSMC-A2),通过地址高低位来区别开写入的数据还是寄存器索引号,其实现数据结构如下:
#define DM9000_BASE ((uint32_t)0x6c100006)typedef struct{ uint16_t REG; uint16_t DATA;}DM9000_TypeDef;#define DM9000 ((DM9000_TypeDef *)DM9000_BASE)//or two methods#define DM9000_REG *(volatile uint16_t*)((uint32_t)0x6c100000)#define DM9000_DAT *(volatile uint16_t*)((uint32_t)0x6c100008) // FSMC A2 -> HADDR[3]
用类似于ST固件库的编写形式来定义设备DM9000,实现对地址的访问,注意16-bit mode下FSMC A[24:0] <=>HADDR[25:1]>>1。单片机的FSMC配置程序如下:
void DM9000_Configuration(void) //config fsmc to adapt dm9000{ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef FSMC_ReadWriteTimingStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOF|RCC_APB2Periph_GPIOA| \ RCC_APB2Periph_GPIOG,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE); //FSMC D0~D15 NWE and NOE GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8| GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11| GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_3 | GPIO_Pin_4; //FSMC A19 A20 GPIO_Init(GPIOE,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //NE4; GPIO_Init(GPIOG,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //FSMC A2 GPIO_Init(GPIOF,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 INT GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_Pin_1); EXTI_InitStructure.EXTI_Line = EXTI_Line1 ; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling; EXTI_Init(&EXTI_InitStructure); EXTI_ClearITPendingBit(EXTI_Line1); NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; // interrupt is not used NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStructure); FSMC_ReadWriteTimingStructure.FSMC_AddressSetupTime = 0x00; FSMC_ReadWriteTimingStructure.FSMC_AddressHoldTime = 0x00; FSMC_ReadWriteTimingStructure.FSMC_DataSetupTime = 0x03; FSMC_ReadWriteTimingStructure.FSMC_BusTurnAroundDuration = 0x00; FSMC_ReadWriteTimingStructure.FSMC_CLKDivision = 0x00; FSMC_ReadWriteTimingStructure.FSMC_DataLatency = 0x00; FSMC_ReadWriteTimingStructure.FSMC_AccessMode = FSMC_AccessMode_A; FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; FSMC_NORSRAMInitStructure.FSMC_WriteBurst =FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &FSMC_ReadWriteTimingStructure; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &FSMC_ReadWriteTimingStructure; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);}
DM9000寄存器读写配置程序如下:
void DM9000_WriteReg(uint16_t dat, uint16_t address){ DM9000->REG = address; DM9000->DATA = dat;}uint16_t DM9000_ReadReg(uint16_t address){ DM9000->REG = address; return DM9000->DATA;}
读取DM9000的ID,验证DM9000读写寄存器配置,程序及串口输出如下:
uint32_t DM9000_GetVPID(void){ uint32_t vpid; vpid = (DM9000_ReadReg(DM9000_VIDH)<<24)(DM9000_ReadReg(DM9000_VIDL)<<16)| (DM9000_ReadReg(DM9000_PIDH)<<8)|(DM9000_ReadReg(DM9000_PIDL)); return vpid;}
DM9000 物理层PHY寄存器读写及验证如下:
void DM9000_WritePHYReg(uint16_t dat, uint8_t phy_offset){ DM9000_WriteReg(0x40|phy_offset,DM9000_EPAR); DM9000_WriteReg((dat&0xff00)>>8, DM9000_EPDRH); DM9000_WriteReg(dat&0xff, DM9000_EPDRL); DM9000_WriteReg(0x0A, DM9000_EPCR); delay_ms(150); while((DM9000_ReadReg(DM9000_EPCR)&0x01)==0x01); DM9000_WriteReg(0x00, DM9000_EPCR);}uint16_t DM9000_ReadPHYReg(uint8_t phy_offset){ uint16_t temp; DM9000_WriteReg(0x40|phy_offset,DM9000_EPAR); DM9000_WriteReg(0x0c, DM9000_EPCR); delay_ms(20); while((DM9000_ReadReg(DM9000_EPCR)&0x01) == 0x01); DM9000_WriteReg(0x00, DM9000_EPCR); temp = (DM9000_ReadReg(DM9000_EPDRH)<<8)|(DM9000_ReadReg(DM9000_EPDRL)); return temp;}uint32_t DM9000_GetPHYID(void){ uint32_t temp; temp = ((uint32_t)(DM9000_ReadPHYReg(DM9000_PHY_PHYID1))<<16)| (DM9000_ReadPHYReg(DM9000_PHY_PHYID2)); return temp;}
DM9000初始化配置如下:
#define DM9000_IDENTIFIER (uint32_t)(0x0a469000)#define DM9000_PHYID (uint32_t)(0x0181b8a0)uint8_t DM9000_Init(void) //1:error{ DM9000_Configuration(); DM9000_Reset(); if(DM9000_GetVPID() == DM9000_IDENTIFIER) printf("dm9000's fsmc configuraion is right!\n"); else return 1; if(DM9000_GetPHYID() == DM9000_PHYID) printf("dm9000's phy configuraion is right!\n"); else return 1; DM9000_SetPHYMode(PHYMODE_AUTO_NEGOTIATION); DM9000_MacAddressSet(); DM9000_MulticastAddressSet(); DM9000_WriteReg(IMR_PAR,DM9000_IMR); // shut interrupt DM9000_WriteReg(0x80,DM9000_TCR2); // setup led mode DM9000_WriteReg(0x00,DM9000_GPR); // power on PHY DM9000_WriteReg(ISR_CLR_STATUS, DM9000_ISR); DM9000_GetPHYMode(); DM9000_WriteReg(IMR_PAR|IMR_PRI, DM9000_IMR); // rx interrupt enable DM9000_WriteReg(RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN, DM9000_RCR); // begin receive return 0;}
// 获取PHY工作模式void DM9000_GetPHYMode(void){ uint8_t temp; uint16_t autonegotiationflag; uint8_t i=0; autonegotiationflag = DM9000_ReadPHYReg(DM9000_PHY_BMCR)&PHYMODE_AUTO_NEGOTIATION; if(autonegotiationflag == PHYMODE_AUTO_NEGOTIATION) { printf("PHY works in auto_negotiation mode!\n"); while(!(DM9000_ReadPHYReg(DM9000_PHY_BMSR)&0x0020)) // wait for auto negatiation completed { i++; if(i>200) { temp = 0xff; //failed break; } } } else { printf("PHY works in setup mode!\n"); // wait for linking { i++; if(i>200) { temp = 0xff; //failed break; } } } temp = (DM9000_ReadReg(DM9000_NSR)>>6)&0x02; // speed 100mbps or 10mbps temp |= (DM9000_ReadReg(DM9000_NCR)>>3)&0x01; // full-duplex or half-duplex switch(temp) { case 0x00: printf("PHY works in 100Mbps half-duplex mode!\n"); break; case 0x01: printf("PHY works in 100Mbps full-duplexmode!\n"); break; case 0x02: printf("PHY works in 10Mbps half-duplex mode!\n"); break; case 0x03: printf("PHY works in 10Mbps full-duplex mode!\n"); break; default : printf("PHY linked failed!\n"); DM9000_Reset(); }}
从输出结果看出网卡DM9000初始化完毕后,网口状态显示已经连接,并且连接速度为100Mbps FULL-DUPLEX。但连接指示并未变成蓝色,表示PC机和硬件平台之间无数据传输,这是因为我们仅只实现了底层网络链路层的缘故。下一小节我们重点关注ethernetif.c文件中网口数据传送部分,这也是移植网卡驱动的难点。
注:本文仅列出核心代码,依照这些代码,便足以自己动手完成移植工作。
- 一步步移植uCOS-II and LwIP (一)
- 一步步移植uCOS-II and LwIP (二)
- 一步步移植uCOS-II and LwIP (三)
- 一步步移植uCOS-II and LwIP (四)
- LPC2388+UCOS/II+LWIP移植
- UCOS移植LWIP
- s3c2440上ucos-ii下lwip移植成功
- lpc2200移植ucos-II总结(一 移植环境)
- ucos-II(一)
- LWIP UCOS 移植步骤记录
- LWIP-1.3.0在S3C2440平台UCOS-II-2.8.6系统上的移植过程DEBUG
- 【读书笔记】ucos-ii(一)
- 认识uCOS-II(一)
- uCOS-II移植(Freescale MPC5644A)
- STM32移植uCOS-II系统(1)
- STM32移植uCOS-II系统(2)
- ucos-ii移植
- 移植ucos II
- spring mvc-@SessionAttributes 忘记清除 导致无法退出登录问题
- hdu 6011 贪心+模拟
- POJ-3126 Prime Path(BFS 求最小步数)
- 自制3D显示引擎七:填充任意多边形
- 火星人家园发送正能量的源泉
- 一步步移植uCOS-II and LwIP (一)
- Leetcode 215. Kth Largest Element in an Array
- hdu 6012 维护区间前缀和
- 用力的想你
- 欢迎使用CSDN-markdown编辑器
- Caffe 在window10+VS2015安装
- 内部类
- grafana elasticsearch date类型问题
- STM32F103的IAP中Bootloader中断以及App程序不能进入中断的问题解决方法。