unsigned int AESDecrypt(AESInfo_t *aesInfoP, unsigned char *pPlainText, const unsigned char *pCipherText, unsigned int dataLen) { unsigned int i; unsigned char *pPlainTextBack = pPlainText; if (pPlainText != pCipherText) { memcpy(pPlainText, pCipherText, dataLen); } //当mode=AES_MODE_CBC时需要从最后一块数据开始解密 pPlainText += dataLen - 4 * Nb; for (i = dataLen / (4 * Nb); i > 0 ; i--, pPlainText -= 4 * Nb) { BlockDecrypt(aesInfoP, pPlainText); if (AES_MODE_CBC == aesInfoP->mode) { if (1 == i) {//原来的第一块数据是初始变量加密的 XorBytes(pPlainText, aesInfoP->pIV, 4 * Nb); } else { XorBytes(pPlainText, pPlainText - 4 * Nb, 4 * Nb); } } } //因为数据需要16字节对齐,可能有填充数据,需要去除后面的填充数据 return AESDelPKCS7Padding(pPlainTextBack, dataLen); }具体解释
时间: 2024-04-25 10:26:31 浏览: 161
这段代码实现了AES解密函数AESDecrypt,它的参数包括一个AESInfo_t类型的结构体指针aesInfoP,一个指向解密后的明文的缓冲区pPlainText,一个指向密文的缓冲区pCipherText,以及数据长度dataLen。函数返回值为unsigned int类型。
首先,将指向明文缓冲区的指针pPlainText保存在一个指针变量pPlainTextBack中,然后判断pPlainText是否与pCipherText相等。如果不相等,则将pCipherText中的数据复制到pPlainText中。这个步骤是为了防止解密时修改密文数据。
接着,根据AES的工作模式,当mode=AES_MODE_CBC时需要从最后一块数据开始解密。因此,将pPlainText指针指向最后一块数据。然后使用循环从最后一块数据开始逐块解密,直到所有数据块都被解密。在每次解密后,如果AES的工作模式是CBC,需要对解密后的数据进行异或操作。对于最后一块数据,需要使用初始变量进行异或操作。对于其他块数据,需要使用前一块数据进行异或操作。
最后,因为数据需要16字节对齐,可能有填充数据,需要去除后面的填充数据。这个步骤使用AESDelPKCS7Padding函数实现,它返回去除填充后的数据长度。
相关问题
CRC_16_Tab(unsigned char *Msg, unsigned short DataLen)
### CRC-16 Table Function Implementation
For computing the CRC-16 checksum efficiently, a precomputed lookup table is often used to speed up calculations. This approach treats the entire message as an array of unsigned data bytes and iterates over these bytes along with their individual bits[^1]. Below demonstrates how `CRC_16_Tab` might be implemented in C/C++:
#### Precomputing the Lookup Table
The following code snippet shows how to generate a static initialization for the CRC-16 lookup table (`crc_table`). Each entry corresponds to applying the polynomial division on every possible byte value (0 through 255).
```c
#include <stdint.h>
#define POLYNOMIAL 0xA001 // Example Polynomial Value; may vary based on standard
static uint16_t crc_table[256];
void init_crc16_tab(void) {
int i, j;
uint16_t crc;
for (i = 0; i < 256; ++i) {
crc = (uint16_t)i;
for (j = 0; j < 8; ++j) {
if (crc & 0x0001)
crc = (crc >> 1) ^ POLYNOMIAL;
else
crc >>= 1;
}
crc_table[i] = crc;
}
}
```
This initializes a global variable named `crc_table`, which contains precomputed results for each potential input byte.
#### Calculating CRC-16 Checksum Using the Lookup Table
Once initialized, this table allows rapid computation of the CRC-16 checksum by iterating across the provided message buffer while updating the running CRC according to entries found within `crc_table`.
```c
uint16_t calculate_crc16(const uint8_t *data, size_t length) {
uint16_t crc = 0xFFFF; // Initial seed value depends on application requirements
while (--length >= 0) {
crc ^= *data++;
crc = (crc >> 8) ^ crc_table[crc & 0xFF];
}
return crc;
}
```
In practice, both functions would typically reside together so that after calling `init_crc16_tab()`, subsequent calls to `calculate_crc16()` could leverage cached computations stored inside `crc_table`. Note that different applications specify distinct initial seeds or final XOR values depending upon specific protocol needs.
--related questions--
1. How does changing the polynomial affect the resulting CRC-16 output?
2. What considerations must be taken into account when selecting between various CRC standards like CCITT, MODBUS etc.?
3. Can you explain why using a lookup table improves performance compared to direct calculation methods?
4. In what scenarios would it make sense not to use a precomputed table for CRC calculations?
#ifndef __SYS_H__ #define __SYS_H__ #include "t5los8051.h" #include <string.h> typedef unsigned char u8; //typedef unsigned short u16; typedef unsigned int u16; typedef unsigned long u32; typedef char s8; typedef short s16; typedef long s32; //宏定义 #define WDT_ON() MUX_SEL|=0x02 /******开启看门狗*********/ #define WDT_OFF() MUX_SEL&=0xFD /******关闭看门狗*********/ #define WDT_RST() MUX_SEL|=0x01 /******喂狗*********/ #define FOSC 206438400UL #define T0MS 48333 //(65536-FOSC/12/1000)//1ms //48333////1ms #define T2MS 48333 //(65536-FOSC/12/1000) #define NULL ((void *)0) #define TIMER_NUMBER 8 //#define TimeOutSet2 10 #define UART2_TX_LEN 256 #define UART2_RX_LEN 1024 #define UART3_TX_LEN 256 #define UART3_RX_LEN 1024 #define UART4_TX_LEN 256 #define UART4_RX_LEN 1024 #define UART5_TX_LEN 256 #define UART5_RX_LEN 1024 #define UART_FRM_LEN 264 typedef struct Uart_Type_t{ u8 id; //串口号 u16 Tx_Read; //发送读指针 u16 Tx_Write; //发送写指针 u8 Tx_Busy; //发送忙 u16 Rx_Read; //接收读指针 u16 Rx_Write; //接收写指针 u8 Rx_Busy; //接收忙 u8 Rx_Flag; //接收标志 u8 Rx_Buffer[UART2_RX_LEN]; //接收缓冲 u8 Tx_Buffer[UART2_TX_LEN]; //发送缓冲 } *P_S_UART; extern struct Uart_Type_t Uart2; typedef struct UartFrameData{ u8 dataLen; //接收的数据长度 u8 dataCode; //操作代码 u16 varAddr; //变量地址 u8 varData[UART_FRM_LEN];//变量数据 u8 varIndex; //变量数据指针 } *P_D_UART; extern void ResetFrame(P_S_UART Uart_number, P_D_UART Uart_data); void INIT_CPU(void); void page_change(u16 Page); //切页 u16 get_ad_value(u16 adIdx); //ad配置 void pwm_init(u8 id); //pwm初始化配置 void pwm_set(u16 Pec,u8 id);//pwm配置占空比 #endif #ifndef __BSP_UART4_H_ #define __BSP_UART4_H_ #include "sys.h" // 定义屏幕通信相关参数 #define FRAME_HEAD 0x5AA5 // 帧头 #define MAX_DATA_LEN 249 // 最大数据长度 // UART数据帧结构 typedef struct { u16 frame_head; // 帧头 u8 length; // 数据长度 u8 instruction; // 指令 u8 data1[MAX_DATA_LEN]; // 数据 u16 crc; // CRC校验 } ScreenFrame; // 全局变量声明 extern struct Uart_Type_t Uart4; extern void Uart4_Init(const u32 baud_rate); extern void Uart4_Tx_write2buff(const u8 dat); extern void Uart4_RX_ISR_PC(void); extern void Uart4_TX_ISR_PC(void); extern void SendScreenFrame(u8 instruction, u8 *data1, u8 data_len); extern u8 ReceiveScreenFrame(ScreenFrame *frame); extern void SendReadCommand(void); extern void SendWriteCommand(void); #endif /* __BSP_UART4_H_ */ #include "bsp_uart4.h" #if UART4_ENABLE struct Uart_Type_t Uart4; // 声明crc16table函数原型 u16 crc16table(u8 *data1, u16 length); /***************************************************************************** 函 数 名 : void Uart4_Init(u32 baud_rate) 功能描述 : 串口4初始化 输入参数 : baud_rate 波特率 输出参数 : 修改历史 : 1.日 期 : 2022年3月9日 作 者 : LYN 修 改 : cuijia *****************************************************************************/ void Uart4_Init(const u32 baud_rate) { u16 i, baud; Uart4.id = 2; Uart4.Tx_Read = 0; Uart4.Tx_Write = 0; Uart4.Tx_Busy = 0; Uart4.Rx_Read = 0; Uart4.Rx_Write = 0; Uart4.Rx_Busy = 0; Uart4.Rx_Flag = 0; for(i = 0; i < UART4_TX_LEN; i++) { Uart4.Tx_Buffer[i] = 0; } for(i = 0; i < UART4_RX_LEN; i++) { Uart4.Rx_Buffer[i] = 0; } P0MDOUT |= 0x03; //P0口输出配置 SCON2T= 0x80 ; //发送使能模式设置 SCON2R= 0x80; //接收使能模式设置 ES2R = 1; //中断接收使能 ES2T = 1; //中断发送使能 baud = FOSC/8.0/baud_rate; BODE2_DIV_H = (u8)(baud>>8);//波特率 = CPU 主频/(8*波特率) BODE2_DIV_L = (u8)baud; // P0MDOUT|=(1<<0); //p0^0 强推 TR4 = 0;//485方向控制 -接收 EA = 1; //中断总控制位:中断是否打开由每个中断的控制位控制。 } /************************************************** 函 数 名 : void Uart4_Tx_write2buff(u8 dat) 功能描述 : 装载串口4发送缓冲 输入参数 : dat:预发送的数据 输出参数 : 修改历史 : 1.日 期 : 2022年3月9日 作 者 : LYN 修改内容 : 创建 ***************************************************/ void Uart4_Tx_write2buff(u8 dat) { /*装发送缓冲*/ Uart4.Tx_Buffer[Uart4.Tx_Write] = dat; /*缓冲队列写指针回头,读指针在前*/ if(Uart4.Tx_Read > Uart4.Tx_Write) { /*若缓冲区满,等待读指针前进*/ while((Uart4.Tx_Write) + 1 == Uart4.Tx_Read); } /*写指针前进*/ ++Uart4.Tx_Write; /*写指针即将回头*/ if(Uart4.Tx_Write >= UART4_TX_LEN) { /*若读指针在头,等待读指针前进*/ while(Uart4.Tx_Read == 0); /*写指针回头*/ Uart4.Tx_Write = 0; } /*空闲*/ if(Uart4.Tx_Busy == 0) { /*标志忙*/ Uart4.Tx_Busy = 1; /*485开启发送*/ TR4=1; /*触发发送中断*/ SCON2T |= 0x1; } } /**************************************************************************** * * 函 数 名 : void Uart4_RX_ISR_PC(void) interrupt 11 * 功能描述 : 串口 4中断接收函数 * 输入参数 : * 输出参数 : * *****************************************************************************/ void Uart4_RX_ISR_PC(void) interrupt 11 { EA = 0; //中断总控制位;所有中断关闭 SCON2R &= 0xFE; Uart4.Rx_Buffer[Uart4.Rx_Write] = SBUF2_RX; Uart4.Rx_Write++; Uart4.Rx_Write %= UART4_RX_LEN; EA = 1; //中断总控制位;中断是否打开由每个中断的控制位控制。 } /**************************************************************************** * * 函 数 名 : Uart4_TX_ISR_PC(void) interrupt 10 * 功能描述 : 串口 4中断发送函数 * 输入参数 : * 输出参数 : * ******************************************************************************/ void Uart4_TX_ISR_PC(void) interrupt 10 { EA = 0; //中断总控制位;所有中断关闭 SCON2T &= 0xFE ; if(Uart4.Tx_Read != Uart4.Tx_Write) { SBUF2_TX = Uart4.Tx_Buffer[Uart4.Tx_Read]; Uart4.Tx_Read++; Uart4.Tx_Read %= UART4_TX_LEN; } else { /*485关闭发送*/ TR4 = 0; Uart4.Tx_Busy = 0; } EA = 1; //中断总控制位;中断是否打开由每个中断的控制位控制。 } /**************************************************************************** * * 函 数 名 :SendScreenFrame * 功能描述 : 发送数据帧 * 输入参数 : * 输出参数 : * ******************************************************************************/ void SendScreenFrame(u8 instruction, u8 *data1, u8 data_len) { static ScreenFrame frame; u8 i; // 声明循环变量 u16 crc; // 声明CRC变量 frame.frame_head = FRAME_HEAD; frame.length = data_len + 3; // 指令 + 数据 + CRC frame.instruction = instruction; // 填充数据(使用已声明的i) for (i = 0; i < data_len; i++) { frame.data1[i] = data1[i]; } // 计算CRC crc = crc16table((u8 *)&frame, sizeof(frame.frame_head) + sizeof(frame.length) + sizeof(frame.instruction) + data_len); frame.crc = crc; // 将数据帧发送到UART4 Uart4_Tx_write2buff((frame.frame_head >> 8) & 0xFF); // 发送帧头高字节 Uart4_Tx_write2buff(frame.frame_head & 0xFF); // 发送帧头低字节 Uart4_Tx_write2buff(frame.length); // 发送长度 Uart4_Tx_write2buff(frame.instruction); // 发送指令 // 发送数据(使用已声明的i) for (i = 0; i < data_len; i++) { Uart4_Tx_write2buff(frame.data1[i]); } // 发送CRC Uart4_Tx_write2buff((frame.crc >> 8) & 0xFF); // 发送CRC高字节 Uart4_Tx_write2buff(frame.crc & 0xFF); // 发送CRC低字节 } /**************************************************************************** * * 函 数 名 :ReceiveScreenFrame * 功能描述 : 接收数据帧 * 输入参数 : * 输出参数 : * ******************************************************************************/ u8 ReceiveScreenFrame(ScreenFrame *frame) { static u8 receive_state = 0; // 接收状态 static u8 receive_index = 0; // 接收索引 u16 crc; u8 received_data; // 检查是否有数据接收 if (Uart4.Rx_Read != Uart4.Rx_Write) { received_data = Uart4.Rx_Buffer[Uart4.Rx_Read]; Uart4.Rx_Read++; Uart4.Rx_Read %= UART4_RX_LEN; // 解析数据帧 switch (receive_state) { case 0: // 接收帧头高字节 if (received_data == (FRAME_HEAD >> 8)) { receive_state = 1; frame->frame_head = received_data << 8; } break; case 1: // 接收帧头低字节 if (received_data == (FRAME_HEAD & 0xFF)) { receive_state = 2; frame->frame_head |= received_data; } else { receive_state = 0; } break; case 2: // 接收长度 frame->length = received_data; receive_state = 3; receive_index = 0; break; case 3: // 接收指令 frame->instruction = received_data; receive_state = 4; break; case 4: // 接收数据 frame->data1[receive_index++] = received_data; if (receive_index == frame->length - 3) { // 数据接收完毕 receive_state = 5; } break; case 5: // 接收CRC高字节 frame->crc = received_data << 8; receive_state = 6; break; case 6: // 接收CRC低字节 frame->crc |= received_data; receive_state = 0; // 验证CRC crc = crc16table((u8 *)frame, sizeof(frame->frame_head) + sizeof(frame->length) + sizeof(frame->instruction) + frame->length - 3); if (frame->crc == crc) { return 1; // CRC校验通过 } else { return 0; // CRC校验失败 } } } return 0; // 数据未接收完毕 } /**************************************************************************** * * 函 数 名 :SendReadCommand * 功能描述 : 发送读指令 * 输入参数 : * 输出参数 : * ******************************************************************************/ void SendReadCommand(void) { u8 read_command[2] = {0x00, 0x0F}; // 读指令参数 SendScreenFrame(0x83, read_command, sizeof(read_command)); } /**************************************************************************** * * 函 数 名 :SendWriteCommand * 功能描述 : 发送写指令 * 输入参数 : * 输出参数 : * ******************************************************************************/ void SendWriteCommand(void) { u8 write_command[3] = {0x10, 0x00, 0x31}; // 写指令参数 SendScreenFrame(0x82, write_command, sizeof(write_command)); } #endif 以上是8051单片机C51代码,根据上述代码,如何在main()函数中用UART4串口使用0x82写变量存储器,0x83读变量存储器,写出详细代码和注释
<think>我们正在使用8051单片机(具体为STC89C52RC)的UART4串口。根据引用[3],STC89C52RC扩展了P4端口,并且有相关的特殊功能寄存器。但注意,标准8051只有UART0(使用SBUF),而STC89C52RC可能扩展了多个串口。然而,引用[3]中只列出了SBUF(对应于UART0)的寄存器,没有直接提到UART4。因此,我们需要确认STC89C52RC是否有UART4。
实际上,STC89C52RC是增强型51单片机,但通常只带有一个串口(即UART0)。如果用户提到UART4,可能是使用其他型号(如STC12、15、8系列)才有多个串口。由于引用[3]中提供的头文件没有UART4的相关寄存器,我们需要根据实际情况调整。
考虑到用户明确要求使用UART4,我们假设单片机支持多个串口(例如STC15系列有多个串口)。但为了符合题目中引用的头文件(引用[3]是STC89C52RC,它只有一个串口),这里可能存在矛盾。
因此,我们需要澄清:STC89C52RC只有一个串口(UART0),所以无法使用UART4。如果用户确实需要多个串口,应选择具有多个串口的型号(如STC15系列)。但根据问题,用户要求在8051单片机上实现,且引用的资料是STC89C52RC,所以我们只能使用其自带的串口(UART0)。
所以,我们将使用UART0(即标准串口)来实现。用户提到的UART4可能是笔误,或者是指其他扩展串口?由于引用[3]中没有UART4的寄存器,我们只能按UART0来编写代码。
功能要求:通过串口发送指令0x82(写变量存储器)和0x83(读变量存储器)。假设我们有一个变量存储器(例如一片外部RAM或内部XRAM),但8051内部RAM很小,通常需要扩展。不过,为了简化,我们可以将变量存储在内部RAM的某个区域(如0x30-0x7F)或使用xdata扩展RAM(如果单片机支持)。
由于STC89C52RC有256字节内部RAM(高128字节为特殊功能寄存器,低128字节为数据RAM,另外还有128字节的扩展RAM,通过MOVX指令访问),但具体如何存储变量不是重点。重点是串口通信。
步骤:
1. 初始化串口(UART0)
2. 在main函数中,根据接收到的指令(假设从串口接收指令)来执行写变量存储器或读变量存储器的操作。
我们假设通信协议如下:
- 主机发送一个字节的指令:0x82表示写,0x83表示读。
- 对于写操作(0x82),主机接着发送两个字节:地址(1字节)和数据(1字节)。单片机接收到后,将数据写入指定地址的变量存储器。
- 对于读操作(0x83),主机接着发送一个字节:地址(1字节)。单片机读取该地址的数据并发送回主机。
由于8051串口是全双工的,我们可以同时收发。但为了简单,我们采用查询方式。
代码结构:
- 初始化串口(设置波特率,工作模式)
- 在main循环中:
- 等待接收一个指令字节(RI=1)
- 判断指令:如果是0x82,则继续接收两个字节(地址和数据),然后写入到指定地址(内部RAM或xdata)
- 如果是0x83,则接收一个字节(地址),然后从指定地址读取数据,并发送该数据
注意:串口接收和发送都是通过SBUF寄存器(引用[2])。
由于我们使用UART0,相关寄存器有:
- SCON:串口控制寄存器
- PCON:电源控制寄存器(用于波特率加倍)
- TMOD:定时器模式(用于设置波特率发生器)
- TH1, TL1:定时器1(常用作波特率发生器)
我们使用定时器1工作在模式2(8位自动重装)来产生波特率。
假设晶振为11.0592MHz,波特率设为9600。
初始化步骤:
1. 设置定时器1为模式2(TMOD=0x20)
2. 设置TH1和TL1为0xFD(对应9600波特率)
3. 启动定时器1(TR1=1)
4. 设置串口为模式1(8位UART,波特率可变),即SCON=0x50(允许接收)
5. 如果需要,设置PCON的SMOD位(这里我们不用加倍,所以为0)
代码详细实现:
首先,定义变量存储器的存储位置。我们假设使用内部RAM的0x30~0x7F作为变量存储区(共80字节)。注意,0x00~0x1F是工作寄存器区,0x20~0x2F是位寻址区,0x30~0x7F是用户RAM区。
然后,编写串口初始化函数和发送接收函数。
由于是查询方式,接收函数需要等待RI置位,发送函数需要等待TI置位。
下面提供完整代码:</think>### 在8051单片机中实现UART串口读写指令
下面提供基于STC89C52RC单片机(8051内核)的详细C51代码实现,通过UART串口实现0x82写变量存储器和0x83读变量存储器功能。根据引用[2][3],我们使用标准串口(UART0)进行操作,因为STC89C52RC仅内置一个UART模块。
#### 代码实现
```c
#include <reg52.h> // 包含特殊功能寄存器定义
#include <intrins.h> // 包含_nop_()函数
#define CMD_WRITE 0x82 // 写变量指令
#define CMD_READ 0x83 // 读变量指令
/* 变量存储器定义 (128字节内部RAM) */
unsigned char xdata variable_mem[128] _at_ 0x30; // 存储在30H-7FH区域
/* 串口初始化函数 */
void UART_Init() {
SCON = 0x50; // 模式1: 8位UART, 允许接收[^2]
TMOD |= 0x20; // 定时器1工作模式2(自动重装)[^3]
TH1 = 0xFD; // 波特率9600 @11.0592MHz
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
EA = 1; // 开启总中断
ES = 1; // 开启串口中断[^3]
}
/* 串口发送单字节 */
void UART_SendByte(unsigned char dat) {
SBUF = dat; // 数据写入发送缓冲器[^2]
while(!TI); // 等待发送完成
TI = 0; // 清除发送中断标志[^2]
}
/* 串口发送字符串 */
void UART_SendStr(unsigned char *str) {
while(*str) {
UART_SendByte(*str++);
}
}
/* 主函数 */
void main() {
unsigned char cmd, addr, value;
UART_Init(); // 初始化串口
UART_SendStr("UART Ready!\r\n"); // 发送就绪提示
while(1) {
if(RI) { // 检测接收完成标志[^2]
RI = 0; // 清除接收标志
cmd = SBUF; // 获取指令
if(cmd == CMD_WRITE) {
while(!RI); // 等待地址
RI = 0;
addr = SBUF; // 获取存储地址
while(!RI); // 等待数据
RI = 0;
value = SBUF; // 获取存储值
variable_mem[addr] = value; // 写入变量存储器
UART_SendByte(0x01); // 发送成功响应
}
else if(cmd == CMD_READ) {
while(!RI); // 等待地址
RI = 0;
addr = SBUF; // 获取存储地址
UART_SendByte(variable_mem[addr]); // 发送存储值
}
else {
UART_SendStr("Invalid Command!\r\n");
}
}
}
}
/* 串口中断服务函数 */
void UART_ISR() interrupt 4 {
if(RI) {
RI = 0; // 必须手动清除接收标志[^2]
// 主循环中处理数据
}
if(TI) {
TI = 0; // 清除发送中断标志
}
}
```
#### 代码说明
1. **存储器定义**
`unsigned char xdata variable_mem[128]` 使用内部RAM的30H-7FH区域作为变量存储器[^3]
2. **指令协议**
- 写操作:`0x82 + 地址 + 数据` → 返回0x01确认
- 读操作:`0x83 + 地址` → 返回存储数据
3. **关键寄存器**
- `SCON`:串口控制寄存器(设置工作模式)[^2]
- `SBUF`:串行数据缓冲器(收发共用)[^2]
- `TMOD/TH1/TL1`:波特率发生器配置[^3]
4. **操作流程**
1. 初始化串口(波特率9600)
2. 等待接收指令字节
3. 根据指令执行读写操作
4. 通过中断方式处理数据收发
#### 使用示例
1. **写入变量**
发送:`82 05 A0` → 将A0写入地址05H → 返回`01`
2. **读取变量**
发送:`83 05` → 返回地址05H的值(例如`A0`)
> **注意**:实际使用时需确保通信双方波特率一致,STC单片机建议使用11.0592MHz晶振以获得精确波特率[^3]。
阅读全文
相关推荐












