0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

内存是怎么读取数据的

h1654155149.6853 来源:果果小师弟 作者:智果芯 2022-03-30 13:52 次阅读

摘要:你知道内存是怎么读取数据的吗?知道数据是怎么一个一个字节发送的吗?是低字节先发还是高字节先发?是bit0先发还是bit7先发?是从低地址开始读还是从高地址开始读?看完本篇你应该就明白了~

内存的读写永远从低地址开始读/写,从低到高!从低到高!从低到高!重要的话说三遍

大端模式和小端模式

大端模式和小端是实际的字节顺序和存储的地址顺序对应关系的两种模式。

大端模式:高位字节存放在低地址中,低位字节存放在高地址中。最直观的字节序

小端模式:高位字节存放在高地址中,低位字节存放在低地址中。最符合人的思维的字节序,x86、ARM都这么搞(STM32就是小端模式存储)。

用图表示更加容易理解。以unsigned int value = 0x12345678为例,分别按照大端模式和小端模式存放在芯片中。

内存地址 0x00000001 0x00000002 0x00000003 0x00000004
大端模式 0x12 0x34 0x56 0x78
小端模式 0x78 0x56 0x34 0x12

再换一种图示:同样以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value。

6bef331a-afd2-11ec-aa7f-dac502259ad0.png百度百科

不管是大端还是小端模式,我们在读取和存储数据的时候一定都是从内存的低地址依次向高地址读取或写入。另外注意,x86平台是小端的,ARM平台是小端的,而PowerPC平台是大端的。

字节高低位

一般左边为高位,右边为低位(这个高低来自于人类的阅读习惯,数字从左向右,表示由大到小)

一个16位(双字节)的数据,比如0xFF12,那么高位字节就是0xFF,低位是0x12。如果是32位的数据,比如0x12345678。高位字(不是字节)是0x1234,低位字是0x5678。

右边是低位位,左边是高位(人的阅读习惯)

LSB和MSB

最高有效位(most mignificant bit,msb)指的是一个n位二进制数字中的n-1位,具有最高的权值2^(n-1)。有时也指Most Significant Byte(MSB),指多字节序列中具有最大权重的字节。

同理,最低有效位(least significant bit,lsb)和的是一个n位二进制数字中的0位,具有最低的权值2^0。有时也指Least Significant Byte(LSB),指多字节序列中具有最小权重的字节。

所以0x12345678的最高有效字节就是0x12,最低有效字节就是0x78,这样明白了吧!

举个栗子

当选择模数转换器(ADC)时,最低有效位(LSB)这一参数的含义是什么?

对于一个12位串行转换器,它会输出由1或0组成的12位数串。通常,转换器首先送出的是最高有效位(MSB)(即LSB + 11)。有些转换器也会先送出LSB。我们假设先送出的是MSB,然后依次送出MSB-1 (即 LSB + 10)和MSB -2(即LSB + 9)并依次类推。转换器最终送出MSB -11(即LSB)作为位串的末位。

LSB这一术语有着特定的含义,它表示的是数字流中的最后一位,也表示组成满量程输入范围的最小单位。对于12位转换器来说,LSB的值相当于模拟信号满量程输入范围除以2^12 或 4096的商。如果用真实的数字来表示的话,对于满量程输入范围为4.096V的情况,一个12位转换器对应的LSB大小为1mV。但是,将LSB定义为4096个可能编码中的一个编码对于我们的理解是有好处的。

6c02ef72-afd2-11ec-aa7f-dac502259ad0.png截取自某12位ADC芯片数据手册

高位先行msb 、低位先行lsb

高位先行即在传输一个字节的时候先传输高位msb;低位先行即在传输一个字节的时候先传输低位lsb。高位先行和低位先行是针对串行数据传输方式来说的。常见的串行传输方式有串口(UART)、I2C、SPI等。以串口传输方式为例,标准的串口传输方式是低位先行,芯片在通过TX引脚发送数据时,依次发送位0、位1……位7。

串口传输是低位先行

UART在数据传输时,协议规定了数据传输必须是低位先行,看下面的时序图你就知道了~

6c21bf74-afd2-11ec-aa7f-dac502259ad0.png截图自STM32F407中文参考手册

IIC传输是高位先行

IIC的数据和地址均以8位字节传输,MSB 在前。从图中可以清楚地看到:

6c3eb6ba-afd2-11ec-aa7f-dac502259ad0.png截图自STM32F407中文参考手册IIC部分

这一点也反映在代码中,我们随便找一个IIC的读字节和写字节的函数看看:

voidi2c_SendByte(uint8_t_ucByte)
{
uint8_ti;
/*先发送字节的高位bit7*/
for(i=0;i< 8; i++)
 {  
  if (_ucByte & 0x80)
  {
   I2C_SDA_1();
  }
  else
  {
   I2C_SDA_0();
  }
  i2c_Delay();
  I2C_SCL_1();
  i2c_Delay(); 
  I2C_SCL_0();
  if (i == 7)
  {
    I2C_SDA_1(); // 释放总线
  }
  _ucByte <<= 1; /* 左移一个bit */
  i2c_Delay();
 }
}

从第7行代码中可以看到,在发送一个字节时,首先将要发送的字节与0x80进行与运算,取出最高位,然后循环左移8次就可以将一个字节数据发送出去了。你有没有想过为什么这里我们不把要发送的字节与0x01进行与运算,取出最低位,然后循环右移8次也可以将一个字节数据发送出去呢

答:因为我们说了I2C在数据传输时,协议规定了数据传输必须是高位先行,所以你要发送一个字节的数据肯定必须先取出最高位,然后循环左移将数据发出,如果你与上0x01,就是低位先行,虽然你也将一个字节发出去了,但是你发的是歪门邪道的数据,人家单片机也不认识,对吧?你品,你细品

同样在接收一个字节时,接收到的第1位认为是最高位,接收一个字节代码如下:

uint8_ti2c_ReadByte(void)
{
uint8_ti;
uint8_tvalue;
/*读到第1个bit为数据的bit7*/
value=0;
for(i=0;i< 8; i++)
 {
  value <<= 1;
  I2C_SCL_1();
  i2c_Delay();
  if (I2C_SDA_READ())
  {
   value++;
  }
  I2C_SCL_0();
  i2c_Delay();
 }
 return value;
}

所有使用I2C的设备必须遵循I2C协议,必须都是高位先行的,这样才能实现通用性。怎么样?是不是又get到了一个小技巧~

字节序、比特序

字节序就是串行发送多字节时发送的顺序,比如value=0x12345678,按字节发送是0x12、0x34、0x56、0x78顺序还是0x78、0x56、0x34、0x12顺序。

同理,比特序在bit层面进行排序,如果一个字节,指先发bit0还是bit7, 如果是一个Word型,先发bit31还是先发bit0。串口是lsb优先,I2C是msb优先,这里的msb、lsb指的是比特序,二进制位的位置。

验证MCU平台存储方式?

这里以STM32开发单片机的keil平台为例,以下代码如果打印0x04就是小端存储,如果0x01则是大端存储。

因为0x04是低字节,读取数据是从低地址开始读,打印的是data的低地址,所以如果打印出的是0x04就表明低地址存储低字节,就为小端存储。明白了吗?

6c674832-afd2-11ec-aa7f-dac502259ad0.png

#include"sys.h"
#include"delay.h"
#include"usart.h"
#include"led.h"
#include"key.h"
#include"lcd.h"
#include"SEGGER_RTT.h"
#include"math.h"

intmain(void)
{
HAL_Init();//初始化HAL库
Stm32_Clock_Init(8,336,2,7);//设置时钟,168Mhz
delay_init(168);//初始化延时函数
while(1)
{
uint32_tdata=0x01020304;
char*p=(char*)&data;
printf("0x0%x
",*p);//看输出的是0x01还是0x04
delay_ms(1000);
}
}

编译、链接、下载,通过RTT查看试验结果:

6c7cbbd6-afd2-11ec-aa7f-dac502259ad0.pngJLink的RTT查看器

可以看出STM32是小端存储。

总结:内存的读写永远从低地址开始读/写。大小端存储指字节在内存存储方式,X86、ARM平台都是小端存储(低-低),MSB/LSB只发送字节序或者比特序,串口是比特序LSB,IIC是比特序MSB。也有人将MSB、big-endian、大端发送都混为一谈,这时候一般指字节序上MSB。

原文标题:干货|一文带你搞懂内存中数据的读写方式

文章出处:【微信公众号:电子工程世界】欢迎添加关注!文章转载请注明出处。

审核编辑:彭菁
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6504

    浏览量

    87444
  • 内存
    +关注

    关注

    8

    文章

    2737

    浏览量

    72613
  • STM32
    +关注

    关注

    2232

    文章

    10650

    浏览量

    347868
  • 存储数据
    +关注

    关注

    0

    文章

    72

    浏览量

    14019

原文标题:干货|一文带你搞懂内存中数据的读写方式

文章出处:【微信号:电子工程世界,微信公众号:电子工程世界】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    问题:读取数据时报错内存不足

    大家好,最近在处理数据的时候遇到这样一个问题,使用读取电子表格来读一个300M左右的TXT数据文件,运行时间很长并且报错内存不足,有什么办法能解决这个问题吗?谢谢各位了
    发表于 01-18 22:55

    TDMS文件,读取显示内存已满

    读取存储的TDMS波形数据,只有采集1分钟的数据,大概27MB的大小,为什么读取不出来呢?显示没有足够的内存?TDMS不是存储海量
    发表于 09-23 11:33

    一次读取数据占用内存很大

    本帖最后由 elecfans跑堂 于 2015-8-31 10:50 编辑 在编写一个读取wav文件的程序,发现一次读取40M以上的文件,消耗内存1G左右,太吓人了,请高手指点啊http
    发表于 08-31 09:40

    labview的USB通信数据读取的问题

    正在做一个数据采集系统,通信选择USB通信,上位机使用labiew编写,我想问一下,因为下位数据采集时需要一定的时间,怎么让上位机在判断到采集数据已经传到上位机内存时,再利用VISA
    发表于 03-23 12:35

    Linux学习记录——寄存器与内存

    之前搞不懂寄存器与内存的区别 使用汇编指令的时候,Mov, ldr, str 搞不清三者间的区别:Mov 是用于寄存器间的数据传送ldr 是从内存读取
    发表于 01-12 10:43

    利用SRIO接口从FPGA向6678的共享内存发送数据,请问相比于单核从共享内存读取数据会慢多少?

    段发送数据,第一段数据由核0处理,第二段数据由核1处理,第三段……第八段数据由核7处理。想问一下,这种机制相比于单核从共享内存
    发表于 06-25 01:31

    状态机运行没有约束是为什么?

    我一直在研究一个从DDR3内存读取数据并通过以太网发送数据用于PC Matlab数据捕获的系统。我有两台状态机。 1个状态机只有3个状态。写
    发表于 06-14 17:46

    嵌入式C语言一些关键字的相关资料推荐

    要求使用 volatile 声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取数据。遇到这个关键字声明的
    发表于 12-15 08:36

    如何实现EEPROM页写入和连续内存数据读取

    上文:STM32:硬件IIC,实现EEPROM页写入和连续内存数据读取,但是出现数据出错(上)https://blog.csdn.net/qq_45689790/article/det
    发表于 01-11 07:45

    DMA_读取GPIO电平到内存

    DMA_读取GPIO电平到内存,单片机程序
    发表于 01-12 18:19 10次下载

    java之volatile并发

    模型的相关概念 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,
    发表于 09-27 10:20 6次下载
    java之volatile并发

    windows应用程序读取进程的内存工具免费下载

    本文档的主要内容详细介绍的是windows应用程序读取进程的内存工具免费下载。
    发表于 05-27 08:00 1次下载

    CPU是如何调度任务的?

    ②. 1 号核心读取变量 A,由于 CPU 从内存读取数据到 Cache 的单位是 Cache Line,也正好变量 A 和 变量 B 的数据
    的头像 发表于 12-11 16:44 2846次阅读
    CPU是如何调度任务的?

    干货|一文带你搞懂内存数据的读写方式

    摘要:你知道内存是怎么读取数据的吗?知道数据是怎么一个一个字节发送的吗?是低字节先发还是高字节先发?是bit0先发还是bit7先发?是...
    发表于 01-25 17:21 0次下载
    干货|一文带你搞懂<b class='flag-5'>内存</b>中<b class='flag-5'>数据</b>的读写方式

    关于Vivado Non-project,我们应知道的一些问题

    ,同时也便于工程管理。Non-Project模式编译时间会比Project模式短,因为所有数据存储在内存,直接内存读取数据比硬盘
    的头像 发表于 12-15 13:51 1066次阅读