提问人:98coolmen 提问时间:8/31/2023 更新时间:8/31/2023 访问量:137
我可以将unit16_t转换为“uint12_t”吗?
Can I convert an unit16_t to an "uint12_t"?
问:
我遇到了一个问题,我需要将两个uint16_t数字压缩成一个 2 字节的空间。 我知道我丢失了信息,但高于 12 位的数字与我无关。 前 12 位应为一个数字,后 12 位应为中数。 CAN报文需要输出。 有没有人知道如何在 C++ 中解决这个问题?
这是我第一次尝试,但它导致了两个字节大小的数字,正如预期的那样。
byte OUT[2];
union OUT_byte {
byte bufferOUT[2];
uint16_t OUT_array_int;
} OUTb;
OUTb.OUT_array_int = (double)(round(INPUT)); // casting from an double originaly
for (int i = 0; i < sizeof(OUTb.bufferOUT); i++) {
OUT[i] = OUTb.bufferOUT[i];
}
这给了我两个字节和一个包含我的信息的数组。 现在我需要两个数字压缩到 12 位,3 个字节。
答:
0赞
Mestkon
8/31/2023
#1
您必须执行一些位操作才能将 2 个值打包成 24 位数据。std::uint16_t
struct input
{
std::uint16_t a, b;
}
struct output
{
std::uint8_t array[3];
}
output pack2into3(input vals)
{
// Assume bit 0 is the most significant bit
std::uint8_t first = (vals.a >> 4) & 0xFF; // Shift down 4 and mask in the lowest 8 bits of a (bit 4-12).
std::uint8_t second_high = vals.a & 0x0F; // Mask in the lowest 4 bits of a (bit 12-16).
std::uint8_t second low = (vals.b >> 8) & 0x0F; // Shift down 8 and mask the lowest 4 bits of b (bit 4-8).
std::uint8_t second = (second_high << 4) | second_low; // Or together the bits from a and b which makes up the second octet.
std::uint8_t third = vals.b & 0xFF; // Mask in the lowest 8 bits of b (bit 8-16).
return { { first, second, third } };
}
评论
1赞
Lundin
8/31/2023
int
从不用于嵌入式系统,也从未用于任何处理按位算术的系统。始终使用无符号类型,并且强烈建议使用类型。stdint.h
0赞
Mestkon
8/31/2023
@Lundin 这是正确的,但更容易输入:)int
1赞
Lundin
8/31/2023
编程就是输入大量文本。对此有疑问的人应该考虑不同的职业。就我个人而言,我可以设法在一秒钟左右的时间内输入uint32_t......对我来说够快了,有什么急的。或者,如果您真的不喜欢输入大量毫无意义的文本,也许可以放弃并在文件顶部使用......std::uint8_t
using std::uint8_t
2赞
Lundin
8/31/2023
#2
这通常是通过使用足够大的 8 位倍数的类型来解决的。因此,在您的情况下,您将使用并忽略 4 个顶部位。uint16_t
但是,如果您确实需要一个由多个 12 位相邻块组成的位字段,则可以改用数组。示例(假设大端序):uint8_t
uint8_t can_data[8] =
{
(data0 & 0xFF0) >> 4,
(data0 & 0x00F) << 4 | (data1 & 0xF00) >> 8,
(data1 & 0x0FF),
...
};
那是:
- data0 位 11-4 到第 1 字节位 7-0
- data0 位 3-0 到第 2 字节位 7-4
- data1 位 11-8 到第 2 字节位 3-0
- 数据 1 位 7-0 到第 3 字节位 7-0
...等等。正如你所知道的,这是一个非常麻烦的格式。这是一种空间优化,以牺牲可读性和性能为代价。因此,除非万不得已,否则我们通常会避免使用这种格式。实际上,我自己在现实世界中使用了非常相似的东西,当时我设计了一个包含多个 12 位模拟值的紧凑型无线协议。
在CAN的世界里,CANopen决定将所有模拟值设为16位,尽管实际上没有多少设备使用这种高分辨率。不过,它使他们免于像上面的位包装练习那样大惊小怪,因此这可能是对行业标准的正确呼吁。
评论
0赞
Mestkon
8/31/2023
这是一个比我的更深入的答案。好答案。
0赞
98coolmen
9/1/2023
谢谢。可悲的是,我需要编程的CAN报文布局需要两个12位整数,所以你帮了我很多。但仍然存在一些问题:它还没有 100% 起作用。为什么代码的 3.line 中有一个位掩码?移动数据时,4 位无论如何都会丢弃其余部分,为什么位掩码比uint16_t本身长,或者我错过了什么?
0赞
98coolmen
9/1/2023
@Lundin第 4 行中的 data0 也不应该向左移动 12 位吗?
0赞
Lundin
9/1/2023
@98coolmen 位掩码是 16 位中的 12 位,用于选择行所针对的 12 位中的哪一部分。通过使用 12 位位掩码,我们可以获得更多自记录代码(否则可以用很多不同的方式编写)。每行的移位是将数据移动到 8 位字节内的正确位置,仅此而已。
0赞
98coolmen
9/1/2023
@Lundin啊,我想我现在明白了。但遗憾的是仍然没有运气。信息仍然是错误的。这里可能是 dbc 文件中的相关部分。data0 : 28|12@1+ (0.5,-400) [-400|1647.5] "mbar" Vector__XXX
data1 : 16|12@1+ (0.025,0) [0|102.375] "%" Vector__XXX
评论
&
(double)(round(INPUT))
std::round
double
double
INPUT