ARM-v7 GCC 环境下的大小端转换实现

1.前言

        什么是大小端转换?为什么叫大小端转换?

        Jonathan Swift的《格列佛游记》中记载,有两国因为剥鸡蛋的方式不同,即一国要求将熟鸡蛋的较大的一端(大端,big endian)敲碎然后剥壳,另一个国家则强制要求敲碎鸡蛋的小端(little endian)。两国无法达成一致,进而交战多年。

        一位名为Danny Cohen的网络协议先驱者,将这两个数据用来描述数据在存储器中的排布方式,进而被广泛使用。例如对于一个四字节数据int data = 0x12345678,将低地址视为敲鸡蛋的地面,则有两种存储分布方式:

156fb2610cc54d039277bd328e672fee.jpeg

 图1 大小端字节序示意图

        如图1所示,data的高字节为0x12,所以左侧的为大端存储顺序,右侧为小端存储顺序。

        CPU的厂家众多,对于数据在存储器中的排布方式,也分为两个派系:

CPU Endian
Intel X86Little-Endian
Power-PC/IBMBig-Endian
ARM默认Little-Endian, 可配置Big-Endian

        大小端模式各有优劣,在小端模式下,指针的强制类型转换不需要调整字节内容,如short * (&data),取的是data低地址的两字节0x56和0x78,对应的也是data较低的16位;大端模式下,数据的符号位固定为第一个字节的最高bit,容易判断符号位,且和人类阅读方式相同,即先写数据的高位,再写数据的低位。

        以上讨论的是字节序大小端,事实上,比特(bit)序也存在大小端模式,规则类似,大端的高bit在低地址,小端相反。在定义C语言结构体时,如果存在位段的定义,则需要使各bit与CPU的大小端一致。例如:

typedef struct
{uint32 b2LatValSts:2;               //bit 31~30 uint32 b2YawRateValSts:2;           //bit 29~28uint32 b3EpbSts:3;                  //bit 27~25uint32 b1BrakePressSts:1;           //bit 24uint32 b2BrakePedlSts:2;            //bit 23~22uint32 b2TurnIndicatorSwtichSts:2;  //bit 21~20uint32 b3EpsSts:3;                  //bit 19~17uint32 b1SteerWhlAngDir:1;          //bit 16uint32 b1SteerWhlAngSpdDir:1;       //bit 15uint32 b15Reserved:15;              //bit 0~14
}BIG_ENDIAN_SAMPLE_ST_TYPE;typedef struct
{uint32 b15Reserved:15;              //bit 0~14uint32 b1SteerWhlAngSpdDir:1;       //bit 15uint32 b1SteerWhlAngDir:1;          //bit 16uint32 b3EpsSts:3;                  //bit 19~17uint32 b2TurnIndicatorSwtichSts:2;  //bit 21~20uint32 b2BrakePedlSts:2;            //bit 23~22uint32 b1BrakePressSts:1;           //bit 24uint32 b3EpbSts:3;                  //bit 27~25uint32 b2YawRateValSts:2;           //bit 29~28uint32 b2LatValSts:2;               //bit 31~30 
}LITTLE_ENDIAN_SAMPLE_ST_TYPE;

        由于Power-PC在网络领域的统治地位,以及其他可能存在的原因,总之网络字节序完成了江湖大一统,统一使用大端字节序。进而,在应用软件开发时,经常需要对网络数据接口进行大小端转换,这个转换主要时针对字节序。那比特序呢?这个通常不需要应用层来做转换,可以姑且理解为某个底层协议栈帮忙做转换了,只需要在应用软件本地按自身CPU的大小端来定义位段即可。

2.大小端转换

2.1 通用的C语言字节序转换方法

#define M_2BYTES_ENDIAN_CONVERT(src)  ((src) = ((typeof(src))0xFF00 & ((src) << 8)) | ((typeof(src))0x00FF & ((src) >> 8)))
#define M_4BYTES_ENDIAN_CONVERT(src)  ((src) = ((typeof(src))0xFF000000 & ((src) << 24)) | ((typeof(src))0x00FF0000 & ((src) << 8))  |((typeof(src))0x0000FF00 & ((src) >> 8)) | ((typeof(src))0x000000FF & ((src) >> 24)))

        这里不多说,具体见上述代码中的两个宏定义。

2.2 CMSIS

        Common Microcontroller Software Interface Standard(CMSIS),是ARM封装的Cortex-M架构微控制器的标准软件接口规范。 显然,这里要说的是ARM针对大小端优化所给出的汇编级指令,但还是通过CMSIS标准接口来使用。

2.2.1 大小端转换的相关ARM指令(Cortex-M

指令解释
REV {condition} Rd, Rn

转换word的字节序,一个word为4字节

REV16 {condition} Rd, Rn转换half word的字节序,half word即为2字节
REVSH {condition} Rd, Rn转换低half word的字节序,并符号拓展至32位(4字节)
RBIT{condition} Rd, Rn对一个32位的word(字)进行比特序大小端转换

        其中,Rd是目标寄存器(destination register), Rn是操作数寄存器(the register holding the operand),conditon是操作条件码。例如:

REV R3, R7   ; Reverse byte order of value in R7 and write it to R3.
REV16 R0, R0 ; Reverse byte order of each 16-bit halfword in R0.
REVSH R0, R5 ; Reverse Signed Halfword of R5 and sing extend to 32 bits and then write to R0
REVHS R3, R7 ; Reverse with Higher or Same condition.
RBIT R7, R8  ; Reverse bit order of value in R8 and write the result to R7

2.2.2 CMSIS接口

        例如在cmsis_gcc.h中定义有以下内联函数:

#define __CMSIS_GCC_USE_REG(r) "r" (r)/**\brief   Reverse byte order (32 bit)\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.\param [in]    value  Value to reverse\return               Reversed value*/
__STATIC_FORCEINLINE uint32_t __REV(uint32_t value)
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)return __builtin_bswap32(value);
#elseuint32_t result;__ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );return result;
#endif
}/**\brief   Reverse byte order (16 bit)\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.\param [in]    value  Value to reverse\return               Reversed value*/
__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value)
{uint32_t result;__ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );return result;
}/**\brief   Reverse byte order (16 bit)\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.\param [in]    value  Value to reverse\return               Reversed value*/
__STATIC_FORCEINLINE int16_t __REVSH(int16_t value)
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)return (int16_t)__builtin_bswap16(value);
#elseint16_t result;__ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) );return result;
#endif
}/**\brief   Reverse bit order of value\details Reverses the bit order of the given value.\param [in]    value  Value to reverse\return               Reversed value*/
__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value)
{uint32_t result;#if ((defined (__ARM_ARCH_7M__      ) && (__ARM_ARCH_7M__      == 1)) || \(defined (__ARM_ARCH_7EM__     ) && (__ARM_ARCH_7EM__     == 1)) || \(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))    )__ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) );
#elseuint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */result = value;                      /* r will be reversed bits of v; first get LSB of v */for (value >>= 1U; value != 0U; value >>= 1U){result <<= 1U;result |= value & 1U;s--;}result <<= s;                        /* shift when v's highest bits are zero */
#endifreturn result;
}

        值得注意的是:

        ①该接口中,包含了使用gcc builtin的转换函数和使用ARM-v7汇编指令两种方式,显然后者效率更高;

        ②int16_t __REVSH(int16_t value)函数中,result的类型是int16_t,即有符号的16位数据类型,运算过程中将拓展后的32位有符号数据截断并返还低16位数据;

3. 总结

        如果当前的CPU是ARM的Cortex-M架构微控制器,建议使用标准的CMSIS接口来进行大小端转换,不仅效率高,软件移植性也会更好。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在CentOS上使用Gunicorn和systemd完整部署Flask应用:详细指南

在现代Web开发中,选择合适的技术栈对于确保应用的稳定性、性能和易于管理至关重要。本篇博客将深入探讨如何在CentOS系统上利用Flask、Gunicorn和systemd的强大组合来部署Web应用。这个全面的流程不仅包括应用的创建和运行,还涉及到如何利用系统服务来管理应用的生命周期,确…

jmeter 业务顺序执行(串行、并行)

简介 当我们在应对某些性能需求时&#xff0c;比如要求测试单个业务串行或并行的响应时间。如果直接设定线程组业务的话&#xff0c;jmeter 会将某个线程组下面的全部业务一起执行&#xff0c;在这种情况下的响应时间是不符合要求的&#xff0c;所以需要勾选指定参数或设置其他…

器件选型【电容,电阻篇】

电阻篇&#xff1a; 一句话先做总结&#xff1a;电阻的选型主要考虑额定电压和过流能力&#xff08;基于封装大小&#xff09; 电阻封装规格越大功率越大。但其功率也与温度有关&#xff0c;如果温度超过 70℃&#xff0c;其额定功率是会下降的。并且&#xff0c;R01005 和 R0…

VUE3项目学习系列--element-plus集成(三)

1、安装依赖 Element-plus官网&#xff1a;快速开始 | Element Plus (element-plus.org) pnpm i element-plus 在项目main.ts中引入element-plus: import { createApp } from "vue"; import App from "./App.vue"; // 从Element官网上参考&#xff0c;…

数据挖掘实战 —— 抖音用户浏览行为数据分析与挖掘(代码部分)

文章: 数据挖掘实战 —— 抖音用户浏览行为数据分析与挖掘(一) 数据挖掘实战 —— 抖音用户浏览行为数据分析与挖掘(二) 数据挖掘实战 —— 抖音用户浏览行为数据分析与挖掘(总) 代码: 数据挖掘实战 —— 抖音用户浏览行为数据分析与挖掘(代码…

代码随想录Day37:单调递增的数字、监控二叉树

单调递增的数字 思路&#xff1a;将数字转换为字符串进行操作&#xff1b;从后往前处理&#xff1b; class Solution { public:int monotoneIncreasingDigits(int n) {string strNum to_string(n);int flag strNum.size();for(int i strNum.size() - 1; i > 0; i--){if…

python识别并控制操作已打开的浏览器进行自动化测试

前提&#xff1a;已安装python和selenium 一、将浏览器以debugger模式打开 打开方法&#xff1a; 1.右击浏览器&#xff0c;选择属性&#xff1a; 2.在目标中加上 --remote-debugging-port9222 --user-data-dir"C:\selenum\AutomationProfile" 二、识别代码 from…

MYSQL的sql性能优化技巧

在编写 SQL 查询时&#xff0c;有一些技巧可以帮助你提高性能、简化查询并避免常见错误。以下是一些 MySQL 的写 SQL 技巧&#xff1a; 1. 使用索引 确保经常用于搜索、排序和连接的列上有索引。避免在索引列上使用函数或表达式&#xff0c;这会导致索引失效。使用 EXPLAIN 关…

Java基础(5) 泛型 日期和时间 线程 File-输入流

泛型 java的泛型有点像ts的泛型 public class ArrayList<T> {private T[] array;private int size;public void add(T e) {...}public void remove(int index) {...}public T get(int index) {...} }// 创建可以存储String的ArrayList: ArrayList<String> strLis…

codeTop01:LRU (最近最少使用) 缓存的实现

问题 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; ● LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 ● int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0c;…

opencart3 添加速卖通商品脚本

非爬虫&#xff0c;只能把速卖通商品信息拿下来解析插入到自己的项目里。 刚接触opencart3没多久&#xff0c;有一些新项目需要添加商品&#xff0c;每次手动从速卖通复制信息又很慢&#xff0c;就自己写了一个脚本。 思路&#xff1a;速卖通商品详情页有一段数据包含了几乎所…

初识Hive

官网地址为&#xff1a; Design - Apache Hive - Apache Software Foundation 一、架构 先来看下官网给的图&#xff1a; 图上显示了Hive的主要组件及其与Hadoop的交互。Hive的主要组件有&#xff1a; UI&#xff1a; 用户向系统提交查询和其他操作的用户界面。截至2011年&…

c++树状数组(超多例题讲解)适合有相应基础的

树状数组&#xff08;Fenwick Tree&#xff09;是一种用于高效计算前缀和的数据结构&#xff0c;具有较小的内存占用和较快的查询、更新操作。它广泛应用于解决一维数组的区间查询问题。 树状数组的原理基于二进制的思想。假设有一个长度为n的数组A&#xff0c;树状数组就是用…

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1638键盘…

C# WinForm AndtUI第三方库 Tree控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Tree控件至窗体。 使用方法集合 1.添加节点、子节点 using AntdUI; private void UpdateTreeView() {Tree tvwTestnew Tree();TreeItem rootTreeItem;TreeItem subTreeItem;Dictionary<str…

高级软件开发知识点

流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题&#xff0c;理解其思想&#xff0c;不要死记最近一家公司所负责的业务和项目&#xff1a; 项目背景、演进之路&#xff0c;有哪个阶段&#xff0c;每个阶段主要做什么项目中技术选型…

【战略前沿】人形机器人制造商Figure获得了OpenAI、Jeff Bezos、Nvidia和其他科技巨头的资助

原文&#xff1a;Humanoid robot-maker Figure gets funding from OpenAI, Jeff Bezos, Nvidia, and other tech giants 作者&#xff1a;ASSOCIATED PRESS ———————————————— Figure成立不到两年&#xff0c;还没有商业产品&#xff0c;但正在说服有影响力的…

STM32 TIM编码器接口

单片机学习&#xff01; 目录 文章目录 前言 一、编码器接口简介 1.1 编码器接口作用 1.2 编码器接口工作流程 1.3 编码器接口资源分布 1.4 编码器接口输入引脚 二、正交编码器 2.1 正交编码器功能 2.2 引脚作用 2.3 如何测量方向 2.4 正交信号优势 2.5 执行逻辑 三、编码器定时…

[DevOps云实践] 彻底删除AWS云资源

[DevOps云实践] 彻底删除AWS云资源 在实际项目当中&#xff0c;我们经常会遇到云设施资源的删除。如果资源删除不干净&#xff0c;时间一长便会被一些莫名其妙的费用所困扰。 本文将把实际项目过程当中&#xff0c;删除资源时候容易遗漏的点讲解给大家。 1. 彻底删除EC2实例 …

[AIGC] Flink中的Max和Reduce操作:区别及使用场景

Apache Flink提供了一系列的操作&#xff0c;用于对流数据进行处理和转换。在这篇文章中&#xff0c;我们将重点关注两种常见的操作&#xff1a;Max和Reduce。虽然这两种操作在表面上看起来类似——都是对数据进行一些形式的聚合&#xff0c;但它们在应用和行为上有一些关键的区…