一步步移植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 *pin configuration*
本文主要是运用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 ID check
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;}

PHY ID check
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 init
网口状态

从输出结果看出网卡DM9000初始化完毕后,网口状态显示已经连接,并且连接速度为100Mbps FULL-DUPLEX。但连接指示并未变成蓝色,表示PC机和硬件平台之间无数据传输,这是因为我们仅只实现了底层网络链路层的缘故。下一小节我们重点关注ethernetif.c文件中网口数据传送部分,这也是移植网卡驱动的难点。

注:本文仅列出核心代码,依照这些代码,便足以自己动手完成移植工作。

0 0