Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: RawHID recv and send timeout in microseconds

  1. #1
    Junior Member
    Join Date
    May 2021
    Posts
    3

    RawHID recv and send timeout in microseconds

    I use RawHID with a Teensy 4.1 in my project and I need a control loop that run at at least 1kHz. Since RawHID's timeout are measured in ms, it is impossible to have a higher frequency than 500Hz since I call the recv and send function in each loop and they each take at least 1 ms.

    To solve my problem, I went to the source code, in the cores/teensy4/usb_rawhid.c file and change the following :

    Code:
    int usb_rawhid_recv(void *buffer, uint32_t timeout)
    {
    	uint32_t wait_begin_at = systick_millis_count;
    	uint32_t tail = rx_tail;
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		if (tail != rx_head) break;
    		if (systick_millis_count - wait_begin_at > timeout)  {
    			return 0;
    		}
    		yield();
    	}
    //	digitalWriteFast(0, LOW);
    	if (++tail > RX_NUM) tail = 0;
    	uint32_t i = rx_list[tail];
    	rx_tail = tail;
    
    	memcpy(buffer,  rx_buffer + i * RAWHID_RX_SIZE, RAWHID_RX_SIZE);
    	rx_queue_transfer(i);
    	//memset(rx_transfer, 0, sizeof(rx_transfer));
    	//usb_prepare_transfer(rx_transfer + 0, rx_buffer, RAWHID_RX_SIZE, 0);
    	//usb_receive(RAWHID_RX_ENDPOINT, rx_transfer + 0);
    	return RAWHID_RX_SIZE;
    }
    
    int usb_rawhid_send(const void *buffer, uint32_t timeout)
    {
    	transfer_t *xfer = tx_transfer + tx_head;
    	uint32_t wait_begin_at = systick_millis_count;
    
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		uint32_t status = usb_transfer_status(xfer);
    		if (!(status & 0x80)) break; // transfer descriptor ready
    		if (systick_millis_count - wait_begin_at > timeout) return 0;
    		yield();
    	}
    	uint8_t *txdata = txbuffer + (tx_head * RAWHID_TX_SIZE);
    	memcpy(txdata, buffer, RAWHID_TX_SIZE);
    	arm_dcache_flush_delete(txdata, RAWHID_TX_SIZE );
    	usb_prepare_transfer(xfer, txdata, RAWHID_TX_SIZE, 0);
    	usb_transmit(RAWHID_TX_ENDPOINT, xfer);
    	if (++tx_head >= TX_NUM) tx_head = 0;
    	return RAWHID_TX_SIZE;
    }
    to :

    Code:
    int usb_rawhid_recv(void *buffer, uint32_t timeout)
    {
    	uint32_t wait_begin_at =micros();
    	uint32_t tail = rx_tail;
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		if (tail != rx_head) break;
    		if (micros() - wait_begin_at > timeout)  {
    			return 0;
    		}
    		yield();
    	}
    //	digitalWriteFast(0, LOW);
    	if (++tail > RX_NUM) tail = 0;
    	uint32_t i = rx_list[tail];
    	rx_tail = tail;
    
    	memcpy(buffer,  rx_buffer + i * RAWHID_RX_SIZE, RAWHID_RX_SIZE);
    	rx_queue_transfer(i);
    	//memset(rx_transfer, 0, sizeof(rx_transfer));
    	//usb_prepare_transfer(rx_transfer + 0, rx_buffer, RAWHID_RX_SIZE, 0);
    	//usb_receive(RAWHID_RX_ENDPOINT, rx_transfer + 0);
    	return RAWHID_RX_SIZE;
    }
    
    int usb_rawhid_send(const void *buffer, uint32_t timeout)
    {
    	transfer_t *xfer = tx_transfer + tx_head;
    	uint32_t wait_begin_at = micros();
    
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		uint32_t status = usb_transfer_status(xfer);
    		if (!(status & 0x80)) break; // transfer descriptor ready
    		if (micros() - wait_begin_at > timeout) return 0;
    		yield();
    	}
    	uint8_t *txdata = txbuffer + (tx_head * RAWHID_TX_SIZE);
    	memcpy(txdata, buffer, RAWHID_TX_SIZE);
    	arm_dcache_flush_delete(txdata, RAWHID_TX_SIZE );
    	usb_prepare_transfer(xfer, txdata, RAWHID_TX_SIZE, 0);
    	usb_transmit(RAWHID_TX_ENDPOINT, xfer);
    	if (++tx_head >= TX_NUM) tx_head = 0;
    	return RAWHID_TX_SIZE;
    }
    In summary, I changed all systick_millis_count to micros().

    I don't know if those changes can bring other bugs, but so far, this has not caused me any problems. The communication is working and the data I receive is good.

    Request: Implement those changes or something similar in the source code.

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    8,325
    Quote Originally Posted by BrunoB81HK View Post
    Request: Implement those changes or something similar in the source code.
    I hope not. This is incompatible to everything.

  3. #3
    Junior Member
    Join Date
    May 2021
    Posts
    3
    Quote Originally Posted by Frank B View Post
    This is incompatible to everything.
    Do you mean incompatible in the sense that older codes which specify the timeout time in milliseconds will need to be changed? If so, there is surely a simple way to make it work regardless. I think that using >= signs instead of > will make it possible to have a timeout of 0 ms.

    If not, I'd like to know why it's incompatible with everything. I wouldn't want my control loop to fail for an unknown reasons.

  4. #4
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    9,119
    Quote Originally Posted by BrunoB81HK View Post
    Do you mean incompatible in the sense that older codes which specify the timeout time in milliseconds will need to be changed? If so, there is surely a simple way to make it work regardless. I think that using >= signs instead of > will make it possible to have a timeout of 0 ms.

    If not, I'd like to know why it's incompatible with everything. I wouldn't want my control loop to fail for an unknown reasons.
    I believe what Frank B is commenting on, is if you have a sketch that
    calls: usb_rawhid_recv(myBuffer, 100);

    They were expecting the code to wait for up to a 100ms or a tenth of a second to receive a packet, with this change the
    code would only wait for .0001 seconds for a packet. Which could break any app that was expecting to wait the 100ms

  5. #5
    Junior Member
    Join Date
    May 2021
    Posts
    3
    Quote Originally Posted by KurtE View Post
    I believe what Frank B is commenting on, is if you have a sketch that
    calls: usb_rawhid_recv(myBuffer, 100);

    They were expecting the code to wait for up to a 100ms or a tenth of a second to receive a packet, with this change the
    code would only wait for .0001 seconds for a packet. Which could break any app that was expecting to wait the 100ms
    Thanks KurtE for the comprehensive explanation.

    I think the problem is that currently, if I call usb_rawhid_recv(myBuffer, 100); the code wait up to 101 ms, so 1 ms more than asked. This is due to the fact that systick_millis_count is an int and the comparison is done with a > instead of >=.

    I tested the use of >= and it work as expected. Basically, I changed :

    Code:
    int usb_rawhid_recv(void *buffer, uint32_t timeout)
    {
    	uint32_t wait_begin_at = systick_millis_count;
    	uint32_t tail = rx_tail;
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		if (tail != rx_head) break;
    		if (systick_millis_count - wait_begin_at > timeout)  {
    			return 0;
    		}
    		yield();
    	}
    //	digitalWriteFast(0, LOW);
    	if (++tail > RX_NUM) tail = 0;
    	uint32_t i = rx_list[tail];
    	rx_tail = tail;
    
    	memcpy(buffer,  rx_buffer + i * RAWHID_RX_SIZE, RAWHID_RX_SIZE);
    	rx_queue_transfer(i);
    	//memset(rx_transfer, 0, sizeof(rx_transfer));
    	//usb_prepare_transfer(rx_transfer + 0, rx_buffer, RAWHID_RX_SIZE, 0);
    	//usb_receive(RAWHID_RX_ENDPOINT, rx_transfer + 0);
    	return RAWHID_RX_SIZE;
    }
    
    int usb_rawhid_send(const void *buffer, uint32_t timeout)
    {
    	transfer_t *xfer = tx_transfer + tx_head;
    	uint32_t wait_begin_at = systick_millis_count;
    
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		uint32_t status = usb_transfer_status(xfer);
    		if (!(status & 0x80)) break; // transfer descriptor ready
    		if (systick_millis_count - wait_begin_at > timeout) return 0;
    		yield();
    	}
    	uint8_t *txdata = txbuffer + (tx_head * RAWHID_TX_SIZE);
    	memcpy(txdata, buffer, RAWHID_TX_SIZE);
    	arm_dcache_flush_delete(txdata, RAWHID_TX_SIZE );
    	usb_prepare_transfer(xfer, txdata, RAWHID_TX_SIZE, 0);
    	usb_transmit(RAWHID_TX_ENDPOINT, xfer);
    	if (++tx_head >= TX_NUM) tx_head = 0;
    	return RAWHID_TX_SIZE;
    }
    to :

    Code:
    int usb_rawhid_recv(void *buffer, uint32_t timeout)
    {
    	uint32_t wait_begin_at = systick_millis_count;
    	uint32_t tail = rx_tail;
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		if (tail != rx_head) break;
    		if (systick_millis_count - wait_begin_at >= timeout)  {
    			return 0;
    		}
    		yield();
    	}
    //	digitalWriteFast(0, LOW);
    	if (++tail > RX_NUM) tail = 0;
    	uint32_t i = rx_list[tail];
    	rx_tail = tail;
    
    	memcpy(buffer,  rx_buffer + i * RAWHID_RX_SIZE, RAWHID_RX_SIZE);
    	rx_queue_transfer(i);
    	//memset(rx_transfer, 0, sizeof(rx_transfer));
    	//usb_prepare_transfer(rx_transfer + 0, rx_buffer, RAWHID_RX_SIZE, 0);
    	//usb_receive(RAWHID_RX_ENDPOINT, rx_transfer + 0);
    	return RAWHID_RX_SIZE;
    }
    
    int usb_rawhid_send(const void *buffer, uint32_t timeout)
    {
    	transfer_t *xfer = tx_transfer + tx_head;
    	uint32_t wait_begin_at = systick_millis_count;
    
    	while (1) {
    		if (!usb_configuration) return -1; // usb not enumerated by host
    		uint32_t status = usb_transfer_status(xfer);
    		if (!(status & 0x80)) break; // transfer descriptor ready
    		if (systick_millis_count - wait_begin_at >= timeout) return 0;
    		yield();
    	}
    	uint8_t *txdata = txbuffer + (tx_head * RAWHID_TX_SIZE);
    	memcpy(txdata, buffer, RAWHID_TX_SIZE);
    	arm_dcache_flush_delete(txdata, RAWHID_TX_SIZE );
    	usb_prepare_transfer(xfer, txdata, RAWHID_TX_SIZE, 0);
    	usb_transmit(RAWHID_TX_ENDPOINT, xfer);
    	if (++tx_head >= TX_NUM) tx_head = 0;
    	return RAWHID_TX_SIZE;
    }
    With this change, it is possible to have a timeout of 0 ms.

    Edit :

    When looking at the teensy3 core, I found another way to achieve a 0 ms timeout without changing > to >=. You just change :

    Code:
    if (systick_millis_count - wait_begin_at > timeout) return 0;
    to :

    Code:
    if (systick_millis_count - wait_begin_at > timeout || !timeout) return 0;
    I didn't test this but it seems like a good way to do it, although it won't fix the fact that the maximum timeout will always be 1 ms more than asked.
    Last edited by BrunoB81HK; 05-27-2021 at 12:46 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •