STM32F107VC的ETH以太网外设+DP83848无法发送和接收数据包的解决办法

5 篇文章 0 订阅

转载自:https://www.itdaan.com/blog/2017/10/14/8f74ff1d3a7ffb59bae43aa29892c659.html

  1. 必须根据DP83848的自动协商结果配置ETH_MACCR的DM(duplex mode)和FES(fast ethernet speed)位。

网线上数据发送线和接收线是分开的。发送用的是白橙(正线)、橙(负线)这对双绞线,而接收用的是白绿(正线)、绿(负线)这对双绞线。

当以太网集线器上只插了两根网线时,一定不会产生碰撞,既可以配置为半双工也可以配置为全双工,配置为半双工也就意味着强制不允许发送和接收同时进行。如果集线器上插了三台及以上的电脑,则有可能产生碰撞,此时只能配置为半双工模式。

  1. 必须在RCC中同时打开ETH的三个RCC时钟,哪怕只想发送数据,也必须打开MACRX的时钟。

注意:以太网外设的DMA是专用DMA,与其他外设所用的DMA1和DMA2没有关系。

RCC->AHBENR |= RCC_AHBENR_ETHMACEN | RCC_AHBENR_ETHMACTXEN | RCC_AHBENR_ETHMACRXEN;
3. 发送描述符指向的缓冲区地址TDES2和TDES3必须为SRAM中的地址(无需对齐),不允许为Flash中的地址。大小TBS1和TBS2为0~8191中的任意整数。
在lwip协议栈中如果遇到q->payload指向Flash区域的情况,必须把数据复制到SRAM中,否则帧肯定会发送失败。

uint8_t tbuf[768];
uint16_t tbuf_used = 0;

desc_tx_ptr[1] = q->len;
desc_tx_ptr[2] = (uint32_t)q->payload;

if ((desc_tx_ptr[2] & 0xffff0000) != 0x20000000)
{
// must be in the 64KB SRAM
printf(“Data 0x%08x isn’t in SRAM!\n”, desc_tx_ptr[2]);
desc_tx_ptr[2] = (uint32_t)tbuf + tbuf_used;
memcpy((void *)desc_tx_ptr[2], q->payload, q->len);
tbuf_used += q->len;
}
4. 注意正确设置FS和LS的值。当发送描述符列表循环使用时,不要忘了清除FS和LS位,例如下面程序中的两个else分支不能去掉:
if (q == p)
desc_tx_ptr[0] |= ETH_TDES0_FS;
else
desc_tx_ptr[0] &= ~ETH_TDES0_FS; // 注意: 描述符是循环使用的, 因此不要忘记清除FS和LS位
if (q->next == NULL)
desc_tx_ptr[0] |= ETH_TDES0_LS;
else
desc_tx_ptr[0] &= ~ETH_TDES0_LS;
【示例程序】

main.c:

#include <stdio.h>
#include <stm32f10x.h>
#include “ETH.h”

// 要发送的数据包内容(数据链路层)
uint8_t packet[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x42, 0x52, 0x4d, 0x4e, 0x45, 0x54, 0x08, 0x06, 0x00, 0x01,
0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x42, 0x52, 0x4d, 0x4e, 0x45, 0x54, 0xc0, 0xa8, 0x1e, 0x0a,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x1e, 0x0a
}; // 192.168.30.10

// 发送、接收描述符
uint32_t desc_tx[10][4];
uint32_t desc_rx[10][4];

// 接收缓冲区
uint8_t rx_buffer[10][2][256];

// 以16进制格式显示数据包内容
void dump_data(const void *data, uint16_t len)
{
const uint8_t *p = data;
while (len–)
printf("%02X", *p++);
printf("\n");
}

// 用于printf, 项目属性必须勾选Use MicroLIB后才能用
int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == ‘\n’)
{
while ((USART3->SR & USART_SR_TXE) == 0);
USART3->DR = ‘\r’;
}
while ((USART3->SR & USART_SR_TXE) == 0);
USART3->DR = ch;
}
return ch;
}

/* 读写PHY上的寄存器, 具体请参阅DP83848手册中的寄存器表, 以及STM32参考手册中的29.4.1 Station management interface: SMI /
/
PHY模块的地址为0x01 */
uint16_t read_reg(uint8_t addr)
{
ETH->MACMIIAR = (1 << ETH_MACMIIAR_PA_Pos) | (addr << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_CR_Div42 | ETH_MACMIIAR_MB;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB);
return ETH->MACMIIDR;
}

void write_reg(uint8_t addr, uint16_t value)
{
ETH->MACMIIDR = value;
ETH->MACMIIAR = (1 << ETH_MACMIIAR_PA_Pos) | (addr << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_CR_Div42 | ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB);
}

/* 查看PHY各寄存器的值 (串口发送b) */
void read_regs(void)
{
uint8_t i;
for (i = 0x00; i <= 0x1d; i++)
printf(“Register 0x%02x: 0x%04x\n”, i, read_reg(i));
}

/* 查看desc_rx的内容 (串口发送c) */
void disp_rx(void)
{
uint8_t i;
for (i = 0; i < sizeof(desc_rx) / sizeof(desc_rx[0]); i++)
printf(“Buffer %d: 0x%08x 0x%08x 0x%08x 0x%08x\n”, i, desc_rx[i][0], desc_rx[i][1], desc_rx[i][2], desc_rx[i][3]);
}

// STM32官方的ETH库en.stsw-stm32045(官网可下载)中的ETH_Init函数可自动完成这一步骤
void auto_negotiation(void)
{
uint16_t value;
while ((read_reg(DP83848_BMSR) & DP83848_BMSR_LS) == 0); // 等待网线插好

value = read_reg(DP83848_BMCR);
if ((value & DP83848_BMCR_ANE) == 0) // 若DP83848外部接线没有接成自动协商模式
write_reg(DP83848_BMCR, value | DP83848_BMCR_ANE); // 则手动执行自动协商
while ((read_reg(DP83848_BMSR) & DP83848_BMSR_ANC) == 0); // 等待自动协商完毕

// 根据自动协商结果配置MACCR寄存器
value = read_reg(DP83848_PHYSTS);
if (value & DP83848_PHYSTS_DS)
ETH->MACCR |= ETH_MACCR_DM;
if ((value & DP83848_PHYSTS_SS) == 0)
ETH->MACCR |= ETH_MACCR_FES;
}

int main(void)
{
uint8_t i;

RCC->APB1ENR = RCC_APB1ENR_USART3EN | RCC_APB1ENR_UART5EN;
RCC->APB2ENR = RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;

/* 请参阅 29.3 Ethernet pins */
GPIOA->CRL = 0x44444f44; // PA2: MDIO复用开漏输出(因为已经接了外部上拉电阻)
GPIOA->CRH = 0x8884444b; // PA8: MCO复用推挽输出
GPIOB->CRH = 0x44bbbb44; // PB10: 串口3的发送引脚, PB11~13: TX_EN, TXD0~1, 全部设为复用推挽输出
GPIOC->CRL = 0x444444b4; // PC1: MDC复用推挽输出
// 其余I/O均为默认值
// 串口5的接收引脚PD2为浮空输入(默认值, 无需配置)

// 给DP83848提供50MHz时钟后, 网卡接口上的灯才会亮, PA8要和PA1接到一起
RCC->CFGR2 |= RCC_CFGR2_PLL3MUL_3; // PLL3CLK: 50MHz
RCC->CR |= RCC_CR_PLL3ON;
while ((RCC->CR & RCC_CR_PLL3RDY) == 0);
RCC->CFGR |= RCC_CFGR_MCO_3 | RCC_CFGR_MCO_1 | RCC_CFGR_MCO_0; // MCO(PA8)=PLL3CLK

AFIO->MAPR = AFIO_MAPR_MII_RMII_SEL | AFIO_MAPR_ETH_REMAP; // 使用RMII接口, 打开ETH Remap
RCC->AHBENR |= RCC_AHBENR_ETHMACEN | RCC_AHBENR_ETHMACTXEN | RCC_AHBENR_ETHMACRXEN; // ETH的三个时钟必须全部打开, 否则ETH_DMA将无法工作

// 用USART3发送字符(PB10), UART5接收字符(PD2)
USART3->BRR = 312;
USART3->CR1 = USART_CR1_UE | USART_CR1_TE;
UART5->BRR = 312;
UART5->CR1 = USART_CR1_UE | USART_CR1_RE | USART_CR1_RXNEIE; // 开串口接收中断
NVIC_EnableIRQ(UART5_IRQn);
printf(“STM32F107VC Ethernet\n”);

// PHY自动协商(非常重要!!!!)
// 根据自动协商结果配置ETH_MACCR的DM(duplex mode)和FES(fast ethernet speed)位
auto_negotiation();

ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE; // 允许发送/接收
ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_AISE | ETH_DMAIER_TIE | ETH_DMAIER_RIE | ETH_DMAIER_RBUIE; // 打开发送/接收完毕中断, 以及接收缓冲满的中断
NVIC_EnableIRQ(ETH_IRQn);

desc_tx[0][0] = ETH_TDES0_OWN | ETH_TDES0_IC | ETH_TDES0_TER | ETH_TDES0_LS | ETH_TDES0_FS; // IC必须为1, 否则无法触发发送完成中断
desc_tx[0][1] = sizeof(packet); // 大小(任意)
desc_tx[0][2] = (uint32_t)packet; // 要发送的数据包 (必须在SRAM中的任意地址, 不能在Flash中!!!)
if ((desc_tx[0][2] & 0xffff0000) != 0x20000000)
printf(“Error: Data must be in SRAM!\n”);
ETH->DMATDLAR = (uint32_t)desc_tx; // 设置发送描述符的首地址

// 初始化接收描述符
for (i = 0; i < sizeof(desc_rx) / sizeof(desc_rx[0]); i++)
{
desc_rx[i][0] = ETH_RDES0_OWN;
desc_rx[i][1] = (sizeof(rx_buffer[0][0]) << ETH_RDES1_RBS2_Pos) | (sizeof(rx_buffer[0][0]) << ETH_RDES1_RBS1_Pos);
desc_rx[i][2] = (uint32_t)rx_buffer[i][0];
desc_rx[i][3] = (uint32_t)rx_buffer[i][1];
}
desc_rx[i - 1][1] |= ETH_RDES1_RER;
ETH->DMARDLAR = (uint32_t)desc_rx;

printf(“ETH->DMATDLAR=0x%08x, ETH->DMARDLAR=0x%08x\n”, ETH->DMATDLAR, ETH->DMARDLAR); // 描述符的首地址必须是32位对齐!

ETH->DMAOMR |= ETH_DMAOMR_TSF | ETH_DMAOMR_RSF | ETH_DMAOMR_ST | ETH_DMAOMR_SR; // 开始发送和接收
while (1)
__WFI();
}

void ETH_IRQHandler(void)
{
printf(“ETH->DMASR=0x%08x\n”, ETH->DMASR);
if (ETH->DMASR & ETH_DMASR_TS) // 发送完毕
{
ETH->DMASR = ETH_DMASR_NIS | ETH_DMASR_TS; // 写1清除标志位
printf(“Transmitted! desc_tx[0]: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n”, desc_tx[0][0], desc_tx[0][1], desc_tx[0][2], desc_tx[0][3]);
}
else if (ETH->DMASR & ETH_DMASR_RS) // 接收完毕
{
ETH->DMASR = ETH_DMASR_NIS | ETH_DMASR_RS;
printf(“Received!\n”);
}
else if (ETH->DMASR & ETH_DMASR_RBUS) // 接收缓冲满
{
ETH->DMASR = ETH_DMASR_AIS | ETH_DMASR_RBUS;
printf(“Receive buffer unavailable!\n”);
}
}

void UART5_IRQHandler(void)
{
uint8_t data = UART5->DR;
if (data == ‘a’)
printf(“ETH->DMASR=0x%08x\n”, ETH->DMASR);
else if (data == ‘b’)
read_regs();
else if (data == ‘c’)
disp_rx();
}

ETH.h:
// 补充stm32f10x.h中缺少的一些寄存器位的定义
#define _BV(n) (1u << (n))

#define ETH_MACMIIAR_PA_Pos 11
#define ETH_MACMIIAR_MR_Pos 6

#define ETH_TDES0_OWN _BV(31)
#define ETH_TDES0_IC _BV(30)
#define ETH_TDES0_LS _BV(29)
#define ETH_TDES0_FS _BV(28)
#define ETH_TDES0_TER _BV(21)
#define ETH_TDES0_TCH _BV(20)

#define ETH_RDES0_OWN _BV(31)

#define ETH_RDES1_DIC _BV(31)
#define ETH_RDES1_RBS2_Pos 16
#define ETH_RDES1_RER _BV(15)
#define ETH_RDES1_RCH _BV(14)
#define ETH_RDES1_RBS1_Pos 0

// DP83848中的一些寄存器位
#define DP83848_BMCR 0x00 // Basic Mode Control Register
#define DP83848_BMCR_ANE _BV(12) // Auto-Negotiation Enable

#define DP83848_BMSR 0x01 // Basic Mode Status Register
#define DP83848_BMSR_ANC _BV(5) // Auto-Negotiation Complete
#define DP83848_BMSR_LS _BV(2) // Link Status

#define DP83848_PHYSTS 0x10 // PHY Status Register
#define DP83848_PHYSTS_DS _BV(2) // Duplex
#define DP83848_PHYSTS_SS _BV(1) // Speed10
【程序运行结果】

STM32F107VC Ethernet
ETH->DMATDLAR=0x20000030, ETH->DMARDLAR=0x200000d0
Transmitted! desc_tx[0]: 0x70200000, 0x0000002a, 0x20000000, 0x00000000
ETH->DMASR=0x00670404
received!
received!
received!
received!
received!
received!
received!
received!
received!
received!
receive buffer unavailable!
receive buffer unavailable!
receive buffer unavailable!
receive buffer unavailable!
receive buffer unavailable!
receive buffer unavailable!

如果没有打开MACRX时钟,则无法触发发送完毕中断。
如果没有配置好STM32中的以太网的速度(FES位)和半/全双工模式(DM位),则只能触发发送完毕中断,不能触发接收完毕中断。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值