0

I am making HID for some data acquisition system. There are a lot of sensors who store test data and when I need I get to them and connect via USB and take it. USB host sent 3 bytes and USB device, if bytes are correct, sends its stored data. Sounds simple.

Previously it was implemented on PC, but now I try to implement it on STM32F769 Discovery and have some serious problems. I am using ARM Keil 5.27, code generated with STM32CubeMX 5.3.0. I tried just to make a plain simple program, later to integrate with the entire touchscreen interface. I tried to implement this code in main:

if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
    while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin))
    {
        Transmission_function();
    }

And the function itself:

#define DLE 0x10
#define STX 0x2
uint8_t tx_buf[]={DLE, STX, 120}, RX_FLAG;
uint32_t size_tx=sizeof(tx_buf);
void Transmission_function (void)
{
    if (Appli_state == APPLICATION_READY)
    {
        i=0;
        USBH_CDC_Transmit(&hUsbHostHS, tx_buf, size_tx);
        HAL_Delay(50);
        RX_FLAG=0;
    }
}

It should send the message after I press the blue button on the Discovery board. All that I get is Hard Fault. While trying to debug, I tried manually to check after which action I get this error and it was functioning in stm32f7xx_ll_usb.c:

HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, 
uint8_t ch_ep_num, uint16_t len, uint8_t dma)
{
   uint32_t USBx_BASE = (uint32_t)USBx;
   uint32_t *pSrc = (uint32_t *)src;
   uint32_t count32b, i;

   if (dma == 0U)
   {
      count32b = ((uint32_t)len + 3U) / 4U;
      for (i = 0U; i < count32b; i++)
      {
          USBx_DFIFO((uint32_t)ch_ep_num) = *((__packed uint32_t *)pSrc);
          pSrc++;
      }
   }

   return HAL_OK;
}

But trying to scroll back in disassembly I notice, that just before Hard Fault program was in this function inside stm32f7xx_hal_hcd.c, in case GRXSTS_PKTSTS_IN:

static void HCD_RXQLVL_IRQHandler(HCD_HandleTypeDef *hhcd)
{
    USB_OTG_GlobalTypeDef *USBx = hhcd->Instance;
    uint32_t USBx_BASE = (uint32_t)USBx;
    uint32_t pktsts;
    uint32_t pktcnt;
    uint32_t temp;
    uint32_t tmpreg;
    uint32_t ch_num;

    temp = hhcd->Instance->GRXSTSP;
    ch_num = temp & USB_OTG_GRXSTSP_EPNUM;
    pktsts = (temp & USB_OTG_GRXSTSP_PKTSTS) >> 17;
    pktcnt = (temp & USB_OTG_GRXSTSP_BCNT) >> 4;

    switch (pktsts)
    {
        case GRXSTS_PKTSTS_IN:
            /* Read the data into the host buffer. */
            if ((pktcnt > 0U) && (hhcd->hc[ch_num].xfer_buff != (void *)0))
            {
                (void)USB_ReadPacket(hhcd->Instance, hhcd->hc[ch_num].xfer_buff, (uint16_t)pktcnt);

                /*manage multiple Xfer */
                hhcd->hc[ch_num].xfer_buff += pktcnt;
                hhcd->hc[ch_num].xfer_count  += pktcnt;

                if ((USBx_HC(ch_num)->HCTSIZ & USB_OTG_HCTSIZ_PKTCNT) > 0U)
                {
                    /* re-activate the channel when more packets are expected */
                    tmpreg = USBx_HC(ch_num)->HCCHAR;
                    tmpreg &= ~USB_OTG_HCCHAR_CHDIS;
                    tmpreg |= USB_OTG_HCCHAR_CHENA;
                    USBx_HC(ch_num)->HCCHAR = tmpreg;
                    hhcd->hc[ch_num].toggle_in ^= 1U;
                }
            }
            break;

        case GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
            break;

        case GRXSTS_PKTSTS_IN_XFER_COMP:
        case GRXSTS_PKTSTS_CH_HALTED:
        default:
            break;
    }
}

Last few lines from Dissasembly shows this: 0x080018B4 E8BD81F0 POP {r4-r8,pc} 0x080018B8 0000 DCW 0x0000 0x080018BA 1FF8 DCW 0x1FF8

Why it fails? How could I fix it? I do not have much experience with USB protocol.

  • A hard fault can mean that the program tried to access invalid memory address, such as using an uninitialized pointer. Set a breakpoint just prior to where the hard fault occurs, in the GRXSTS_PKTSTS_IN case of the interrupt handler. Step until it crashes. Repeat and examine all the variable values for validity. Are the buffers that the pointers point to allocated and are they large enough? Does the stack pointer remain within the bounds of the stack? – kkrambo Dec 01 '19 at 14:04
  • Following your advice I tried to step there and I program skipped it and went to HardFault. I found out that it does it when it tries to execute USBx_DFIFO((uint32_t)ch_ep_num) = *((__packed uint32_t *)pSrc); in USB_WritePacket function. – Random_Person Dec 01 '19 at 15:22
  • 2
    You have a significant call tree to `HCD_RXQLVL_IRQHandler()` which includes a call to `HAL_Delay()` in `Transmission_Function()`, any kind of blocking or delay in an ISR is ill-advised. The debugging information you added in a comment, should be edited into the question. At the point it fails, what are the values of the variables involved in the expression? Are they valid? That information should also be added to the question not a comment. – Clifford Dec 01 '19 at 15:30
  • 1
    This kind of thing is going to be very difficult for even a highly skilled embedded developer to sort out. What is your basis for believing that an embedded USB host vs. something like a small embedded Linux system is the right way to solve the problem? Given you are talking to something via CDC, why are you using USB at all instead of an order of magnitude simpler traditional UART? Those of us who could debug this would first start by making sure it's even a good approach to the application problem. Then trying something simpler like static test messages to a well understood USB device. – Chris Stratton Dec 01 '19 at 23:57
  • Sorry for the late answer. I updated the values of DLE and STX, they are simple hex values from ASCII table. I will try to make them simple uint32_t variables instead of defines for a try. Delay was added while I tried to troubleshoot it-if I can, I avoid them. What about solution itself, I do not have much space to maneuver-sensors themselves already uses USB, I would anything simpler than USB myself. Why not Linux or any other OS-I already made so far with touchscreen interface without it. If the solution is OS, I will take it. – Random_Person Dec 04 '19 at 17:24

1 Answers1

0

I will post my walkaround this, but I am not sure why it worked. Solution was to use EXTI0 interrupt instead of just detection if PA0 is high, as I showed I used here:

if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin)) while (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin)) Transmission_function(); I changed it to this:

void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */
  if(Appli_state == APPLICATION_READY){
      USBH_CDC_Transmit(&hUsbHostHS, Buffer, 3);
  }
  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}