概述
记录下DM9000AE在rt-thread上的使用
-
FMC的配置
-
rt-thread的网络设备驱动注册
硬件连接
-
16bit总线
-
挂在FMC_A0 地址0x6000_0000
FMC的配置
FMC是STM32H7的一个外设,通过FMC把DM9000当做一个SRAM来访问,只需要配置好FCM的时序就可以了。
DM9000时序描述
来看一下DM9000的数据手册上对时序的描述
从手册上来看DM9000的时序情况如下
访问方式 | 数据建立时间 | 地址建立时间 |
---|---|---|
读取 | 最小 10ns | 80ns (F0寄存器需要80ns) |
写入 | 最小 10ns | 40ns |
FMC的时序配置
STM32H7的FMC外设,支持读写时序分离配置,也可以读写使用同一个时序配置,这里为了简单就直接使用一个,会牺牲效率, 性能优化可以在这里把读写分开,因为DM9000的读写速度明显不一样。
FMC的时钟配置,将地址建立时间大于80ns, 数据建立时间大于10ns
Timing.BusTurnAroundDuration = 2; // 片选信号,高脉宽
这个参数,实测影响的是片选信号的高电平,如果设置为0,则相当于片选一直有效
但是DM9000的SD口是地址和数据复用的,需要一定的切换时间,所以这个值需要设置,不然会发生异常。
这里设置为2,只是参考值,读者可以根据实际情况设置。
MPU配置(重要)
STM32H7有专用的内存保护单元,能够管理内存的访问权限。
这里FMC是挂在0x6000_0000的内存区域,为了能够正常访问外部的DM9000需要对该区域进行权限访问。
网络设备驱动注册
rt-thread的网络设备驱动的注册还是比较易懂的,我们需要实现的是操作硬件的接口即可。
这里重点关注这三个函数
-
rt_dm9000_init // DM9000的初始化
-
rt_dm9000_rx // DM9000读取网络数据包
-
rt_dm9000_tx // 发送网络数据包
实际上DM9000负责收发以太网帧, lwip负责协议处理。
dm9000的初始化没有什么特别的,按照初始化流程走,硬件、软件复位、校验ID、配置中断、使能等。
重点看看接收和发送
// DM9000
DM90000 INT-> rt_dm9000_isr-> eth_device_ready(&(dm9000_device.parent));-> return rt_mb_send(ð_rx_thread_mb, (rt_uint32_t)dev);// 通过信号量通知接收线程-> eth_rx_thread_entry -> if (rt_mb_recv(ð_rx_thread_mb, (rt_ubase_t *)&device, RT_WAITING_FOREVER) == RT_EOK) // 等待信号量, DM9000中断触发while(1) // 注意这里,循环读取,所以在eth_rx接口中,**读取一帧**就立即返回即可。{-> p = device->eth_rx(&(device->parent)); // 注册驱动时指定的 读取DM9000的接口-> if( device->netif->input(p, device->netif) != ERR_OK ) // 提交给lwip协议栈}
DM9000的收发
DM9000有一个16KB的硬件FIFO, 默认配置下,前3KB用于发送,后13KB用于接收,这个大小可以通过配置相应的寄存器修改。
发送过程
-
数据放入发送FIFO
-
告诉DM9000数据总量
-
启动发送
-
可以轮询等待,也可以等中断通知
// 概要
rt_err_t rt_dm9000_tx( rt_device_t dev, struct pbuf* p)-> dm9000_io_write(DM9000_IMR, IMR_PAR); // 屏蔽中断,防止干扰-> DM9000_IO = DM9000_MWCMD; // 写入操作while(1){-> word[word_index++] = ((u8_t*)q->payload)[pbuf_index++];-> DM9000_DATA = (word[1] << 8) | word[0]; // write two bytes to DM9000_DATA 每次2字节写入}-> dm9000_io_write(DM9000_TXPLL, p->tot_len & 0xff);-> dm9000_io_write(DM9000_TXPLH, (p->tot_len >> 8) & 0xff);-> dm9000_io_write(DM9000_TCR, TCR_TXREQ); // 使能bit0启动发送,发送完成后会自动清除-> dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM | ISR_ROS | ISR_ROOS); // 恢复中断
接收过程
接收过程需要注意的地方多一点,主要是数据帧的组织和读取动作。
先来看看数据帧在FIFI中分布
-
数据帧:4B的头+pyload+4B CRC
-
多个数据帧连续排布
特别注意
-
DM9000的FIFO是双字节对齐的
当pyload为奇数时,会出现一下情况,在最后一个字节会多一字节,让下一帧对齐地址。
-
DM9000触发中断后RX-FIFO可能会有多帧
在rt-thread的网络驱动中,接收函数只需要构建一帧数据就好,在驱动线程中会一次性读取完,直到读取到空数据为止。 -
DM9000的RX-FIFO每一帧都有4B的CRC
协议栈不需要这4B 需要丢弃掉
dummy_u16 = DM9000_DATA; dummy_u16 = DM9000_DATA;
ps: 源码放最后,感谢看完本篇的你!
源码
#ifndef __DRV_DM9000_H__
#define __DRV_DM9000_H__
// #define DM9000_IO_BASE 0x64000000
// // #define DM9000_DATA_BASE 0x64000100 // FSMC_A7 -- CMD = 1
// #define DM9000_DATA_BASE (0x64000000 + 0x00000002) // FSMC_A7 -- CMD = 1
// #define DM9000_IO (*((volatile rt_uint16_t *) DM9000_IO_BASE)) // CMD = 0
// #define DM9000_DATA (*((volatile rt_uint16_t *) DM9000_DATA_BASE)) // CMD = 1
#include "main.h"typedef __IO uint32_t vu32;typedef __IO uint16_t vu16;typedef __IO uint8_t vu8;
typedef uint32_t u32;typedef uint16_t u16;typedef uint8_t u8;
//DM9000地址结构体
typedef struct
{vu16 REG;vu16 DATA;
}DM9000_TypeDef;
#define DM9000_BASE ((u32)(0x60000000))
#define DM9000 ((DM9000_TypeDef *) DM9000_BASE)
#define DM9000_IO_BASE (DM9000->REG)
#define DM9000_DATA_BASE (DM9000->DATA) // FSMC_A0 -- CMD = 1
#define DM9000_IO (DM9000->REG) // CMD = 0
#define DM9000_DATA (DM9000->DATA) // CMD = 1
// #define DM9000_IO_BASE 0x64000000
// #define DM9000_DATA_BASE (0x64000000 + 0x2) // FSMC_A0 -- CMD = 1
// #define DM9000_IO (*((volatile rt_uint16_t *) DM9000_IO_BASE)) // CMD = 0
// #define DM9000_DATA (*((volatile rt_uint16_t *) DM9000_DATA_BASE)) // CMD = 1
// #define DM9000_inb(r) (*(volatile rt_uint8_t *)r)
// #define DM9000_outb(r, d) (*(volatile rt_uint8_t *)r = d)
// #define DM9000_inw(r) (*(volatile rt_uint16_t *)r)
// #define DM9000_outw(r, d) (*(volatile rt_uint16_t *)r = d)
#define DM9000_ID 0x90000A46 /* DM9000 ID */
#define DM9000_PKT_MAX 1536 /* Received packet max size */
#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */
#define DM9000_NCR 0x00
#define DM9000_NSR 0x01
#define DM9000_TCR 0x02
#define DM9000_TSR1 0x03
#define DM9000_TSR2 0x04
#define DM9000_RCR 0x05
#define DM9000_RSR 0x06
#define DM9000_ROCR 0x07
#define DM9000_BPTR 0x08
#define DM9000_FCTR 0x09
#define DM9000_FCR 0x0A
#define DM9000_EPCR 0x0B
#define DM9000_EPAR 0x0C
#define DM9000_EPDRL 0x0D
#define DM9000_EPDRH 0x0E
#define DM9000_WCR 0x0F
#define DM9000_PAR 0x10
#define DM9000_MAR 0x16
#define DM9000_GPCR 0x1e
#define DM9000_GPR 0x1f
#define DM9000_TRPAL 0x22
#define DM9000_TRPAH 0x23
#define DM9000_RWPAL 0x24
#define DM9000_RWPAH 0x25
#define DM9000_VIDL 0x28
#define DM9000_VIDH 0x29
#define DM9000_PIDL 0x2A
#define DM9000_PIDH 0x2B
#define DM9000_CHIPR 0x2C
#define DM9000_TCR2 0x2D
#define DM9000_OTCR 0x2E
#define DM9000_SMCR 0x2F
#define DM9000_ETCR 0x30 /* early transmit control/status register */
#define DM9000_CSCR 0x31 /* check sum control register */
#define DM9000_RCSSR 0x32 /* receive check sum status register */
#define DM9000_MRCMDX 0xF0
#define DM9000_MRCMD 0xF2
#define DM9000_MRRL 0xF4
#define DM9000_MRRH 0xF5
#define DM9000_MWCMDX 0xF6
#define DM9000_MWCMD 0xF8
#define DM9000_MWRL 0xFA
#define DM9000_MWRH 0xFB
#define DM9000_TXPLL 0xFC
#define DM9000_TXPLH 0xFD
#define DM9000_ISR 0xFE
#define DM9000_IMR 0xFF
#define CHIPR_DM9000A 0x19
#define CHIPR_DM9000B 0x1B
#define NCR_EXT_PHY (1<<7)
#define NCR_WAKEEN (1<<6)
#define NCR_FCOL (1<<4)
#define NCR_FDX (1<<3)
#define NCR_LBK (3<<1)
#define NCR_RST (1<<0)
#define NSR_SPEED (1<<7)
#define NSR_LINKST (1<<6)
#define NSR_WAKEST (1<<5)
#define NSR_TX2END (1<<3)
#define NSR_TX1END (1<<2)
#define NSR_RXOV (1<<1)
#define TCR_TJDIS (1<<6)
#define TCR_EXCECM (1<<5)
#define TCR_PAD_DIS2 (1<<4)
#define TCR_CRC_DIS2 (1<<3)
#define TCR_PAD_DIS1 (1<<2)
#define TCR_CRC_DIS1 (1<<1)
#define TCR_TXREQ (1<<0)
#define TSR_TJTO (1<<7)
#define TSR_LC (1<<6)
#define TSR_NC (1<<5)
#define TSR_LCOL (1<<4)
#define TSR_COL (1<<3)
#define TSR_EC (1<<2)
#define RCR_WTDIS (1<<6)
#define RCR_DIS_LONG (1<<5)
#define RCR_DIS_CRC (1<<4)
#define RCR_ALL (1<<3)
#define RCR_RUNT (1<<2)
#define RCR_PRMSC (1<<1)
#define RCR_RXEN (1<<0)
#define RSR_RF (1<<7)
#define RSR_MF (1<<6)
#define RSR_LCS (1<<5)
#define RSR_RWTO (1<<4)
#define RSR_PLE (1<<3)
#define RSR_AE (1<<2)
#define RSR_CE (1<<1)
#define RSR_FOE (1<<0)
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot) ( ot & 0xf )
#define IMR_PAR (1<<7)
#define IMR_ROOM (1<<3)
#define IMR_ROM (1<<2)
#define IMR_PTM (1<<1)
#define IMR_PRM (1<<0)
#define ISR_ROOS (1<<3)
#define ISR_ROS (1<<2)
#define ISR_PTS (1<<1)
#define ISR_PRS (1<<0)
#define ISR_CLR_STATUS (ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS)
#define EPCR_REEP (1<<5)
#define EPCR_WEP (1<<4)
#define EPCR_EPOS (1<<3)
#define EPCR_ERPRR (1<<2)
#define EPCR_ERPRW (1<<1)
#define EPCR_ERRE (1<<0)
#define GPCR_GEP_CNTL (1<<0)
/* 原子例程 dm9000内部的phy寄存器 */
#define DM9000_PHY_BMCR 0X00
#define DM9000_PHY_BMSR 0X01
#define DM9000_PHY_PHYID1 0X02
#define DM9000_PHY_PHYID2 0X03
#define DM9000_PHY_ANAR 0X04
#define DM9000_PHY_ANLPAR 0X05
#define DM9000_PHY_ANER 0X06
#define DM9000_PHY_DSCR 0X10
#define DM9000_PHY_DSCSR 0X11
#define DM9000_PHY_10BTCSR 0X12
#define DM9000_PHY_PWDOR 0X13
#define DM9000_PHY_SCR 0X14
#ifdef __cplusplus
extern "C" {
#endif
int rt_hw_dm9000_init(void);
#ifdef __cplusplus
}
#endif
#endif // __DRV_DM9000_H__
#include "drv_dm9000.h"
#define DBG_TAG "dm9k"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <netif/ethernetif.h>
#include "stm32h7xx_hal_sram.h"
// #define DM9000_DEBUG
#ifdef DM9000_DEBUG
#define DM9000_TRACE rt_kprintf
#else
#define DM9000_TRACE(...)
#endif
/* dm9000 reset pin : GPIOD PIN7, LOW is RESET */
#define DM9000_RST_0 rt_pin_write(GET_PIN(A, 10), PIN_LOW)
#define DM9000_RST_1 rt_pin_write(GET_PIN(A, 10), PIN_HIGH)
#define MAX_ADDR_LEN 6 /* max length of hw address */
#define DM9000_PHY 0x40 /* PHY address 0x01 */
#define PIN_NRESET GET_PIN(A, 10) // 复位引脚
#define PIN_IRQ GET_PIN(A, 15) // 例如:PA0 作为中断引脚
enum DM9000_PHY_mode
{DM9000_10MHD = 0, DM9000_100MHD = 1,DM9000_10MFD = 4, DM9000_100MFD = 5,DM9000_AUTO = 8, DM9000_1M_HPNA = 0x10
};
enum DM9000_TYPE
{TYPE_DM9000E,TYPE_DM9000A,TYPE_DM9000B
};
struct rt_dm9000_eth
{/* inherit from ethernet device */struct eth_device parent;
enum DM9000_TYPE type;enum DM9000_PHY_mode mode;
rt_uint8_t packet_cnt; /* packet I or II */rt_uint16_t queue_packet_len; /* queued packet (packet II) */
/* interface address info. */rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */rt_uint8_t init_complete; /* init complete flag */
};
static struct rt_dm9000_eth dm9000_device;
static struct rt_semaphore sem_ack, sem_lock;
int dm_irq_cnt, dm_pkg_max;
// 这个一定要放在全局作用域下
//static SRAM_HandleTypeDef DM9000_Handler; //DM9000句柄
/* --- */
static inline void dm9000_delay_ms(rt_uint32_t ms)
{rt_thread_mdelay(ms); return;
}
/* Read a byte from I/O port */
rt_inline rt_uint16_t dm9000_io_read(rt_uint16_t reg) {DM9000_IO = reg;return DM9000_DATA;
}
/* Write a byte to I/O port */
rt_inline void dm9000_io_write(rt_uint16_t reg, rt_uint16_t value) {DM9000_IO = reg;DM9000_DATA = value;
}
/* Get DeviceID of DM9000 */
static rt_uint32_t dm9000_get_device_id(void)
{rt_uint32_t value;value = dm9000_io_read(DM9000_VIDL);value |= dm9000_io_read(DM9000_VIDH) << 8;value |= dm9000_io_read(DM9000_PIDL) << 16;value |= dm9000_io_read(DM9000_PIDH) << 24;return value;
}
/* Reset DM9000 */
static void dm9000_reset(void) {DM9000_TRACE("enter dm9000_reset\n");DM9000_RST_0; // set rst pin lowdm9000_delay_ms(10);
DM9000_RST_1;dm9000_delay_ms(100); // hardware rst over
dm9000_io_write(DM9000_GPCR, 0x01);dm9000_io_write(DM9000_GPR, 0);dm9000_io_write(DM9000_NCR, (0x02 | NCR_RST)); // soft rst
do{dm9000_delay_ms(25);}while(dm9000_io_read(DM9000_NCR) & 1); // wait for soft rst over
dm9000_io_write(DM9000_NCR,0);dm9000_io_write(DM9000_NCR, (0x02 | NCR_RST)); // soft rst again
do{dm9000_delay_ms(25);}while (dm9000_io_read(DM9000_NCR) & 1);
}
/* Read a word from phyxcer */
rt_inline rt_uint16_t dm9000_phy_read(rt_uint16_t reg) {rt_uint16_t val;
/* Fill the phyxcer register into REG_0C */dm9000_io_write(DM9000_EPAR, DM9000_PHY | reg);dm9000_io_write(DM9000_EPCR, 0x0C); /* Issue phyxcer read command */
dm9000_delay_ms(100); /* Wait read complete */
dm9000_io_write(DM9000_EPCR, 0x00); /* Clear phyxcer read command */val = (dm9000_io_read(DM9000_EPDRH) << 8) | dm9000_io_read(DM9000_EPDRL);
return val;
}
/* Write a word to phyxcer */
rt_inline void dm9000_phy_write(rt_uint16_t reg, rt_uint16_t value)
{/* Fill the phyxcer register into REG_0C */dm9000_io_write(DM9000_EPAR, DM9000_PHY | reg);
/* Fill the written data into REG_0D & REG_0E */dm9000_io_write(DM9000_EPDRL, (value & 0xFF));dm9000_io_write(DM9000_EPDRH, ((value >> 8) & 0xFF));dm9000_io_write(DM9000_EPCR, 0x0A); /* Issue phyxcer write command */
dm9000_delay_ms(500); /* Wait write complete */
dm9000_io_write(DM9000_EPCR, 0x00); /* Clear phyxcer write command */
}
/* Set PHY operationg mode */
rt_inline void dm9000_phy_mode_set(rt_uint8_t mode)
{rt_uint16_t phy_BMCR, phy_ANAR;switch(mode){case DM9000_10MHD:phy_BMCR = 0X0000;phy_ANAR = 0X21;break;case DM9000_10MFD:phy_BMCR = 0X0100;phy_ANAR = 0X41;break;case DM9000_100MHD:phy_BMCR = 0X2000;phy_ANAR = 0X81;break;case DM9000_100MFD:phy_BMCR = 0X2100;phy_ANAR = 0X101;break;case DM9000_AUTO:phy_BMCR = 0X1000;phy_ANAR = 0X01E1;break;}
dm9000_phy_write(DM9000_PHY_BMCR, phy_BMCR);dm9000_phy_write(DM9000_PHY_ANAR, phy_ANAR); /* Set PHY media mode */
dm9000_io_write(DM9000_GPCR, 0x01); /* Let GPIO0 output */dm9000_io_write(DM9000_GPR, 0X00); /* enable PHY */
}
/* interrupt service routine */
void rt_dm9000_isr(void *arg)
{rt_uint16_t int_status;rt_uint16_t last_io;
// rt_uint32_t eint_pend;
last_io = DM9000_IO;
/* Disable all interrupts */dm9000_io_write(DM9000_IMR, IMR_PAR);
/* Got DM9000 interrupt status */int_status = dm9000_io_read(DM9000_ISR); /* Got ISR */dm9000_io_write(DM9000_ISR, int_status); /* Clear ISR status */
DM9000_TRACE("dm9000 isr: int status %04x\n", int_status);
/* receive overflow */if (int_status & ISR_ROS){LOG_W("overflow, ISR:%02x", int_status);}
if (int_status & ISR_ROOS){LOG_W("overflow counter overflow, ISR:%02x", int_status);}
/* Received the coming packet */if (int_status & ISR_PRS){/* a frame has been received */eth_device_ready(&(dm9000_device.parent));dm_irq_cnt ++;}
/* Transmit Interrupt check */if (int_status & ISR_PTS){/* clear int_status */dm9000_io_write(DM9000_ISR, ISR_PTS);
/* transmit done */int tx_status = dm9000_io_read(DM9000_NSR); /* Got TX status */
if (tx_status & (NSR_TX2END | NSR_TX1END)){dm9000_device.packet_cnt --;if (dm9000_device.packet_cnt > 0){DM9000_TRACE("dm9000 isr: tx second packet\n");
/* transmit packet II *//* Set TX length to DM9000 */dm9000_io_write(DM9000_TXPLL, dm9000_device.queue_packet_len & 0xff);dm9000_io_write(DM9000_TXPLH, (dm9000_device.queue_packet_len >> 8) & 0xff);
/* Issue TX polling command */dm9000_io_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */}
/* One packet sent complete *//* clear tx isr */if (sem_ack.value != 0) {LOG_W("isr: trying to release sem_ack while its value > 0 / failed");} else {rt_sem_release(&sem_ack);}}}
/* Re-enable interrupt mask */dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM | ISR_ROS | ISR_ROOS);
DM9000_IO = last_io;
}
static void dm9000_softrst_wait(rt_uint32_t ms)
{dm9000_io_write(DM9000_NCR, NCR_RST);do{rt_thread_mdelay(ms);} while (dm9000_io_read(DM9000_NCR) & 1); /* wait for soft rst over */
/* initialize regs */
/* GPIO0 on pre-activate PHY */dm9000_io_write(DM9000_GPR, 0x00); /* REG_1F bit0 activate phyxcer */dm9000_io_write(DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */dm9000_io_write(DM9000_GPR, 0x00); /* Enable PHY */
/* Set PHY */dm9000_phy_mode_set(dm9000_device.mode);
/* Program operating register */dm9000_io_write(DM9000_NCR, 0x0); /* only intern phy supported by now */dm9000_io_write(DM9000_TCR, 0); /* TX Polling clear */dm9000_io_write(DM9000_BPTR, 0x3f); /* Less 3Kb, 200us */dm9000_io_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); /* Flow Control : High/Low Water */dm9000_io_write(DM9000_FCR, 0x0); /* SH FIXME: This looks strange! Flow Control */dm9000_io_write(DM9000_SMCR, 0); /* Special Mode */dm9000_io_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); /* clear TX status */dm9000_io_write(DM9000_ISR, 0x0f); /* Clear interrupt status */dm9000_io_write(DM9000_TCR2, 0x80); /* Switch LED to mode 1 */
/* Activate DM9000 */dm9000_io_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); /* RX enable */dm9000_io_write(DM9000_IMR, IMR_PAR);
}
/* RT-Thread Device Interface */
/* initialize the interface */
static rt_err_t rt_dm9000_init(rt_device_t dev)
{LOG_I("Driver dm9000 init / start");int i, oft, lnk;rt_uint32_t dm9000_id;
/* RESET device */dm9000_reset();dm9000_delay_ms(100);
/* identfy DM9000 */dm9000_id = dm9000_get_device_id();LOG_I("dm9000 id: 0x%x", dm9000_id);if (dm9000_id != DM9000_ID) {LOG_E("dm9000 id error");return -RT_ERROR;}
/* set mac address */for (i = 0, oft = DM9000_PAR; i < 6; ++i, ++oft)dm9000_io_write(oft, dm9000_device.dev_addr[i]);/* set multicast address */for (i = 0, oft = DM9000_MAR; i < 8; ++i, ++oft)dm9000_io_write(oft, 0xff);
dm9000_softrst_wait(25); /* init regs here */
if (dm9000_device.mode == DM9000_AUTO){i = 0;while (!(dm9000_phy_read(1) & 0x20)){/* autonegation complete bit */rt_thread_delay( RT_TICK_PER_SECOND/10 );i++;if (i > 30 ) /* wait 3s */{LOG_E("could not establish link");return 0;}}}
/* send a notify */eth_device_linkchange(&dm9000_device.parent, RT_TRUE);
/* see what we've got */lnk = dm9000_phy_read(17) >> 12;switch (lnk){case 1:LOG_I("10M half duplex ");break;case 2:LOG_I("10M full duplex ");break;case 4:LOG_I("100M half duplex ");break;case 8:LOG_I("100M full duplex ");break;default:LOG_I("unknown: %d ", lnk);break;}
/* Enable TX/RX interrupt mask */dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM | ISR_ROS | ISR_ROOS);
LOG_I("Driver dm9000 init / end");dm9000_device.init_complete = 1;return RT_EOK;
}
static rt_err_t rt_dm9000_open(rt_device_t dev, rt_uint16_t oflag)
{return RT_EOK;
}
static rt_err_t rt_dm9000_close(rt_device_t dev)
{/* RESET devie */dm9000_phy_write(0, 0x8000); /* PHY RESET */dm9000_io_write(DM9000_GPR, 0x01); /* Power-Down PHY */dm9000_io_write(DM9000_IMR, 0x80); /* Disable all interrupt */dm9000_io_write(DM9000_RCR, 0x00); /* Disable RX */
return RT_EOK;
}
static rt_size_t rt_dm9000_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{rt_set_errno(-RT_ENOSYS);return 0;
}
static rt_size_t rt_dm9000_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{rt_set_errno(-RT_ENOSYS);return 0;
}
static rt_err_t rt_dm9000_control(rt_device_t dev, int cmd, void *args)
{switch (cmd){case NIOCTL_GADDR:/* get mac address */if (args) rt_memcpy(args, dm9000_device.dev_addr, 6);else return -RT_ERROR;break;
default :break;}
return RT_EOK;
}
/* ethernet device interface */
/* transmit packet. */
rt_err_t rt_dm9000_tx( rt_device_t dev, struct pbuf* p)
{
// LOG_D("enter rt_dm9000_tx, p->tot_len: %d\n", p->tot_len);DM9000_TRACE("rt_dm9000_tx: %d\n", p->tot_len);
/* lock DM9000 device */rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
/* disable dm9000a interrupt */dm9000_io_write(DM9000_IMR, IMR_PAR);
/* Move data to DM9000 TX RAM */// DM9000_outb(DM9000_IO_BASE, DM9000_MWCMD);DM9000_IO = DM9000_MWCMD;
{/* q traverses through linked list of pbuf's* This list MUST consist of a single packet ONLY */struct pbuf *q;rt_uint16_t pbuf_index = 0;rt_uint8_t word[2], word_index = 0;
q = p;/* Write data into dm9000a, two bytes at a time* Handling pbuf's with odd number of bytes correctly* No attempt to optimize for speed has been made */while (q){if (pbuf_index < q->len){word[word_index++] = ((u8_t*)q->payload)[pbuf_index++];if (word_index == 2){// DM9000_outw(DM9000_DATA_BASE, (word[1] << 8) | word[0]);DM9000_DATA = (word[1] << 8) | word[0]; // write two bytes to DM9000_DATAword_index = 0;}}else{q = q->next;pbuf_index = 0;}}/* One byte could still be unsent */if (word_index == 1){// DM9000_outw(DM9000_DATA_BASE, word[0]);DM9000_DATA = word[0]; // write one byte to DM9000_DATA}}
// /* Set TX length to DM9000 */
// dm9000_io_write(DM9000_TXPLL, p->tot_len & 0xff);
// dm9000_io_write(DM9000_TXPLH, (p->tot_len >> 8) & 0xff);
//
// /* Issue TX polling command */
// dm9000_io_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
if (dm9000_device.packet_cnt == 0){DM9000_TRACE("dm9000 tx: first packet\n");
dm9000_device.packet_cnt ++;/* Set TX length to DM9000 */dm9000_io_write(DM9000_TXPLL, p->tot_len & 0xff);dm9000_io_write(DM9000_TXPLH, (p->tot_len >> 8) & 0xff);
/* Issue TX polling command */dm9000_io_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */}else{DM9000_TRACE("dm9000 tx: second packet\n");
dm9000_device.packet_cnt ++;dm9000_device.queue_packet_len = p->tot_len;}
/* enable dm9000a all interrupt */dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM | ISR_ROS | ISR_ROOS);
/* unlock DM9000 device */rt_sem_release(&sem_lock);
/* wait ack */rt_sem_take(&sem_ack, RT_WAITING_FOREVER);
DM9000_TRACE("rt_dm9000_tx done\n");
return RT_EOK;
}
/* reception packet. */
struct pbuf *rt_dm9000_rx(rt_device_t dev)
{struct pbuf* p;rt_uint32_t rx_ready; /* first rx byte */rt_uint16_t rx_status, rx_len;rt_uint16_t* data;rt_uint8_t dummy_u8;rt_uint16_t dummy_u16; // used for dummyrt_int32_t len;
/* init p pointer */p = RT_NULL;
/* lock DM9000 device */rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
/* disable dm9000a interrupt */dm9000_io_write(DM9000_IMR, IMR_PAR);
/* Check packet ready or not */// dm9000_io_read(DM9000_MRRH); // 读取这两个寄存器// dm9000_io_read(DM9000_MRRL);dm9000_io_read(DM9000_MRCMDX); /* Dummy read */// rx_ready = DM9000_inb(DM9000_DATA_BASE); /* Got most updated data */rx_ready = (u8)DM9000_DATA; /* Got most updated data */if (rx_ready == 0x01){/* A packet ready now & Get status/length */// DM9000_outb(DM9000_IO_BASE, DM9000_MRCMD);// rx_status = DM9000_inw(DM9000_DATA_BASE) & 0xff00;// rx_len = DM9000_inw(DM9000_DATA_BASE);
DM9000_IO = DM9000_MRCMD;rx_status = DM9000_DATA & 0xff00;rx_len = DM9000_DATA;
DM9000_TRACE("dm9000 rx: status %04x len %d\n", rx_status, rx_len);
/* error handle */if ((rx_status & 0xbf00) || (rx_len < 0x40) || (rx_len > DM9000_PKT_MAX)){LOG_E("rx error: status %04x, rx_len: %d", rx_status, rx_len);
if (rx_status & 0x100){LOG_E("rx fifo error");}if (rx_status & 0x200){LOG_E("rx crc error");}if (rx_status & 0x8000){LOG_E("rx length error");}if (rx_len > DM9000_PKT_MAX){LOG_E("rx length too big");}
/* software-reset and re-init */dm9000_softrst_wait(25);
/* it issues an error, release pbuf */if (p != RT_NULL)pbuf_free(p);p = RT_NULL;goto _rx_end;}
/* allocate buffer */// p = pbuf_alloc(PBUF_LINK, rx_len, PBUF_RAM);rx_len -= 4; // remove 4B CRCp = pbuf_alloc(PBUF_RAW, rx_len, PBUF_POOL);if (p != RT_NULL){// RT_ASSERT(p->type == PBUF_RAM); /* set PBUF_RAM above */// if (p->type == PBUF_RAM) {/* p is one large chunk */
// int i;
// RT_ASSERT(p->next == RT_NULL);// RT_ASSERT(p->len == p->tot_len);
data = (rt_uint16_t*)p->payload;len = p->len;
while (len > 1) {// *data = DM9000_inw(DM9000_DATA_BASE);*data = DM9000_DATA;data++;len -= 2;}
/* just read a byte, protect memory */if (len == 1) {// dummy_u8 = DM9000_inb(DM9000_DATA_BASE);dummy_u8 = (u8)DM9000_DATA;((rt_uint8_t*)p->payload)[p->len - 1] = dummy_u8;}
dummy_u16 = DM9000_DATA;dummy_u16 = DM9000_DATA;
// } else { /* p is not one large chunk */// struct pbuf* q;// rt_int32_t len;
// for (q = p; q != RT_NULL; q= q->next)// {// data = (rt_uint16_t*)q->payload;// len = q->len;
// while (len > 0)// {// *data = DM9000_inw(DM9000_DATA_BASE);// data ++;// len -= 2;// }// }// }}else /* pbuf allocate failed */{LOG_E("dm9000 rx: no pbuf, rx_len:%d", rx_len);len = rx_len;
/* no pbuf, discard data from DM9000 */while (len > 1){// dummy_u16 = DM9000_inw(DM9000_DATA_BASE); /* dummy read 2 bytes */dummy_u16 = DM9000_DATA; /* dummy read 2 bytes */len -= 2;}
/* len == 1, if remaining 1 byte not read */if (len == 1){// dummy_u8 = DM9000_inb(DM9000_DATA_BASE); /* dummy read 1 byte */dummy_u8 = DM9000_DATA;}}}else if (rx_ready > 0x01) /* error, stop interface and wait to reset */{LOG_E("dm9000 rx: rx error, stop device rx_ready:%d", rx_ready);
dm9000_io_write(DM9000_ISR, 0x80); /* Stop INT request */dm9000_io_write(DM9000_ISR, 0x0F); /* Clear ISR status */dm9000_io_write(DM9000_RCR, 0x00); /* Stop Rx Function */
dm9000_softrst_wait(5); /* software-reset and re-init */goto _rx_end;}/*else rx_ready == 0x00, no message should be read */
_rx_end:
/* clear packet received latch status */dm9000_io_write(DM9000_ISR, ISR_PRS);
/* restore receive interrupt */dm9000_io_write(DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM | ISR_ROS | ISR_ROOS);
/* unlock DM9000 device */rt_sem_release(&sem_lock);
return p;
}
static uint32_t FMC_Initialized = 0;
static void DM9000_GPIO_Init(void)
{/* USER CODE BEGIN FMC_MspInit 0 */
__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOF_CLK_ENABLE();/* USER CODE END FMC_MspInit 0 */GPIO_InitTypeDef GPIO_InitStruct = {0};if (FMC_Initialized){return;}FMC_Initialized = 1;
/* Peripheral clock enable */__HAL_RCC_FMC_CLK_ENABLE();
/** FMC GPIO ConfigurationPF0 ------> FMC_A0PE7 ------> FMC_D4PE8 ------> FMC_D5PE9 ------> FMC_D6PE10 ------> FMC_D7PE11 ------> FMC_D8PE12 ------> FMC_D9PE13 ------> FMC_D10PE14 ------> FMC_D11PE15 ------> FMC_D12PD8 ------> FMC_D13PD9 ------> FMC_D14PD10 ------> FMC_D15PD14 ------> FMC_D0PD15 ------> FMC_D1PC7 ------> FMC_NE1PD0 ------> FMC_D2PD1 ------> FMC_D3PD4 ------> FMC_NOEPD5 ------> FMC_NWE*//* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/* GPIO_InitStruct */GPIO_InitStruct.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_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* GPIO_InitStruct */GPIO_InitStruct.Pin = GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF9_FMC;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN FMC_MspInit 1 */
/* USER CODE END FMC_MspInit 1 */
}
SRAM_HandleTypeDef hsram1;
/* FMC initialization function */
void DM9000_FMC_Config(void)
{/* USER CODE BEGIN FMC_Init 0 */
/* USER CODE END FMC_Init 0 */
FMC_NORSRAM_TimingTypeDef Timing = {0};
/* USER CODE BEGIN FMC_Init 1 */DM9000_GPIO_Init();/* USER CODE END FMC_Init 1 */
hsram1.Instance = FMC_NORSRAM_DEVICE;hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;/* hsram1.Init */hsram1.Init.NSBank = FMC_NORSRAM_BANK1;hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16; hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;hsram1.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;hsram1.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;hsram1.Init.WriteFifo = FMC_WRITE_FIFO_DISABLE;hsram1.Init.PageSize = FMC_PAGE_SIZE_NONE;
// 使用的HCLK 120M = 8.3ns/* Timing */Timing.AddressSetupTime = 10; // DM9000手册建议地址建立时间为大于80ns F0寄存器Timing.AddressHoldTime = 0; // 模式A没用上Timing.DataSetupTime = 2; // DM9000手册建议数据建立时间为大于10ns Timing.BusTurnAroundDuration = 2; // 片选信号,高脉宽Timing.CLKDivision = 0; // 模式A没用上Timing.DataLatency = 0; // 模式A没用上Timing.AccessMode = FMC_ACCESS_MODE_A;if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK){Error_Handler();}
/* USER CODE BEGIN FMC_Init 2 *///设置引脚为输入模式(下降沿触发)rt_pin_mode(PIN_IRQ, PIN_MODE_INPUT);rt_pin_attach_irq(PIN_IRQ, PIN_IRQ_MODE_FALLING, rt_dm9000_isr, RT_NULL);rt_pin_irq_enable(PIN_IRQ, PIN_IRQ_ENABLE);/* USER CODE END FMC_Init 2 */
}
// 获取DM9000的连接速度和双工模式
// 返回值: 0,100M半双工
// 1,100M全双工
// 2,10M半双工
// 3,10M全双工
// 0XFF,连接失败!
u8 DM9000_Get_SpeedAndDuplex(void)
{u8 temp;u8 i = 0;if (dm9000_device.mode == DM9000_AUTO) // 如果开启了自动协商模式一定要等待协商完成{while (!(dm9000_phy_read (0X01) & 0X0020)) // 等待自动协商完成{dm9000_delay_ms(10);i++;if (i > 100)return 0XFF; // 自动协商失败}}else // 自定义模式,一定要等待连接成功{while (!(dm9000_io_read(DM9000_NSR) & 0X40)) // 等待连接成功{dm9000_delay_ms(10);i++;if (i > 100)return 0XFF; // 连接失败}}temp = ((dm9000_io_read(DM9000_NSR) >> 6) & 0X02); // 获取DM9000的连接速度temp |= ((dm9000_io_read(DM9000_NCR) >> 3) & 0X01); // 获取DM9000的双工状态return temp;
}
static void phy_linkchange()
{static rt_uint8_t phy_speed = 0;rt_uint8_t temp;
temp = DM9000_Get_SpeedAndDuplex(); // 获取DM9000的连接速度和双工状态if (temp != 0XFF) // 连接成功,通过串口显示连接速度和双工状态{if(phy_speed != temp) // 如果连接状态发生变化{phy_speed = temp;LOG_D("DM9000 Speed:%dMbps,Duplex:%s duplex mode", (temp & 0x02) ? 10 : 100, (temp & 0x01) ? "Full" : "Half");eth_device_linkchange(&dm9000_device.parent, RT_TRUE);}else{return; // 没有变化,直接返回}}else{LOG_D("link down");phy_speed = 0;eth_device_linkchange(&dm9000_device.parent, RT_FALSE);}
}
void phy_state_check(void *arg)
{LOG_D("phy_state_check started");while(1){if (dm9000_device.init_complete == 1) phy_linkchange();rt_thread_mdelay(1000); // 每隔1秒检测一次PHY状态}
}
int rt_hw_dm9000_init(void) {/* stm32 hal lib dm9000 specific init */rt_uint32_t temp;DM9000_FMC_Config(); // FMC配置
/* general dm9000 init */rt_sem_init(&sem_ack, "tx_ack", 0, RT_IPC_FLAG_FIFO); // 同步信号量,初始为0,发送tx后等待中断释放信号量表示tx完成rt_sem_init(&sem_lock, "eth_lock", 1, RT_IPC_FLAG_FIFO); // 互斥信号量,初始为1,用于保护tx和rx过程不冲突dm9000_device.type = TYPE_DM9000A;dm9000_device.mode = DM9000_AUTO;dm9000_device.packet_cnt = 0;dm9000_device.queue_packet_len = 0;
/** SRAM Tx/Rx pointer automatically return to start address,* Packet Transmitted, Packet Received*/// temp = *(volatile rt_uint16_t*)(0x1FFFF7E8); //获取STM32的唯一ID的前24位作为MAC地址后三字节temp = HAL_GetUIDw0();dm9000_device.dev_addr[0] = 0x02;dm9000_device.dev_addr[1] = 0x00;dm9000_device.dev_addr[2] = 0x00;dm9000_device.dev_addr[3] = (temp >> 16) & 0xFF; //低三字节用STM32的唯一IDdm9000_device.dev_addr[4] = (temp >> 8) & 0xFFF;dm9000_device.dev_addr[5] = temp &0xFF;
dm9000_device.parent.parent.init = rt_dm9000_init;dm9000_device.parent.parent.open = rt_dm9000_open;dm9000_device.parent.parent.close = rt_dm9000_close;dm9000_device.parent.parent.read = rt_dm9000_read;dm9000_device.parent.parent.write = rt_dm9000_write;dm9000_device.parent.parent.control = rt_dm9000_control;dm9000_device.parent.parent.user_data = RT_NULL;
dm9000_device.parent.eth_rx = rt_dm9000_rx;dm9000_device.parent.eth_tx = rt_dm9000_tx;eth_device_init(&(dm9000_device.parent), "e0");
rt_thread_t tid;tid = rt_thread_create("phy", phy_state_check, RT_NULL, 512, RT_THREAD_PRIORITY_MAX - 2, 2);rt_thread_startup(tid);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_dm9000_init);
void dm9000a(void)
{rt_kprintf("\n");rt_kprintf("NCR (%02X): %02x\n", DM9000_NCR, dm9000_io_read(DM9000_NCR));rt_kprintf("NSR (%02X): %02x\n", DM9000_NSR, dm9000_io_read(DM9000_NSR));rt_kprintf("TCR (%02X): %02x\n", DM9000_TCR, dm9000_io_read(DM9000_TCR));rt_kprintf("TSRI (%02X): %02x\n", DM9000_TSR1, dm9000_io_read(DM9000_TSR1));rt_kprintf("TSRII (%02X): %02x\n", DM9000_TSR2, dm9000_io_read(DM9000_TSR2));rt_kprintf("RCR (%02X): %02x\n", DM9000_RCR, dm9000_io_read(DM9000_RCR));rt_kprintf("RSR (%02X): %02x\n", DM9000_RSR, dm9000_io_read(DM9000_RSR));rt_kprintf("ORCR (%02X): %02x\n", DM9000_ROCR, dm9000_io_read(DM9000_ROCR));rt_kprintf("CRR (%02X): %02x\n", DM9000_CHIPR, dm9000_io_read(DM9000_CHIPR));rt_kprintf("CSCR (%02X): %02x\n", DM9000_CSCR, dm9000_io_read(DM9000_CSCR));rt_kprintf("RCSSR (%02X): %02x\n", DM9000_RCSSR, dm9000_io_read(DM9000_RCSSR));rt_kprintf("ISR (%02X): %02x\n", DM9000_ISR, dm9000_io_read(DM9000_ISR));rt_kprintf("IMR (%02X): %02x\n", DM9000_IMR, dm9000_io_read(DM9000_IMR));rt_kprintf("pin_G_6: %d\n", rt_pin_read(GET_PIN(G, 6)));rt_kprintf("\n");
}
#ifdef RT_USING_FINSH
#include <finsh.h>
MSH_CMD_EXPORT(dm9000a, dm9000a register dump);
#endif