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

news2024/7/6 18:19:07

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~28
    uint32 b3EpbSts:3;                  //bit 27~25
    uint32 b1BrakePressSts:1;           //bit 24
    uint32 b2BrakePedlSts:2;            //bit 23~22
    uint32 b2TurnIndicatorSwtichSts:2;  //bit 21~20
    uint32 b3EpsSts:3;                  //bit 19~17
    uint32 b1SteerWhlAngDir:1;          //bit 16
    uint32 b1SteerWhlAngSpdDir:1;       //bit 15
    uint32 b15Reserved:15;              //bit 0~14
}BIG_ENDIAN_SAMPLE_ST_TYPE;

typedef struct
{
    uint32 b15Reserved:15;              //bit 0~14
    uint32 b1SteerWhlAngSpdDir:1;       //bit 15
    uint32 b1SteerWhlAngDir:1;          //bit 16
    uint32 b3EpsSts:3;                  //bit 19~17
    uint32 b2TurnIndicatorSwtichSts:2;  //bit 21~20
    uint32 b2BrakePedlSts:2;            //bit 23~22
    uint32 b1BrakePressSts:1;           //bit 24
    uint32 b3EpbSts:3;                  //bit 27~25
    uint32 b2YawRateValSts:2;           //bit 29~28
    uint32 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);
#else
  uint32_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);
#else
  int16_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) );
#else
  uint32_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 */
#endif
  return 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.coloradmin.cn/o/1500187.html

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

相关文章

NLP_文本数据分析_3(代码示例)

目标 了解文本数据分析的作用.掌握常用的几种文本数据分析方法. 一、 文件数据分析介绍 文本数据分析的作用: 文本数据分析能够有效帮助我们理解数据语料, 快速检查出语料可能存在的问题, 并指导之后模型训练过程中一些超参数的选择. 常用的几种文本数据分析方法: 标签数量分…

【嵌入式——QT】Model/View

【嵌入式——QT】Model/View 基本原理数据模型视图组件代理Model/View结构的一些概念QFileSystemModelQStringListModelQStandardItemModel自定义代理 基本原理 GUI应用程序的一个很重要的功能是由用户在界面上编辑和修改数据&#xff0c;典型的如数据库应用程序&#xff0c;数…

leetcode刷题(javaScript)——字典哈希表相关场景题总结

在 JavaScript 刷题中&#xff0c;字典&#xff08;Dictionary&#xff09;和哈希表&#xff08;Hash Table&#xff09;通常用来存储键值对&#xff0c;提供快速的查找、插入和删除操作。它们在很多算法题目中都有广泛的应用&#xff0c;特别是在需要快速查找元素或统计元素出…

Unity插件之天气系统UniStorm

首先呢&#xff0c;它是一款强大的动态昼夜天气系统&#xff0c;能够以较快的帧速率创建AAA级动态生成的天气、照明和天空&#xff0c;并且具有300多个可定制的组件&#xff0c;允许用户创建任何可以想象的环境。 第一步&#xff1a;他需要两个物体Camera摄像机、Player播放器…

用于回归的概率模型

机器学习中的回归方法&#xff1a; 机器学习中的概率模型 机器学习&#xff5c;总结了11种非线性回归模型&#xff08;理论代码可视化&#xff09; 高斯过程回归&#xff1a; Gaussian Processes for Machine Learning GPML——Datasets and Code Gaussian Processes 学…

淘宝商品详情数据丨商品搬家丨商品采集丨商城建站

淘宝商品详情数据、商品搬家、商品采集以及商城建站都是电子商务领域的重要环节&#xff0c;它们共同构成了一个完整的在线销售体系。下面我将分别对这几个概念进行详细的解释。 请求示例&#xff0c;API接口接入Anzexi58 一、淘宝商品详情数据 淘宝商品详情数据指的是在淘宝…

react native封装ScrollView,实现(滑到底部)和(滑到顶部+手指继续向下滑)时拉取新数据

里面的tw是在react native中使用tailwind的第三方库 只求读者把样式看个大概&#xff0c;主要还是功能的实现 ScrollView的官方文档如下 https://reactnative.cn/docs/scrollview import tw from twrnc import { View, Text, ScrollView, RefreshControl } from react-native …

LED显示控制芯片SM5166PF:可消除LED显示屏拖影

在数字化时代的浪潮下&#xff0c;LED显示屏作为信息传播的重要媒介&#xff0c;其显示质量和效果日益受到人们的关注。尤其在商业广告、体育赛事、公共信息发布等领域&#xff0c;高清、流畅、稳定的显示效果显得尤为重要。然而&#xff0c;传统的LED显示屏在刷新率和显示效果…

金智维售前总监屈文浩,将出席“ISIG-RPA超级自动化产业发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;RPA中国、AIGC开放社区、LowCode低码时代&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索R…

在Linux以命令行方式(静默方式/非图形化方式)安装MATLAB(正版)

1.根据教程&#xff0c;下载windows版本matlab&#xff0c;打开图形化界面&#xff0c;选择linux版本的只下载不安装 2.获取安装文件夹 3.获取许可证 4.安装 &#xff08;1&#xff09;跳过引用文章的2.2章节 &#xff08;2&#xff09;本文的安装文件夹代替引用文章的解压IS…

使用Julia语言及R语言进行格拉布斯检验

在日常的计量检测工作中经常会处理各种数据&#xff0c;在处理数据之前会提前使用格拉布斯准则查看数据中是否存在异常值&#xff0c;如果存在异常值的话应该重新进行计量检测&#xff0c;没有异常值则对数据进行下一步操作。判断异常值常用的格拉布斯方法基于数据来自正态分布…

【leetcode热题】环形链表 II

难度&#xff1a; 中等通过率&#xff1a; 30.1%题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个链表&#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数…

SqlServer 默认值约束示例

创建表&#xff0c;创建时指定 money 字段默认值为0.00&#xff1b; create table t_24 ( account varchar(19) not null, id_card char(18) not null, name varchar(20) not null, money decimal(16,2) default 0.00 not null ); 录入2条记录&#xff0c;money字…

Windows Docker 部署 MySQL

部署 MySQL 打开 Docker Desktop&#xff0c;切换到 Linux 容器。然后在 PowerShell 执行下面命令&#xff0c;即可启动一个 MySQL 服务。这里安装的是 8.3.0 Tag版本&#xff0c;如果需要安装其他或者最新版本&#xff0c;可以到 Docker Hub 进行查找。 docker run -itd --n…

在微信小程序里的ecahrts图表,tooltip中内容有黑色阴影的问题

tooltip并没有设置文字阴影相关配置&#xff0c;但是实际真机测试出来有黑色阴影&#xff08;本地编译器没有阴影&#xff09;&#xff0c; 经过研究发现&#xff0c;需要在tooltip中加上以下配置就OK了 tooltip: {// .....textStyle:{textShadowColor:transparent,//文字块背景…

go|一道算法题引发的思考|slice底层剖析

文章目录 引发思考的一道算法题slicemake初始化切片扩容原理切片截取原理切片复制原理算法题的正解 补充string和[]byte互转string 与[]byte相互转换 引发思考的一道算法题 链接&#xff1a;组合 给定两个整数 n 和 k&#xff0c;返回 1 … n 中所有可能的 k 个数的组合。 大致…

处理error: remote origin already exists.及其Gitee文件上传保姆级教程

解决error: remote origin already exists.&#xff1a; 删除远程 Git 仓库 git remote rm origin 再添加远程 Git 仓库 git remote add origin &#xff08;HTTPS&#xff09; 比如这样&#xff1a; 然后再push过去就ok了 好多人可能还是不熟悉怎么将文件上传 Gitee:我…

Unity零基础到进阶 | Unity中 屏蔽指定UI点击事件 的多种方法整理

Unity零基础到进阶 | Unity中 屏蔽指定UI点击事件 的多种方法整理一、Unity中 屏蔽透明区域的点击事件1.1 使用Image组件自带的参数检测1.2 根据点击的坐标计算该点的像素值是否满足阈值 二、Unity中屏蔽 不规则图片按钮点击的事件 总结 &#x1f3ac; 博客主页&#xff1a;htt…

LInux-多线程基础概念

文章目录 前言预备页表详解缺页中断页表的映射 一、多线程是什么&#xff1f;轻量级进程 二、Pthread库pthread_create 前言 从本章的多线程开始&#xff0c;我们开始进入Linux系统的尾声&#xff0c;所以&#xff0c;在学习多线程的过程中&#xff0c;我们也会逐步对之前的内…

Spring基础——Spring配置Mybatis连接数据库

目录 Spring配置MyBatis流程1. 添加Mybatis依赖2. 配置MySQL数据库连接池2.1 首先创建jdbc配置文件2.2 配置数据库DataSource 3. 配置MyBatis3.1 配置SqlSessionFactoryBean3.2 配置mybatis Mapper Bean 4. 创建MyBatis Mapper接口6. 测试数据输出 数据库类型&#xff1a;MySQL…