开发者

How do I convert little Endian to Big Endian using a Perl Script?

开发者 https://www.devze.com 2023-03-22 13:11 出处:网络
I am using the Perl Win32::SerialPort module. In this paticular module I sent over data using the input command. The data that I sent over to a embedded system were scalar data (numbers) using the tra

I am using the Perl Win32::SerialPort module. In this paticular module I sent over data using the input command. The data that I sent over to a embedded system were scalar data (numbers) using the transmit_char function (if it were C it would be integers, but since its a scripting language I am not sure what the internal format is in perl. My guess is that perl always stores all numbers as 32 bit floating points, which are adjusted by the module when transmitting).

Then after sending the data I receive data using the input command. The data that I recieve is probably in binary form, but perl doesn't know how to interpret it. I use the unpack function like this

my $binData = $PortObj->input;
my $hexData = unpack("H*",$binData);

Suppose I transmit 0x4294 over the serial cable, which is a command on the embedded system that I am communicating with, I expect a response of 0x5245. Now the problem is with the endianess: when I unpack I get 0x4552, which is wrong. Is there a way开发者_开发技巧 to correct that by adjusting the binary data. I also tried h*, which gives me 0x5425, which is also not correct.

Note: the data I receive is sent over Byte at a time and the LSB is sent first


Endianess applies to the ordering of bytes of an integer (primarily). You need to know the size of the integer.

Example for 32-bit unsigned:

my $bytes = pack('H*', '1122334455667788');
my @n = unpack('N*', $bytes);
# @n = ( 0x11223344, 0x55667788 );

my $bytes = pack('H*', '4433221188776655');
my @n = unpack('V*', $bytes);
# @n = ( 0x11223344, 0x55667788 );

See pack. Note the "<" and ">" modifiers to control the endianess where of instructions where the default endianess is not the one you want.

Note: If you're reading from the file, you already have bytes. Don't create bytes using pack 'H*'.

Note: If you're reading from the file, don't forget to binmode the handle.


Regarding the example the OP added to his post:

To get 0x5245 from "\x45\x52", use unpack("v", $two_bytes).


What sort of data types are these? Perl's pack has the N and V format specifiers for integers, and Perl 5.10 added the > and < modifiers so you can read shorts, floats, doubles, and quads (and some other types) in the endianness you want.

With these, you read the data in the endianness it uses in the input. After you do that, you have the data internally-represented as the number you expect and you can re-pack them anyway that you like.

For example, the Q format doesn't have an endianness partner like the pair N and V. I'm always going to get the architecture's interpretation of the octet sequence:

my @octets = ( 0x19, 0x36 );

my $bom = pack 'C*', @octets;
my ( $short ) = unpack 'S', $bom;
my $last  =   $short & 0x00FF;
my $first = ( $short & 0xFF00 ) >> 8;

printf "SHORT: %x FIRST: %x LAST: %x\n", $short, $first, $last;

my $quad_format = $first == $octets[0] ? 'Q' : 'Q>';
say "QUAD_FORMAT: $quad_format";

my $data = pack 'C*', 0b11011110, 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC, 0xDD;

my $q = unpack $quad_format, $data;
printf "$quad_format: %x\n", $q;

The output shows that I get the packed value of 0x1936 comes back as 0x3619 with the plain S format. That means that this was run on a little-endian architecture. A same thing will happen with a quad value, so I want to read the quad and tell Perl to interpret get the value then force it to be big-endian (the "big" part of '>' touches the Q) to get the expected internal numerical value:

SHORT: 3619 FIRST: 36 LAST: 19
QUAD_FORMAT: Q>
Q>: deadbeefaabbccdd

I write more about this in Use the > and < pack modifiers to specify the architecture.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号