W5100 正在发送垃圾

W5100 is sending garbage

我尝试使用 W5100 Ethernet Controller 和 XMega 实现 Web 界面,但我的浏览器打印出这个奇怪的结果:

请看我的代码:

SPIM_Config_t Config_SPIM = {
    .Device = &SPIC,
    .Mode = SPI_MODE_0,
    .Prescaler = SPI_PRESCALER_64,
};

W5100_Config_t Config_Ethernet = {
    .Submask = {255, 255, 0, 0},
    .IP = {169, 254, 133, 121},
    .Gateway = {169, 154, 133, 129},
    .MAC = {0x00, 0x00, 0x00, 0x00, 0x00, 0xAA}
};

uint8_t Rx_Buffer[2048];
uint8_t Tx_Buffer[2048];

const char HTTP[]  =    "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"
                        "<html>\r\n"
                            "<body>\r\n"
                                "<title>Title</title>\r\n"
                                "<p>Hello world</p>\r\n"
                            "</body>\r\n"
                        "</html>\r\n";

int main(void)
{
    W5100_Init(&Config_SPIM, &Config_Ethernet);

    while(1) 
    {
        W5100_Status_t Status;
        W5100_GetState(0, &Status);

        switch(Status)
        {
            case W5100_SOCK_CLOSED:
            {
                if(W5100_Open(0, W5100_PROT_TCP, 80, W5100_MEM_2K, W5100_MEM_2K, 65535) == W5100_NO_ERROR)
                {
                    W5100_Listen(0, ETHERNET_TIMEOUT);
                }

                break;
            }
            case W5100_SOCK_ESTABLISHED:
            {
                uint16_t Rx_Bytes;

                if(W5100_GetBytes(0, &Rx_Bytes) == W5100_NO_ERROR)
                {
                    if(Rx_Bytes)
                    {
                        W5100_Receive(0, Rx_Buffer, Rx_Bytes);

                        strcpy((char*)Tx_Buffer, HTTP);
                        W5100_Send(0, Tx_Buffer, strlen((char*)HTTP), ETHERNET_TIMEOUT);
                    }
                    else
                    {
                        
                    }
                }

                W5100_Disconnect(0, ETHERNET_TIMEOUT);

                break;
            }
            case W5100_SOCK_FIN_WAIT:
            case W5100_SOCK_CLOSING:
            case W5100_SOCK_TIME_WAIT:
            case W5100_SOCK_CLOSE_WAIT:
            case W5100_SOCK_LAST_ACK:
            {
                W5100_Close(0, ETHERNET_TIMEOUT);

                break;
            }
        }
    }
}

我认为错误出在我的 W5100_Send 函数中,似乎控制器正在发送不同内存位置的内容,但我无法找出错误。基于以太网控制器数据表的代码:

W5100_ErrorCode_t W5100_Send(uint8_t Socket, uint8_t* Buffer, uint16_t Length, uint32_t Timeout)
{
    uint8_t Temp[2];
    uint8_t Mask;
    uint16_t SocketBase;
    uint16_t Offset;
    uint16_t Free;
    uint16_t SocketMemory;
    uint32_t Timeout_Temp = Timeout;

    if(!_W5100_IsInitialized)
    {
        return W5100_NOT_INITIALIZED;
    }
    else if((Socket > 0x04) || (Buffer == NULL) || (Length == 0x00))
    {
        return W5100_INVALID_PARAM;
    }

    // Get the memory mask for address calculation
    W5100_ReadRegister(W5100_REGISTER_TMSR, &Mask);
    Mask &= (0x03 << (Socket << 0x01));

    // Check for invalid memory by comparing the memory mask for the given socket and the socket index
    if(((Socket > 0) && (Mask == 3)) || ((Socket > 1) && (Mask == 2)))
    {
        return W5100_INVALID_PARAM;
    }

    SocketBase = W5100_SOCKET_ADDR(Socket);
    SocketMemory = W5100_SOCKET_MEM_OFFSET << Mask;

    // Wait while the buffer is full
    do
    {
        // Get the free bytes
        W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_FSR0, &Temp[0]);
        W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_FSR1, &Temp[1]);
        Free = ((uint16_t)(Temp[0] << 0x08)) | Temp[1];

        if(Timeout_Temp-- == 0x00)
        {
            W5100_Disconnect(Socket, Timeout);

            return W5100_TIMEOUT;
        }

        _delay_ms(1);
    }while(Free < Length);

    // Get the write pointer address
    W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_WR0, &Temp[0]);
    W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_WR1, &Temp[1]);
    Offset = (((uint16_t)(Temp[0] << 0x08)) | Temp[1]) & W5100_TX_MEM_MASK;

    // Check for an overflow
    if(Offset + Length > SocketMemory)
    {
        uint16_t Upper;
        uint16_t Left;

        Upper = SocketMemory - Offset;
        Left = Length - Upper;

        W5100_WriteMemory(W5100_TX_BUFFER_BASE + (SocketMemory * Socket) + Offset, Buffer, Upper);
        W5100_WriteMemory(W5100_TX_BUFFER_BASE + (SocketMemory * Socket), Buffer, Left);
    }
    else
    {
        W5100_WriteMemory(W5100_TX_BUFFER_BASE + (SocketMemory * Socket) + Offset, Buffer, Length);
    }

    W5100_WriteRegister(SocketBase + W5100_OFFSET_TX_WR0, Offset >> 0x08);
    W5100_WriteRegister(SocketBase + W5100_OFFSET_TX_WR1, Offset & 0xFF);

    return W5100_ExecuteCommand(Socket, W5100_CMD_SEND, Timeout);
}

你应该完全重写你的 W5100_Send,因为它充满了问题。

例如,Mask 值的计算没有任何意义。

等待Free值的循环总是延迟至少1毫秒,即使从一开始就获得了好的值。此外,当超时时,它会中断,即使收到 Free 值是好的。

Offset 值被 & 操作损坏:

Offset = (((uint16_t)(Temp[0] << 0x08)) | Temp[1]) & W5100_TX_MEM_MASK;

这个值永远不会增加写入的数据大小,损坏的值被写回W5100_OFFSET_TX_WR1:W5100_OFFSET_TX_WR0

wrapping数据写入出错:

        W5100_WriteMemory(W5100_TX_BUFFER_BASE + (SocketMemory * Socket) + Offset, Buffer, Upper);
        W5100_WriteMemory(W5100_TX_BUFFER_BASE + (SocketMemory * Socket), Buffer, Left);

您正在从 Buffer 开始复制到两个部分。在第二行应该是 &Buffer[Upper]

等等……

首先您需要确定套接字的大小。我鼓励您从一开始就设置套接字大小,从而避免在运行时计算偏移量和大小。

但是如果你想动态确定套接字大小,那么你可以这样做:

    uint16_t SocketBufAddr = W5100_TX_BUFFER_BASE; // Start of the socket memory block
    SocketMemory = 0; // Size of the socket memory block
    W5100_ReadRegister(W5100_REGISTER_TMSR, &Mask);
    for (uint8_t i = 0 ; i <= Socket ; i++) {
        SocketBufAddr += SocketMemory; // Increase the offset by the previous socket size
        SocketMemory = 1024 << ((Mask >> (i * 2)) & 3);
    }

现在写的过程应该是这样的:

// Get the write pointer address
    W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_WR0, &Temp[0]);
    W5100_ReadRegister(SocketBase + W5100_OFFSET_TX_WR1, &Temp[1]);
    uint16_t WrPointer = (((uint16_t)(Temp[0] << 0x08)) | Temp[1]); // no & operation! It is the 16-bit pointer!!!

    
    Offset = WrPointer & (SocketMemory - 1); // Offset inside the socket memory block. SocketMemory is always = 2^n

    // Check for an overflow
    if(Offset + Length > SocketMemory)
    {
        uint16_t Upper;
        uint16_t Left;

        Upper = SocketMemory - Offset ;
        Left = Length - Upper;

        W5100_WriteMemory(SocketBufAddr + Offset, Buffer, Upper);
        W5100_WriteMemory(SocketBufAddr, &Buffer[Upper], Left);
    }
    else
    {
        W5100_WriteMemory(SocketBufAddr + Offset, Buffer, Length);
    }

    WrPointer += Length; // Increase full 16-bit pointer value

    // Write the new pointer back
    W5100_WriteRegister(SocketBase + W5100_OFFSET_TX_WR0, WrPointer >> 0x08);
    W5100_WriteRegister(SocketBase + W5100_OFFSET_TX_WR1, WrPointer & 0xFF);

    return W5100_ExecuteCommand(Socket, W5100_CMD_SEND, Timeout);