探秘嵌入式位运算:基础与高级技巧

目录

一、位运算基础知识

1.1. 位运算符

1.1.1. 与运算(&)

1.1.2. 或运算(|)

1.1.3. 异或运算(^)

1.1.4. 取反运算(~)

1.1.5. 双重按位取反运算符(~~)

1.1.6. 逻辑取反(!)

1.1.7. 双重逻辑非运算符 (!!)

1.2. 位移运算

1.2.1. 左移运算(<<)

1.2.2. 右移运算(>>)

二、嵌入式位运算基础应用

2.1. 设置标志位

2.2. 清除标志位

2.3. 检查标志位

2.4. 位域

2.5. 硬件控制

2.6. 内存优化

2.7. 加密和校验

三、嵌入式位运算高级技巧

3.1. 位运算优化

3.2. 位运算实现复杂逻辑

3.3. 位运算与硬件接口

3.4. 利用位运算实现快速判断奇偶性

3.5. 不使用临时变量交换两个数

3.6. 求一个数的二进制中1的个数

3.7. 判断一个数是否是2的幂次方

3.8. 利用位运算实现标志位的设置和清除

四、总结


在嵌入式系统开发中,位运算扮演着举足轻重的角色。它允许开发者直接对二进制位进行操作,从而实现数据的高效处理和系统性能的优化。本文深入探讨嵌入式位运算的基础知识与高级技巧,从基本的位操作如与、或、非、异或,到高级的位运算优化、复杂逻辑实现及硬件接口控制等,全面掌握并灵活应用这一关键技能,提升嵌入式系统的性能和稳定性。

一、位运算基础知识

1.1. 位运算符

1.1.1. 与运算(&)

  • 规则:当两个对应的二进制位都为1时,结果位才为1,否则为0。
  • 示例:5(二进制0101)与3(二进制0011)进行与运算,结果为0001,即1。
  • 应用场景
    • 屏蔽某些特定的位。例如,清除一个整数的低两位。
    • 检查某个位是否为1(通过与一个只有一个位为1的数进行与运算)。
  • 示例代码:
#include <stdio.h>

int main() {
    int a = 5; // 二进制0101
    int b = 3; // 二进制0011
    int result = a & b; // 结果为0001,即1
    printf("a & b = %d\n", result);

    // 清除低两位
    int clear_low_two = a & 0b11111100; // 0b表示二进制,结果为0100,即4
    printf("清除低两位后的a = %d\n", clear_low_two);

    return 0;
}

运行结果: 

 

1.1.2. 或运算(|)

  • 规则:两个对应的二进制位只要有一个为1,结果位就为1。
  • 示例:5(二进制0101)或3(二进制0011)进行或运算,结果为0111,即7。
  • 应用场景
    • 设置某些特定的位。例如,将一个整数的低两位设置为1。
    • 合并两个数的位。
  • 示例代码:
#include <stdio.h>

int main() {
    int a = 5; // 二进制0101
    int b = 3; // 二进制0011
    int result = a | b; // 结果为0111,即7
    printf("a | b = %d\n", result);

    // 设置低两位为1
    int set_low_two = a | 0b00000011; // 结果为0111,即7
    printf("设置低两位为1后的a = %d\n", set_low_two);

    return 0;
}

 

1.1.3. 异或运算(^)

  • 规则:两个对应的二进制位不同时,结果位为1,相同时为0。
  • 示例:5(二进制0101)异或3(二进制0011)进行异或运算,结果为0110,即6。
  • 应用场景
    • 交换两个变量的值而无需使用临时变量。
    • 检查两个数是否相等(如果异或结果为0,则两数相等)。
  • 示例代码:
#include <stdio.h>

int main() {
    int a = 5; // 二进制0101
    int b = 3; // 二进制0011
    int result = a ^ b; // 结果为0110,即6
    printf("a ^ b = %d\n", result);

    // 交换a和b的值
    a = a ^ b; // a = 0111 (7)
    b = a ^ b; // b = 0101 (5), a的值已经传递给b
    a = a ^ b; // a = 0011 (3), b的值已经传递给a
    printf("交换后a = %d, b = %d\n", a, b);

    return 0;
}

 

1.1.4. 取反运算(~)

  • 规则
    • 按位取反运算符~作用于一个整数的二进制表示。
    • 它将二进制表示中的每一位都进行取反操作,即0变为1,1变为0。
    • 结果通常是一个补码表示的整数。
  • 示例:~5(二进制0101)结果为1010,但在有符号整数中,最高位为符号位,因此结果为-6(补码表示)。
  • 应用场景
    • 位掩码操作:用于设置、清除或切换特定位。
    • 某些加密算法中:作为位操作的一部分。
    • 调试和测试:用于检查二进制表示的特定位是否设置。
  • 代码示例
#include <stdio.h>

int main() {
    int a = 5; // 二进制0101
    int result = ~a; // 结果为1010,但在有符号整数中表示为-6(补码)
    printf("~a = %d\n", result);

    // 注意:在有符号整数中,取反后得到的是补码表示的负数
    // 如果需要得到无符号整数的结果,可以使用无符号类型
    unsigned int unsigned_a = 5; // 二进制0101
    unsigned int unsigned_result = ~unsigned_a; // 结果为11111111111111111111111111110101(32位系统),即4294967291(十进制)
    printf("~unsigned_a (unsigned) = %u\n", unsigned_result);

    return 0;
}

 

注意:在C语言中,整数的表示方式(有符号或无符号)会影响取反运算的结果。对于有符号整数,取反后得到的是补码表示的负数;对于无符号整数,取反后得到的是对应位数的全1减去该数的结果。

1.1.5. 双重按位取反运算符(~~)

~~在编程中是一种常用的位运算技巧,主要利用按位取反运算符“~”的特性进行两次取反操作。

  • 规则:
    • 双重按位取反运算符 “~~” 实际上是对一个值先进行一次按位取反操作(将每一位的 0 变为 1,1 变为 0),然后再进行一次按位取反操作。对于整数类型的值,这通常会将一些特殊的值转换为更 “正常” 的整数值。
    • 如果原始值为正整数,第一次取反后变为负数(最高位变为 1),再次取反则变为对应的正整数。如果原始值为负数,第一次取反后变为一个很大的正数(因为负数在计算机中以补码形式存储,取反后得到其绝对值对应的正数减 1 的值),再次取反则变为原来的负数。对于零值,取反两次后仍然是零。
  • 示例:

    • 假设原始值为 5,其二进制表示为 00000101。
      • 第一次取反变为 11111010。
      • 再次取反变为 00000101,即又变回了 5。
    • 若原始值为 -3,以 8 位二进制表示,原码为 10000011,补码为 11111101。
      • 第一次取反变为 00000010。
      • 再次取反变为 11111101,即 -3。
  • 应用场景:
    • 数值转换和规范化:在某些情况下,可能需要将一些特殊的整数值(如从外部输入的可能带有特殊标记的值)转换为正常的整数范围。例如,将可能为 -1 表示无效的值转换为 0 表示有效。可以使用 “~~” 运算符,如果输入值为 -1,取反两次后变为 0。
    • 简洁的布尔值转换:在某些情况下,可以将非零值转换为 1,零值转换为 0,类似于布尔值的真和假。虽然可以使用其他方式(如条件表达式)进行这种转换,但 “~~” 运算符可以提供一种简洁的方式。
    • 处理特殊标志值:在一些编程场景中,可能使用特定的整数值作为标志,通过双重按位取反可以将这些标志值转换为更易于处理的形式。
  • 代码示例:
#include <stdio.h>

int main() {
    int value1 = 5;
    int result1 = ~~value1;
    printf("For value1 = %d, double bitwise NOT result is %d\n", value1, result1);

    int value2 = -3;
    int result2 = ~~value2;
    printf("For value2 = %d, double bitwise NOT result is %d\n", value2, result2);

    int value3 = -1;
    int result3 = ~~value3;
    printf("For value3 = %d, double bitwise NOT result is %d\n", value3, result3);

    return 0;
}

 

在这个示例中,展示了对不同值使用双重按位取反运算符的结果。程序会输出三个值经过双重按位取反后的结果,验证了上述规则和应用场景。 

1.1.6. 逻辑取反(!)

  • 规则
    • 逻辑取反运算符!作用于一个布尔值或可以转换为布尔值的表达式。
    • 它将非零值转换为0(假),将零值转换为1(真)。
  • 示例
    • !0 的结果是1(真),因为0是假。
    • !5 的结果是0(假),因为5是非零值,被视为真。
  • 应用场景
    • 条件判断:在if语句或其他需要布尔值的上下文中使用。
    • 逻辑运算:与&&(逻辑与)和||(逻辑或)结合使用,构建复杂的逻辑表达式。
  • C语言代码示例
#include <stdio.h>

int main() {
    int a = 0;
    int b = 5;
    int result_a = !a; // 逻辑取反,0变为1(真)
    int result_b = !b; // 逻辑取反,非0值变为0(假)
    printf("!%d = %d, !%d = %d\n", a, result_a, b, result_b); // 输出!0 = 1, !5 = 0
    return 0;
}

 

1.1.7. 双重逻辑非运算符 (!!)

  • 规则

    • 第一个逻辑非 “!” 将操作数转换为布尔值并取反,即非零值变为 false,零值变为 true。
    • 第二个逻辑非再次取反,将 false 变为 true,true 变为 false,最终实现将非零值转换为 true,零值转换为 false。
  • 示例:如果有一个变量 x,值为 5。!!x 的结果为 true。如果 x 为 0,!!x 的结果为 false。

  • 应用场景

    • 在需要明确的布尔值的情况下,可以使用 “!!” 将可能是各种类型的值转换为布尔类型。例如,判断一个指针是否为 NULL,或者判断一个数组是否为空。
    • 在一些条件判断中,确保得到一个明确的布尔值可以使代码更加清晰和易于理解。
  • C 语言代码示例: 

int x = 5;
bool y =!!x;
if (y) {
    printf("x is non-zero.\n");
} else {
    printf("x is zero.\n");
}

在这个例子中,由于 x 非零,所以 y 的值为 true,程序将输出 “x is non-zero.”。 

1.2. 位移运算

位移运算分为左移运算和右移运算,它们分别将一个数的二进制表示向左或向右移动指定的位数。

1.2.1. 左移运算(<<)

  • 规则:将一个数的二进制位向左移动指定的位数,相当于乘以2的幂次方。左移会丢弃高位(对于有符号数,可能会导致符号位的丢失,从而改变数的符号),低位补0。
  • 示例:5(二进制0101)左移2位,结果为20(二进制10100)。
  • 应用场景
    • 快速实现乘以2的幂次方的操作。
    • 在某些算法中用于位操作和数据打包。
  • C语言代码示例
#include <stdio.h>

int main() {
    int a = 5; // 二进制0101
    int result = a << 2; // 左移2位,结果为20(二进制10100)
    printf("%d << 2 = %d\n", a, result);

    // 验证左移相当于乘以2的幂次方
    int check_result = a * 4; // 5 * 4 = 20
    printf("%d * 4 = %d\n", a, check_result);

    return 0;
}

 

1.2.2. 右移运算(>>)

  • 规则
    • 对于有符号数,右移时会保留符号位(算术右移)。如果最高位(符号位)是1,则左边填充1;如果是0,则左边填充0。
    • 对于无符号数,右边用0填充(逻辑右移)。
  • 示例:5(二进制0101)右移1位,对于有符号数结果为2(二进制0010);对于无符号数结果也是2(但内部表示可能不同,取决于编译器和平台)。
  • 应用场景
    • 快速实现除以2的幂次方的操作(注意,对于负数结果可能不是简单的除法)。
    • 在某些算法中用于位操作和数据解包。
  • C语言代码示例(有符号数和无符号数的右移):
#include <stdio.h>

int main() {
    int signed_a = 5; // 有符号数,二进制0101
    int signed_result = signed_a >> 1; // 右移1位,结果为2(二进制0010)
    printf("%d >> 1 (signed) = %d\n", signed_a, signed_result);

    unsigned int unsigned_a = 5; // 无符号数,二进制0101
    unsigned int unsigned_result = unsigned_a >> 1; // 右移1位,结果也为2(但内部表示可能不同)
    printf("%u >> 1 (unsigned) = %u\n", unsigned_a, unsigned_result);

    // 验证右移相当于除以2的幂次方
    int check_result = signed_a / 2; // 5 / 2 = 2
    printf("%d / 2 = %d\n", signed_a, check_result);

    // 注意:对于负数,右移的结果可能不是简单的除法
    int negative_a = -5; // 二进制(补码)11111111111111111111111111111011(32位系统)
    int negative_result = negative_a >> 1; // 右移1位,结果为-3(算术右移,保留符号位)
    printf("%d >> 1 (signed, negative) = %d\n", negative_a, negative_result);

    return 0;
}

 

注意

  • 对于有符号数的右移,C语言标准规定使用算术右移,即保留符号位。但是,具体的实现可能依赖于编译器和平台。
  • 对于无符号数的右移,C语言标准规定使用逻辑右移,即右边用0填充。
  • 在处理负数时,右移的结果可能不是简单的除法,因为算术右移会保留符号位并可能导致数值的“向下取整”(向更小的负数方向)。 

二、嵌入式位运算基础应用

在嵌入式系统中,位运算是一种高效且常用的操作手段,它允许开发者在不使用复杂数据结构或额外内存的情况下,对硬件寄存器、状态标志等进行精确控制。

2.1. 设置标志位

使用按位或运算(|)可以将一个数的特定位设置为1,而不影响其他位。这常用于设置硬件寄存器的某个功能位。例如,要设置某个寄存器的第n位为1,可以使用如下操作:寄存器 |= (1 << n)。

#define REGISTER_ADDRESS *((volatile uint32_t*)0x40000000) // 假设寄存器地址
#define BIT_N 3 // 要设置的位

void setBitN() {
    REGISTER_ADDRESS |= (1 << BIT_N); // 设置第BIT_N位为1
}

2.2. 清除标志位

使用按位与运算(&)和取反运算(~)可以将一个数的特定位清零,而不影响其他位。这常用于关闭硬件寄存器的某个功能。例如,要清除某个寄存器的第n位,可以使用如下操作:寄存器 &= ~(1 << n)。

void clearBitN() {
    REGISTER_ADDRESS &= ~(1 << BIT_N); // 清除第BIT_N位
}

2.3. 检查标志位

使用按位与运算,可以检查一个数的特定位是否为1。例如,要检查某个寄存器的第n位是否为1,可以使用如下操作:if (寄存器 & (1 << n))。

int isBitNSet() {
    return (REGISTER_ADDRESS & (1 << BIT_N)) != 0; // 检查第BIT_N位是否为1
}

2.4. 位域

位域是一种将一个或多个字段打包到一个单一的机器字中的数据结构。位域可以有效地压缩存储空间,并且可以提高程序的执行效率。常用于控制寄存器、状态寄存器等。

struct {
    unsigned int bit0 : 1;
    unsigned int bit1 : 1;
    unsigned int bit2 : 1;
    unsigned int bit3 : 1;
    // 其他位域...
} bitField;

// 假设bitField位于某个寄存器的地址上,通过指针访问
#define BITFIELD_ADDRESS *((volatile struct { /* 同上结构 */ }*)0x40000004)

void setBitFieldBit0() {
    BITFIELD_ADDRESS.bit0 = 1; // 设置bit0为1
}

int isBitFieldBit1Set() {
    return BITFIELD_ADDRESS.bit1 != 0; // 检查bit1是否为1
}

注意:直接使用位域访问硬件寄存器时,需要确保位域的结构与硬件寄存器的布局完全匹配,并且编译器不会引入额外的内存对齐或填充。

2.5. 硬件控制

通过位运算可以直接操作硬件寄存器的特定位,实现对硬件设备的控制。例如,设置GPIO引脚的输出状态、配置中断控制器等。 

// 假设有一个GPIO控制寄存器,其中第0位控制GPIO0的输出状态
#define GPIO_CONTROL_REGISTER *((volatile uint32_t*)0x40020000)

void setGPIO0High() {
    GPIO_CONTROL_REGISTER |= (1 << 0); // 设置GPIO0为高电平
}

void setGPIO0Low() {
    GPIO_CONTROL_REGISTER &= ~(1 << 0); // 设置GPIO0为低电平
}

2.6. 内存优化

位运算可以在不使用额外内存的情况下实现一些复杂的逻辑操作,从而节省内存资源。例如,可以使用位掩码来检查、设置或清除多个标志位。

2.7. 加密和校验

位运算可以用于一些简单的加密算法和数据校验算法中。例如,可以使用循环冗余校验(CRC)算法来检测数据传输中的错误。

注意:在实际应用中,加密和校验算法通常比这里提到的要复杂得多,并且需要使用专门的库或硬件加速器来实现。

位运算在嵌入式系统中具有广泛的应用,从硬件控制到内存优化,再到加密和校验,都离不开位运算的支持。通过熟练掌握位运算的基本操作和应用场景,开发者可以编写出更加高效、可靠的嵌入式系统代码。

三、嵌入式位运算高级技巧

在嵌入式系统开发中,位运算不仅是一种基础技能,更是优化性能、实现复杂逻辑和精确控制硬件设备的关键。

3.1. 位运算优化

在嵌入式系统中,性能优化至关重要。位运算因其高效性而常被用于替代乘法、除法等耗时操作。

示例:使用位运算实现乘法(针对2的幂次方)

#define MULTIPLY_BY_TWO(x) ((x) << 1) // 左移一位相当于乘以2
#define MULTIPLY_BY_FOUR(x) ((x) << 2) // 左移两位相当于乘以4
#define DIVIDE_BY_TWO(x) ((x) >> 1) // 右移一位相当于除以2(注意:结果为整数除法)
 
int main() {
    int a = 8;
    int b = MULTIPLY_BY_TWO(a); // b = 16
    int c = DIVIDE_BY_TWO(a); // c = 4
    return 0;
}

注意:上述优化仅适用于乘以或除以2的幂次方的情况。对于其他乘法或除法操作,位运算并不能直接替代,但可以通过查表法、分段线性逼近等方法进行近似优化。

3.2. 位运算实现复杂逻辑

位运算不仅可以用于简单的标志位操作,还可以用于实现复杂的逻辑判断和数据处理。例如,可以使用位运算实现状态机的状态转移、数据的压缩和解压缩等。

示例1:使用位运算实现状态机的状态转移 

typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_ERROR,
    // 其他状态...
} State;
 
State currentState = STATE_IDLE;
 
void stateMachine(uint8_t event) {
    switch (currentState) {
        case STATE_IDLE:
            if (event & 0x01) { // 假设事件0x01表示启动
                currentState = STATE_RUNNING;
            }
            break;
        case STATE_RUNNING:
            if (event & 0x02) { // 假设事件0x02表示错误
                currentState = STATE_ERROR;
            } else if (event & 0x03) { // 假设事件0x03表示停止(包含0x01的启动位,但这里通过逻辑判断忽略)
                currentState = STATE_IDLE;
            }
            break;
        // 其他状态处理...
    }
}

注意:上述示例中的状态机实现较为简单,实际应用中可能需要更复杂的逻辑判断和状态转移条件。

示例2:数据的压缩和解压缩:可以使用位运算来实现简单的数据压缩算法。例如,将多个小的整数打包到一个较大的整数中,通过位操作来提取和解压这些数据。

// 压缩两个 4 位的整数到一个 8 位的整数
unsigned char compressData(unsigned char data1, unsigned char data2) {
    return (data1 << 4) | data2;
}

// 解压一个 8 位的整数得到两个 4 位的整数
void decompressData(unsigned char compressedData, unsigned char *data1, unsigned char *data2) {
    *data1 = (compressedData >> 4) & 0x0F;
    *data2 = compressedData & 0x0F;
}

3.3. 位运算与硬件接口

在嵌入式系统中,许多硬件接口都是通过寄存器进行控制的。通过位运算,可以直接操作这些寄存器,实现对硬件设备的精确控制。例如,可以通过位运算设置GPIO引脚的输出状态、配置定时器的参数等。

示例1:通过位运算设置GPIO引脚的输出状态

#define GPIO_PORT *((volatile uint32_t*)0x40021000) // 假设GPIO端口地址
#define GPIO_PIN 5 // 假设要操作的GPIO引脚编号

void setGPIOPin(uint8_t state) {
    if (state) {
        GPIO_PORT |= (1 << GPIO_PIN); // 设置GPIO引脚为高电平
    } else {
        GPIO_PORT &= ~(1 << GPIO_PIN); // 设置GPIO引脚为低电平
    }
}

示例2:配置定时器的参数:可以使用位运算来设置定时器的各种参数,如预分频值、计数模式等。

// 假设定时器寄存器地址为 0x40010000
volatile unsigned int *timerRegister = (volatile unsigned int *)0x40010000;

// 设置预分频值为 128
*timerRegister &= ~(0xFF << 8);
*timerRegister |= (128 << 8);

3.4. 利用位运算实现快速判断奇偶性

对于一个整数n,如果n & 1的结果为0,则n是偶数;如果结果为1,则n是奇数。 

int isEven(int n) {
    return !(n & 1); // 如果n & 1为0,则n是偶数,返回1(真);否则返回0(假)
}

int isOdd(int n) {
    return n & 1; // 如果n & 1为1,则n是奇数,返回1(真);否则返回0(假)
}

3.5. 不使用临时变量交换两个数

使用异或运算可以实现不借助临时变量交换两个数的值。 

void swap(int *a, int *b) {
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}

注意:虽然这种方法很有趣,但在实际应用中,由于现代编译器的优化和处理器指令集的发展,使用临时变量的交换操作通常与这种方法在性能上相差无几,甚至可能更优。因此,在选择交换方法时,应更关注代码的可读性和可维护性。

3.6. 求一个数的二进制中1的个数

可以通过Brian Kernighan算法(也称为“每次消除最低位的1”算法)来高效求解。 

int countOnes(int n) {
    int count = 0;
    while (n) {
        n &= (n - 1); // 每次消除最低位的1
        count++;
    }
    return count;
}

注意:这种方法的时间复杂度为O(k),其中k是二进制中1的个数。对于稀疏的二进制数(即1的个数较少),这种方法比逐位检查要快。

3.7. 判断一个数是否是2的幂次方

如果一个数n是2的幂次方,那么n的二进制表示中只有一位是1。可以通过判断n & (n - 1)是否为0来确定。

int isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0; // 如果n大于0且n & (n - 1)为0,则n是2的幂次方
}

3.8. 利用位运算实现标志位的设置和清除

在嵌入式系统中,常常需要设置和清除一些标志位。可以使用或运算设置标志位,与运算清除标志位。

// 设置标志位
#define FLAG_BIT 1 << 3
void setFlag(unsigned int *flags) {
    *flags |= FLAG_BIT;
}

// 清除标志位
void clearFlag(unsigned int *flags) {
    *flags &= ~FLAG_BIT;
}

// 检查标志位是否设置
int isFlagSet(unsigned int flags) {
    return flags & FLAG_BIT;
}

位运算在嵌入式系统开发中具有广泛的应用和重要的价值。通过合理使用位运算技巧,可以优化代码性能、实现复杂逻辑判断和精确控制硬件设备。同时,也需要注意代码的可读性和可维护性,在性能与可读性之间找到平衡点。 

四、总结

综上所述,嵌入式位运算是嵌入式开发的核心技能,对系统性能优化至关重要。掌握位运算基础与高级技巧,如位运算优化、复杂逻辑实现、硬件接口控制等,能显著提升代码效率与硬件控制能力。这些技能有助于开发者深入理解嵌入式系统,实现性能与可读性的最佳平衡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/923641.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot - 优雅的实现【账号登录错误次数的限制和锁定】

文章目录 Pre需求实现步骤简易实现1. 添加依赖2. 配置文件3. 自定义注解4. AOP切面5. 使用自定义注解&#xff1a;6. 测试 附总结 Pre SpringBoot - 优雅的实现【流控】 需求 需求描述&#xff1a; 登录错误次数限制&#xff1a;在用户登录时&#xff0c;记录每个账号的登录错…

SRIO DRP动态速率配置说明(详细讲解)

目录 一、SRIO IP时钟结构 1、时钟内部结构 2、时钟直接的关系 3、时钟计算原理 ​二、SRIO DRP介绍 ​1、MMCM DRP配置(xapp888) 2、CPLL DRP配置(ug476) 关于CPLL DRP配置详细介绍&#xff1a; GTX中CPLL、QPLL DRP动态配置方法&#xff08;详解&#xff09;-CSDN博客…

动态规划之背包问题

0/1背包问题 1.二维数组解法 题目描述&#xff1a;有一个容量为m的背包&#xff0c;还有n个物品&#xff0c;他们的重量分别为w1、w2、w3.....wn&#xff0c;他们的价值分别为v1、v2、v3......vn。每个物品只能使用一次&#xff0c;求可以放进背包物品的最大价值。 输入样例…

推荐一款龙迅HDMI2.0转LVDS芯片 LT6211UX LT6211UXC

龙迅的HDMI2.0转LVDS芯片LT6211UX和LT6211UXC是两款高性能的转换器芯片&#xff0c;它们在功能和应用上有所差异&#xff0c;同时也存在一些共同点。以下是对这两款芯片的详细比较和分析&#xff1a; 一、LT6211UX 主要特性&#xff1a; HDMI2.0至LVDS和MIPI转换器。HDMI2.0输…

深度学习模型:循环神经网络(RNN)

一、引言 在深度学习的浩瀚海洋里&#xff0c;循环神经网络&#xff08;RNN&#xff09;宛如一颗独特的明珠&#xff0c;专门用于剖析序列数据&#xff0c;如文本、语音、时间序列等。无论是预测股票走势&#xff0c;还是理解自然语言&#xff0c;RNN 都发挥着举足轻重的作用。…

[STM32]从零开始的STM32 FreeRTOS移植教程

一、前言 如果能看到这个教程的话&#xff0c;说明大家已经学习嵌入式有一段时间了。还记得嵌入式在大多数时候指的是什么吗&#xff1f;是的&#xff0c;我们所说的学习嵌入式大部分时候都是在学习嵌入式操作系统。从简单的一些任务状态机再到复杂一些的RTOS&#xff0c;再到最…

《操作系统 - 清华大学》5 -4:虚拟技术

文章目录 0. 虚拟存储的定义1. 目标2.局部性原理3. 虚拟存储的思路与规则4. 虚拟存储的基本特征5. 虚拟页式存储管理5.1 页表表项5.2 示例 0. 虚拟存储的定义 1. 目标 虚拟内存管理技术&#xff0c;简称虚存技术。那为什么要虚存技术&#xff1f;在于前面覆盖和交换技术&#…

2024APMCM亚太杯数学建模C题【宠物行业】原创论文分享

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年APMCM亚太地区大学生数学建模竞赛C题的成品论文。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 14 二&#xff0e;问题分析 15 2.1问题一 15 2.2问题二 15 2.3问题三…

YOLOv8模型pytorch格式转为onnx格式

一、YOLOv8的Pytorch网络结构 model DetectionModel((model): Sequential((0): Conv((conv): Conv2d(3, 64, kernel_size(3, 3), stride(2, 2), padding(1, 1))(act): SiLU(inplaceTrue))(1): Conv((conv): Conv2d(64, 128, kernel_size(3, 3), stride(2, 2), padding(1, 1))(a…

零基础3分钟快速掌握 ——Linux【终端操作】及【常用指令】Ubuntu

1.为啥使用Linux做嵌入式开发 能广泛支持硬件 内核比较高效稳定 原码开放、软件丰富 能够完善网络通信与文件管理机制 优秀的开发工具 2.什么是Ubuntu 是一个以桌面应用为主的Linux的操作系统&#xff0c; 内核是Linux操作系统&#xff0c; 具有Ubuntu特色的可视…

VScode 连不上远程云服务器

今天下午写代码&#xff0c;打开 VScode 突然发现连不上云服务器了&#xff0c;一开始以为自己密码输错了&#xff0c;试了好多次&#xff0c;依然是这样的 经过查资料发现&#xff0c;应该是版本的自动升级导致的&#xff01;解决方案如下&#xff1a; 1、删除 windows 端的 …

图像分割——区域增长

一 区域增长 图像灰度阈值分割技术都没有考虑到图像像素空间的连通性。区域增长法则正好相反,顾及像素的连接性. 方法&#xff1a;1&#xff09;选择一个或一组种子&#xff1b; 2&#xff09;选择特征及相似性判决准则&#xff1b; 3&#xff09;从该种子开始向外生长&#x…

音视频相关的一些基本概念

音视频相关的一些基本概念 文章目录 音视频相关的一些基本概念RTTH264profile & levelI帧 vs IDRMP4 封装格式AAC封装格式TS封装格式Reference RTT TCP中的RTT指的是“往返时延”&#xff08;Round-Trip Time&#xff09;&#xff0c;即从发送方发送数据开始&#xff0c;到…

春秋云境 CVE 复现

CVE-2022-4230 靶标介绍 WP Statistics WordPress 插件13.2.9之前的版本不会转义参数&#xff0c;这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下&#xff0c;具有管理选项功能 (admin) 的用户可以使用受影响的功能&#xff0c;但是该插件有一个设置允许低权限用…

Linux—进程概念学习-03

目录 Linux—进程学习—31.进程优先级1.1Linux中的进程优先级1.2修改进程优先级—top 2.进程的其他概念3.进程切换4.环境变量4.0环境变量的理解4.1环境变量的基本概念4.2添加环境变量—export4.3Linux中环境变量的由来4.4常见环境变量4.5和环境变量相关的命令4.6通过系统调用获…

go语言逆向-基础basic

文章目录 go 编译命令 ldflags -w -s的作用和问题使用 file 命令查看文件类型 go 语言逆向参考go ID版本GOROOT和GOPATHGOROOTGOPATHGOROOT和GOPATH的关系示例 go build和 go modpclntab &#xff08;Program Counter Line Table 程序计数器行数映射表&#xff09;Moduledata程…

RAG架构类型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

PostgreSQL详细安装教程

#安装PostgreSQL的yum仓库 sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm#安装PostgreSQL 15版本 sudo yum install -y postgresql15-server#初始化数据库&#xff08;若要自定义数据库存储目录…

uniapp介入极光推送教程 超级详细

直接按照下面教程操作 一步一步来 很快就能 完成 下面的文章非常详细 &#xff0c;我就不班门弄斧了 直接上原文链接 https://blog.csdn.net/weixin_52830464/article/details/143823231

公司金融期末考试题目

公司金融期末考试题 选择题 1.现金折扣和信用条件&#xff08;教材P253&#xff09; 题目类似&#xff1a; 下列不属于信用条件的是&#xff08;&#xff09;。 现金折扣 数量折扣信用期限 折扣期限 给定的信用条件为"1/10&#xff0c;n/40"&#xff0c;则其含义…