[RA0 & RA2] 使用瑞萨移植并验证软件算法-09 大小端转换篇

[复制链接]
407|3
暮雨之仁 发表于 2026-4-28 23:37 | 显示全部楼层 |阅读模式
移植, , 使用, ,
    跨架构通信 / 跨设备 Flash 读取绝对刚需、纯位操作零复杂运算、资源占用为零的轻量级工具 —— 大小端转换通用封装。支持16/32/64 位单值转换、任意长度数组批量转换、自动检测当前系统端序(适配瑞萨 RA Cortex-M 默认小端 + 其他可能的大端场景),在瑞萨 e²s IDE + RA6M5 上实测通过.


一、函数封装介绍
大小端是指多字节数据在内存中的存储顺序:

  • 小端(Little Endian,LE):低字节存低地址(瑞萨 RA Cortex-M 默认,x86/x64 也默认)
  • 大端(Big Endian,BE):高字节存低地址(TCP/IP 网络协议、8051 部分外设、部分工业协议默认)

函数放在 endian_utils.c 和 endian_utils.h 中,覆盖所有常用场景,还加了端序检测宏(编译时或运行时可选,默认运行时检测,兼容性更好)。

头文件(endian_utils.h)
  1. #ifndef ENDIAN_UTILS_H
  2. #define ENDIAN_UTILS_H

  3. #include <stdint.h>
  4. #include <stddef.h>
  5. #include <stdbool.h>

  6. // 端序检测:运行时检测(默认,兼容性好)
  7. // 若需编译时检测,可注释下面的宏,取消注释上面的
  8. // #define ENDIAN_IS_LITTLE 1
  9. // #define ENDIAN_IS_BIG 0
  10. bool endian_is_little(void);

  11. // 16位单值大小端互换
  12. uint16_t endian_swap16(uint16_t val);
  13. // 16位单值:小端→大端
  14. uint16_t endian_le16_to_be16(uint16_t le_val);
  15. // 16位单值:大端→小端
  16. uint16_t endian_be16_to_le16(uint16_t be_val);

  17. // 32位单值大小端互换
  18. uint32_t endian_swap32(uint32_t val);
  19. // 32位单值:小端→大端
  20. uint32_t endian_le32_to_be32(uint32_t le_val);
  21. // 32位单值:大端→小端
  22. uint32_t endian_be32_to_le32(uint32_t be_val);

  23. // 64位单值大小端互换
  24. uint64_t endian_swap64(uint64_t val);
  25. // 64位单值:小端→大端
  26. uint64_t endian_le64_to_be64(uint64_t le_val);
  27. // 64位单值:大端→小端
  28. uint64_t endian_be64_to_le64(uint64_t be_val);

  29. // 16位数组批量大小端互换
  30. void endian_swap16_array(uint16_t *arr, size_t count);
  31. // 16位数组批量:小端→大端
  32. void endian_le16_to_be16_array(uint16_t *arr, size_t count);
  33. // 16位数组批量:大端→小端
  34. void endian_be16_to_le16_array(uint16_t *arr, size_t count);

  35. // 32位数组批量大小端互换
  36. void endian_swap32_array(uint32_t *arr, size_t count);
  37. // 32位数组批量:小端→大端
  38. void endian_le32_to_be32_array(uint32_t *arr, size_t count);
  39. // 32位数组批量:大端→小端
  40. void endian_be32_to_le32_array(uint32_t *arr, size_t count);

  41. // 64位数组批量大小端互换
  42. void endian_swap64_array(uint64_t *arr, size_t count);
  43. // 64位数组批量:小端→大端
  44. void endian_le64_to_be64_array(uint64_t *arr, size_t count);
  45. // 64位数组批量:大端→小端
  46. void endian_be64_to_le64_array(uint64_t *arr, size_t count);

  47. #endif
实现文件(endian_utils.c)
  1. #include "endian_utils.h"

  2. // 运行时端序检测
  3. bool endian_is_little(void) {
  4.     union {
  5.         uint16_t u16;
  6.         uint8_t u8[2];
  7.     } test_union = {0x1234};
  8.     return (test_union.u8[0] == 0x34);
  9. }

  10. // 16位单值大小端互换
  11. uint16_t endian_swap16(uint16_t val) {
  12.     return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
  13. }

  14. // 16位单值:小端→大端
  15. uint16_t endian_le16_to_be16(uint16_t le_val) {
  16.     return endian_is_little() ? endian_swap16(le_val) : le_val;
  17. }

  18. // 16位单值:大端→小端
  19. uint16_t endian_be16_to_le16(uint16_t be_val) {
  20.     return endian_is_little() ? endian_swap16(be_val) : be_val;
  21. }

  22. // 32位单值大小端互换
  23. uint32_t endian_swap32(uint32_t val) {
  24.     return ((val & 0x000000FF) << 24) |
  25.            ((val & 0x0000FF00) << 8)  |
  26.            ((val & 0x00FF0000) >> 8)  |
  27.            ((val & 0xFF000000) >> 24);
  28. }

  29. // 32位单值:小端→大端
  30. uint32_t endian_le32_to_be32(uint32_t le_val) {
  31.     return endian_is_little() ? endian_swap32(le_val) : le_val;
  32. }

  33. // 32位单值:大端→小端
  34. uint32_t endian_be32_to_le32(uint32_t be_val) {
  35.     return endian_is_little() ? endian_swap32(be_val) : be_val;
  36. }

  37. // 64位单值大小端互换
  38. uint64_t endian_swap64(uint64_t val) {
  39.     return ((val & 0x00000000000000FFULL) << 56) |
  40.            ((val & 0x000000000000FF00ULL) << 40) |
  41.            ((val & 0x0000000000FF0000ULL) << 24) |
  42.            ((val & 0x00000000FF000000ULL) << 8)  |
  43.            ((val & 0x000000FF00000000ULL) >> 8)  |
  44.            ((val & 0x0000FF0000000000ULL) >> 24) |
  45.            ((val & 0x00FF000000000000ULL) >> 40) |
  46.            ((val & 0xFF00000000000000ULL) >> 56);
  47. }

  48. // 64位单值:小端→大端
  49. uint64_t endian_le64_to_be64(uint64_t le_val) {
  50.     return endian_is_little() ? endian_swap64(le_val) : le_val;
  51. }

  52. // 64位单值:大端→小端
  53. uint64_t endian_be64_to_le64(uint64_t be_val) {
  54.     return endian_is_little() ? endian_swap64(be_val) : be_val;
  55. }

  56. // 16位数组批量大小端互换
  57. void endian_swap16_array(uint16_t *arr, size_t count) {
  58.     for (size_t i = 0; i < count; i++) {
  59.         arr[i] = endian_swap16(arr[i]);
  60.     }
  61. }

  62. // 16位数组批量:小端→大端
  63. void endian_le16_to_be16_array(uint16_t *arr, size_t count) {
  64.     if (endian_is_little()) {
  65.         endian_swap16_array(arr, count);
  66.     }
  67. }

  68. // 16位数组批量:大端→小端
  69. void endian_be16_to_le16_array(uint16_t *arr, size_t count) {
  70.     if (endian_is_little()) {
  71.         endian_swap16_array(arr, count);
  72.     }
  73. }

  74. // 32位数组批量大小端互换
  75. void endian_swap32_array(uint32_t *arr, size_t count) {
  76.     for (size_t i = 0; i < count; i++) {
  77.         arr[i] = endian_swap32(arr[i]);
  78.     }
  79. }

  80. // 32位数组批量:小端→大端
  81. void endian_le32_to_be32_array(uint32_t *arr, size_t count) {
  82.     if (endian_is_little()) {
  83.         endian_swap32_array(arr, count);
  84.     }
  85. }

  86. // 32位数组批量:大端→小端
  87. void endian_be32_to_le32_array(uint32_t *arr, size_t count) {
  88.     if (endian_is_little()) {
  89.         endian_swap32_array(arr, count);
  90.     }
  91. }

  92. // 64位数组批量大小端互换
  93. void endian_swap64_array(uint64_t *arr, size_t count) {
  94.     for (size_t i = 0; i < count; i++) {
  95.         arr[i] = endian_swap64(arr[i]);
  96.     }
  97. }

  98. // 64位数组批量:小端→大端
  99. void endian_le64_to_be64_array(uint64_t *arr, size_t count) {
  100.     if (endian_is_little()) {
  101.         endian_swap64_array(arr, count);
  102.     }
  103. }

  104. // 64位数组批量:大端→小端
  105. void endian_be64_to_le64_array(uint64_t *arr, size_t count) {
  106.     if (endian_is_little()) {
  107.         endian_swap64_array(arr, count);
  108.     }
  109. }


二、使用方法
  • 端序检测:调用 endian_is_little() 确认当前系统端序(可选,转换函数已自动适配);
  • 单值转换:根据需求调用对应函数(如 TCP/IP 网络数据用 endian_be32_to_le32() 转成本地小端);
  • 数组批量转换:适合处理传感器数据包、固件配置块等连续多字节数据,直接传数组指针和元素个数即可。


三、移植方法
  • 将 endian_utils.c 和 endian_utils.h 复制到 e²s 工程目录(如 src/utils/);
  • 在 e²s 中右键工程 → Add Files,将 endian_utils.c 加入编译;
  • 若需编译时端序检测(节省 1-2 字节 Flash 和极短的运行时间),可在头文件中注释运行时检测的宏,取消注释编译时检测的宏(需提前确认目标系统端序);
  • 在需要使用的文件中包含头文件:#include "endian_utils.h";
  • 按场景调用对应函数即可。


三、调用函数代码验证
    本次贴合瑞萨 RATCP/IP 网络通信 + 跨设备 Flash 读取的真实场景:

1007769f0d1e1ceaac.png
    验证代码(基于瑞萨 RA6M5 + e²s IDE)
  1. #include "hal_data.h"
  2. #include "endian_utils.h"
  3. #include <stdio.h>
  4. #include <string.h>

  5. void uart_print(const char *str) {
  6.     R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t *)str, strlen(str));
  7. }

  8. void endian_test(void) {
  9.     char buf[256];
  10.     union {
  11.         uint16_t u16;
  12.         uint8_t u8[2];
  13.     } test16;
  14.     union {
  15.         uint32_t u32;
  16.         uint8_t u8[4];
  17.     } test32;
  18.     union {
  19.         uint64_t u64;
  20.         uint8_t u8[8];
  21.     } test64;
  22.     uint16_t arr16[] = {0x1234, 0x5678, 0x9ABC};
  23.     uint32_t arr32[] = {0x12345678, 0x9ABCDEF0};

  24.     // 1. 端序检测
  25.     uart_print("--- 1. 端序检测 ---\r\n");
  26.     snprintf(buf, sizeof(buf), "当前系统端序:%s\r\n\r\n", endian_is_little() ? "小端(Little Endian)" : "大端(Big Endian)");
  27.     uart_print(buf);

  28.     // 2. 16位单值转换+内存验证
  29.     uart_print("--- 2. 16位单值转换+内存验证 ---\r\n");
  30.     test16.u16 = 0x1234;
  31.     snprintf(buf, sizeof(buf), "本地小端内存:%02X %02X\r\n", test16.u8[0], test16.u8[1]);
  32.     uart_print(buf);
  33.     uint16_t be16 = endian_le16_to_be16(test16.u16);
  34.     // 用临时union看大端内存(注意:be16在本地小端系统中存的是“大端的数值”,内存顺序还是小端,需手动反转看逻辑)
  35.     snprintf(buf, sizeof(buf), "转换后大端数值:0x%04X,逻辑内存:%02X %02X\r\n\r\n", be16, (uint8_t)(be16 >> 8), (uint8_t)(be16 & 0xFF));
  36.     uart_print(buf);

  37.     // 3. 32位单值转换+内存验证
  38.     uart_print("--- 3. 32位单值转换+内存验证 ---\r\n");
  39.     test32.u32 = 0x12345678;
  40.     snprintf(buf, sizeof(buf), "本地小端内存:%02X %02X %02X %02X\r\n", test32.u8[0], test32.u8[1], test32.u8[2], test32.u8[3]);
  41.     uart_print(buf);
  42.     uint32_t be32 = endian_le32_to_be32(test32.u32);
  43.     snprintf(buf, sizeof(buf), "转换后大端数值:0x%08X,逻辑内存:%02X %02X %02X %02X\r\n\r\n", be32, (uint8_t)(be32 >> 24), (uint8_t)((be32 >> 16) & 0xFF), (uint8_t)((be32 >> 8) & 0xFF), (uint8_t)(be32 & 0xFF));
  44.     uart_print(buf);

  45.     // 4. 64位单值转换+内存验证
  46.     uart_print("--- 4. 64位单值转换+内存验证 ---\r\n");
  47.     test64.u64 = 0x123456789ABCDEF0ULL;
  48.     snprintf(buf, sizeof(buf), "本地小端内存:%02X %02X %02X %02X %02X %02X %02X %02X\r\n", test64.u8[0], test64.u8[1], test64.u8[2], test64.u8[3], test64.u8[4], test64.u8[5], test64.u8[6], test64.u8[7]);
  49.     uart_print(buf);
  50.     uint64_t be64 = endian_le64_to_be64(test64.u64);
  51.     snprintf(buf, sizeof(buf), "转换后大端数值:0x%016llX,逻辑内存:%02X %02X %02X %02X %02X %02X %02X %02X\r\n\r\n", (unsigned long long)be64, (uint8_t)(be64 >> 56), (uint8_t)((be64 >> 48) & 0xFF), (uint8_t)((be64 >> 40) & 0xFF), (uint8_t)((be64 >> 32) & 0xFF), (uint8_t)((be64 >> 24) & 0xFF), (uint8_t)((be64 >> 16) & 0xFF), (uint8_t)((be64 >> 8) & 0xFF), (uint8_t)(be64 & 0xFF));
  52.     uart_print(buf);

  53.     // 5. 16位数组批量转换
  54.     uart_print("--- 5. 16位数组批量转换 ---\r\n");
  55.     snprintf(buf, sizeof(buf), "本地小端数组:");
  56.     uart_print(buf);
  57.     for (size_t i = 0; i < sizeof(arr16)/sizeof(arr16[0]); i++) {
  58.         snprintf(buf, sizeof(buf), "0x%04X ", arr16[i]);
  59.         uart_print(buf);
  60.     }
  61.     uart_print("\r\n");
  62.     endian_le16_to_be16_array(arr16, sizeof(arr16)/sizeof(arr16[0]));
  63.     snprintf(buf, sizeof(buf), "转换后大端数值数组:");
  64.     uart_print(buf);
  65.     for (size_t i = 0; i < sizeof(arr16)/sizeof(arr16[0]); i++) {
  66.         snprintf(buf, sizeof(buf), "0x%04X ", arr16[i]);
  67.         uart_print(buf);
  68.     }
  69.     uart_print("\r\n\r\n");

  70.     // 6. 32位数组批量转换
  71.     uart_print("--- 6. 32位数组批量转换 ---\r\n");
  72.     snprintf(buf, sizeof(buf), "本地小端数组:");
  73.     uart_print(buf);
  74.     for (size_t i = 0; i < sizeof(arr32)/sizeof(arr32[0]); i++) {
  75.         snprintf(buf, sizeof(buf), "0x%08X ", arr32[i]);
  76.         uart_print(buf);
  77.     }
  78.     uart_print("\r\n");
  79.     endian_le32_to_be32_array(arr32, sizeof(arr32)/sizeof(arr32[0]));
  80.     snprintf(buf, sizeof(buf), "转换后大端数值数组:");
  81.     uart_print(buf);
  82.     for (size_t i = 0; i < sizeof(arr32)/sizeof(arr32[0]); i++) {
  83.         snprintf(buf, sizeof(buf), "0x%08X ", arr32[i]);
  84.         uart_print(buf);
  85.     }
  86.     uart_print("\r\n");
  87. }

  88. void hal_entry(void) {
  89.     R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
  90.     endian_test();
  91.     while (1);
  92. }


五、结果对比
    我们通过串口助手验证数据与与其是否相符:

3609269f0d30da2842.png
    我们换一组数据继续验证:

6135069f0d3a26b8dd.png
    新验证代码(直接替换原 endian_test 函数即可)
  1. void endian_test(void) {
  2.     char buf[256];
  3.     int16_t signed16_val;
  4.     int32_t signed32_val;

  5.     // 1. 端序环境确认
  6.     uart_print("--- 1. 端序环境确认 ---\r\n");
  7.     snprintf(buf, sizeof(buf), "当前系统端序:%s\r\n\r\n", endian_is_little() ? "小端(Little Endian,瑞萨RA默认)" : "大端(Big Endian)");
  8.     uart_print(buf);

  9.     // 2. Modbus温度寄存器解析(16位大端→小端)
  10.     uart_print("--- 2. Modbus温度寄存器解析(16位大端→小端) ---\r\n");
  11.     uint16_t temp_raw = 0xF401; // 小端系统读取到的原始值
  12.     uint16_t temp_real = endian_be16_to_le16(temp_raw);
  13.     snprintf(buf, sizeof(buf), "原始读取值:0x%04X,转换后正确值:0x%04X(十进制:%d,对应温度:%.1f℃)\r\n\r\n",
  14.              temp_raw, temp_real, temp_real, temp_real / 10.0f);
  15.     uart_print(buf);

  16.     // 3. CAN总线32位流量数据解析(32位大端→小端)
  17.     uart_print("--- 3. CAN总线32位流量数据解析(32位大端→小端) ---\r\n");
  18.     uint32_t flow_raw = 0x78563412; // 小端系统读取到的原始值
  19.     uint32_t flow_real = endian_be32_to_le32(flow_raw);
  20.     snprintf(buf, sizeof(buf), "原始读取值:0x%08X,转换后正确值:0x%08X(十进制:%lu)\r\n\r\n",
  21.              flow_raw, flow_real, flow_real);
  22.     uart_print(buf);

  23.     // 4. 负温度传感器数据解析(16位有符号负数,大端→小端)
  24.     uart_print("--- 4. 负温度传感器数据解析(16位有符号负数) ---\r\n");
  25.     uint16_t neg_temp_raw = 0xD8FF; // 小端系统读取到的原始值
  26.     uint16_t neg_temp_u16 = endian_be16_to_le16(neg_temp_raw);
  27.     signed16_val = (int16_t)neg_temp_u16;
  28.     snprintf(buf, sizeof(buf), "原始读取值:0x%04X,转换后正确值:0x%04X(十进制:%d,对应温度:%.1f℃)\r\n\r\n",
  29.              neg_temp_raw, neg_temp_u16, signed16_val, signed16_val / 1.0f);
  30.     uart_print(buf);

  31.     // 5. 负流量累计值解析(32位有符号负数,大端→小端)
  32.     uart_print("--- 5. 负流量累计值解析(32位有符号负数) ---\r\n");
  33.     uint32_t neg_flow_raw = 0x80FFFFFF; // 小端系统读取到的原始值
  34.     uint32_t neg_flow_u32 = endian_be32_to_le32(neg_flow_raw);
  35.     signed32_val = (int32_t)neg_flow_u32;
  36.     snprintf(buf, sizeof(buf), "原始读取值:0x%08X,转换后正确值:0x%08X(十进制:%d)\r\n\r\n",
  37.              neg_flow_raw, neg_flow_u32, signed32_val);
  38.     uart_print(buf);

  39.     // 6. Modbus批量读4个保持寄存器(16位数组批量大端→小端)
  40.     uart_print("--- 6. Modbus批量寄存器解析(16位数组批量转换) ---\r\n");
  41.     uint16_t modbus_arr[] = {0x0201, 0x0403, 0x0605, 0x0807};
  42.     size_t arr16_len = sizeof(modbus_arr)/sizeof(modbus_arr[0]);
  43.     // 打印原始数组
  44.     snprintf(buf, sizeof(buf), "原始读取数组:");
  45.     uart_print(buf);
  46.     for (size_t i = 0; i < arr16_len; i++) {
  47.         snprintf(buf, sizeof(buf), "0x%04X ", modbus_arr[i]);
  48.         uart_print(buf);
  49.     }
  50.     uart_print("\r\n");
  51.     // 批量转换
  52.     endian_be16_to_le16_array(modbus_arr, arr16_len);
  53.     // 打印转换后数组
  54.     snprintf(buf, sizeof(buf), "转换后正确数组:");
  55.     uart_print(buf);
  56.     for (size_t i = 0; i < arr16_len; i++) {
  57.         snprintf(buf, sizeof(buf), "0x%04X ", modbus_arr[i]);
  58.         uart_print(buf);
  59.     }
  60.     uart_print("\r\n\r\n");

  61.     // 7. 多通道传感器批量数据(32位数组批量大端→小端)
  62.     uart_print("--- 7. 多通道传感器批量数据(32位数组批量转换) ---\r\n");
  63.     uint32_t sensor_arr[] = {0x44332211, 0x88776655};
  64.     size_t arr32_len = sizeof(sensor_arr)/sizeof(sensor_arr[0]);
  65.     // 打印原始数组
  66.     snprintf(buf, sizeof(buf), "原始读取数组:");
  67.     uart_print(buf);
  68.     for (size_t i = 0; i < arr32_len; i++) {
  69.         snprintf(buf, sizeof(buf), "0x%08X ", sensor_arr[i]);
  70.         uart_print(buf);
  71.     }
  72.     uart_print("\r\n");
  73.     // 批量转换
  74.     endian_be32_to_le32_array(sensor_arr, arr32_len);
  75.     // 打印转换后数组
  76.     snprintf(buf, sizeof(buf), "转换后正确数组:");
  77.     uart_print(buf);
  78.     for (size_t i = 0; i < arr32_len; i++) {
  79.         snprintf(buf, sizeof(buf), "0x%08X ", sensor_arr[i]);
  80.         uart_print(buf);
  81.     }
  82.     uart_print("\r\n\r\n");

  83.     // 8. 边界极值验证
  84.     uart_print("--- 8. 边界极值验证 ---\r\n");
  85.     uint16_t u16_min = 0x0000, u16_max = 0xFFFF;
  86.     uint32_t u32_min = 0x00000000, u32_max = 0xFFFFFFFF;
  87.     snprintf(buf, sizeof(buf), "16位0x0000转换后:0x%04X\r\n", endian_swap16(u16_min));
  88.     uart_print(buf);
  89.     snprintf(buf, sizeof(buf), "16位0xFFFF转换后:0x%04X\r\n", endian_swap16(u16_max));
  90.     uart_print(buf);
  91.     snprintf(buf, sizeof(buf), "32位0x00000000转换后:0x%08X\r\n", endian_swap32(u32_min));
  92.     uart_print(buf);
  93.     snprintf(buf, sizeof(buf), "32位0xFFFFFFFF转换后:0x%08X\r\n", endian_swap32(u32_max));
  94.     uart_print(buf);
  95. }


2144269f0d3e4991cc.png
注意事项
  • 端序检测的准确性:运行时检测用的是 union,符合 C 标准,兼容性 100%;
  • 批量转换的数组指针:批量转换函数会直接修改原数组,若需保留原数组,可先复制一份再转换;
  • 数值与内存的区别:转换函数返回的是 “目标端序的数值”,在本地小端系统中,这个数值的内存顺序还是小端,但逻辑上是目标端序的(比如 TCP/IP 发送时,直接把这个数值的内存发出去就是大端的逻辑内存);
  • 资源占用:代码约 1KB Flash,无额外 RAM 缓冲区,适合所有 8/16/32/64 位 MCU;
  • 适用场景:适合跨架构通信(TCP/IP、Modbus TCP、CAN FD 扩展帧)、跨设备 Flash 读取(如从 8051 的 Flash 读配置到 RA)、跨平台数据交换
  • 硬件加速:瑞萨 RA 系列部分高端型号(如 RA6M5)的 DMA 或加密引擎可能支持硬件大小端转换,后续有机会再分享~

将爱藏于深海 发表于 2026-6-3 15:23 | 显示全部楼层
我注意到这个工具还支持自动检测系统端序,这对于不确定目标系统端序的情况非常有用。
春日负喧 发表于 2026-6-22 11:10 | 显示全部楼层
我注意到这个工具还支持自动检测系统端序,这对于不同架构的设备来说是一个很大的优势。
风吻过你的不羁 发表于 2026-6-23 20:10 | 显示全部楼层
端序检测和自动适配的功能很棒,可以减少很多手动配置的工作量。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

58

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0