Передать массив, который является частью структуры, как указатель uint8_t на функцию - PullRequest
1 голос
/ 13 июля 2020

Я работаю с Renesas RA2A1, используя их гибкий программный пакет, пытаясь отправить данные через uart.

Я отправляю int и float через uart, поэтому я создал объединение float и 4-байтовый массив uint8_t, то же самое для int.

Я помещаю несколько из них в структуру, а затем помещаю их в объединение с массивом, который является размером всех данных, содержащихся в структуре.

Я не могу заставить его работать, передавая массив из структуры в функцию .. Если я создаю массив uint8_t, он проходит и работает нормально ... Я не уверен, что не так с попыткой передать массив, как я.

Не удается выполнить утверждение в R_SCI_UART_WRITE, которое проверяет размер, который не выполняется, потому что он равен 0.

typedef union{
    float num_float;
    uint32_t num_uint32;
    int32_t num_int32;
    uint8_t num_array[4];
} comms_data_t;

typedef struct{
    comms_data_t a;
    comms_data_t b;
    comms_data_t c;
    comms_data_t d;
    comms_data_t e;
    uint8_t lr[2];
} packet_data_t;

typedef union{
    packet_data_t msg_packet_data;
    uint8_t packet_array[22];
}msg_data_t;


/* Works */
    uint8_t myData[10] = "Hi Dave!\r\n";

    uart_print_main_processor_msg(myData);

/* Doesn't work */
msg_data_t msg_data;

/* code removed that puts data into msg_data,ex below */
msg_data.msg_packet_data.a.num_float = 1.2f;

uart_print_main_processor_msg(msg_data.packet_array);

// Functions below

 /****************************************************************************************************************/
fsp_err_t uart_print_main_processor_msg(uint8_t *p_msg)
{
    fsp_err_t err   = FSP_SUCCESS;
    uint8_t msg_len = RESET_VALUE;
    uint32_t local_timeout = (DATA_LENGTH * UINT16_MAX);
    char *p_temp_ptr = (char *)p_msg;

    /* Calculate length of message received */
    msg_len = ((uint8_t)(strlen(p_temp_ptr)));

    /* Reset callback capture variable */
    g_uart_event = RESET_VALUE;

    /* Writing to terminal */
        err = R_SCI_UART_Write (&g_uartMainProcessor_ctrl, p_msg, msg_len);
        if (FSP_SUCCESS != err)
        {
            APP_ERR_PRINT ("\r\n**  R_SCI_UART_Write API Failed  **\r\n");

            return err;
        }

        /* Check for event transfer complete */
        while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))
        {
            /* Check if any error event occurred */
            if (UART_ERROR_EVENTS == g_uart_event)
            {
                APP_ERR_PRINT ("\r\n**  UART Error Event Received  **\r\n");
                return FSP_ERR_TRANSFER_ABORTED;
            }
        }
        if(RESET_VALUE == local_timeout)
        {
            err = FSP_ERR_TIMEOUT;
        }


    return err;

}

fsp_err_t R_SCI_UART_Write (uart_ctrl_t * const p_api_ctrl, uint8_t const * const p_src, uint32_t const bytes)
{
#if (SCI_UART_CFG_TX_ENABLE)
    sci_uart_instance_ctrl_t * p_ctrl = (sci_uart_instance_ctrl_t *) p_api_ctrl;
 #if SCI_UART_CFG_PARAM_CHECKING_ENABLE || SCI_UART_CFG_DTC_SUPPORTED
    fsp_err_t err = FSP_SUCCESS;
 #endif

 #if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
    err = r_sci_read_write_param_check(p_ctrl, p_src, bytes);
    FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
    FSP_ERROR_RETURN(0U == p_ctrl->tx_src_bytes, FSP_ERR_IN_USE);
 #endif

    /* Transmit interrupts must be disabled to start with. */
    p_ctrl->p_reg->SCR &= (uint8_t) ~(SCI_SCR_TIE_MASK | SCI_SCR_TEIE_MASK);

    /* If the fifo is not used the first write will be done from this function. Subsequent writes will be done
     * from txi_isr. */
 #if SCI_UART_CFG_FIFO_SUPPORT
    if (p_ctrl->fifo_depth > 0U)
    {
        p_ctrl->tx_src_bytes = bytes;
        p_ctrl->p_tx_src     = p_src;
    }
    else
 #endif
    {
        p_ctrl->tx_src_bytes = bytes - p_ctrl->data_bytes;
        p_ctrl->p_tx_src     = p_src + p_ctrl->data_bytes;
    }

 #if SCI_UART_CFG_DTC_SUPPORTED

    /* If a transfer instance is used for transmission, reset the transfer instance to transmit the requested
     * data. */
    if ((NULL != p_ctrl->p_cfg->p_transfer_tx) && p_ctrl->tx_src_bytes)
    {
        uint32_t data_bytes    = p_ctrl->data_bytes;
        uint32_t num_transfers = p_ctrl->tx_src_bytes >> (data_bytes - 1);
        p_ctrl->tx_src_bytes = 0U;
  #if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)

        /* Check that the number of transfers is within the 16-bit limit. */
        FSP_ASSERT(num_transfers <= SCI_UART_DTC_MAX_TRANSFER);
  #endif

        err = p_ctrl->p_cfg->p_transfer_tx->p_api->reset(p_ctrl->p_cfg->p_transfer_tx->p_ctrl,
                                                         (void const *) p_ctrl->p_tx_src,
                                                         NULL,
                                                         (uint16_t) num_transfers);
        FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
    }
 #endif

 #if SCI_UART_CFG_FLOW_CONTROL_SUPPORT
    if ((((sci_uart_extended_cfg_t *) p_ctrl->p_cfg->p_extend)->uart_mode == UART_MODE_RS485_HD) &&
        (p_ctrl->flow_pin != SCI_UART_INVALID_16BIT_PARAM))
    {
        R_BSP_PinAccessEnable();
        R_BSP_PinWrite(p_ctrl->flow_pin, BSP_IO_LEVEL_HIGH);
        R_BSP_PinAccessDisable();
    }
 #endif

    /* Trigger a TXI interrupt. This triggers the transfer instance or a TXI interrupt if the transfer instance is
     * not used. */
    p_ctrl->p_reg->SCR |= SCI_SCR_TIE_MASK;
 #if SCI_UART_CFG_FIFO_SUPPORT
    if (p_ctrl->fifo_depth == 0U)
 #endif
    {
        /* On channels with no FIFO, the first byte is sent from this function to trigger the first TXI event.  This
         * method is used instead of setting TE and TIE at the same time as recommended in the hardware manual to avoid
         * the one frame delay that occurs when the TE bit is set. */
        if (2U == p_ctrl->data_bytes)
        {
            p_ctrl->p_reg->FTDRHL = *((uint16_t *) (p_src)) | (uint16_t) ~(SCI_UART_FIFO_DAT_MASK);
        }
        else
        {
            p_ctrl->p_reg->TDR = *(p_src);
        }
    }

    return FSP_SUCCESS;
#else
    FSP_PARAMETER_NOT_USED(p_api_ctrl);
    FSP_PARAMETER_NOT_USED(p_src);
    FSP_PARAMETER_NOT_USED(bytes);

    return FSP_ERR_UNSUPPORTED;
#endif
}



1 Ответ

1 голос
/ 13 июля 2020

Есть несколько проблем с этой программой. Большая часть этого кода зависит от поведения undefined . Объединения также являются UB, если используются для наложения имен, даже если почти все компиляторы C имеют тенденцию допускать это, но если вы используете объединение, я все же предпочел бы использовать char[] для массива, используемого для псевдонима. Как упоминалось в комментариях, "Hi Dave!\r\n"; фактически занимает 11 байтов с нулевым символом. Безопаснее использовать uint8_t myData[] = "Hi Dave!\r\n"; или const * uint8_t = "Hi Dave!\r\n"; и избавить себя от проблем.

Вторая проблема заключается в том, что strlen не может корректно работать с двоичными данными. strlen работает путем поиска первого появления нулевого символа в строке , поэтому он не применим для двоичных данных. Если вы передадите значение с плавающей запятой, которое имеет единственный нулевой байт в его представлении IEEE 754, оно отметит конец этой «строки».

Проще говоря, ваша функция должна быть объявлена ​​как fsp_err_t uart_write(const char * msg, size_t msg_len); и вызывается с помощью uart_write(data_array, sizeof data_array);. Если вы хотите передавать сообщения переменного размера через UART, вам также необходимо определить определенный протокол связи, то есть создать сообщение, которое можно будет однозначно проанализировать. Скорее всего, это будет означать: 1) некоторую готовность ie в начале, 2) длину передаваемых данных, 3) фактические данные, 4) cr c - но это выходит за рамки этого вопроса.

Итак, strlen не сообщит вам длину данных, вы сами передадите его функции, и вам вообще не нужны объединения. Если вы решите не сериализовать данные должным образом (например, используя protobuf или какой-либо другой протокол), вы можете просто передать указатель на структуру функции, т.е. вызвать вышеупомянутый uart_write((char*)&some_struct, sizeof some_struct);, и он будет работать как если бы вы передали массив.

Обратите внимание, что char в этом случае не означает «символ ascii» или «символ в строке». Суть использования char* заключается в том, что это единственный указатель, которому разрешено использовать псевдоним других указателей . Итак, вы получаете указатель на свою структуру (&str), преобразуете его в char* и передаете его функции, которая затем может читать свое представление в памяти. Я знаю, что R_SCI_UART_Write, вероятно, генерируется вашей IDE, и, к сожалению, в этих блоках часто используется uint8_t* вместо char*, поэтому вам, вероятно, в какой-то момент придется выполнить приведение к uint8_t*.

...