min()的宏定义中的(void) (&_x == &_y)的含义

本文详细解析了C语言中常见min宏的实现逻辑,包括其内部巧妙利用void与指针比较来隐式进行类型检查的过程,并讨论了在宏定义中加入const的作用以及避免副作用的方法。此外,文章还对比了简单宏定义与复杂宏定义在处理特殊参数时的表现差异,旨在帮助读者深入理解C语言中的宏特性及其应用。

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

近日无意间发现,关于常见的min的宏定义,在Linux中是这样的:

/*
* min()/max()/clamp() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x, y) ({     \
typeof(x) _x = (x);    \
typeof(y) _y = (y);    \
(void) (&_x == &_y);   \
_x < _y ? _x : _y; })

关于其中的:

(void) (&_x == &_y);

很是疑惑,表面看起来,这句话,好像不起作用,算是一句废话,所以去找了一下别人的解释,才大概搞懂是啥意思。

首先,我们此处想要实现的目的是,在计算两个数的最小值之前,希望去判断一下两个值的类型是否一致,而由于C语言本身不支持我们去做类似于这样的操作typeof(_x)==typeof(_y),所以在此,通过故意判断他们2个的地址指针是否相等,而显然&_x,即x的地址,是不可能等于&_y的,但是这句话(void) (&_x == &_y);使得,如果_x和_y的类型不一样,其指针类型也会不一样,2个不一样的指针类型进行比较操作,则会引起编译器产生一个编译警告,提示你这两个值的类型不同。

比如,如果你编译下面这段代码:

int x = 2;
char y = 3;
int m;
m = min(x,y);

编译的时候,经过预处理后,就会有这样的判断操作:

int * == char *;

因此编译器就会提示你:

warning: comparison of distinct pointer types lacks a cast

所以,这个宏的巧妙之处就在于此。

所以,总结起来就是:

【提示】

1。其实关于min的宏,更好的做法是再加个const,即:

#define min(x, y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })

2。(void) (&_x == &_y); 中的void,表示将表达式(&_x == &_y); 所得到的结果(此处肯定是逻辑上的假,值为0)忽略掉。如果不加void,则会提示你这行代码是无意义的,没人用到。

3。关于min的宏定义,为何这么复杂,而不是用简单的#define min(x,y) ((x) < (y) ? x : y)

因为,如果如此定义,那么对于一些特殊的值传入此宏之后,就会产生一些副作用,产生的结果,就不是我们想要的了,比如:

min(++a,++b) ==> ((++a)<(++b))?(++a) : (++b) 

就使得,a++和b++分别执行了2次,而且min的结果,也不对了。而用上面那个复杂的定义,多加了局部变量_x和_y,就可以避免此类问题了。

【引用】

1。(void) (&_x == &_y);

2。如下的宏定义中(void) (&_x == &_y);是怎么做到判断类型的?

http://linux.chinaunix.net/bbs/viewthread.php?tid=1161263

3。Linux内核中的Min和Max函数

http://www.armfans.net/thread-1527-1-1.html


原文地址:http://hi.baidu.com/serial_story/blog/item/b6fd81098b5b1b296a60fb4d.html

MM32F0163项目,新建一个查表法的表单,以下是我相关程序#define WADC_0MM 2589 #define WADC_1MM 2542 #define WADC_2MM 2495 #define WADC_16MM 1851 #define WADC_17MM 1795 #define WADC_18MM 1731 #define MIN_ADC WADC_18MM #define MAX_ADC WADC_0MM const uint16_t W_seg_points[SEGMENTS+1][2]= { {WADC_0MM,50}, //x1,y1(5%占空比) 0mm {WADC_1MM,95}, //x2,y2(9.5%占空比) 1mm {WADC_2MM,140}, //x3,y3(14.0%占空比) 2mm {WADC_16MM,770}, //x4,y4(77.0%占空比) 16mm {WADC_17MM,815}, //x5,y5(81.5%占空比) 17mm {WADC_18MM,860}, //x6,y6(86.0%占空比) 18mm }; uint16_t W_pwm_lookup_table[MAX_ADC-MIN_ADC+1]; void InitLookupTable_W() { for(uint8_t seg=0; seg&lt;SEGMENTS; seg++) { uint16_t x_start = W_seg_points[seg][0]; uint16_t x_end = W_seg_points[seg+1][0]; uint16_t y_start = W_seg_points[seg][1]; uint16_t y_end = W_seg_points[seg+1][1]; int32_t delta_x = (int32_t)x_end - x_start; int32_t step = (delta_x &gt; 0) ? 1 : -1; uint32_t iterations = abs(delta_x); for(uint32_t i=0; i&lt;=iterations; i++) { uint32_t x = x_start + i*step; // 带符号的精确计算 int32_t numerator = (int32_t)(x - x_start) * (y_end - y_start); int32_t denominator = delta_x; uint16_t y = y_start + (uint16_t)(numerator / denominator); // 计算数组偏移量 uint32_t idx = x - MIN_ADC; if(idx &lt;= (MAX_ADC - MIN_ADC)) { W_pwm_lookup_table[idx] = y; } printf(&quot;x=%d, y_calc=%d, y_expected=%.1f\r\n&quot;, x, y, y_start + (x - x_start)*(float)(y_end - y_start)/(x_end - x_start)); } } }这是相关函数,然后是主函数初始化,然后调用相关语句如下 InitLookupTable_W();// 初始化查找表 WcompareValue=W_pwm_lookup_table[ch7_median];程序的问题是:当我变量ch7_median值为WADC_XMM跟WADC_(X+1)MM中间的一个值时,W_pwm_lookup_table[]输出的不是这中间的一个值,而是定义的端点值,通过打印串口数据,数据如下: x=2589、y_calc=50、y_expected=50.0 x=2588, y_calc=50, y_expected=-4112202752.0 x=2587, y_calc=51, y_expected=-4112202752.0 x=2586, y_calc=52, y_expected=-4112202752.0 x=2585, y_calc=53, y_expected=-4112202752.0 x=2584, y_calc=54, y_expected=-4112202752.0 x=2583, y_calc=55, y_expected=-4112202752.0 x=2582, y_calc=56, y_expected=-4112202752.0 x=2581, y_calc=57, y_expected=-4112202752.0 &hellip; x=2542, y_calc=95, y_expected=-4112202752.0 x=2542, y_calc=95, y_expected=95.0 x=2541, y_calc=95, y_expected=-4112202752.0 x=2540, y_calc=96, y_expected=-4112202752.0 &hellip; x=2495, y_calc=140, y_expected=-4112202752.0 x=2495, y_calc=140, y_expected=140.0 x=2494, y_calc=140, y_expected=-4201598208.0 &hellip; x=1851, y_calc=770, y_expected=-4201597440.0 x=1851, y_calc=770, y_expected=770.0 x=1850, y_calc=770, y_expected=-3451312128.0 &hellip; x=1795, y_calc=815, y_expected=-3451312128.0 x=1795, y_calc=815, y_expected=815.0 x=1794, y_calc=815, y_expected=-3019898112.0 x=1793, y_calc=816, y_expected=-3019898112.0 &hellip;
03-31
#include &ldquo;stm32f10x.h&rdquo; #include &ldquo;math.h&rdquo; #include &ldquo;delay.h&rdquo; // 舵机参数定义 #define PWM_PERIOD 20000 // 20ms周期 #define MIN_PULSE 500 // 0.5ms脉冲宽度 #define MAX_PULSE 2500 // 2.5ms脉冲宽度 #define ANGLE_RANGE 270.0f // 舵机角度范围 // 云台参数 #define CENTER_X 135.0f // 水平中心角度 #define CENTER_Y 135.0f // 垂直中心角度 #define RECT_WIDTH 10.0f // 矩形宽度 #define RECT_HEIGHT 10.0f // 矩形高度 #define STEPS_PER_SIDE 100 // 每条边的步数 // 函数声明 void PWM_Init(void); void Set_Servo_Angle(uint8_t servo_id, float angle); float LinearInterpolate(float start, float end, float ratio); void MoveToPoint(float x, float y, uint16_t steps); void DrawRectangle(void); // 确保起点终点重合的完整矩形 void Go_To_Center(uint16_t steps); // 当前位置存储(全局变量) float current_x = CENTER_X; float current_y = CENTER_Y; /** 函 数:PWM初始化 / void PWM_Init(void) { / 开启时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /* GPIO初始化 */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &amp;GPIO_InitStructure); /* 配置时钟源 */ TIM_InternalClockConfig(TIM2); /* 时基单元初始化 */ TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = PWM_PERIOD - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &amp;TIM_TimeBaseInitStructure); /* 输出比较初始化 */ TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&amp;TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OC2Init(TIM2, &amp;TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OC3Init(TIM2, &amp;TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE); } /** 函 数:设置舵机角度 */ void Set_Servo_Angle(uint8_t servo_id, float angle) { if (angle &lt; 0) angle = 0; if (angle &gt; ANGLE_RANGE) angle = ANGLE_RANGE; uint16_t pulse = MIN_PULSE + (uint16_t)((MAX_PULSE - MIN_PULSE) * angle / ANGLE_RANGE); switch(servo_id) { case 0: TIM_SetCompare2(TIM2, pulse); break; case 1: TIM_SetCompare3(TIM2, pulse); break; } } /** 函 数:线性插值 */ float LinearInterpolate(float start, float end, float ratio) { return start + ratio * (end - start); } /** 函 数:移动到指定点 */ void MoveToPoint(float x, float y, uint16_t steps) { for (uint16_t i = 0; i &lt;= steps; i++) { float ratio = (float)i / steps; float interp_x = LinearInterpolate(current_x, x, ratio); float interp_y = LinearInterpolate(current_y, y, ratio); Set_Servo_Angle(0, interp_x); Set_Servo_Angle(1, interp_y); delay_ms(2); } current_x = x; current_y = y; } /** 函 数:云台归中 */ void Go_To_Center(uint16_t steps) { MoveToPoint(CENTER_X, CENTER_Y, steps); } /** 函 数:绘制完整矩形(起点和终点重合) */ void DrawRectangle(void) { // 计算矩形的四个顶点 float top_left_x = CENTER_X - RECT_WIDTH/2; float top_left_y = CENTER_Y - RECT_HEIGHT/2; float top_right_x = CENTER_X + RECT_WIDTH/2; float top_right_y = CENTER_Y - RECT_HEIGHT/2; float bottom_right_x = CENTER_X + RECT_WIDTH/2; float bottom_right_y = CENTER_Y + RECT_HEIGHT/2; float bottom_left_x = CENTER_X - RECT_WIDTH/2; float bottom_left_y = CENTER_Y + RECT_HEIGHT/2; // 绘制完整矩形路径(4条边) MoveToPoint(top_left_x, top_left_y, STEPS_PER_SIDE); // 起点:左上角 MoveToPoint(top_right_x, top_right_y, STEPS_PER_SIDE); // 右上角 MoveToPoint(bottom_right_x, bottom_right_y, STEPS_PER_SIDE); // 右下角 MoveToPoint(bottom_left_x, bottom_left_y, STEPS_PER_SIDE); // 左下角 MoveToPoint(top_left_x, top_left_y, STEPS_PER_SIDE); // 终点:返回起点(左上角) } 我在云台上加了一个激光灯,请在此代码的基础上,补充代码,添加一个可以控制激光灯亮灭的代码
最新发布
07-30
#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; // PID控制模式枚举 typedef enum { YUNTAI_INCREMENTAL, // 增量式PID YUNTAI_POSITIONAL // 位置式PID } yuntai_PID_Mode; // PID参数结构体 typedef struct { float yuntai_Kp; // 比例系数 float yuntai_Ki; // 积分系数 float yuntai_Kd; // 微分系数 float yuntai_max_output; // 最大输出限制 float yuntai_min_output; // 最小输出限制 float yuntai_max_integral; // 积分项限幅 } yuntai_PID_Params; // PID状态结构体 typedef struct { float yuntai_integral; // 积分项累积值 float yuntai_prev_error; // 上一次误差 float yuntai_prev_error2; // 上上次误差(增量式用) } yuntai_PID_State; // PID控制器上下文(整合参数和状态) typedef struct { yuntai_PID_Params params; // PID参数 yuntai_PID_State x_state; // x轴状态 yuntai_PID_State y_state; // y轴状态 } yuntai_PID_Controller; // 初始化PID控制器(封装函数) void yuntai_PID_Init( yuntai_PID_Controller *ctrl, // 控制器上下文 float Kp, float Ki, float Kd, // PID系数 float max_out, float min_out, // 输出限幅 float max_int // 积分限幅 ) { // 初始化参数结构体 ctrl-&gt;params = (yuntai_PID_Params){ .yuntai_Kp = Kp, .yuntai_Ki = Ki, .yuntai_Kd = Kd, .yuntai_max_output = max_out, .yuntai_min_output = min_out, .yuntai_max_integral = max_int }; // 初始化x轴状态 ctrl-&gt;x_state = (yuntai_PID_State){ .yuntai_integral = 0.0f, .yuntai_prev_error = 0.0f, .yuntai_prev_error2 = 0.0f }; // 初始化y轴状态 ctrl-&gt;y_state = (yuntai_PID_State){ .yuntai_integral = 0.0f, .yuntai_prev_error = 0.0f, .yuntai_prev_error2 = 0.0f }; } // PID控制器核心函数(使用上下文) void yuntai_PID_Update( yuntai_PID_Controller *ctrl, // 控制器上下文 float x_target, float x_current, // x轴目标/当前值 float y_target, float y_current, // y轴目标/当前值 yuntai_PID_Mode mode, // 控制模式 float *x_pulse, float *y_pulse // 输出脉冲 ) { // 获取参数和状态指针 yuntai_PID_Params *p = &amp;ctrl-&gt;params; yuntai_PID_State *x_s = &amp;ctrl-&gt;x_state; yuntai_PID_State *y_s = &amp;ctrl-&gt;y_state; // x轴PID计算 float x_error = x_target - x_current; float x_output; if (mode == YUNTAI_POSITIONAL) { // 位置式PID: $u(k) = K_p \cdot e(k) + K_i \cdot \sum e(k) + K_d \cdot [e(k) - e(k-1)]$ x_s-&gt;yuntai_integral += x_error; if (x_s-&gt;yuntai_integral &gt; p-&gt;yuntai_max_integral) x_s-&gt;yuntai_integral = p-&gt;yuntai_max_integral; else if (x_s-&gt;yuntai_integral &lt; -p-&gt;yuntai_max_integral) x_s-&gt;yuntai_integral = -p-&gt;yuntai_max_integral; x_output = p-&gt;yuntai_Kp * x_error + p-&gt;yuntai_Ki * x_s-&gt;yuntai_integral + p-&gt;yuntai_Kd * (x_error - x_s-&gt;yuntai_prev_error); x_s-&gt;yuntai_prev_error = x_error; } else { // 增量式PID // $\Delta u(k) = K_p \cdot [e(k) - e(k-1)] + K_i \cdot e(k) + K_d \cdot [e(k) - 2e(k-1) + e(k-2)]$ x_output = p-&gt;yuntai_Kp * (x_error - x_s-&gt;yuntai_prev_error) + p-&gt;yuntai_Ki * x_error + p-&gt;yuntai_Kd * (x_error - 2*x_s-&gt;yuntai_prev_error + x_s-&gt;yuntai_prev_error2); x_s-&gt;yuntai_prev_error2 = x_s-&gt;yuntai_prev_error; x_s-&gt;yuntai_prev_error = x_error; } // y轴PID计算(同理) float y_error = y_target - y_current; float y_output; if (mode == YUNTAI_POSITIONAL) { y_s-&gt;yuntai_integral += y_error; if (y_s-&gt;yuntai_integral &gt; p-&gt;yuntai_max_integral) y_s-&gt;yuntai_integral = p-&gt;yuntai_max_integral; else if (y_s-&gt;yuntai_integral &lt; -p-&gt;yuntai_max_integral) y_s-&gt;yuntai_integral = -p-&gt;yuntai_max_integral; y_output = p-&gt;yuntai_Kp * y_error + p-&gt;yuntai_Ki * y_s-&gt;yuntai_integral + p-&gt;yuntai_Kd * (y_error - y_s-&gt;yuntai_prev_error); y_s-&gt;yuntai_prev_error = y_error; } else { y_output = p-&gt;yuntai_Kp * (y_error - y_s-&gt;yuntai_prev_error) + p-&gt;yuntai_Ki * y_error + p-&gt;yuntai_Kd * (y_error - 2*y_s-&gt;yuntai_prev_error + y_s-&gt;yuntai_prev_error2); y_s-&gt;yuntai_prev_error2 = y_s-&gt;yuntai_prev_error; y_s-&gt;yuntai_prev_error = y_error; } // 输出限幅 *x_pulse = (x_output &gt; p-&gt;yuntai_max_output) ? p-&gt;yuntai_max_output : (x_output &lt; p-&gt;yuntai_min_output) ? p-&gt;yuntai_min_output : x_output; *y_pulse = (y_output &gt; p-&gt;yuntai_max_output) ? p-&gt;yuntai_max_output : (y_output &lt; p-&gt;yuntai_min_output) ? p-&gt;yuntai_min_output : y_output; } // 示例使用 int main() { // 创建控制器实例 yuntai_PID_Controller ctrl; // 初始化控制器(单次调用完成所有初始化) yuntai_PID_Init(&amp;ctrl, 0.8f, 0.2f, 0.1f, // Kp, Ki, Kd 1000.0f, -1000.0f, // 输出限幅 200.0f // 积分限幅 ); // 目标位置和当前位置 float x_target = 150.0f, x_current = 100.0f; float y_target = 80.0f, y_current = 60.0f; // 输出变量 float x_pulse, y_pulse; // 增量式PID控制 yuntai_PID_Update( &amp;ctrl, x_target, x_current, y_target, y_current, YUNTAI_INCREMENTAL, &amp;x_pulse, &amp;y_pulse ); printf(&quot;增量式PID: x=%.1f, y=%.1f\n&quot;, x_pulse, y_pulse); // 位置式PID控制 yuntai_PID_Update( &amp;ctrl, x_target, x_current, y_target, y_current, YUNTAI_POSITIONAL, &amp;x_pulse, &amp;y_pulse ); printf(&quot;位置式PID: x=%.1f, y=%.1f\n&quot;, x_pulse, y_pulse); return 0; } 把这个函数变为可移植的.c和.h文件
07-28
为什么我采不到电压#include &ldquo;stm32f4xx.h&rdquo; #include &ldquo;delay.h&rdquo; #include &ldquo;oled.h&rdquo; #include &ldquo;stdio.h&rdquo; #include &ldquo;stdlib.h&rdquo; #include &ldquo;arm_math.h&rdquo; #include &ldquo;pid.h&rdquo; #include &ldquo;./adc/bsp_adc.h&rdquo; #include &ldquo;tim.h&rdquo; float pid_out; volatile uint8_t adc_data_ready = 0; volatile uint8_t tim_update_flag ; volatile uint32_t last_adc_value = 0; float Vout_actual = 0.0f; float Target = 20.0f; // 目标输出电压 float voltage1; // 全局PID控制器 PID_Controller pid; extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern uint16_t TIM1_Impluse ;//高级定时器占空比 int main(void) { OLED_Init(); Adc_Init(); TIM_Init(); // 输入20V&rarr;输出15V:kp=0.3-0.6, ki=0.05-0.2, kd=0.01-0.05 //输入35V&rarr;输出20V:kp=0.2-0.4, ki=0.02-0.1, kd=0.005-0.02 pid.kp = 0.5f; // 从较小值开始调试 pid.ki = 0.1f; pid.kd = 0.01f; pid.max_output = 100.0f; pid.min_output = 0.0f; pid.integral = 0; pid.prev_error = 0; while(1) { if (adc_data_ready) { // 计算电压 voltage1 = last_adc_value * 3.3f*0.000244140625; Vout_actual = voltage1; adc_data_ready = 0; } if (tim_update_flag) { // 使用PID计算 pid_out = pid_control(&amp;pid, Target, Vout_actual); // 安全更新PWM (限制在0-8400) TIM1_Impluse = pid_out * 84; // 0-100% -&gt; 0-8400 TIM1-&gt;CCR1 = TIM1_Impluse; // // 重置标志 tim_update_flag = 0; } // a=pid_control (5 , 0.25, 0 ,Target ,Vout_actual); // ADC_Read(); // float six = 6; // char str[40]; // sprintf(str,&ldquo;Vout_actual = %.3f&rdquo;,Vout_actual); // OLED_ShowString(WORD_WIDTH0,WORD_HIGH1,(u8 *)str,WORD_SIZE); // OLED_Refresh_Gram(); // delay_us(100); static char display_buffer[2][40]; snprintf(display_buffer[0], 40, &ldquo;Vout: %.2fV&rdquo;, Vout_actual); delay_ms(50); snprintf(display_buffer[1], 40, &ldquo;Duty: %d&rdquo;, TIM1-&gt;CCR1); OLED_ShowString(0, 1, (u8*)display_buffer[0], 12); OLED_ShowString(0, 18, (u8*)display_buffer[1], 12); OLED_Refresh_Gram(); } } #include &ldquo;stm32f4xx_it.h&rdquo; #include &ldquo;oled.h&rdquo; #include &lt;math.h&gt; #include &ldquo;./adc/bsp_adc.h&rdquo; #include &ldquo;pid.h&rdquo; extern uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern float voltage1; extern float pid_out; extern float Vout_actual; extern uint16_t TIM1_Impluse ;//高级定时器占空比 extern volatile uint8_t adc_data_ready ; extern volatile uint8_t tim_update_flag ; extern volatile uint32_t last_adc_value; void TIM1_UP_TIM10_IRQHandler(void) { if(TIM_GetITStatus(TIM1,TIM_IT_Update) == SET) { // Vout_actual = voltage1; // pid_out = pid_control (5 , 0.25, 0 ,Vout_set ,Vout_actual); // TIM1-&gt;CCR1 = pid_out; // tim_update_flag = 1; // 设置标志,表示发生了一次更新中断 TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } void DMA2_Stream0_IRQHandler(void) { // 处理传输完成中断 if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); last_adc_value = ADC_ConvertedValue[0]; adc_data_ready = 1; } // 处理半传输中断 if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0); } // 处理传输错误中断 if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TEIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TEIF0); // 这里可以添加错误处理代码 } } void NMI_Handler(void) { } void HardFault_Handler(void) { /* Go to infinite loop when Hard Fault exception occurs */ while (1) {} } void MemManage_Handler(void) { /* Go to infinite loop when Memory Manage exception occurs / while (1) {} } void BusFault_Handler(void) { / Go to infinite loop when Bus Fault exception occurs / while (1) {} } void UsageFault_Handler(void) { / Go to infinite loop when Usage Fault exception occurs */ while (1) {} } void DebugMon_Handler(void) { } void SVC_Handler(void) { } void PendSV_Handler(void) { } void SysTick_Handler(void) { } #include &ldquo;tim.h&rdquo; uint16_t TIM1_Impluse = 4200;//预设占空比 float z = 0; const uint32_t spwm[400] = { 4200,4265,4331,4397,4463,4529,4595,4660,4726,4791,4857,4922,4987,5051,5116,5180, 5244,5308,5371,5434,5497,5560,5622,5684,5746,5807,5868,5928,5988,6047,6106,6165, 6223,6280,6337,6394,6450,6505,6560,6615,6668,6721,6774,6826,6877,6927,6977,7026, 7075,7122,7169,7216,7261,7306,7350,7393,7436,7477,7518,7558,7597,7636,7673,7710, 7746,7781,7815,7848,7880,7911,7942,7971,8000,8027,8054,8080,8105,8128,8151,8173, 8194,8214,8233,8251,8268,8283,8298,8312,8325,8337,8348,8358,8366,8374,8381,8387, 8391,8395,8397,8399,8400,8399,8397,8395,8391,8387,8381,8374,8366,8358,8348,8337, 8325,8312,8298,8283,8268,8251,8233,8214,8194,8173,8151,8128,8105,8080,8054,8027, 8000,7971,7942,7911,7880,7848,7815,7781,7746,7710,7673,7636,7597,7558,7518,7477, 7436,7393,7350,7306,7261,7216,7169,7122,7075,7026,6977,6927,6877,6826,6774,6721, 6668,6615,6560,6505,6450,6394,6337,6280,6223,6165,6106,6047,5988,5928,5868,5807, 5746,5684,5622,5560,5497,5434,5371,5308,5244,5180,5116,5051,4987,4922,4857,4791, 4726,4660,4595,4529,4463,4397,4331,4265,4200,4134,4068,4002,3936,3870,3804,3739, 3673,3608,3542,3477,3412,3348,3283,3219,3155,3091,3028,2965,2902,2839,2777,2715, 2653,2592,2531,2471,2411,2352,2293,2234,2176,2119,2062,2005,1949,1894,1839,1784, 1731,1678,1625,1573,1522,1472,1422,1373,1324,1277,1230,1183,1138,1093,1049,1006, 963,922,881,841,802,763,726,689,653,618,584,551,519,488,457,428, 399,372,345,319,294,271,248,226,205,185,166,148,131,116,101,87, 74,62,51,41,33,25,18,12,8,4,2,0,0,0,2,4, 8,12,18,25,33,41,51,62,74,87,101,116,131,148,166,185, 205,226,248,271,294,319,345,372,399,428,457,488,519,551,584,618, 653,689,726,763,802,841,881,922,963,1006,1049,1093,1138,1183,1230,1277, 1324,1373,1422,1472,1522,1573,1625,1678,1731,1784,1839,1894,1949,2005,2062,2119, 2176,2234,2293,2352,2411,2471,2531,2592,2653,2715,2777,2839,2902,2965,3028,3091, 3155,3219,3283,3348,3412,3477,3542,3608,3673,3739,3804,3870,3936,4002,4068,4134 }; //TIM1的GPIO static void TIM_GPIO_Config(void) { GPIO_InitTypeDef TIM_GPIO_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);//开钟 /-----------------------------PA8,PA7------------------------------------/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1);//引脚复用 主 PA8,PA7 GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &amp;TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &amp;TIM_GPIO_InitStruct); /-----------------------------------------------------------------------/ /-----------------------------PA9,PB14------------------------------------/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_TIM1);//引脚复用 主 GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &amp;TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &amp;TIM_GPIO_InitStruct); /-----------------------------------------------------------------------/ /-----------------------------PA10,PB1------------------------------------/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_TIM1);//引脚复用 主 GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &amp;TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &amp;TIM_GPIO_InitStruct); /-----------------------------------------------------------------------/ // TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; //模拟模式 pa6死刹 // TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; //引脚 // TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 // TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 // TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空 // GPIO_Init(GPIOA, &amp;TIM_GPIO_InitStruct); //写入 } //TIM1 static void TIM_A1_Mode_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStruct; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);///使能时钟 //168MHZ-&gt;20kHZ 主频/(计数+1)*(预分频系数+1) //168MHz/8 * 1050 = 20khz /-----------------------------基本结构体------------------------------------/ TIM_TimeBaseInitStructure.TIM_Period = (8400-1); //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler=(1-1); //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //1分频 TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //不需要重复计数 TIM_TimeBaseInit(TIM1,&amp;TIM_TimeBaseInitStructure); //初始化TIM /-----------------------------基本结构体------------------------------------/ /-----------------------------输出比较------------------------------------/ TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; //pwm模式选择 TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; ///使能输出通道 TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable; //使能互补通道 TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; //预设占空比 TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //PWM1和2中的CH和CCR之间值的大小(多用pwm1的模式1) TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High; //当使用了刹车功能时,两路PWM1和2都会被强制禁止,进而输出我们配置的的空闲先状态 TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set; //刹车时输出通道的状态 Set = high TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //刹车时互补通道的状态 Reset = low TIM_OC1Init(TIM1, &amp;TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &amp;TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &amp;TIM_OCInitStruct); TIM_OC3PreloadConfig(TIM1,TIM_OCPreload_Enable); /-----------------------------输出比较------------------------------------/ /-----------------------------死区刹车------------------------------------/ TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; //开启死区 TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; //开启1空闲状态 TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; //不同的锁定级别 (看BDTR寄存器) TIM_BDTRInitStructure.TIM_DeadTime = 20; //刹车时间,(看BDTR寄存器中的DTG[7:0]) //11转换成二进制为0000 1011 死区时间看[7;5]位,此处为000 TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; //允许刹车 //BKIN 测到低电平 比较信号禁止 TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; //高极性 TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; //自动输出使能(刹车输入无效) TIM_BDTRConfig(TIM1, &amp;TIM_BDTRInitStructure); //写入 /-----------------------------死区刹车------------------------------------/ TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许定时器更新中断 | TIM_IT_Trigger TIM_Cmd(TIM1,ENABLE); //使能定时器 TIM_CtrlPWMOutputs(TIM1, ENABLE); //主动输出使能 } static void TIM_A1_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; /-----------------------------中断------------------------------------/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组 NVIC_InitStructure.NVIC_IRQChannel=TIM1_UP_TIM10_IRQn; //定时器1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&amp;NVIC_InitStructure); //写入 /-----------------------------中断------------------------------------/ } void TIM_Init(void) { TIM_A1_NVIC_Config(); TIM_GPIO_Config(); TIM_A1_Mode_Config(); } #include &ldquo;oled.h&rdquo; #include &ldquo;oledfont.h&rdquo; #include &ldquo;delay.h&rdquo; //OLED的显存 //存放格式如下. //[0]0 1 2 3 &hellip; 127 //[1]0 1 2 3 &hellip; 127 //[2]0 1 2 3 &hellip; 127 //[3]0 1 2 3 &hellip; 127 //[4]0 1 2 3 &hellip; 127 //[5]0 1 2 3 &hellip; 127 //[6]0 1 2 3 &hellip; 127 //[7]0 1 2 3 &hellip; 127 u8 OLED_GRAM[128][8]; #if OLED_MODE==0 //向SSD1106写入一个字节。 //dat:要写入的数据/命令 //cmd:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(u8 dat,u8 cmd) { u8 i; OLED_DC=cmd; OLED_CS=0; for(i=0;i&lt;8;i++) { OLED_SCL=0; if(dat&amp;0x80) OLED_SDA=1; else OLED_SDA=0; OLED_SCL=1; dat&lt;&lt;=1; } OLED_CS=1; OLED_DC=1; } #endif #if OLED_MODE==1 //向SSD1106写入一个字节。 //dat:要写入的数据/命令 //cmd:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(u8 dat,u8 cmd) { OLED_DATA_OUT(dat); OLED_RST=cmd; OLED_CS=0; OLED_WR=0; OLED_WR=1; OLED_CS=1; OLED_DC=1; } #endif #if OLED_MODE==2 void OLED_WR_Byte(u8 dat,u8 cmd) { } #endif //更新显存到LCD void OLED_Refresh_Gram(void) { u8 i,n; for(i=0;i&lt;8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置&mdash;列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置&mdash;列高地址 for(n=0;n&lt;128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA); } } void OLED_Set_Pos(unsigned char x, unsigned char y) { OLED_WR_Byte(0xb0+y,OLED_CMD); OLED_WR_Byte(((x&amp;0xf0)&gt;&gt;4)|0x10,OLED_CMD); OLED_WR_Byte((x&amp;0x0f)|0x01,OLED_CMD); } //开启OLED显示 void OLED_Display_On(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON } //关闭OLED显示 void OLED_Display_Off(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i&lt;8;i++) { for(n=0;n&lt;128;n++) { OLED_GRAM[n][i]=0; } } OLED_Refresh_Gram();//更新显示 } //画点 //x:0~127 //y:0~63 //t:1 填充 0,清空 void OLED_DrawPoint(u8 x,u8 y,u8 t) { u8 pos,bx,temp=0; if(x&gt;127||y&gt;63)return;//超出范围了. pos=7-y/8; bx=y%8; temp=1&lt;&lt;(7-bx); if(t)OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&amp;=~temp; } void OLED_DrawLine(u8 x1, u8 y1, u8 x2, u8 y2) { u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance; int incx,incy,uRow,uCol; delta_x=x2-x1; //计算坐标增量 delta_y=y2-y1; uRow=x1; uCol=y1; if(delta_x&gt;0)incx=1; //设置单步方向 else if(delta_x==0)incx=0;//垂直线 else {incx=-1;delta_x=-delta_x;} if(delta_y&gt;0)incy=1; else if(delta_y==0)incy=0;//水平线 else{incy=-1;delta_y=-delta_y;} if( delta_x&gt;delta_y)distance=delta_x; //选取基本增量坐标轴 else distance=delta_y; for(t=0;t&lt;=distance+1;t++ )//画线输出 { OLED_DrawPoint(uRow,uCol,1);//画点 xerr+=delta_x ; yerr+=delta_y ; if(xerr&gt;distance) { xerr-=distance; uRow+=incx; } if(yerr&gt;distance) { yerr-=distance; uCol+=incy; } } } void OLED_DrawRectangle(u8 x1, u8 y1, u8 x2, u8 y2) { OLED_DrawLine(x1,y1,x2,y1); OLED_DrawLine(x1,y1,x1,y2); OLED_DrawLine(x1,y2,x2,y2); OLED_DrawLine(x2,y1,x2,y2); } //x1,y1,x2,y2 填充区域的对角坐标 //确保x1&lt;=x2;y1&lt;=y2 0&lt;=x1&lt;=127 0&lt;=y1&lt;=63 //dot:0,清空;1,填充 void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot) { u8 x,y; for(x=x1;x&lt;=x2;x++) { for(y=y1;y&lt;=y2;y++) { OLED_DrawPoint(x,y,dot); } } OLED_Refresh_Gram();//更新显示 } //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 12/16/24 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0)) * (size/2); //得到字体一个字符对应点阵集所占的字节数 chr=chr-&rsquo; &#39;;//得到偏移后的值 for(t=0;t&lt;csize;t++) { if(size12)temp=ascii_1206[chr][t]; //调用1206字体 else if(size16)temp=ascii_1608[chr][t]; //调用1608字体 else if(size==24)temp=ascii_2412[chr][t]; //调用2412字体 else return; //没有的字库 for(t1=0;t1&lt;8;t1++) { if(temp&amp;0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp&lt;&lt;=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } } //m^n函数 u32 oled_pow(u8 m,u8 n) { u32 result=1; while(n&ndash;)result*=m; return result; } //显示2个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size) { u8 t,temp; u8 enshow=0; for(t=0;t&lt;len;t++) { temp=(num/oled_pow(10,len-t-1))%10; if(enshow0&amp;&amp;t&lt;(len-1)) { if(temp0) { OLED_ShowChar(x+(size/2)*t,y,&rsquo; &#39;,size,1); continue; }else enshow=1; } OLED_ShowChar(x+(size/2)*t,y,temp+&#39;0&#39;,size,1); } } //显示字符串 //x,y:起点坐标 //size:字体大小 //*p:字符串起始地址 void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size) { while((*p&lt;=&lsquo;~&rsquo;)&amp;&amp;(*p&gt;=&rsquo; &#39;))//判断是不是非法字符! { if(x&gt;(128-(size/2))){x=0;y+=size;} if(y&gt;(64-size)){y=x=0;OLED_Clear();} OLED_ShowChar(x,y,*p,size,1); x+=size/2; p++; } } //显示汉字 //x,y:起点坐标 //pos:数组位置汉字显示 //size:字体大小 //mode:0,反白显示;1,正常显示 void OLED_ShowFontHZ(u8 x,u8 y,u8 pos,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数 if(size!=12&amp;&amp;size!=16&amp;&amp;size!=24&amp;&amp;size!=32)return; //不支持的size for(t=0;t&lt;csize;t++) { if(size==12)temp=FontHzk_12[pos][t]; //调用1206字体 else if(size==16)temp=FontHzk_16[pos][t]; //调用1608字体 else if(size==24)temp=FontHzk_24[pos][t]; //调用2412字体 else if(size==32)temp=FontHzk_32[pos][t]; //调用3216字体 else return; //没有的字库 for(t1=0;t1&lt;8;t1++) { if(temp&amp;0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp&lt;&lt;=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } } //显示BMP图片128&times;64 //起始点坐标(x,y),x的范围0~127,y为页的范围0~7 void OLED_DrawBMP(u8 x0, u8 y0,u8 x1, u8 y1,u8 BMP[]) { u16 j=0; u8 x,y; if(y1%8==0)y=y1/8; else y=y1/8+1; for(y=y0;y&lt;y1;y++) { OLED_Set_Pos(x0,y); for(x=x0;x&lt;x1;x++) { OLED_WR_Byte(BMP[j++],OLED_DATA); } } } //GND 接电源地 //VCC 接5V或3.3v电源 //D0 接PD6(SCL) //D1 接PD7(SDA) //RES 接PD4 //DC 接PD5 //CS 接PD3 void OLED_Init() { GPIO_InitTypeDef GPIO_InitStructure; #if OLED_MODE==0 //4线SPI模式 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_6;//管脚设置 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出 GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不拉 GPIO_Init(GPIOE,&amp;GPIO_InitStructure); //初始化结构体 GPIO_SetBits(GPIOE,GPIO_Pin_2|GPIO_Pin_6); //拉高电平 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_13;//管脚设置 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出 GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不拉 GPIO_Init(GPIOC,&amp;GPIO_InitStructure); //初始化结构体 GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_13); //拉高电平 OLED_Clear(); #endif #if OLED_MODE==1 //8080模式 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;//管脚设置 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出 GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOD,&amp;GPIO_InitStructure); //初始化结构体 GPIO_SetBits(GPIOD,GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); GPIO_InitStructure.GPIO_Pin = 0XFF; //PC0-7 GPIO_Init(GPIOC, &amp;GPIO_InitStructure); GPIO_SetBits(GPIOC,0xFF); //PC0-7输出高 #endif #if OLED_MODE==2 //IIC模式 #endif OLED_RST=1; delay_ms(100); OLED_RST=0; delay_ms(100); OLED_RST=1; OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64) OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00,OLED_CMD); //默认为0 OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数. OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0-&gt;0;1,0-&gt;127; OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]-&gt;COM0;N:驱动路数 OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置 OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81,OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮) OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率 OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_WR_Byte(0xAF,OLED_CMD); //开启显示 OLED_Clear(); } #ifndef _oled_H #define _oled_H #include &ldquo;sys/system.h&rdquo; #include &ldquo;stdlib.h&rdquo; #define WORD_SIZE 12 #define X_OFFSET_WORD 0 #define Y_OFFSET_WORD 0 #define X_OFFSTE_PIXEL 0 #define Y_OFFSTE_PIXEL 0 #if WORD_SIZE != 12 &amp;&amp; WORD_SIZE != 16 &amp;&amp; WORD_SIZE != 24 #define WORD_SIZE 12 #endif #if WORD_SIZE == 24 #define WORD_WIDTH 12 #define WORD_HIGH 24 #endif #if WORD_SIZE == 16 #define WORD_WIDTH 8 #define WORD_HIGH 16 #endif #if WORD_SIZE == 12 #define WORD_WIDTH 6 #define WORD_HIGH 12 #endif //OLED模式设置 //0:4线串行SPI模式 //1:并行8080模式 //2:IIC模式 #define OLED_MODE 0 #define SIZE 16 #define XLevelL 0x00 #define XLevelH 0x10 #define Max_Column 128 #define Max_Row 64 #define Brightness 0xFF #define X_WIDTH 128 #define Y_WIDTH 64 #if OLED_MODE==0 //OLDE-SPI4线控制管脚定义 #define OLED_SCL PCout(1) #define OLED_SDA PCout(0) #define OLED_RST PCout(13) #define OLED_DC PEout(6) #define OLED_CS PEout(2) #endif #if OLED_MODE==1 //OLDE-8080总线控制管脚定义 #define OLED_CS PDout(3) #define OLED_RST PDout(4) #define OLED_DC PDout(5) #define OLED_WR PDout(6) #define OLED_RD PDout(7) #define OLED_DATA_OUT(x) GPIO_Write(GPIOC,x);//输出 #endif #if OLED_MODE==2 //OLDE-IIC总线控制管脚定义 #endif #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 //OLED控制用函数 void OLED_WR_Byte(u8 dat,u8 cmd); void OLED_Display_On(void); void OLED_Display_Off(void); void OLED_Set_Pos(unsigned char x, unsigned char y); void OLED_Init(void); void OLED_Refresh_Gram(void); void OLED_Clear(void); void OLED_DrawPoint(u8 x,u8 y,u8 t); void OLED_DrawLine(u8 x1, u8 y1, u8 x2, u8 y2); void OLED_DrawRectangle(u8 x1, u8 y1, u8 x2, u8 y2); void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size); void OLED_ShowFontHZ(u8 x,u8 y,u8 pos,u8 size,u8 mode); void OLED_DrawBMP(u8 x0, u8 y0,u8 x1, u8 y1,u8 BMP[]); #endif #include &ldquo;./adc/bsp_adc.h&rdquo; __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]={0}; static void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /=通道1==/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK1,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT1, &amp;GPIO_InitStructure); } void ADC_DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; // 1. 使能 DMA 时钟 RCC_AHB1PeriphClockCmd(ADC_DMA_CLK, ENABLE); // 2. 配置 DMA 参数 DMA_InitStructure.DMA_Channel = ADC_DMA_CHANNEL; // DMA 通道 0 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)ADC_ConvertedValue ; // ADC 数据寄存器地址 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC_ConvertedValue; // 内存缓冲区地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设到内存 DMA_InitStructure.DMA_BufferSize = RHEOSTAT_NOFCHANEL; // 缓冲区大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外设数据大小:半字(16位) DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 内存数据大小:半字(16位) DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 高优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // 禁用 FIFO 模式 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; // FIFO 阈值 DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; // 内存突发传输:单次 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 外设突发传输:单次 // 3. 初始化 DMA DMA_Init(ADC_DMA_STREAM, &amp;DMA_InitStructure); // 4. 使能 DMA 中断(传输完成、传输错误) DMA_ITConfig(ADC_DMA_STREAM, DMA_IT_TC | DMA_IT_TE | DMA_IT_HT, ENABLE); // 5. 使能 DMA 流 DMA_Cmd(ADC_DMA_STREAM, ENABLE); } void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // 1. 使能 ADC 时钟 RCC_APB2PeriphClockCmd(ADC_CLK, ENABLE); // 2. 配置 ADC 通用参数 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // ADC 时钟分频:PCLK2/4 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // DMA 访问模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // 采样延迟 ADC_CommonInit(&amp;ADC_CommonInitStructure); // 3. 配置 ADC 参数 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 12位分辨率 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 扫描模式使能 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; // 无外部触发 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; // 外部触发源 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐 ADC_InitStructure.ADC_NbrOfConversion = RHEOSTAT_NOFCHANEL ; // 转换通道数 ADC_Init(ADC_, &amp;ADC_InitStructure); // 4. 配置 ADC 通道(通道4,PA4) ADC_RegularChannelConfig(ADC_, ADC_Channel_4, 1, ADC_SampleTime_84Cycles); // 5. 使能 ADC DMA ADC_DMACmd(ADC_, ENABLE); // 6. 使能 ADC ADC_Cmd(ADC_, ENABLE); // 7. 启动 ADC 转换 ADC_SoftwareStartConv(ADC_); } static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&amp;NVIC_InitStructure); } void Adc_Init(void) { ADC_GPIO_Config(); ADC_DMA_Config(); ADC_Config(); ADC_NVIC_Config(); } #ifndef __BSP_ADC_H #define __BSP_ADC_H #include &ldquo;stm32f4xx.h&rdquo; #define RHEOSTAT_NOFCHANEL 1 /=通道1 IO==/ // ADC IO宏定义 #define ADC_GPIO_PORT1 GPIOA #define ADC_GPIO_PIN1 GPIO_Pin_4 #define ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOA #define ADC_CHANNEL1 ADC_Channel_4 // ADC 序号宏定义 #define ADC_ ADC1 #define ADC_CLK RCC_APB2Periph_ADC1 // ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1+0x4c) // ADC DMA 通道宏定义,这里我们使用DMA传输 // DMA 配置 #define ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define ADC_DMA_CHANNEL DMA_Channel_0 #define ADC_DMA_STREAM DMA2_Stream0 void Adc_Init(void); #endif /* __BSP_ADC_H */ #include &ldquo;pid.h&rdquo; float pid_control(PID_Controller* pid, float setpoint, float input) { // 计算当前误差 float error = setpoint - input; // 比例项 float p_term = pid-&gt;kp * error; // 积分项(带抗饱和) pid-&gt;integral += error; // 积分限幅 if(pid-&gt;integral &gt; pid-&gt;max_output) pid-&gt;integral = pid-&gt;max_output; else if(pid-&gt;integral &lt; pid-&gt;min_output) pid-&gt;integral = pid-&gt;min_output; float i_term = pid-&gt;ki * pid-&gt;integral; // 微分项(标准实现) float d_term = pid-&gt;kd * (error - pid-&gt;prev_error); // PID输出 float output = p_term + i_term + d_term; // 输出限幅 if(output &gt; pid-&gt;max_output) output = pid-&gt;max_output; else if(output &lt; pid-&gt;min_output) output = pid-&gt;min_output; // 更新误差历史 pid-&gt;prev_error = error; return output; } #ifndef _PID_H #define _PID_H typedef struct { float kp, ki, kd; float integral; float prev_error; float max_output; float min_output; } PID_Controller; float pid_control(PID_Controller* pid, float setpoint, float input); #endif,这代码哪有问题
07-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值