MCU中的LSB、MSB和大端模式、小端模式

devtools/2025/1/23 2:42:44/

第一章 LSB和MSB

1.1 最低有效位(Least Significant Bit, LSB)

红外接收器接收了0x45(0100 0101)之后,怎么将这个数据发送给MCU;

LSB(least significant bit):最低有效位优先,例如红外通信是以最低有效位发送和接收的

LSB发送的比特顺序: 1010 0010


当接收到按键数据时,红外接收头将数据按照从低到高,一个bit一个bit的将数据发生转发给MCU的GPIO口;

1.1.1 MCU的接收示例 

int32_t readIRData(uint8_t *pbuff){uint32_t timeout;			// 超时时间uint8_t tempData;			// 保存临时的1Byte数据int8_t i, j;				uint8_t *pData = pbuff;		// 使用一个指针来指向传递进来的数组	/* 红外通讯协议要求引导码为低电平;* 如果检测端口有高电平说明错误*/if(PAin(8) == 1){return -1;}/* 检测端口低电平持续时间 */timeout = 0;while(PAin(8) == 0){timeout++;delay_us(10);/* 红外接收头需要给端口输入9ms左右的低电平“引导码”* delay_us(10) * 1000 = 10000us = 10ms */if(timeout >= 1000){return -2;}}/* 检测端口是高电平持续时间 */timeout = 0;while(PAin(8)){timeout++;delay_us(10);/* 红外接收头需要给端口输入4.5ms左右的高电平“引导码”* delay_us(10) * 500 = 5000us = 5ms */if(timeout >= 500){return -3;}}/* 接收红外接收头的4Byte数据 = 地址码 + !地址码 + 功能码 + !功能码 */for(j = 0; j < 4; j++){/* 接收红外接收头的1Byte数据 */tempData = 0;for(i = 0; i < 8; i++){/* 检测端口是低电平持续时间 */timeout = 0;while(PAin(8) == 0){timeout++;delay_us(10);/* 红外接收头需要给端口输入0.56ms(0.56ms * 1000 = 560us)左右的低电平时序*/if(timeout >= 100){return -4;}}/* * 借助低电平在延时一段时间,检测再检测高电平还是低电平来判断是0还是1* 延时只需要在0.56~1.685ms之间即可 * 如果高电平时间为0.56ms表示传输数据0, 1.685ms表示数据1;*/delay_us(600);/* 延时600us后,检测再检测高电平还是低电平* 如果是高电平,认为红外接收头输入的是数据1,将数据1写入tempData* 否则tempData已经全部初始化为是0,所以不必理会。*/if(PAin(8)){tempData |= 1 << i;/* 检测端口是高电平持续时间 */timeout = 0;while(PAin(8)){timeout++;delay_us(10);/* 已经延时了delay_us(600); 现在再延时2ms。* 总的延时了2.6ms,远远大于1.685ms。所以要报错*/if(timeout >= 200){return -5;}}}}/* 保存一个字节的数据 */pData[j] = tempData;}/* 红外接收头拉低电平50us,一次通讯结束 */delay_us(50);/* 校验数据 */if((pData[0] + pData[1]) == 0xFF){if((pData[2] + pData[3]) == 0xFF){return 0;}}/* 返回错误 */return -6;
}

1.1.2  MUC接收示意 

1.2 最高有效位(Most Significant Bit, MSB)

DHT11温湿度传感器一次完整的数据传输为40bit,高位先出。

数据格式:8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据 + 8bit校验和。

数据传送正确时校验和数据等于“8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据”所得结果的末8位。

温湿度传感器产生了一个字节的数据0xF4(1111 0100)之后,怎么将这个数据发送给MCU;

MSB(Most Significant Bit):最高有效位优先,例如获取温湿度比特流数据的时候是以最高有效位接收的。

MSB发送的比特顺序: 1111 0100


当传感器产生数据时,DHT11将数据按照从高到低,一个bit一个bit的将数据发生转发给MCU的GPIO口;

1.2.1 MCU的接收示例

int32_t readDHT11(uint8_t *pbuff){uint32_t timeout;			// 超时时间uint8_t tempData;			// 保存临时的1Byte数据int8_t i, j;				uint8_t *pData = pbuff;		// 使用一个指针来指向传递进来的数组	uint16_t check_sum = 0;		// 定义一个变量。用于计数效验和/* 配置端口为输出模式 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOG,&GPIO_InitStructure);/* 端口输出低电平 */PGout(9) = 0;delay_ms(20);/* 端口输出高电平 */PGout(9) = 1;delay_us(30);/* 将端口设置为输入模式,准备接受数据 */	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOG, &GPIO_InitStructure);/* 检测端口高电平持续时间 */timeout = 0;while(PGin(9)){timeout++;delay_us(1);/* 高电平时间超过4000ms(DHT11数据手持可知道), 认为DHT11设备损坏 */if(timeout >= 4000){return -1;}}/* 检测端口低电平持续时间 */timeout = 0;while(PGin(9) == 0){timeout++;delay_us(1);/* DHT11需要给端口输入至少80us的低电平时序*/if(timeout >= 85){return -2;}}/* 检测端口是高电平持续时间 */timeout = 0;while(PGin(9)){timeout++;delay_us(1);/* DHT11需要给端口输入至少80us的高电平时序*/if(timeout >= 85){return -3;}}/* 接收DHT11的40bit(5Byte)数据 */for(j = 0; j < 5; j++){/* 接收DHT11的8bit(1Byte)数据 */tempData = 0;for(i = 7; i >= 0; i--){/* 检测端口是低电平持续时间 */timeout = 0;while(PGin(9) == 0){timeout++;delay_us(1);/* DHT11需要给端口输入至少50us的低电平时序*/if(timeout >= 60){return -4;}}/* * 借助低电平在延时一段时间,检测再检测高电平还是低电平来判断是0还是1* 延时只需要在28~70us之间即可 * 如果高电平时间为26~28us表示传输数据0, 28~70us范围内表示数据1;*/delay_us(40);/* 延时40us后,检测再检测高电平还是低电平* 如果是高电平,认为DHT11输入的是数据1,将数据1写入tempData* 否则tempData已经全部初始化为是0,所以不必理会。*/if(PGin(9)){tempData |= 1 << i;/* 检测端口是高电平持续时间 */timeout = 0;while(PGin(9)){timeout++;delay_us(1);/* DHT11需要给端口输入大于70us的高电平时序,认为错误*/if(timeout >= 75){return -5;}}}}/* 保存一个字节的数据 */pData[j] = tempData;}/* DHT11拉低电平50us,一次通讯结束 */delay_us(50);/* 判断效验和 */check_sum = (pData[0] + pData[1] + pData[2] + pData[3]) & 0xFF;if(check_sum != pData[4]){return -6;}/* 全部正确,返回0 */return 0;
}

1.2.2  MUC接收示意

第二章 大端模式和小端模式

2.1 大端模式

在大端模式下,多字节数据的高字节存储在低地址,低字节存储在高地址。也就是说,数据的高位在内存中存放得较前。

举个例子,如果要存储 32 位的十六进制数 0x12345678,在大端模式下,它会被按以下顺序存储在内存中:

地址:    ...     0x01    0x02    0x03    0x04
数据:   ...      0x12    0x34    0x56    0x78

在内存中的存储顺序是从高位到低位,先存储 0x12(最高字节),再存储 0x34,依此类推。

2.1.1 Modbus通讯协议

Modbus协议使用的是大端模式来表示16位和32位的数据类型。

  1. 16位数据---2Byte(表示一个寄存器数据)

例如,Modbus中读取的16位数据 0x1234 将会按以下方式传输:

  • 高字节(MSB)0x12
  • 低字节(LSB)0x34

因此,数据将会被按以下顺序传输:0x12 0x34(大端模式)。

  1. 32位数据----4byte (表示2个寄存器数据)

如果Modbus需要传输32位数据,协议也会将其按照大端模式进行存储,即先发送高字节,再发送低字节。例如,32位数据 0x12345678 会按以下顺序传输:

  • 高字节0x12
  • 次高字节0x34
  • 次低字节0x56
  • 低字节0x78

传输顺序为:0x12 0x34 0x56 0x78(大端模式)。

 

 2.1.1.1 ModbusRTU主机
#include <stdio.h>
#include <stdint.h>// 定义宏 MAKEWORD 来组合两个字节
#define MAKEWORD(a, b) ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8))// CRC计算函数(标准的 CRC-16-IBM 算法)
uint16_t crc16(const uint8_t *data, uint16_t length) {uint16_t crc = 0xFFFF;for (uint16_t i = 0; i < length; ++i) {crc ^= data[i];for (uint8_t j = 0; j < 8; ++j) {if (crc & 0x0001) {crc = (crc >> 1) ^ 0xA001;} else {crc >>= 1;}}}return crc;
}int main() {// 1. 从站地址:0x01// 2. 功能码:0x03// 3. 起始寄存器地址:MAKEWORD(0x00, 0x01) => 0x0001// 4. 寄存器数量:MAKEWORD(0x00, 0x02) => 0x0002// 定义一个足够大的数组来存放 Modbus RTU 请求帧uint8_t request_frame[8];// 填充请求帧数据request_frame[0] = 0x01;   // 从站地址request_frame[1] = 0x03;   // 功能码request_frame[2] = (uint8_t)(MAKEWORD(0x00, 0x01) >> 8); // 起始寄存器地址高字节request_frame[3] = (uint8_t)(MAKEWORD(0x00, 0x01) & 0xFF); // 起始寄存器地址低字节request_frame[4] = (uint8_t)(MAKEWORD(0x00, 0x02) >> 8); // 寄存器数量高字节request_frame[5] = (uint8_t)(MAKEWORD(0x00, 0x02) & 0xFF); // 寄存器数量低字节// 计算 CRC 校验码uint16_t crc = crc16(request_frame, 6); // CRC计算不包含 CRC 字节request_frame[6] = (uint8_t)(crc & 0xFF);       // CRC 低字节request_frame[7] = (uint8_t)((crc >> 8) & 0xFF); // CRC 高字节// 输出 Modbus RTU 请求帧printf("Modbus RTU Request Frame: ");for (int i = 0; i < 8; i++) {printf("%02X ", request_frame[i]);}printf("\n");// 发送数据(在实际应用中可以通过串口发送)// send_data(request_frame, 8); // 伪代码,实际发送数据的函数return 0;
}

Modbus RTU Request Frame: 01 03 00 01 00 02 F7 D4

2.1.1.2 ModbusRTU从机
  • 主机发送

  • 从机解析 
#define MAKEWORD(a, b)      ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8))

假设你调用 MAKEWORD(0x12, 0x34),那么:

  • a = 0x12(低字节)
  • b = 0x34(高字节)

过程如下:

  1. 低字节: (BYTE)(a) 结果是 0x12
  2. 高字节: (WORD)((BYTE)(b)) << 8 结果是 0x34 << 8 = 0x3400

结果是:0x34 12

使用上述宏定义解析主机发送来的帧;

typedef struct TModbusProtocol
{/* 缓冲区 */BYTE pRxd[256];					/* 接受帧缓冲地址 */BYTE pTxd[256];					/* 发送帧缓冲地址 */BYTE byAddress; 				/* 装置地址 *//* 浏览结构 */DWORD dwEventSend;				/* 动作报告浏览指针 *//* 计数器 */WORD wErrorCount;				/* 异常报文计数,CPT3 */WORD wSuccessCount;				/* 成功报文计数,CPT4 */DWORD dwNetStateCount;			/* 网络状态 */
}TModbusProtocol;static TModbusProtocol me;
static BYTE* s_pRxd; 			/* 接受帧缓冲地址 */
static BYTE s_byWritePtr;		/* 发送缓冲区写指针 *//* modbus归约处理 */
BOOL ManageModbusProtocol(void){........./* 读取接收帧数据 */dwLen = hw485_read(me.pRxd, 256);s_pRxd = me.pRxd;.........}/* 读取多个Hold Register */
static BYTE _Frame_03_ReadHoldRegisters(TModbusProtocol* me)WORD wStartAddr = MAKEWORD(s_pRxd[3], s_pRxd[2]);        // 0x00 01WORD wCount = MAKEWORD(s_pRxd[5], s_pRxd[4]);            // 0x00 01/* 个数判断 */if (wCount < 1 || wCount > MAX_HOLDING_REGISTER_COUNT)return ExeptionCode_3_ValidateDataValue;/* 准备工作 */me->pTxd[s_byWritePtr++] = wCount * 2;		/* BYTE Count */
}

 

2.2 小端模式

小端模式模式下,多字节数据的**低字节(LSB)**存储在低地址,**高字节(MSB)**存储在高地址。即数据的低位存放得较前。

同样以 0x12345678 为例,在小端模式下,它会被按以下顺序存储:

内存存储顺序:
地址:    ...     0x01    0x02    0x03    0x04
数据:   ...    0x78    0x56    0x34    0x12

2.3 总结

网络字节序、Modbus协议都是大端模式;

主机字节序是小端模式;

 


http://www.ppmy.cn/devtools/152757.html

相关文章

启效云低代码:从物联网到数联网的应用延伸

随着物联网&#xff08;IOT&#xff09;技术的飞速发展&#xff0c;数据采集与处理成为企业数字化转型的重要一环。物联网平台通过传感器、RFID标签等设备实时采集数据&#xff0c;并利用各种通信协议和存储技术确保数据的准确性和实时性&#xff0c;数据采集和处理为产品的升级…

DM8 SQL 错误 [22000]: 锁超时

问题描述 DM管理工具删除会卡死DBeaver删除会一直加载中使用truncate语句会显示SQL 错误 [22000]: 锁超时使用如下语句也没有用 select a.*,b.NAME,c.SESS_ID, sp_close_session( || c.SESS_ID || ); AS CLOSE_SESSION_COMMAND from v$lock a left join sysobjects b on b.IDa…

Android实战经验篇-AndroidScrcpyClient投屏一

系列文章转如下链接&#xff1a; Android Display Graphics系列文章-汇总 Android实战经验篇-系列文章汇总 本文主要包括部分&#xff1a; 一、方案说明 1.1 适用场景 1.2 方案框架 二、功能演示 2.1 环境准备 2.2 演示 一、方案说明 1.1 适用场景 优秀的开源的scrc…

吴恩达深度学习——建立逻辑回归分类器识别猫

本文来自吴恩达《深度学习》L1W2作业2&#xff0c;仅为个人学习所用。 理论来自吴恩达深度学习——神经网络编程的基础知识 在理论中说明了一些函数的形式&#xff0c;本文不再累述。 文章目录 数据下载相关包lr_utils文件解读 数据预处理加载数据数据预处理 构建过程选用函数计…

deepin分享-Linux 下恢复误删文件

在 Linux 系统中&#xff0c;误删文件是常见的问题&#xff0c;但幸运的是&#xff0c;通过一些工具和方法&#xff0c;我们仍然有可能恢复这些文件。本文将通过一个实际案例&#xff0c;介绍在 Linux 下恢复误删文件的步骤和注意事项。 恢复步骤 停止写入操作 首先&#xff0…

第 3 章 核心处理层(上)

3.1 MyBatis初始化 MyBatis 初始化过程中&#xff0c;除了会读取 mybatis-config.xml 配置文件以及映射配置文件&#xff0c;还会加载配置文件指定的类&#xff0c;处理类中的注解&#xff0c;创建一些配置对象&#xff0c;最终完成框架中各个模块的初始化。 3.1.1 建造者模式…

工业制造离不开的BOM

在制造业的浩瀚星空中&#xff0c;物料清单&#xff08;BOM&#xff09;犹如“北极星”&#xff0c;牢牢指引着产品从设计蓝图迈向实物诞生的全过程。 BOM的分类 按照设计制造的不同阶段&#xff0c;将BOM划分为设计BOM、工艺BOM、制造BOM三种类型。 设计BOM Engineering BO…

Cyber Security 101-Security Solutions-Firewall Fundamentals(防火墙基础)

了解防火墙并亲身体验 Windows 和 Linux 内置防火墙。 任务1&#xff1a;防火墙的用途是什么 我们看到商场、银行、 餐馆和房屋。这些警卫被安置在 这些区域用于检查进出人员。这 维护此检查的目的是确保没有人在没有 被允许。这个警卫充当了他所在区域和访客之间的一堵墙。 …