环球资讯:STM32 + RT-Thread + LwIp + DM9000
2023-06-05 16:56:05 来源:博客园
一、概述开发板:STM32F103ZET6(战舰)RT-Thread:5.0.0LwIp:2.1.2网卡芯片:DM9000编译环境:keil
我简单了解了一下,在嵌入式中,网络芯片的使用方式大致有三种,如下:
(MCU + MAC + PHY)(MUC + MAC) —— PHYMCU —— (MAC + PHY)注意:我用括号里面的表示在同一块芯片中
【资料图】
二、RT-Thread 移植移植 RT-Thread 不是此文章的重点,可以参考一下我之前的笔记,或者直接使用 RT-Thread Studio、STM32CubeMX等工具直接生成,这里我就不过多介绍了
三、添加 LwIp添加使用是需要的API文件
添加内核源码注意:这里的动态内存是使用的RT-Thread中完成的,在 sys_arch.c 文件中完成
添加IPv4或者IPv6需要使用的文件,这里我只用到了IPv4,所以没有添加IPv6的文件、
添加网卡文件注意:ethernet文件在路径 src\netif 中
在 keil 中添加头文件路径 “src\include”
四、添加 LwIp 需要的头文件五、添加驱动文件RT-Thread 已经写好了驱动,我们值需要拷贝就行,不需要更改其中的内容,主要需要的文件有 sys_arch.c、sys_arch.h、ethernetif.c、ethernetif.h。
其中 sys_arch 文件主要实现了 LwIp 在操作系统下需要的功能好书,比如线程的创建、信号量、锁等功能。ethernetif 文件主要实现 LwIp的驱动实现,移植时明白接收线程和发送线程的工作,相对就比较简单了。
完成以上步奏后,编译应该是可以通过的,接下来值需要完成 BSP 程序的实现即可。
六、BSP 驱动文件以上的操作都不需要我们编写代码的,只需要完成 BSP 部分的代码即可,因为我用的是正点原子的战舰开发板,所以这里我直接使用 DM9000 部分的代码,进行简单更改即可。
注册网卡
/** * @brief 注册网卡设备 */static int rt_hw_stm32_eth_init(void){ rt_err_t state = RT_EOK; /* 设置工作方式为自动模式 */ stm32_eth_device.eth_mode = DM9000_AUTO; /* DM9000的SRAM的发送和接收指针自动返回到开始地址,并且开启接收中断 */ stm32_eth_device.imr_all = IMR_PAR | IMR_PRI; /* 前三位可以自定义 */ stm32_eth_device.dev_addr[0] = 0x02; stm32_eth_device.dev_addr[1] = 0x00; stm32_eth_device.dev_addr[2] = 0x00; /* 根据 96 位唯一 ID 生成 MAC 地址(仅用于测试) */ stm32_eth_device.dev_addr[3] = *(rt_uint8_t *)(UID_BASE + 4); stm32_eth_device.dev_addr[4] = *(rt_uint8_t *)(UID_BASE + 2); stm32_eth_device.dev_addr[5] = *(rt_uint8_t *)(UID_BASE + 0); // 初始化组播地址 stm32_eth_device.multicase_addr[0] = 0Xff; stm32_eth_device.multicase_addr[1] = 0Xff; stm32_eth_device.multicase_addr[2] = 0Xff; stm32_eth_device.multicase_addr[3] = 0Xff; stm32_eth_device.multicase_addr[4] = 0Xff; stm32_eth_device.multicase_addr[5] = 0Xff; stm32_eth_device.multicase_addr[6] = 0Xff; stm32_eth_device.multicase_addr[7] = 0Xff; stm32_eth_device.parent.parent.init = rt_stm32_eth_init; stm32_eth_device.parent.parent.open = rt_stm32_eth_open; stm32_eth_device.parent.parent.close = rt_stm32_eth_close; stm32_eth_device.parent.parent.read = rt_stm32_eth_read; stm32_eth_device.parent.parent.write = rt_stm32_eth_write; stm32_eth_device.parent.parent.control = rt_stm32_eth_control; stm32_eth_device.parent.parent.user_data = RT_NULL; stm32_eth_device.parent.eth_rx = rt_stm32_eth_rx; stm32_eth_device.parent.eth_tx = rt_stm32_eth_tx; /* 注册网卡设备 */ state = eth_device_init(&(stm32_eth_device.parent), "e0"); if (RT_EOK != state) { LOG_E("emac device init faild: %d", state); return -RT_ERROR; } /* 初始化DM9000 */ state = DM9000_Init(); if (RT_EOK != state) { LOG_E("DM9000 initialization failed"); return -RT_ERROR; } /* 启动 PHY 监视器, 在线程中完成网卡的配置 */ rt_thread_t tid; tid = rt_thread_create("dm9000", dm9000_monitor_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX - 2, 2); if (tid != RT_NULL) { rt_thread_startup(tid); state = RT_EOK; } else { state = -RT_ERROR; } return state;}INIT_DEVICE_EXPORT(rt_hw_stm32_eth_init);#ifdef RT_USING_FINSH#include static void red_dm9000_reg(int argv, char *argc[]){ if (argv != 2) { rt_kprintf("Please enter the correct command, such as dm9000_ Red 0 \r\n"); return; } rt_kprintf("0x%02X \r\n", DM9000_ReadReg(atoi(argc[1])));}MSH_CMD_EXPORT_ALIAS(red_dm9000_reg, DM9000_read_reg, Read the register value of DM9000);
读取函数的实现
/** * @brief DM9000接收数据包,接收到的数据包存放在DM9000的RX FIFO中,地址为0X0C00~0X3FFF * 接收到的数据包的前四个字节并不是真实的数据,而是有特定含义的,如下 * byte1:表明是否接收到数据,为0x00或者0X01,如果两个都不是的话一定要软件复位DM9000 * 0x01,接收到数据 * 0x00,未接收到数据 * byte2:第二个字节表示一些状态信息,和DM9000的RSR(0X06)寄存器一致的 * byte3:本帧数据长度的低字节 * byte4:本帧数据长度的高字节 * @return pbuf格式的接收到的数据包 */struct pbuf *rt_stm32_eth_rx(rt_device_t dev){ // LOG_E("rt_stm32_eth_rx"); struct pbuf *p; struct pbuf *q; rt_uint8_t rxbyte; volatile rt_uint16_t rx_status, rx_length; rt_uint16_t *data; rt_uint16_t dummy; rt_int32_t len; p = NULL;__error_retry: DM9000_ReadReg(DM9000_MRCMDX); // 假读 rxbyte = (rt_uint8_t)DM9000->DATA; // 进行第二次读取 if (rxbyte) // 接收到数据 { if (rxbyte > 1) // rxbyte大于1,接收到的数据错误,挂了 { LOG_E("dm9000 rx: rx error, stop device\r\n"); DM9000_WriteReg(DM9000_RCR, 0x00); DM9000_WriteReg(DM9000_ISR, 0x80); return (struct pbuf *)p; } DM9000->REG = DM9000_MRCMD; rx_status = DM9000->DATA; rx_length = DM9000->DATA; // if(rx_length>512)printf("rxlen:%d\r\n",rx_length); p = pbuf_alloc(PBUF_RAW, rx_length, PBUF_POOL); // pbufs内存池分配pbuf if (p != NULL) // 内存申请成功 { for (q = p; q != NULL; q = q->next) { data = (rt_uint16_t *)q->payload; len = q->len; while (len > 0) { *data = DM9000->DATA; data++; len -= 2; } } } else // 内存申请失败 { LOG_E("pbuf内存申请失败:%d\r\n", rx_length); data = &dummy; len = rx_length; while (len) { *data = DM9000->DATA; len -= 2; } } // 根据rx_status判断接收数据是否出现如下错误:FIFO溢出、CRC错误 // 对齐错误、物理层错误,如果有任何一个出现的话丢弃该数据帧, // 当rx_length小于64或者大于最大数据长度的时候也丢弃该数据帧 if ((rx_status & 0XBF00) || (rx_length < 0X40) || (rx_length > DM9000_PKT_MAX)) { LOG_E("rx_status:%#x\r\n", rx_status); if (rx_status & 0x100) LOG_E("rx fifo error\r\n"); if (rx_status & 0x200) LOG_E("rx crc error\r\n"); if (rx_status & 0x8000) LOG_E("rx length error\r\n"); if (rx_length > DM9000_PKT_MAX) { LOG_E("rx length too big\r\n"); DM9000_WriteReg(DM9000_NCR, NCR_RST); // 复位DM9000 rt_thread_delay(10); } if (p != NULL) pbuf_free((struct pbuf *)p); // 释放内存 p = NULL; goto __error_retry; } } else { DM9000_WriteReg(DM9000_ISR, ISR_PTS); // 清除所有中断标志位 stm32_eth_device.imr_all = IMR_PAR | IMR_PRI; // 重新接收中断 DM9000_WriteReg(DM9000_IMR, stm32_eth_device.imr_all); } return (struct pbuf *)p;}
写入函数的实现
/** * @brief 通过DM9000发送数据包 * @param dev 设备结构体 * @param p pbuf结构体指针 */rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf *p){ struct pbuf *q; rt_uint16_t pbuf_index = 0; rt_uint8_t word[2], word_index = 0; DM9000_WriteReg(DM9000_IMR, IMR_PAR); // 关闭网卡中断 DM9000->REG = DM9000_MWCMD; // 发送此命令后就可以将要发送的数据搬到DM9000 TX SRAM中 q = p; // 向DM9000的TX SRAM中写入数据,一次写入两个字节数据 // 当要发送的数据长度为奇数的时候,我们需要将最后一个字节单独写入DM9000的TX SRAM中 while (q) { if (pbuf_index < q->len) { word[word_index++] = ((u8_t *)q->payload)[pbuf_index++]; if (word_index == 2) { DM9000->DATA = ((rt_uint16_t)word[1] << 8) | word[0]; word_index = 0; } } else { q = q->next; pbuf_index = 0; } } // 还有一个字节未写入TX SRAM if (word_index == 1) DM9000->DATA = word[0]; // 向DM9000写入发送长度 DM9000_WriteReg(DM9000_TXPLL, p->tot_len & 0XFF); DM9000_WriteReg(DM9000_TXPLH, (p->tot_len >> 8) & 0XFF); // 设置要发送数据的数据长度 DM9000_WriteReg(DM9000_TCR, 0X01); // 启动发送 while ((DM9000_ReadReg(DM9000_ISR) & 0X02) == 0) ; // 等待发送完成 DM9000_WriteReg(DM9000_ISR, 0X02); // 清除发送完成中断 DM9000_WriteReg(DM9000_IMR, stm32_eth_device.imr_all); // DM9000网卡接收中断使能 return ERR_OK;}
七、BSP 驱动文件#include #ifdef BSP_USING_ETH#include "drv_config.h"#include "drv_eth_dm9000.h"#include #include #include #include /* debug option */// #define DRV_DEBUG#define LOG_TAG "drv.dm9000"#include struct rt_stm32_eth{ /* 从 LwIp 中继承 */ struct eth_device parent;#ifndef PHY_USING_INTERRUPT_MODE rt_timer_t poll_link_timer;#endif /* 网卡模式 */ DM9000_PHY_mode eth_mode; /* 中断类型 */ rt_uint8_t imr_all; /* 每个数据包大小 */ rt_uint16_t queue_packet_len; /* 设备 MAC 地址 */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* 组播地址 */ rt_uint8_t multicase_addr[MULTICASE_ADDR_LEN];};/* 网卡设备结构体 */static struct rt_stm32_eth stm32_eth_device;/* 注意:SRAM 句柄不能为局部变量 */SRAM_HandleTypeDef DM9000_Handler; // SRAM句柄/** * @brief 初始化DM9000 */rt_err_t DM9000_Init(void){ int result = RT_EOK; /* 配置 GPIO */ GPIO_InitTypeDef GPIO_InitStruct; /* FSMC */ FSMC_NORSRAM_TimingTypeDef DM9000_Timing; /*----------------------------------------- 使能时钟 -----------------------------------------*/ __HAL_RCC_GPIOD_CLK_ENABLE(); // 使能GPIOD时钟 __HAL_RCC_GPIOE_CLK_ENABLE(); // 使能GPIOE时钟 __HAL_RCC_GPIOF_CLK_ENABLE(); // 使能GPIOF时钟 __HAL_RCC_GPIOG_CLK_ENABLE(); // 使能GPIOG时钟 __HAL_RCC_FSMC_CLK_ENABLE(); // 使能FSMC时钟 __HAL_RCC_AFIO_CLK_ENABLE(); // 使能复用功能时钟 /* PD7 网卡的复位引脚 */ GPIO_InitStruct.Pin = GPIO_PIN_7; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出 GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); /* PD0 1 4 5 8 9 10 14 15复用 */ GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出 HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); /* PG7 8 9 10 11 12 13 14 15复用 */ 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.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出 HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /* PF13复用 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出 HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); /* PG9复用 */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); /*----------------------------------------- 配置 SRAM -----------------------------------------*/ DM9000_Handler.Instance = FSMC_NORSRAM_DEVICE; DM9000_Handler.Extended = FSMC_NORSRAM_EXTENDED_DEVICE; DM9000_Handler.Init.NSBank = FSMC_NORSRAM_BANK2; // 使用NE2 DM9000_Handler.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; // 地址/数据线不复用 DM9000_Handler.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM; // SRAM DM9000_Handler.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; // 16位数据宽度 DM9000_Handler.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; // 是否使能突发访问,仅对同步突发存储器有效,此处未用到 DM9000_Handler.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; // 等待信号的极性,仅在突发模式访问下有用 DM9000_Handler.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; // 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT DM9000_Handler.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; // 存储器写使能 DM9000_Handler.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; // 等待使能位,此处未用到 DM9000_Handler.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE; // 读写使用相同的时序 DM9000_Handler.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; // 是否使能同步传输模式下的等待信号,此处未用到 DM9000_Handler.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; // 禁止突发写 /*----------------------------------------- FSMC 读写时序控制 -----------------------------------------*/ DM9000_Timing.AddressSetupTime = 0x00; // 地址建立时间(ADDSET)为1个HCLK 1/72M = 13.8ns DM9000_Timing.AddressHoldTime = 0x00; // 地址保持时间(ADDHLD)模式A未用到 DM9000_Timing.DataSetupTime = 0x03; // 数据保存时间为3个HCLK = 4*13.8 = 55ns DM9000_Timing.BusTurnAroundDuration = 0X00; DM9000_Timing.CLKDivision = 0X00; DM9000_Timing.DataLatency = 0X00; DM9000_Timing.AccessMode = FSMC_ACCESS_MODE_A; // 模式A /* 使能FSMC的Bank1_Bank1_NORSRAM2 */ if (HAL_SRAM_Init(&DM9000_Handler, &DM9000_Timing, &DM9000_Timing) != HAL_OK) { LOG_E("SDRAM init failed!"); result = -RT_ERROR; } return result;}/** * @brief 向DM9000指定寄存器中写入指定值 * @param reg 要写入的寄存器 * @param data 要写入的值 */void DM9000_WriteReg(rt_uint16_t reg, rt_uint16_t data){ DM9000->REG = reg; DM9000->DATA = data;}/** * @brief 读取DM9000指定寄存器的值 * @param reg 寄存器地址 * @param data DM9000指定寄存器的值 */rt_uint16_t DM9000_ReadReg(rt_uint16_t reg){ DM9000->REG = reg; return DM9000->DATA;}/** * @brief 向DM9000的PHY寄存器写入指定值 * @param reg PHY寄存器 * @param data 要写入的值 */void DM9000_PHY_WriteReg(rt_uint16_t reg, rt_uint16_t data){ DM9000_WriteReg(DM9000_EPAR, DM9000_PHY | reg); DM9000_WriteReg(DM9000_EPDRL, (data & 0xff)); // 写入低字节 DM9000_WriteReg(DM9000_EPDRH, ((data >> 8) & 0xff)); // 写入高字节 DM9000_WriteReg(DM9000_EPCR, 0X0A); // 选中PHY,发送写命令 rt_thread_delay(50); DM9000_WriteReg(DM9000_EPCR, 0X00); // 清除写命令}/** * @brief 读取DM9000的PHY的指定寄存器 * @param reg 要读的PHY寄存器 * @return 返回值:读取到的PHY寄存器值 */rt_uint16_t DM9000_PHY_ReadReg(rt_uint16_t reg){ rt_uint16_t temp; DM9000_WriteReg(DM9000_EPAR, DM9000_PHY | reg); DM9000_WriteReg(DM9000_EPCR, 0X0C); // 选中PHY,发送读命令 rt_thread_delay(10); DM9000_WriteReg(DM9000_EPCR, 0X00); // 清除读命令 temp = (DM9000_ReadReg(DM9000_EPDRH) << 8) | (DM9000_ReadReg(DM9000_EPDRL)); return temp;}/** * @brief 复位DM9000 */void DM9000_Reset(void){ // 复位DM9000,复位步骤参考手册29页 DM9000_RST(0); // DM9000硬件复位 rt_thread_delay(10); DM9000_RST(1); // DM9000硬件复位结束 rt_thread_delay(100); // 一定要有这个延时,让DM9000准备就绪! DM9000_WriteReg(DM9000_GPCR, 0x01); // 第一步:设置GPCR寄存器(0X1E)的bit0为1 DM9000_WriteReg(DM9000_GPR, 0); // 第二步:设置GPR寄存器(0X1F)的bit1为0,DM9000内部的PHY上电 DM9000_WriteReg(DM9000_NCR, (0x02 | NCR_RST)); // 第三步:软件复位DM9000 do { rt_thread_delay(25); } while (DM9000_ReadReg(DM9000_NCR) & 1); // 等待DM9000软复位完成 DM9000_WriteReg(DM9000_NCR, 0); DM9000_WriteReg(DM9000_NCR, (0x02 | NCR_RST)); // DM9000第二次软复位 do { rt_thread_delay(25); } while (DM9000_ReadReg(DM9000_NCR) & 1);}/** * @brief 设置DM9000的MAC地址 * @param macaddr 指向MAC地址 */void DM9000_Set_MACAddress(rt_uint8_t *macaddr){ rt_uint8_t i; for (i = 0; i < 6; i++) { DM9000_WriteReg(DM9000_PAR + i, macaddr[i]); }}/** * @brief 设置DM9000的组播地址 * @param multicastaddr 指向多播地址 */void DM9000_Set_Multicast(rt_uint8_t *multicastaddr){ rt_uint8_t i; for (i = 0; i < 8; i++) { DM9000_WriteReg(DM9000_MAR + i, multicastaddr[i]); }}/** * @brief 设置DM900的PHY工作模式 * @param mode PHY模式 */void DM9000_Set_PHYMode(rt_uint8_t mode){ rt_uint16_t BMCR_Value, ANAR_Value; switch (mode) { case DM9000_10MHD: // 10M半双工 BMCR_Value = 0X0000; ANAR_Value = 0X21; break; case DM9000_10MFD: // 10M全双工 BMCR_Value = 0X0100; ANAR_Value = 0X41; break; case DM9000_100MHD: // 100M半双工 BMCR_Value = 0X2000; ANAR_Value = 0X81; break; case DM9000_100MFD: // 100M全双工 BMCR_Value = 0X2100; ANAR_Value = 0X101; break; case DM9000_AUTO: // 自动协商模式 BMCR_Value = 0X1000; ANAR_Value = 0X01E1; break; } DM9000_PHY_WriteReg(DM9000_PHY_BMCR, BMCR_Value); DM9000_PHY_WriteReg(DM9000_PHY_ANAR, ANAR_Value); DM9000_WriteReg(DM9000_GPR, 0X00); // 使能PHY}/** * @brief 获取DM9000的芯片ID * @return 返回值:DM9000的芯片ID值 */rt_uint32_t DM9000_Get_DeiviceID(void){ rt_uint32_t value; value = DM9000_ReadReg(DM9000_VIDL); value |= DM9000_ReadReg(DM9000_VIDH) << 8; value |= DM9000_ReadReg(DM9000_PIDL) << 16; value |= DM9000_ReadReg(DM9000_PIDH) << 24; return value;}/** * @brief 获取DM9000的连接速度和双工模式 * @return 返回值:0 是 100M半双工 * 1 是 100M全双工 * 2 是 10M半双工 * 3 是 10M全双工 * 0XFF 是连接失败! */rt_uint8_t DM9000_Get_SpeedAndDuplex(void){ rt_uint8_t temp; rt_uint8_t i = 0; if (stm32_eth_device.eth_mode == DM9000_AUTO) // 如果开启了自动协商模式一定要等待协商完成 { while (!(DM9000_PHY_ReadReg(0X01) & 0X0020)) // 等待自动协商完成 { rt_thread_delay(100); i++; if (i > 100) return 0XFF; // 自动协商失败 } } else // 自定义模式,一定要等待连接成功 { while (!(DM9000_ReadReg(DM9000_NSR) & 0X40)) // 等待连接成功 { rt_thread_delay(100); i++; if (i > 100) return 0XFF; // 连接失败 } } temp = ((DM9000_ReadReg(DM9000_NSR) >> 6) & 0X02); // 获取DM9000的连接速度 temp |= ((DM9000_ReadReg(DM9000_NCR) >> 3) & 0X01); // 获取DM9000的双工状态 return temp;}static rt_err_t rt_stm32_eth_init(rt_device_t dev){ return RT_EOK;}static rt_err_t rt_stm32_eth_open(rt_device_t dev, rt_uint16_t oflag){ LOG_D("emac open"); return RT_EOK;}static rt_err_t rt_stm32_eth_close(rt_device_t dev){ LOG_D("emac close"); return RT_EOK;}static rt_size_t rt_stm32_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size){ LOG_D("emac read"); rt_set_errno(-RT_ENOSYS); return 0;}static rt_size_t rt_stm32_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size){ LOG_D("emac write"); rt_set_errno(-RT_ENOSYS); return 0;}static rt_err_t rt_stm32_eth_control(rt_device_t dev, int cmd, void *args){ LOG_D("rt_stm32_eth_control"); switch (cmd) { case NIOCTL_GADDR: /* get mac address */ if (args) { SMEMCPY(args, stm32_eth_device.dev_addr, 6); } else { return -RT_ERROR; } break; default: break; } return RT_EOK;}/** * @brief 通过DM9000发送数据包 * @param dev 设备结构体 * @param p pbuf结构体指针 */rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf *p){ struct pbuf *q; rt_uint16_t pbuf_index = 0; rt_uint8_t word[2], word_index = 0; DM9000_WriteReg(DM9000_IMR, IMR_PAR); // 关闭网卡中断 DM9000->REG = DM9000_MWCMD; // 发送此命令后就可以将要发送的数据搬到DM9000 TX SRAM中 q = p; // 向DM9000的TX SRAM中写入数据,一次写入两个字节数据 // 当要发送的数据长度为奇数的时候,我们需要将最后一个字节单独写入DM9000的TX SRAM中 while (q) { if (pbuf_index < q->len) { word[word_index++] = ((u8_t *)q->payload)[pbuf_index++]; if (word_index == 2) { DM9000->DATA = ((rt_uint16_t)word[1] << 8) | word[0]; word_index = 0; } } else { q = q->next; pbuf_index = 0; } } // 还有一个字节未写入TX SRAM if (word_index == 1) DM9000->DATA = word[0]; // 向DM9000写入发送长度 DM9000_WriteReg(DM9000_TXPLL, p->tot_len & 0XFF); DM9000_WriteReg(DM9000_TXPLH, (p->tot_len >> 8) & 0XFF); // 设置要发送数据的数据长度 DM9000_WriteReg(DM9000_TCR, 0X01); // 启动发送 while ((DM9000_ReadReg(DM9000_ISR) & 0X02) == 0) ; // 等待发送完成 DM9000_WriteReg(DM9000_ISR, 0X02); // 清除发送完成中断 DM9000_WriteReg(DM9000_IMR, stm32_eth_device.imr_all); // DM9000网卡接收中断使能 return ERR_OK;}/** * @brief DM9000接收数据包,接收到的数据包存放在DM9000的RX FIFO中,地址为0X0C00~0X3FFF * 接收到的数据包的前四个字节并不是真实的数据,而是有特定含义的,如下 * byte1:表明是否接收到数据,为0x00或者0X01,如果两个都不是的话一定要软件复位DM9000 * 0x01,接收到数据 * 0x00,未接收到数据 * byte2:第二个字节表示一些状态信息,和DM9000的RSR(0X06)寄存器一致的 * byte3:本帧数据长度的低字节 * byte4:本帧数据长度的高字节 * @return pbuf格式的接收到的数据包 */struct pbuf *rt_stm32_eth_rx(rt_device_t dev){ // LOG_E("rt_stm32_eth_rx"); struct pbuf *p; struct pbuf *q; rt_uint8_t rxbyte; volatile rt_uint16_t rx_status, rx_length; rt_uint16_t *data; rt_uint16_t dummy; rt_int32_t len; p = NULL;__error_retry: DM9000_ReadReg(DM9000_MRCMDX); // 假读 rxbyte = (rt_uint8_t)DM9000->DATA; // 进行第二次读取 if (rxbyte) // 接收到数据 { if (rxbyte > 1) // rxbyte大于1,接收到的数据错误,挂了 { LOG_E("dm9000 rx: rx error, stop device\r\n"); DM9000_WriteReg(DM9000_RCR, 0x00); DM9000_WriteReg(DM9000_ISR, 0x80); return (struct pbuf *)p; } DM9000->REG = DM9000_MRCMD; rx_status = DM9000->DATA; rx_length = DM9000->DATA; // if(rx_length>512)printf("rxlen:%d\r\n",rx_length); p = pbuf_alloc(PBUF_RAW, rx_length, PBUF_POOL); // pbufs内存池分配pbuf if (p != NULL) // 内存申请成功 { for (q = p; q != NULL; q = q->next) { data = (rt_uint16_t *)q->payload; len = q->len; while (len > 0) { *data = DM9000->DATA; data++; len -= 2; } } } else // 内存申请失败 { LOG_E("pbuf内存申请失败:%d\r\n", rx_length); data = &dummy; len = rx_length; while (len) { *data = DM9000->DATA; len -= 2; } } // 根据rx_status判断接收数据是否出现如下错误:FIFO溢出、CRC错误 // 对齐错误、物理层错误,如果有任何一个出现的话丢弃该数据帧, // 当rx_length小于64或者大于最大数据长度的时候也丢弃该数据帧 if ((rx_status & 0XBF00) || (rx_length < 0X40) || (rx_length > DM9000_PKT_MAX)) { LOG_E("rx_status:%#x\r\n", rx_status); if (rx_status & 0x100) LOG_E("rx fifo error\r\n"); if (rx_status & 0x200) LOG_E("rx crc error\r\n"); if (rx_status & 0x8000) LOG_E("rx length error\r\n"); if (rx_length > DM9000_PKT_MAX) { LOG_E("rx length too big\r\n"); DM9000_WriteReg(DM9000_NCR, NCR_RST); // 复位DM9000 rt_thread_delay(10); } if (p != NULL) pbuf_free((struct pbuf *)p); // 释放内存 p = NULL; goto __error_retry; } } else { DM9000_WriteReg(DM9000_ISR, ISR_PTS); // 清除所有中断标志位 stm32_eth_device.imr_all = IMR_PAR | IMR_PRI; // 重新接收中断 DM9000_WriteReg(DM9000_IMR, stm32_eth_device.imr_all); } return (struct pbuf *)p;}/** * @brief DM9000 中断处理函数 */static void eth_phy_isr(void *args){ rt_uint16_t int_status; rt_uint16_t last_io; last_io = DM9000->REG; int_status = DM9000_ReadReg(DM9000_ISR); DM9000_WriteReg(DM9000_ISR, int_status); // 清除中断标志位,DM9000的ISR寄存器的bit0~bit5写1清零 if (int_status & ISR_ROS) rt_kprintf("overflow\r\n"); if (int_status & ISR_ROOS) rt_kprintf("overflow counter overflow \r\n"); if (int_status & ISR_PRS) // 接收中断 { /* 线程中接收数据 */ eth_device_linkchange(&stm32_eth_device.parent, RT_TRUE); // eth_device_linkchange(&stm32_eth_device.parent, RT_FALSE); } if (int_status & ISR_PTS) // 发送中断 { // 发送完成中断,用户自行添加所需代码 } DM9000->REG = last_io; // rt_kprintf("RT_INT.... \r\n");}/** * @brief DM9000 管理线程 */static void dm9000_monitor_thread_entry(void *parameter){ rt_uint32_t temp; /* 设置 PG6 引脚为外部中断 */ rt_pin_mode(PHY_INT_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(PHY_INT_PIN, PIN_IRQ_MODE_FALLING, eth_phy_isr, (void *)"callbackargs"); rt_pin_irq_enable(PHY_INT_PIN, PIN_IRQ_ENABLE); /* 复位DM9000 */ DM9000_Reset(); rt_thread_delay(100); /* 判断是否复位成功 */ temp = DM9000_Get_DeiviceID(); // 获取DM9000ID if (temp != DM9000_ID) // 读取ID错误 { LOG_E("DM9000 reset failed"); return; } LOG_D("DM9000 ID: %#x", temp); /* 设置PHY工作模式 */ DM9000_Set_PHYMode(stm32_eth_device.eth_mode); DM9000_WriteReg(DM9000_NCR, 0X00); DM9000_WriteReg(DM9000_TCR, 0X00); // 发送控制寄存器清零 DM9000_WriteReg(DM9000_BPTR, 0X3F); DM9000_WriteReg(DM9000_FCTR, 0X38); DM9000_WriteReg(DM9000_FCR, 0X00); DM9000_WriteReg(DM9000_SMCR, 0X00); // 特殊模式 DM9000_WriteReg(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); // 清除发送状态 DM9000_WriteReg(DM9000_ISR, 0X0F); // 清除中断状态 DM9000_WriteReg(DM9000_TCR2, 0X80); // 切换LED到mode1 /* 设置MAC地址和组播地址 */ DM9000_Set_MACAddress(stm32_eth_device.dev_addr); // 设置MAC地址 DM9000_Set_Multicast(stm32_eth_device.multicase_addr); // 设置组播地址 DM9000_WriteReg(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); DM9000_WriteReg(DM9000_IMR, IMR_PAR); /* 获取DM9000的连接速度和双工状态 */ temp = DM9000_Get_SpeedAndDuplex(); if (temp != 0XFF) // 连接成功,通过串口显示连接速度和双工状态 { LOG_I("DM9000 Speed:%dMbps,Duplex:%s duplex mode\r\n", (temp & 0x02) ? 10 : 100, (temp & 0x01) ? "Full" : "Half"); } else { LOG_E("DM9000 Establish Link Failed!\r\n"); } /* 设置 DM900 为中断接收方式 */ DM9000_WriteReg(DM9000_IMR, stm32_eth_device.imr_all);}/** * @brief 注册网卡设备 */static int rt_hw_stm32_eth_init(void){ rt_err_t state = RT_EOK; /* 设置工作方式为自动模式 */ stm32_eth_device.eth_mode = DM9000_AUTO; /* DM9000的SRAM的发送和接收指针自动返回到开始地址,并且开启接收中断 */ stm32_eth_device.imr_all = IMR_PAR | IMR_PRI; /* 前三位可以自定义 */ stm32_eth_device.dev_addr[0] = 0x02; stm32_eth_device.dev_addr[1] = 0x00; stm32_eth_device.dev_addr[2] = 0x00; /* 根据 96 位唯一 ID 生成 MAC 地址(仅用于测试) */ stm32_eth_device.dev_addr[3] = *(rt_uint8_t *)(UID_BASE + 4); stm32_eth_device.dev_addr[4] = *(rt_uint8_t *)(UID_BASE + 2); stm32_eth_device.dev_addr[5] = *(rt_uint8_t *)(UID_BASE + 0); // 初始化组播地址 stm32_eth_device.multicase_addr[0] = 0Xff; stm32_eth_device.multicase_addr[1] = 0Xff; stm32_eth_device.multicase_addr[2] = 0Xff; stm32_eth_device.multicase_addr[3] = 0Xff; stm32_eth_device.multicase_addr[4] = 0Xff; stm32_eth_device.multicase_addr[5] = 0Xff; stm32_eth_device.multicase_addr[6] = 0Xff; stm32_eth_device.multicase_addr[7] = 0Xff; stm32_eth_device.parent.parent.init = rt_stm32_eth_init; stm32_eth_device.parent.parent.open = rt_stm32_eth_open; stm32_eth_device.parent.parent.close = rt_stm32_eth_close; stm32_eth_device.parent.parent.read = rt_stm32_eth_read; stm32_eth_device.parent.parent.write = rt_stm32_eth_write; stm32_eth_device.parent.parent.control = rt_stm32_eth_control; stm32_eth_device.parent.parent.user_data = RT_NULL; stm32_eth_device.parent.eth_rx = rt_stm32_eth_rx; stm32_eth_device.parent.eth_tx = rt_stm32_eth_tx; /* 注册网卡设备 */ state = eth_device_init(&(stm32_eth_device.parent), "e0"); if (RT_EOK != state) { LOG_E("emac device init faild: %d", state); return -RT_ERROR; } /* 初始化DM9000 */ state = DM9000_Init(); if (RT_EOK != state) { LOG_E("DM9000 initialization failed"); return -RT_ERROR; } /* 启动 PHY 监视器, 在线程中完成网卡的配置 */ rt_thread_t tid; tid = rt_thread_create("dm9000", dm9000_monitor_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX - 2, 2); if (tid != RT_NULL) { rt_thread_startup(tid); state = RT_EOK; } else { state = -RT_ERROR; } return state;}INIT_DEVICE_EXPORT(rt_hw_stm32_eth_init);#ifdef RT_USING_FINSH#include static void red_dm9000_reg(int argv, char *argc[]){ if (argv != 2) { rt_kprintf("Please enter the correct command, such as dm9000_ Red 0 \r\n"); return; } rt_kprintf("0x%02X \r\n", DM9000_ReadReg(atoi(argc[1])));}MSH_CMD_EXPORT_ALIAS(red_dm9000_reg, DM9000_read_reg, Read the register value of DM9000);#endif /* RT_USING_FINSH */#endif /* BSP_USING_LCD */
标签:
相关阅读
精彩推荐
- 环球资讯:STM32 + RT-Thread + LwIp + DM90002023-06-05
- 全球看点:向向向造句三年级短一点_向向向2023-06-05
- 全球讯息:200多名政协委员“一线协商”,2023-06-05
- 全国举行直肠癌保肛手术视频精英赛 襄阳医2023-06-05
- 财政部门强化财会和预算执行监督 微资讯2023-06-05
- 当前简讯:金至尊铂金多少钱一克(2023年062023-06-05
- 苏富比拍卖臻品钜献:波摩ARC-52“木纹金版2023-06-05
- 中国星辰|为航天员保驾护航 天天新消息2023-06-05
- 星火成炬 |万物有灵 和谐共生_当前报道2023-06-05
- 环球快资讯丨公益微纪录片|众生的地球2023-06-05
- 世界看热讯:环境保护的耀眼“实绩”2023-06-05
- 广州低保标准提高至每人每月1238元 增幅达3.5%2023-06-05
- 焦点精选!宏微科技: 江苏宏微科技股份有2023-06-05
- 山西汾酒: 关于谭忠豹副董事长到龄退休的2023-06-05
- 宏微科技: 中信证券股份有限公司关于江苏2023-06-05
- 保变电气: 保定天威保变电气股份有限公司2023-06-05
- 云维股份: 云维股份公司关于召开2022年度2023-06-05
- 东方明珠: 东方明珠关于召开2022年度业绩2023-06-05
- 汉字找茬王消失的董事长怎么过2023-06-05
- 天天微动态丨hook是什么意思中文_hook是什2023-06-05
- 实时:阿里巴巴旗下一达通商业保理公司增资2023-06-05
- 国考补录6月6日起开始报名 计划招录3249人2023-06-05
- 传闻:《女神异闻录5》衍生游戏“P5T”即将2023-06-05
- 天天通讯!港股异动|创梦天地涨超5%5日累计2023-06-05
- 英可瑞(300713)6月5日主力资金净卖出11512023-06-05
- 姚记科技(002605)6月5日主力资金净买入242023-06-05
- 焦点热门:*ST豆神(300010)6月5日主力资金2023-06-05
- ST德豪(002005)6月5日主力资金净买入271.2023-06-05
- ST华铁(000976)6月5日主力资金净买入651.2023-06-05
- 全球滚动:ST恒久(002808)6月5日主力资金2023-06-05