简体   繁体   English

划分由16位和32位变量组成的48位数字

[英]Divide a 48-bit number which is composed of a 16bit and a 32bit variable

I am developing a software for an embedded microcontroller (STN1110). 我正在开发用于嵌入式微控制器(STN1110)的软件。 It has no FPU or SFP emulation. 它没有FPU或SFP仿真。

There is an SDK method which returns the uptime of the controller in microseconds, split in two variables: uint16 (msw) and uint32 (lsw). 有一个SDK方法可返回控制器的正常运行时间(以微秒为单位),分为两个变量:uint16(msw)和uint32(lsw)。

I want to convert this "pair" into a single uint32 with seconds precision ( / 1000 / 1000 ). 我想将此“对”转换为具有秒精度( / 1000 / 1000 1000/1000)的单个uint32。

The SDK API docs state the following: 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.

The main problem here is, the controller supports only up to uint32 , so no uint64 . 这里的主要问题是,控制器最多仅支持uint32 ,因此不支持uint64

I "glued" some code together that I thought may be the solution. 我“粘合”了一些我认为可能是解决方案的代码。 It is not, as there are errors as soon as the uint16 value is > 0 and I'm not able to find it. 并非如此,因为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); 
}

Note: Speed is not an issue here, as the function will not be called often during the applications lifetime. 注意:这里速度不是问题,因为在应用程序生存期内不会经常调用该函数。

Edit: Added the uint32 capability limit. 编辑:添加了uint32功能限制。

.. there are errors as soon as the uint16 value is > 0 and I'm not able to find it. ..一旦uint16值> 0,就会出现错误,而我找不到它。

At least one error given "uptime of the controller in microseconds" 在“控制器正常运行时间(以微秒为单位)”中给出至少一个错误

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

Makes more sense to simply use uint64_t . 仅仅使用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);
}

Recommend: using ms in code for microseconds looks like milliseconds . 推荐:在代码中使用ms毫秒数看起来像毫秒 Suggest us for "μSeconds". 建议us使用“μSeconds”。

Given the CPU you target you probably want to optimize this for a fixed divisor. 给定您定位的CPU,您可能需要针对固定除数对其进行优化。 In your case a division by 1000000. 您的情况除以1000000。

For that you can use a/1000000 = a * (2^64 / 1000000) / 2^64. 为此,您可以使用a / 1000000 = a *(2 ^ 64/1000000)/ 2 ^ 64。 (2^64 / 1000000) isn't an integer but an approximation is enough. (2 ^ 64/1000000)不是整数,但是一个近似值就足够了。 The multiplication can be done as a series of shifts and adds. 乘法可以通过一系列移位和加法来完成。 You can add a bit of error to the (2^64 / 1000000) to get fewer shifts and adds. 您可以对(2 ^ 64/1000000)添加一些错误,以减少移位和增加。 The result will be slightly off but not much. 结果将略有下降,但不会太大。

Done correctly your result will be no more than 1 off the right answer. 正确完成后,正确答案的结果将不超过1。 If that is important you can multiply the result by 1000000 again, compare and then adjust the result by 1 as needed. 如果那很重要,则可以再次将结果乘以1000000,进行比较,然后根据需要将结果调整为1。

Note: You don't have to use 2^64 in the above approximation. 注意:在上面的近似中,您不必使用2 ^ 64。 Try 2^32 or 2^48 or other number to see if that gives you fewer shifts and adds. 尝试2 ^ 32或2 ^ 48或其他数字,看看是否能减少移位和增加次数。

PS: Check out what the compiler does for (a * uint64_t(1<<48 / 1000000)) >> 48 . PS:检查编译器的作用是(a * uint64_t(1<<48 / 1000000)) >> 48

Should be: 应该:

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); 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM