这是一个非常关键的问题,直接关系到如何正确地理解内存中的数据。STM32F103C8T6的Cortex-M3内核明确采用小端存储模式。
一.核心差异:字节的排列顺序
大端和小端,解决的根本问题是:当一个多字节数据(如32位的 0x12345678)存入内存时,它的高位字节和低位字节,谁放在低地址?
-
大端模式 :高位字节在前。像阅读一样从左到右,最重要的部分放在最前面。
-
存储
0x12345678到地址0x20000000:地址: 0x20000000 | 0x20000001 | 0x20000002 | 0x20000003
内容(字节): 0x12 | 0x34 | 0x56 | 0x78
(最高字节
0x12在最低地址)
-
-
小端模式 :低位字节在前。像做加法从个位算起,最不重要的部分放在最前面。
-
存储
0x12345678到地址0x20000000:地址: 0x20000000 | 0x20000001 | 0x20000002 | 0x20000003
内容(字节): 0x78 | 0x56 | 0x34 | 0x12
(最低字节
0x78在最低地址)
-
STM32F103C8T6的Cortex-M3内核明确采用小端模式。
二 小端模式的内存视角与优势
-
变量访问符合直觉:在小端模式下,用不同宽度的指针访问同一地址,得到的是数据的"低位部分",这更符合编程思维。
cs// 假设0x20000000起始存放32位数据 0x12345678 uint32_t *p32 = (uint32_t*)0x20000000; // *p32 == 0x12345678 uint16_t *p16 = (uint16_t*)0x20000000; // *p16 == 0x5678 (低16位) uint8_t *p8 = (uint8_t*) 0x20000000; // *p8 == 0x78 (低8位)在大端模式下,
*p16会得到0x1234,这通常不是我们想要的。 -
计算效率高:CPU做加法、乘法等运算时,通常从最低有效位开始。小端模式让CPU读取到的第一个字节就是最低位,可以立刻开始计算,无需等待或调整字节顺序。
三 必须警惕的实战场景
虽然CPU在内部自动处理字节序,但在数据交换时你必须手动处理:
1. 与外围设备通信 (如传感器、屏幕、其他MCU):
当通过UART、SPI、I2C发送/接收多字节数据时,双方必须约定字节顺序。
cs
// 示例:向一个大端设备发送一个16位数据 0x1234
uint16_t data = 0x1234;
uint8_t buffer[2];
// 错误发送(直接按内存字节序发送,STM32是小端):
// buffer[0] = data & 0xFF; // 得到 0x34
// buffer[1] = (data >> 8) & 0xFF; // 得到 0x12
// 对方大端设备会理解为 0x3412!
// 正确转换后发送(小端->大端转换):
buffer[0] = (data >> 8) & 0xFF; // 先发高字节 0x12
buffer[1] = data & 0xFF; // 后发低字节 0x34
// 通过UART发送buffer
2. 处理网络协议 :
TCP/IP协议明确规定使用大端字节序(网络字节序)。任何网络数据都必须转换。
cs
#include <stdint.h>
// 简化的转换函数(实际使用标准库的htonl/htons)
uint16_t htons(uint16_t host_short) {
return ((host_short & 0xFF) << 8) | ((host_short >> 8) & 0xFF);
}
uint32_t htonl(uint32_t host_long) {
return ((host_long & 0xFF) << 24) |
((host_long & 0xFF00) << 8) |
((host_long >> 8) & 0xFF00) |
((host_long >> 24) & 0xFF);
}
// 使用示例
uint16_t my_port = 12345;
uint16_t network_port = htons(my_port); // 转换为网络字节序
3. 读取Flash中的常量数据 :
当你在代码中定义 const uint32_t value = 0x12345678; 时,编译器会直接按小端模式将其写入Flash。如果你用烧写工具读取Flash的二进制内容,会看到 78 56 34 12 的排列。
4. 调试时查看内存 :
在调试器(如ST-Link Utility)中查看内存 0x20000000,你会看到:
0x20000000: 78 56 34 12 ...
要知道这实际代表32位值 0x12345678。
四 如何验证你的STM32是小端模式?
用一个简单的程序即可验证:
cs
#include <stdint.h>
#include <stdio.h> // 通过串口打印
int main(void) {
uint32_t test_value = 0x12345678;
uint8_t *byte_ptr = (uint8_t*)&test_value;
// 打印每个字节的地址和内容
printf("地址 %p: 0x%02X\n", &byte_ptr[0], byte_ptr[0]);
printf("地址 %p: 0x%02X\n", &byte_ptr[1], byte_ptr[1]);
printf("地址 %p: 0x%02X\n", &byte_ptr[2], byte_ptr[2]);
printf("地址 %p: 0x%02X\n", &byte_ptr[3], byte_ptr[3]);
// 判断
if(byte_ptr[0] == 0x78 && byte_ptr[3] == 0x12) {
printf("这是小端模式!\n");
} else {
printf("这是大端模式!\n");
}
return 0;
}
总结:
STM32F103C8T6采用小端模式 是出于效率考虑和ARM生态的统一。在纯单片机编程时,你几乎感觉不到它的存在。但每当你的系统需要与外部世界(网络、其他设备、PC工具)交换原始字节数据时,字节序问题就会凸显,必须谨慎处理。