#pragma pack 用法详解

本文介绍了 #pragma pack 指令,它能为 struct、union 和 class 等成员指定字节对齐边界,仅针对模块。详细说明了 #pragma pack 各用法,如显示当前对齐值、设置对齐值、压栈、出栈等,并通过代码示例展示了不同使用场景下的操作及效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pack 为 struct, union 和 class 等的成员对齐指定字节边界. 与编译选项(属性 -> 配置属性 -> C/C++ ->代码生成 -> 结构成员对齐)的 /Zp 开关不同, 它不针对整个项目, 而仅针对模块, 比如一个编译单元.
 
1. #pragma pack(show)
    以警告信息的形式显示当前字节对齐的值.
2. #pragma pack(n)
    将当前字节对齐值设为 n .
3. #pragma pack()
    将当前字节对齐值设为默认值(通常是8) .
4. #pragma pack(push)
    将当前字节对齐值压入编译栈栈顶.
5. #pragma pack(pop)
    将编译栈栈顶的字节对齐值弹出并设为当前值.
6. #pragma pack(push, n)
    先将当前字节对齐值压入编译栈栈顶, 然后再将 n 设为当前值.
7. #pragma pack(pop, n)
    将编译栈栈顶的字节对齐值弹出, 然后丢弃, 再将 n 设为当前值.
8. #pragma pack(push, identifier)
    将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier .
9. #pragma pack(pop, identifier)
    将编译栈栈中标识为 identifier 位置的值弹出, 并将其设为当前值. 注意, 如果栈中所标识的位置之上还有值, 那会先被弹出并丢弃.
10. #pragma pack(push, identifier, n)
    将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier, 再将 n 设为当前值.
11. #pragma pack(pop, identifier, n)
    将编译栈栈中标识为 identifier 位置的值弹出, 然后丢弃, 再将 n 设为当前值. 注意, 如果栈中所标识的位置之上还有值, 那会先被弹出并丢弃.
   
注意: 如果在栈中没有找到 pop 中的标识符, 则编译器忽略该指令, 而且不会弹出任何值.
   
// 代码段 1: 弹出编译栈的顺序跟压入的顺序相反
#pragma pack(show)     // 8 (默认值)
#pragma pack(push, 16) // 默认值 8 压入编译栈栈顶, 并将当前对齐值设为 16 .
#pragma pack(show)     // 上句设定的 16
#pragma pack(push, 4)  // 上上句 16 压入编译栈栈顶, 并将当前对齐值设为 4 .
#pragma pack(show)     // 上句设定的 4
#pragma pack(push, 2)  // 上上句 4 压入编译栈栈顶, 并将当前对齐值设为 2 .
#pragma pack(show)     // 上句设定的 2
#pragma pack(push, 1)  // 上上句 2 压入编译栈栈顶, 并将当前对齐值设为 1 .
#pragma pack(show)     // 上句设定的 1
#pragma pack(pop)      // 弹出编译栈栈顶的 2 , 并将其设为当前对齐值.
#pragma pack(show)     // 2
#pragma pack(pop)      // 弹出编译栈栈顶的 4 , 并将其设为当前对齐值.
#pragma pack(show)     // 4
#pragma pack(pop)      // 弹出编译栈栈顶的 16 , 并将其设为当前对齐值.
#pragma pack(show)     // 16
#pragma pack(pop)      // 弹出编译栈栈顶的 8 , 并将其设为当前对齐值.
#pragma pack(show)     // 8
 
// 代码段 2: pop 带有参数 n 时, 当前字节对齐值被设为了 n, 而不是从栈顶弹出的之前所压入的值.
#pragma pack(show)     // 8 (默认值)
#pragma pack(push, 16) // 默认值 8 压入编译栈栈顶, 并将当前对齐值设为 16 .
#pragma pack(show)     // 16
#pragma pack(push, 4)  // 上上句 16 压入编译栈栈顶, 并将当前对齐值设为 4 .
#pragma pack(show)     // 4
#pragma pack(push, 2)  // 上上句 4 压入编译栈栈顶, 并将当前对齐值设为 2 .
#pragma pack(show)     // 2
#pragma pack(push, 1)  // 上上句 2 压入编译栈栈顶, 并将当前对齐值设为 1 .
#pragma pack(show)     // 1
#pragma pack(pop, 8)   // 弹出编译栈栈顶的 2 , 然后丢弃, 再将当前对齐值设为 8 .
#pragma pack(show)     // 8
#pragma pack(pop, 1)   // 弹出编译栈栈顶的 4 , 然后丢弃, 再将当前对齐值设为 1 .
#pragma pack(show)     // 1
#pragma pack(pop, 2)   // 弹出编译栈栈顶的 16 , 然后丢弃, 再将当前对齐值设为 2 .
#pragma pack(show)     // 2
#pragma pack(pop, 16)  // 弹出编译栈栈顶的 8 , 然后丢弃, 再将当前对齐值设为 16 .
#pragma pack(show)     // 16
 
// 代码段3: push 和 pop 可以带有标识符, 此标识符能够弹出指定值. 但是, 位于栈中指定值之上的那些值均会被弹出并丢弃. 
#pragma pack(show)                   // 8 (默认值)
#pragma pack(push, identifier_1, 1)  // 默认值 8 压入编译栈栈顶, 并将栈中 8 对应的位置用 identifier_1 标识起来, 然后将当前对齐值设为 1 .
#pragma pack(show)                   // 1
#pragma pack(push, identifier_2, 2)  // 上上句 1 压入编译栈栈顶, 并将栈中 1 对应的位置用 identifier_2 标识起来, 然后将当前对齐值设为 2 .
#pragma pack(show)                   // 2
#pragma pack(push, identifier_3, 4)  // 上上句 2 压入编译栈栈顶, 并将栈中 2 对应的位置用 identifier_3 标识起来, 然后将当前对齐值设为 4 .
#pragma pack(show)                   // 4
#pragma pack(push, identifier_4, 8)  // 上上句 4 压入编译栈栈顶, 并将栈中 4 对应的位置用 identifier_4 标识起来, 然后将当前对齐值设为 8 .
#pragma pack(show)                   // 8
#pragma pack(push, identifier_5, 16) // 上上句 8 压入编译栈栈顶, 并将栈中 8 对应的位置用 identifier_5 标识起来, 然后将当前对齐值设为 16 .
#pragma pack(show)           

 

转:https://blog.youkuaiyun.com/cbNotes/article/details/75467497

<think>我们使用STM32F103C8T6控制二维云台舵机(两个舵机构成,一个控制水平方向,一个控制垂直方向)实现圆形轨迹运动。 思路: 1. 舵机控制:舵机通常通过PWM信号控制,控制信号周期一般为20ms(50Hz),脉冲宽度在0.5ms到2.5ms之间对应角度0°到180°(具体范围可能因舵机而异)。 2. 圆形轨迹:我们可以将圆形分解为水平方向和垂直方向的两个正弦运动(相位差90°)。设圆的参数方程为: $$x = r \cos(\theta) + x_0$$ $$y = r \sin(\theta) + y_0$$ 其中,$r$为半径,$(x_0, y_0)$为圆心位置(在舵机角度空间中),$\theta$为当前角度(随时间变化)。 3. 舵机角度范围:假设两个舵机都可以在0°到180°范围内运动,我们需要将圆形轨迹映射到这个范围内。注意,如果圆形轨迹超出舵机范围,需要调整半径或圆心。 步骤: a. 初始化两个舵机的PWM输出(使用TIM的PWM模式)。 b. 确定圆形轨迹的参数:圆心(x0, y0)和半径r。确保整个圆在舵机可运动范围内(x0±r在0~180,y0±r在0~180)。 c. 在循环中,以一定时间间隔改变角度θ(例如,每20ms增加一个步长),计算当前的水平角度和垂直角度: horizontal_angle = x0 + r * cos(θ) vertical_angle = y0 + r * sin(θ) d. 将计算得到的角度转换为PWM的占空比(脉冲宽度)。 假设舵机角度0°对应0.5ms,180°对应2.5ms,则脉冲宽度(单位ms): pulse_width = 0.5 + (angle / 180.0) * 2.0 然后,将脉冲宽度转换为TIM的CCR值(计数器的比较寄存器值): CCR = (pulse_width / 20) * ARR (ARR为自动重装载值,即一个PWM周期的总计数) e. 更新两个舵机的CCR值。 注意:由于舵机运动有一定惯性,步长不能太大(即θ变化不能太快),否则轨迹会不平滑。另外,如果希望画圆的速度可调,可以调整θ的步长。 硬件连接: - 两个舵机的信号线分别连接到两个PWM输出引脚(例如PA0和PA1,使用TIM2的CH1和CH2) 代码框架(使用标准外设库): 1. 初始化系统时钟和PWM用到的定时器(TIM2或TIM3等)。 2. 配置PWM输出的GPIO和定时器。 3. 在循环中,根据时间改变θ,计算两个舵机的角度,并更新CCR。 下面是一个简化的代码示例(只包含关键部分):</think>### STM32F103C8T6 控制二维云台舵机实现圆形轨迹(标准库实现) #### 1. **硬件连接** - **舵机接口**:两个舵机分别连接TIM2/3/4的PWM输出通道(如TIM2_CH1水平舵机,TIM2_CH2垂直舵机) - **电源**:舵机需独立5V电源(避免MCU电源过载) - **信号线**:舵机信号线接MCU PWM输出引脚(如PA0/PA1) #### 2. **核心原理** - **圆形参数方程**: $$x = x_0 + r \cdot \cos(\omega t)$$ $$y = y_0 + r \cdot \sin(\omega t)$$ 其中: - $(x_0, y_0)$:圆心位置(舵机中位角度) - $r$:半径(角度单位) - $\omega$:角速度(控制运动速度) - **PWM占空比转换**: $$\text{CCR} = \frac{\text{angle}}{180} \times (\text{max\_pulse} - \text{min\_pulse}) + \text{min\_pulse}$$ 舵机角度范围:0°~180° → PWM脉宽:0.5ms~2.5ms(周期20ms) #### 3. **代码实现** ```c #include "stm32f10x.h" // 舵机参数 #define PWM_PERIOD 20000 // 20ms周期(72MHz时钟下) #define MIN_PULSE 500 // 0.5ms对应计数值 #define MAX_PULSE 2500 // 2.5ms对应计数值 #define CENTER_X 90 // 水平舵机中位(90°) #define CENTER_Y 90 // 垂直舵机中位(90°) #define RADIUS 30 // 运动半径(角度) #define OMEGA 0.05 // 角速度系数(控制速度) void PWM_Init(void) { // 1. 开启时钟和GPIO RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置PA0/PA1为复用推挽输出 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. 定时器基础配置 TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InitStruct.TIM_Period = PWM_PERIOD - 1; // ARR值 TIM_InitStruct.TIM_Prescaler = 72 - 1; // 1MHz计数频率 TIM_InitStruct.TIM_ClockDivision = 0; TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_InitStruct); // 4. PWM通道配置 TIM_OCInitTypeDef OC_InitStruct; OC_InitStruct.TIM_OCMode = TIM_OCMode_PWM1; OC_InitStruct.TIM_OutputState = TIM_OutputState_Enable; OC_InitStruct.TIM_Pulse = MIN_PULSE; // 初始脉宽 OC_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &OC_InitStruct); // PA0: 水平舵机 TIM_OC2Init(TIM2, &OC_InitStruct); // PA1: 垂直舵机 // 5. 启动定时器 TIM_Cmd(TIM2, ENABLE); } // 更新舵机角度 void Update_Servo(float angle_x, float angle_y) { // 限制角度范围 angle_x = (angle_x < 0) ? 0 : (angle_x > 180) ? 180 : angle_x; angle_y = (angle_y < 0) ? 0 : (angle_y > 180) ? 180 : angle_y; // 转换为PWM脉宽 uint16_t pulse_x = MIN_PULSE + (MAX_PULSE - MIN_PULSE) * angle_x / 180.0f; uint16_t pulse_y = MIN_PULSE + (MAX_PULSE - MIN_PULSE) * angle_y / 180.0f; // 更新CCR寄存器 TIM_SetCompare1(TIM2, pulse_x); TIM_SetCompare2(TIM2, pulse_y); } int main(void) { PWM_Init(); float t = 0; // 时间参数 while(1) { // 计算当前点坐标 float x = CENTER_X + RADIUS * cos(t); float y = CENTER_Y + RADIUS * sin(t); Update_Servo(x, y); t += OMEGA; // 更新角度 if(t > 2*3.14159) t = 0; // 重置周期 Delay_ms(20); // 控制轨迹更新速度 } } ``` #### 4. **关键优化点** 1. **运动平滑性**: - 减小时间步长(如5ms更新) - 使用查表法预计算正弦/余弦值[^2] ```c const float sin_table[360]; // 预生成0°~359°正弦值 ``` 2. **舵机非线性补偿**: ```c // 添加舵机死区补偿 if(angle_x < 5) angle_x += 2; ``` 3. **轨迹自适应**: ```c // 根据半径动态调整速度 float dynamic_omega = OMEGA * (50.0f / RADIUS); ``` #### 5. **调试建议** 1. **示波器验证**:检查PWM波形脉宽是否符合预期 2. **分步调试**: - 先调试单舵机0°→180°线性运动 - 再调试二维直线运动 - 最后实现圆形轨迹 3. **安全保护**:添加软件限位防止舵机过冲 ```c #define SAFE_MIN 10 #define SAFE_MAX 170 ``` > **注意**:实际舵机角度范围可能因型号而异,需通过`Update_Servo(0,0)`和`Update_Servo(180,180)`校准边界值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值