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,一经查实,立即删除!

相关文章

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;…

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

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

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年&…

基于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;每个阶段主要做什么项目中技术选型…

STM32 TIM编码器接口

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

15-Java责任链模式 ( Chain of Responsibility)

Java责任链模式 摘要实现范例 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;为请求创建了一个接收者对象的链 责任链模式给予请求的类型&#xff0c;对请求的发送者和接收者进行解耦 责任链模式中通常每个接收者都包含对另一个接收者的引用&#xff0c…

头像剪切上传

头像剪切上传 文章说明核心Api示例源码效果展示源码下载 文章说明 本文主要为了学习头像裁剪功能&#xff0c;以及熟悉canvas绘图和转文件的相关操作&#xff0c;参考教程&#xff08;Web渡一前端–图片裁剪上传原理&#xff09; 核心Api 主要就一个在canvas绘图的操作 context…

2.8k star! 用开源免费的edge-tts平替科大讯飞的语音合成服务

edge-tts是github上的一个开源项目&#xff0c;可以免费将文本转为语音&#xff0c;别看它只有2.8k star&#xff0c;替代科大讯飞的收费TTS服务完全没问题&#xff0c;因为这个项目实际是调用的微软edge的在线语音合成服务&#xff0c;支持40多种语言&#xff0c;300多种声音&…

注意力机制(代码实现案例)

学习目标 了解什么是注意力计算规则以及常见的计算规则.了解什么是注意力机制及其作用.掌握注意力机制的实现步骤. 1 注意力机制介绍 1.1 注意力概念 我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的), 是因为我们大脑能够很快把注意力放在事物…

EdgeX Foundry 安装部署

文章目录 一、概述1.官方文档2.Docker Compose 生成器3.创建 docker-compose 文件 二、安装准备1. 克隆服务器2.安装 Docker3.安装 docker-compose 三、非安全模式部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Fou…

Android之Handler原理解析与问题分享

一、Handler运行原理剖析 1.关系剖析图 如果把整个Handler交互看做一个工厂&#xff0c;Thread就是动力MessageQueue是履带Looper是转轴Loooper的loop方法就是开关&#xff0c;当调用loop方法时整个工厂开始循环工作&#xff0c;处理来自send和post提交到MessageQueue的消息&a…

08. Nginx进阶-Nginx动静分离

简介 什么是动静分离&#xff1f; 通过中间件将动态请求和静态请求进行分离。分离资源&#xff0c;减少不必要的请求消耗&#xff0c;减少请求延时。 动静分离的好处 动静分离以后&#xff0c;即使动态服务不可用&#xff0c;静态资源仍不受影响。 动静分离示意图 动静分离…

Day16:信息打点-语言框架开发组件FastJsonShiroLog4jSpringBoot等

目录 前置知识 指纹识别-本地工具-GotoScan&#xff08;CMSEEK&#xff09; Python-开发框架-Django&Flask PHP-开发框架-ThinkPHP&Laravel&Yii Java-框架组件-Fastjson&Shiro&Solr&Spring 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/…