sprintf повторный вход в 64-битную операцию в 32-битном MCU с прерываниями
По вопросу SO: 52164135
Настройка:
У меня есть функция, которая преобразует многие double
значения в предопределенную строку. Вход является array of struct
из которого мы объединяем два двойных значения в строку. Дабл имеет размер 8 байт или 64 бита, и мой MCU - STM32, 32-битный микроконтроллер ARM.
interrupt
также работает параллельно.
Данные должны выглядеть так:
[[12.11111111,12.11111111], [12.22222222,12.22222222],...]
Но я получаю (очень редко):
[[12.11111111,12.11111111], [55.01 [12.33333333,12.33333333],...]
Примечание: я пропустил [12.22222222,12.22222222]
sprintf не возвращается
Согласно этому обсуждению, на AVRFreaks, sprintf is not re-entrant.
(Обсуждается использование sprintf в аппаратной среде с поддержкой прерываний.) Что означает, что прерывание происходит между sprintf
операция стек не может продолжить операцию, которую он делал.
Поскольку мой MCU 32-битный, для выполнения 64-битной операции потребуется два тактовых цикла. И если мы предположим, что между операцией sprintf произошло прерывание в соответствии с приведенным выше обсуждением sprintf
должен потерпеть неудачу.
Вопрос
1. Будет sprintf
потерпеть неудачу в случае его прерывания?
Вот строковая функция, подпрограмма прерывания также выполняется в фоновом режиме, которая работает с другими данными датчика (локальными и глобальными).
/* @brief From the array of GPS structs we create a string of the format
* [[lat,long],[lat,long],..]
* @param input The input array of GPS structs
* @param output The output string which will contain lat, long
* @param sz Size left in the output buffer
* @return 0 Successfully completed operation
* 1 Failed / Error
*/
int get_gps60secString(GPS_periodic_t input[GPS_PERIODIC_ARRAY_SIZE],
char *output, size_t sz)
{
int cnt = snprintf(output, sz, "[");
if (cnt < 0 || cnt >= sz)
return 1;
output += cnt;
sz -= cnt;
int i = 0;
for (i = 0; i < GPS_PERIODIC_ARRAY_SIZE; i++) {
cnt = snprintf(output, sz, "[%0.8f,%0.8f]%s",
input[i].point.latitude, input[i].point.longitude,
i + 1 == GPS_PERIODIC_ARRAY_SIZE ? "" : ",");
if (cnt < 0 || cnt >= sz)
return 1;
output += cnt;
sz -= cnt;
}
cnt = snprintf(output, sz, "]");
if (cnt < 0 || cnt >= sz)
return 1;
return 0; // no error
}
Что происходит внутри процедуры прерывания
void GPS_InterruptHandler(UART_HandleTypeDef *UartHandle)
{
gps_UART_RxInterrupt_Disable();
GPS_t l_sGpsInfo;
memset(&l_sGpsInfo,0,sizeof(GPS_t));
status=Validate_get_gpsInfo((char*)g_gps_readBuff,&l_sGpsInfo,100);
MEMS_interruptHandler(); //Inertial sensor ISR
gps_UART_RxInterrupt_Enable();
}
1 ответ
sprintf
потерпит неудачу только во время прерывания, если он будет вызван снова во время этого прерывания (при условии, что он использует глобальные переменные, которые используются повторно; если он будет использовать только переменные стека, то он будет повторно введен).
Так что если ваш обработчик прерываний вызывает sprintf
и во время этого вызова возникает новое прерывание с таким же или более высоким приоритетом, после чего оно может дать сбой. Однако во время обработки прерывания прерывания обычно отключаются, поэтому не может быть (не должно быть!) Другого прерывания того же типа.
Но зачем преобразовывать эти необработанные данные во время обработки прерываний? Почему бы не сохранить / передать эти данные в подпрограмму уровня пользователя через буфер и сделать так, чтобы эти функции преобразовывали необработанные данные? Это будет соответствовать идее, что обработчик прерываний должен быть как можно более коротким (быстрым).