Разделите 48-битное число, состоящее из 16-битной и 32-битной переменных - PullRequest
2 голосов
/ 04 апреля 2019

Я занимаюсь разработкой программного обеспечения для встроенного микроконтроллера (STN1110). У него нет эмуляции FPU или SFP.

Существует метод SDK, который возвращает время работы контроллера в микросекундах, разделенное на две переменные: uint16 (msw) и uint32 (lsw).

Я хочу преобразовать эту «пару» в один uint32 с точностью до секунд (/ 1000 / 1000).

В документации SDK API указано следующее:

uint32 TimeGet48 (uint16 * time_msw)
-----------------------------------------   
Read the current full 48-bit system time.

Returns all 48 bits of the current value of the system's 1MHz clock. The resolution of the returned 32 bit value is one microsecond, with the most significant word returned through the pointer passed as a parameter. The clock therefore wraps after approximately 8.9 years. That should be enough for most purposes.

Parameters:
- time_msw: A pointer to store the most significant word of the current system time
Returns:
- Lower 32 bits to the current system time.

Основная проблема здесь в том, что контроллер поддерживает только до uint32, поэтому нет uint64.

Я «склеил» некоторый код, который, как я думал, может быть решением. Это не так, поскольку возникают ошибки, как только значение uint16> 0, и я не могу его найти.

uint32 divide(uint32 time_in_us_lsb, uint16 time_in_us_msb, uint32 divisor)
{
    uint32 value1 = (uint32)time_in_us_msb & 0xFFFF;
    uint32 value2 = (uint32)(time_in_us_lsb >> 16) & 0xFFFF;
    uint32 value3 = (uint32)time_in_us_lsb & 0xFFFF;

    value2 += (value1 % divisor) << 16;
    value3 += (value2 % divisor) << 16;

    return (((value2 / divisor) << 16) & 0xFFFF0000) | ( value3 / divisor);
}

uint32 getUptimeSeconds() {
    uint32 time_in_us_lsb;
    uint16 time_in_us_msb;

    time_in_us_lsb = TimeGet48(&time_in_us_msb);   
    uint32 result = divide(time_in_us_lsb, time_in_us_msb, 100000);

    time_in_us_lsb = (uint32)result & 0xFFFFFFFF;
    time_in_us_msb = 0;

    return divide(time_in_us_lsb, time_in_us_msb, 10); 
}

Примечание. Скорость здесь не является проблемой, поскольку функция не будет вызываться часто в течение срока службы приложений.

Edit: добавлен предел возможностей uint32.

Ответы [ 3 ]

2 голосов
/ 04 апреля 2019

.. возникают ошибки, как только значение uint16> 0, и я не могу его найти.

По крайней мере, одна ошибка с учетоммикросекунды "

// uint32 result = divide(time_in_ms_lsb, time_in_ms_msb, 100000);
uint32 result    = divide(time_in_ms_lsb, time_in_ms_msb, 1000000);

Имеет смысл просто использовать uint64_t.

uint32 divide(uint32 time_in_ms_lsb, uint16 time_in_ms_msb, uint32 divisor) {
  uint64_t sum = ((uint64_t)time_in_ms_msb << 32) | divisor;
  return (uint32)(sum / divisor);
}

Рекомендовать: использование ms в коде для микросекунд выглядит как миллисекунд .Предложите us для "μSeconds".

1 голос
/ 04 апреля 2019

Должно быть:

uint32 getUptimeSeconds() {
    uint32 time_in_us_lsb;
    uint16 time_in_us_msb;

    time_in_us_lsb = TimeGet48(&time_in_us_msb);   
    uint32 result = divide(time_in_us_lsb, time_in_us_msb, 10000);

    time_in_us_lsb = (uint32)result & 0xFFFFFFFF;
    time_in_us_msb = 0;

    return divide(time_in_us_lsb, time_in_us_msb, 100); 
}
1 голос
/ 04 апреля 2019

Учитывая целевой процессор, вы, вероятно, хотите оптимизировать его для фиксированного делителя.В вашем случае деление на 1000000.

Для этого вы можете использовать / 1000000 = a * (2 ^ 64/1000000) / 2 ^ 64.(2 ^ 64/1000000) не является целым числом, но достаточно приближения.Умножение может быть сделано в виде серии сдвигов и сложений.Вы можете добавить немного ошибки в (2 ^ 64/1000000), чтобы получить меньше смен и добавлений.Результат будет слегка отклонен, но не очень.

Если все сделано правильно, ваш результат будет не более 1 от правильного ответа.Если это важно, вы можете снова умножить результат на 1000000, сравнить и затем скорректировать результат на 1. При необходимости.

Примечание: вам не нужно использовать 2 ^ 64 в приведенном выше приближении.Попробуйте 2 ^ 32 или 2 ^ 48 или другое число, чтобы увидеть, дает ли это меньше сдвигов и добавляет.

PS: Проверьте, что компилятор делает для (a * uint64_t(1<<48 / 1000000)) >> 48.

...