Estoy tratando de averiguar cómo utilizar este nuevo controlador HAL. Quiero recibir datos a través de la HAL_UART_Receive_IT() que configura el dispositivo para ejecutar una función de la interrupción cuando se reciben datos.

Problema es que usted tiene que especificar la longitud de los datos a leer antes de la interrupción desencadenantes. Pienso en el envío de la consola como de los comandos de longitud variable, así que no puede tener una longitud fija. Supongo que la única manera de hacer esto sería leer los caracteres individuales en un tiempo y construir un string.

El controlador HAL parece tener un problema por el que si se establece la HAL_UART_Receive_IT() para recibir x número de caracteres y, a continuación, intente enviar más de x personajes, habrá un error.

Actualmente no tengo idea de si estoy haciendo de la manera correcta, alguna idea?

InformationsquelleAutor HammerFet | 2014-07-21

6 Comentarios

  1. 15

    Me decidí a ir con DMA para obtener el recibir de trabajo. Estoy usando un 1 byte buffer circular para manejar los datos y como se escribió en el transmisor de serie del terminal. Aquí está mi código final (sólo el recibir parte, más info sobre la transmisión en la parte inferior).

    Algunos define y variables:

    #define BAUDRATE              9600
    #define TXPIN                 GPIO_PIN_6
    #define RXPIN                 GPIO_PIN_7
    #define DATAPORT              GPIOB
    #define UART_PRIORITY         6
    #define UART_RX_SUBPRIORITY   0
    #define MAXCLISTRING          100 //Biggest string the user will type
    
    uint8_t rxBuffer = '
    #define BAUDRATE              9600
    #define TXPIN                 GPIO_PIN_6
    #define RXPIN                 GPIO_PIN_7
    #define DATAPORT              GPIOB
    #define UART_PRIORITY         6
    #define UART_RX_SUBPRIORITY   0
    #define MAXCLISTRING          100 //Biggest string the user will type
    uint8_t rxBuffer = '\000'; //where we store that one character that just came in
    uint8_t rxString[MAXCLISTRING]; //where we build our string from characters coming in
    int rxindex = 0; //index for going though rxString
    
    0'; //where we store that one character that just came in uint8_t rxString[MAXCLISTRING]; //where we build our string from characters coming in int rxindex = 0; //index for going though rxString

    Configurar IO:

    __GPIOB_CLK_ENABLE();
    __USART1_CLK_ENABLE();
    __DMA2_CLK_ENABLE();
    
    GPIO_InitTypeDef GPIO_InitStruct;
    
    GPIO_InitStruct.Pin = TXPIN | RXPIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(DATAPORT, &GPIO_InitStruct);
    

    Configurar la UART:

    UART_HandleTypeDef huart1;
    DMA_HandleTypeDef hdma_usart1_rx;
    
    huart1.Instance = USART1;
    huart1.Init.BaudRate = BAUDRATE;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart1);
    

    Configurar DMA:

    extern DMA_HandleTypeDef hdma_usart1_rx; //assuming this is in a different file
    
    hdma_usart1_rx.Instance = DMA2_Stream2;
    hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_DISABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    HAL_DMA_Init(&hdma_usart1_rx);
    
    __HAL_LINKDMA(huart, hdmarx, hdma_usart1_rx);
    
    HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, UART_PRIORITY, UART_RX_SUBPRIORITY);
    HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
    

    Configurar DMA de interrupción:

    extern DMA_HandleTypeDef hdma_usart1_rx;
    
    void DMA2_Stream2_IRQHandler(void)
    {
        HAL_NVIC_ClearPendingIRQ(DMA2_Stream2_IRQn);
        HAL_DMA_IRQHandler(&hdma_usart1_rx);
    }
    

    Inicio DMA:

    __HAL_UART_FLUSH_DRREGISTER(&huart1);
    HAL_UART_Receive_DMA(&huart1, &rxBuffer, 1);
    

    DMA recibir devolución de llamada:

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
        __HAL_UART_FLUSH_DRREGISTER(&huart1); //Clear the buffer to prevent overrun
    
        int i = 0;
    
        print(&rxBuffer); //Echo the character that caused this callback so the user can see what they are typing
    
        if (rxBuffer == 8 || rxBuffer == 127) //If Backspace or del
        {
            print(" \b"); //"\b space \b" clears the terminal character. Remember we just echoced a \b so don't need another one here, just space and \b
            rxindex--; 
            if (rxindex < 0) rxindex = 0;
        }
    
        else if (rxBuffer == '\n' || rxBuffer == '\r') //If Enter
        {
            executeSerialCommand(rxString);
            rxString[rxindex] = 0;
            rxindex = 0;
            for (i = 0; i < MAXCLISTRING; i++) rxString[i] = 0; //Clear the string buffer
        }
    
        else
        {
            rxString[rxindex] = rxBuffer; //Add that character to the string
            rxindex++;
            if (rxindex > MAXCLISTRING) //User typing too much, we can't have commands that big
            {
                rxindex = 0;
                for (i = 0; i < MAXCLISTRING; i++) rxString[i] = 0; //Clear the string buffer
                print("\r\nConsole> ");
            }
        }
    }
    

    Por lo que es casi todo el código para recibir caracteres y construir una cadena (char array) que muestra lo que el usuario ha introducido. Si el usuario pulsa la tecla retroceso o supr, el último carácter de la matriz se sobrescribe y si se pulsa intro, esa matriz es enviado a otra función y se procesa como un comando.

    Para ver cómo el comando de análisis y transmitir el código funciona, ver mi proyecto Aquí

    Gracias a @Flip y @Dormen por sus sugerencias!

  2. 6

    Recepción de datos, mientras que el Registro de Datos (DR) está llena resultará en un error de saturación. El problema es que la función UART_Receive_IT(UART_HandleTypeDef*) va a dejar de leer el DR registro una vez se ha recibido la cantidad suficiente de datos. Los nuevos datos hará que el error de saturación.

    Lo que hice fue en vez de utilizar una circular en la DMA recibir de la estructura. Usted puede utilizar currentPosInBuffer - uart->hdmarx->Instance->NDTR para determinar cuánto se recibió información de que usted no ha procesado todavía.

    Es un poco más complicado, porque mientras que la DMA hace el almacenamiento en búfer circular a sí mismo, usted tiene que implementar de forma manual el bucle al principio si se pasa de la final del búfer.

    También he encontrado un fallo en el que el controlador dice que ha transferido los datos (es decir, NDTR ha disminuido), pero los datos todavía no está en el buffer. Puede ser que algunos DMA/bus de acceso a la contención del problema, pero es molesto.

    • El fallo es probablemente debido a la caché de datos en el procesador. Debe establecer el búfer sin almacenamiento en caché en el MPU, o el uso de la caché de instrucciones de lavado antes de leer el búfer.
  3. 3

    El STM32 UART controladores son un poco flojo. La única forma en la que trabajan fuera de la caja es si usted sabe el número exacto de caracteres que usted va a recibir. Si desea recibir un número indeterminado de caracteres hay un par de soluciones que he encontrado y probado:

    1. Establecer la cantidad de caracteres a recibir a 1 y construir una separada de la cadena. Esto funciona, pero tiene problemas al recibir datos muy rápido, porque cada vez que el controlador lee el rxBuffer se dissables la interrupción, por lo que algunos caracteres se pueden perder.

    2. Establecer la cantidad de caracteres a recibir al mayor tamaño de los mensajes y aplicar un tiempo de espera, después de que el mensaje sea leído.

    3. Escribir su propia UART_Receive_IT función, que se escribe directamente en un búfer circular. Esto es más trabajo, pero es lo que he encontrado que funciona mejor en la final. Tienes que cambiar algunos de los hal controladores, aunque, por lo que el código es menos portable.

    Otra manera es usar DMA como @Flip sugerido.

    • Otra idea: el uso de un «protocolo» al recibir de 1 byte, que contiene el tamaño de la siguiente cantidad de datos. Wait for 1 byte -> receive value "5", Wait for 5 bytes -> receive the 5 bytes, Wait for 1 byte -> receive value "28", Wait for 28 bytes -> receive the 28 bytes, ..., Wait for 1 byte -> receive value "0", END
    • sí, pero esto sólo funciona si usted tiene el control sobre ambos extremos de la comunicación.
  4. 0

    He tenido que enfrentar el mismo problema en mi proyecto.
    Lo que hice es de comenzar la lectura 1 byte con HAL_USART_Receive_IT() a la derecha después de que el periférico de inicialización.

    Luego escribí una devolución de llamada en la transferencia completa que pone el byte en un búfer, se establece un indicador si el comando se completa y, a continuación, llama HAL_USART_Receive_IT() de nuevo por otro byte.

    Parece de trabajo agradable para mí, desde que recibe comandos a través de la USART, cuyo primer byte me dice cuántos bytes más el comando va a ser largo.
    Tal vez podría funcionar para usted también!

  5. 0

    Tener un enfoque diferente de parches por ejemplo, «void USART2_IRQHandler(void)» en el archivo «stm32l0xx_it.c» (o l4xx como sea necesario). Cada vez que un carácter es recibido esta interrupción se llama. Hay espacio para insertar el código de usuario que se mantiene sin cambios a la hora de actualizar con CubeMX generador de código. Parche:

    void USART2_IRQHandler(void)
    {
      /* USER CODE BEGIN USART2_IRQn 0 */
    
      /* USER CODE END USART2_IRQn 0 */
      HAL_UART_IRQHandler(&huart2);
      /* USER CODE BEGIN USART2_IRQn 1 */
      usart_irqHandler_callback( &huart2 ); //patch: call to my function 
      /* USER CODE END USART2_IRQn 1 */
    }
    

    I suministro de un pequeño búfer de caracteres y empezar a recibir la función de TI. Hasta 115200 Baudios nunca consumió más de 1 Byte, dejando el resto de la búfer no utilizados.

    st = HAL_UART_Receive_IT( &huart2, (uint8_t*)rx2BufIT, RX_BUF_IT_SIZE );
    

    Cuando se recibe un byte I de captura y se pone a mi propio anillo de búfer y establece el carácter puntero y -contador:

    //placed in my own source-code module:
    void usart_irqHandler_callback( UART_HandleTypeDef* huart ) {
      HAL_UART_StateTypeDef  st;
      uint8_t c;
      if(huart->Instance==USART2) {
        if( huart->RxXferCount >= RX_BUF_IT_SIZE ) {
          rx2rb.err = 2;           //error: IT buffer overflow
        }
        else {
          huart->pRxBuffPtr--;     //point back to just received char
          c = (uint8_t) *huart->pRxBuffPtr; //newly received char
          ringbuf_in( &rx2rb, c ); //put c in rx ring-buffer
          huart2.RxXferCount++;    //increment xfer-counter avoids end of rx
        }
      }
    }
    

    Este método ha resultado ser bastante rápido. La recepción de un solo byte de usarlo o DMA siempre de-inicializa y las necesidades de inicializar el proceso de recepción de nuevo, que resultó ser demasiado lento. El código de arriba es sólo un marco, que he utilizado para el recuento de caracteres de salto de línea aquí en una estructura de estado que me permite en cualquier momento para leer líneas completas de el anillo-buffer. También revise si recibe un personaje o algún otro evento causó la interrupción debe ser incluido.

    EDIT:

    Este método probado para que funcione bien con USARTS que no son compatibles con la DMA y utilizarlo en su lugar.
    El uso de DMA con 1 byte en modo circular es más corto y más fácil de implementar cuando se utiliza CubeMX generador con HAL biblioteca.

  6. 0

    Generalmente escribí mi propia UART buffer circular de aplicación. Como se dijo antes, STM32 HAL de la biblioteca de la UART interrumpir las funciones son un poco extrañas.
    Usted puede escribir su propio búfer circular con solo 2 matriz de punteros y el uso de la UART de interrupción de banderas.

Dejar respuesta

Please enter your comment!
Please enter your name here