Hi dkay,
The random register read/write operations work normally.
My original code was this:
/**************************************************************************//**
* @brief
* Get received data from a UDP socket.
* @param[in] socket
* Number of socket (0..3).
* @param[out] recvdata
* Address of the buffer which receives the data.
* @param[in] recvmax
* Maximum number of bytes to read (practically the destination buffer
* size). If the amount of data is more than this parameter, the first
* recvmax bytes will be copied into the buffer, the others will be
* discarded. Set to zero to disable this feature.
* @param[out] info
* Points to a receive info structure which will be filled up with
* information about the action.
* @return
* TRUE on success, otherwise false.
*****************************************************************************/
bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
uint16_t sbaddr = getSocketRegBaseAddress(socket);
uint16_t recvlen, rx_rd, start, data_ahead;
uint8_t data[UDP_HEADER_SIZE];
/* check for errors */
if (rxMemory[socket].size == 0) return false; // socket is unusable
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2); // read received data size
recvlen = (data[0] << 8) + data[1]; // (big-endian)
if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false; // no data or error
/* initialize */
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // read read pointer
rx_rd = (data[0] << 8) + data[1]; // (big-endian)
start = rxMemory[socket].base + rx_rd % rxMemory[socket].size; // compute starting address
/* read header (circular read from the Rx buffer) */
/* the 8-byte header structure:
* offset 0..3: source IP address
* offset 4..5: source port number
* offset 6..7: data size (in bytes)
*/
data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < UDP_HEADER_SIZE) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead); // read the first part of the header from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead); // read the remaining part of header restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE); // read all
}
start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // update starting address
/* process header */
memcpy(info->srcip, data, 4); // source IP address
info->srcport = (data[4] << 8) + data[5]; // source port number (big-endian)
data_ahead = (data[6] << 8) + data[7]; // data length (big-endian), data_ahead re-used
info->rxlen = data_ahead;
info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);
/* read data (circular read from the Rx buffer) */
if (recvmax != 0) // determine the actual number of data to read
info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
else
info->rxlen_act = recvlen - UDP_HEADER_SIZE;
data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < info->rxlen_act) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead); // read the first part of data from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead); // read the remaining part of data restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act); // read all
}
/* finalize and indicate that Rx is processed */
rx_rd += recvlen;
data[0] = rx_rd >> 8;
data[1] = rx_rd & 0xFF;
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // re-write read pointer
data[0] = SCR_RECV; // receive processed indicator command
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);
return true;
} /* W3150AP_udpRecv */
Before reading your message, I implemented a version that uses the UDP header size parameter (as the datasheet suggests) if the RX_RSR value is smaller than it. This works fine.
bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
uint16_t sbaddr = getSocketRegBaseAddress(socket);
uint16_t recvlen, rx_rd, start, data_ahead;
uint8_t data[UDP_HEADER_SIZE];
/* check for errors */
if (rxMemory[socket].size == 0) return false; // socket is unusable
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2); // read received data size
recvlen = (data[0] << 8) + data[1]; // (big-endian)
if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false; // no data or error
/* initialize */
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // read read pointer
rx_rd = (data[0] << 8) + data[1]; // (big-endian)
start = rxMemory[socket].base + rx_rd % rxMemory[socket].size; // compute starting address
/* read header (circular read from the Rx buffer) */
/* the 8-byte header structure:
* offset 0..3: source IP address
* offset 4..5: source port number
* offset 6..7: data size (in bytes)
*/
data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < UDP_HEADER_SIZE) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead); // read the first part of the header from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead); // read the remaining part of header restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE); // read all
}
start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // update starting address
/* process header */
memcpy(info->srcip, data, 4); // source IP address
info->srcport = (data[4] << 8) + data[5]; // source port number (big-endian)
data_ahead = (data[6] << 8) + data[7]; // data length (big-endian), data_ahead re-used
info->rxlen = data_ahead;
info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);
/* read data (circular read from the Rx buffer) */
if (info->herror < 0) { // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
if (recvmax != 0) // determine the actual number of data to read
info->rxlen_act = (info->rxlen > recvmax) ? recvmax : info->rxlen;
else
info->rxlen_act = info->rxlen;
} else { // normal operation
if (recvmax != 0) // determine the actual number of data to read
info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
else
info->rxlen_act = recvlen - UDP_HEADER_SIZE;
}
data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < info->rxlen_act) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead); // read the first part of data from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead); // read the remaining part of data restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act); // read all
}
/* finalize and indicate that Rx is processed */
if (info->herror < 0) // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
rx_rd += info->rxlen_act + UDP_HEADER_SIZE;
else // normal operation
rx_rd += recvlen;
data[0] = rx_rd >> 8;
data[1] = rx_rd & 0xFF;
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // re-write read pointer
data[0] = SCR_RECV; // receive processed indicator command
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);
return true;
} /* W3150AP_udpRecv */
Then I implemented the multiple RX_RSR reading suggested by you, and it also works well.
bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
uint16_t sbaddr = getSocketRegBaseAddress(socket);
uint16_t recvlen, rx_rd, start, data_ahead;
uint8_t data[UDP_HEADER_SIZE];
/* check for errors */
if (rxMemory[socket].size == 0) return false; // socket is unusable
{
uint16_t x1, x2;
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
do {
x2 = x1;
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
} while (x2 != x1);
recvlen = (((uint8_t*) &x1)[0] << 8) + ((uint8_t*) &x1)[1];
}
if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false; // no data or error
/* initialize */
SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // read read pointer
rx_rd = (data[0] << 8) + data[1]; // (big-endian)
start = rxMemory[socket].base + rx_rd % rxMemory[socket].size; // compute starting address
/* read header (circular read from the Rx buffer) */
/* the 8-byte header structure:
* offset 0..3: source IP address
* offset 4..5: source port number
* offset 6..7: data size (in bytes)
*/
data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < UDP_HEADER_SIZE) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead); // read the first part of the header from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead); // read the remaining part of header restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE); // read all
}
start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // update starting address
/* process header */
memcpy(info->srcip, data, 4); // source IP address
info->srcport = (data[4] << 8) + data[5]; // source port number (big-endian)
data_ahead = (data[6] << 8) + data[7]; // data length (big-endian), data_ahead re-used
info->rxlen = data_ahead;
info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);
/* read data (circular read from the Rx buffer) */
if (recvmax != 0) // determine the actual number of data to read
info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
else
info->rxlen_act = recvlen - UDP_HEADER_SIZE;
data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size; // compute amount of (possible) data ahead
if (data_ahead < info->rxlen_act) { // read in 2 steps
SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead); // read the first part of data from the data ahead the read pointer
SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead); // read the remaining part of data restarting from the base address
} else { // read in 1 step
SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act); // read all
}
/* finalize and indicate that Rx is processed */
rx_rd += recvlen;
data[0] = rx_rd >> 8;
data[1] = rx_rd & 0xFF;
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2); // re-write read pointer
data[0] = SCR_RECV; // receive processed indicator command
SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);
return true;
} /* W3150AP_udpRecv */
So my problem is solved, thank you But I am wondering what is the reason of this thing that RX_RSR sometimes contains too small number…
If you are interested, I could send the whole driver. It only supports UDP, but it is much less complicated than the example driver provided by WIZnet.
Best wishes.