活动介绍

#include stdio.h uint8_t arr[100]="i love you! china! ";求arr中被赋值的数组成员数量

时间: 2024-04-13 11:29:35 浏览: 96
在你提供的代码中,`arr`是一个长度为100的`uint8_t`类型的数组,并且已经被赋值为字符串 "i love you! china! "。如果要确定`arr`中被赋值的数组成员数量,可以使用字符串处理函数 `strlen()` 来计算字符串的长度。 请注意,`strlen()` 函数计算的是以空字符 `\0` 结尾的字符串的长度。所以,如果数组中没有以 `\0` 结尾,或者数组中包含了其他的非零值,那么 `strlen()` 函数将无法正确计算。 以下是一个使用 `strlen()` 函数计算被赋值数组成员数量的示例代码: ```c #include <stdio.h> #include <stdint.h> #include <string.h> int main() { uint8_t arr[100] = "i love you! china! "; int count = strlen((char*)arr); printf("被赋值的数组成员数量:%d\n", count); return 0; } ``` 在上述示例中,将 `arr` 强制转换为 `char*` 类型,然后使用 `strlen()` 函数计算字符串的长度。最后输出结果为 19,即被赋值的数组成员数量。 请注意根据实际情况修改数组的长度和数据类型,并确保数组以 `\0` 结尾或满足其他适当的条件。
相关问题

#include "stm32f10x.h" // Device header #include <stdio.h> #include <stdarg.h> #include <string.h> // 新增字符串处理头文件 #include "Dht11.h" #include "Servo.h" //char Serial_RxPacket[100]; //定义接收数据包数组,数据包格式"@MSG\r\n" //uint8_t Serial_RxFlag; //定义接收数据包标志位 // 修改后的AT指令缓冲区定义 #define AT_CMD_MAX_LEN 64 char Serial_RxPacket[AT_CMD_MAX_LEN]; // AT指令缓冲区 uint8_t Serial_RxFlag = 0; // 接收完成标志 // 新增全局变量(阈值和舵机控制) int temp_high = 30, temp_low = 20; int humi_high = 60, humi_low = 40; uint32_t servo_active_time = 0; uint32_t servo_active_counter = 0; // 舵机运行时间计数器 uint8_t threshold_check_flag = 0; // 阈值检测触发标志 /** * 函 数:串口初始化 * 参 数:无 * 返 回 值:无 */ void Serial_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA9引脚初始化为复用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA10引脚初始化为上拉输入 /*USART初始化*/ USART_InitTypeDef USART_InitStructure; //定义结构体变量 USART_InitStructure.USART_BaudRate = 9600; //波特率 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制,不需要 USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //模式,发送模式和接收模式均选择 USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶校验,不需要 USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位,选择1位 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位 USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1 /*中断输出配置*/ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口接收数据的中断 /*NVIC中断

<think>嗯,用户需要STM32F10x的串口初始化、DHT11温湿度传感器和舵机控制的示例代码。首先,我得回忆一下STM32的USART配置步骤。通常需要开启时钟,配置GPIO引脚为复用推挽输出,设置波特率、数据位、停止位等参数,然后使能串口。可能还需要中断接收,不过用户提到的是初始化,所以先提供基本配置代码。 接下来是DHT11传感器。DHT11是单总线协议,需要严格的时序。初始化时要拉低总线至少18ms,然后等待响应。读取数据时,每个位的高电平持续时间决定是0还是1。需要用到微秒级延时函数,可能涉及到系统时钟配置。要注意时序的精确性,否则读取会失败。 然后是舵机控制,比如SG90。舵机通常使用PWM信号,周期20ms,高电平持续时间在0.5ms到2.5ms之间对应不同角度。STM32的定时器PWM模式可以生成这样的信号。需要配置定时器的ARR和PSC来设置周期,然后调整CCR的值来改变占空比,从而控制角度。 用户提供的引用资料中提到了DHT11和SG90的使用,所以需要确保代码与这些组件兼容。同时,用户可能希望代码结构清晰,模块化,方便集成到他们的项目中。比如,将各个部分的初始化函数分开,主循环中调用读取传感器和控制舵机的函数。 可能还需要考虑错误处理,比如DHT11的校验和检查,以及串口通信的数据接收处理。不过用户主要请求的是示例代码,所以先提供基础实现,再建议添加错误处理。 另外,用户提到的参考引用[3]中的项目使用了STM32、蓝牙、DHT11、舵机等,这可能意味着用户在做类似的项目,需要整合这些模块。因此,代码示例应该易于与其他模块结合,比如通过串口发送数据到蓝牙模块。 需要检查每个模块的引脚连接是否正确,比如USART1的TX和RX引脚是PA9和PA10,DHT11连接到某个GPIO,舵机的信号线连接到定时器的PWM输出通道,比如PA6(TIM3_CH1)。 最后,确保代码基于标准库,因为用户没有提到使用HAL库,可能需要使用ST的固件库函数。同时,提醒用户根据实际硬件调整引脚和定时器配置。</think>根据引用[3][^3]和[4][^4],以下是STM32F10x USART初始化、DHT11传感器和舵机控制的示例代码框架: ### 一、USART初始化配置 ```c #include "stm32f10x.h" void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX(PA9)为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置RX(PA10)为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 串口参数配置 USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } ``` ### 二、DHT11温湿度传感器驱动 ```c #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_PIN GPIO_Pin_12 void DHT11_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure); } uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi) { // 具体时序实现(包含启动信号、响应检测、数据采集等) // 需要实现微秒级延时函数 // 返回校验结果 } ``` ### 三、SG90舵机控制(PWM实现) ```c void TIM3_PWM_Init(u16 arr, u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置PA6为复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频系数 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM3, ENABLE); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE); } void Set_Servo_Angle(float angle) { // 角度转占空比(例:0.5ms-2.5ms对应0-180度) uint16_t pulse = 500 + (2000 * angle)/180; // 20ms周期下计算脉冲值 TIM_SetCompare1(TIM3, pulse); } ``` ### 四、主程序框架示例 ```c int main(void) { USART1_Init(); DHT11_Init(); TIM3_PWM_Init(19999, 71); // 20ms周期(72MHz/(71+1)/(19999+1)=50Hz) while(1) { uint8_t temperature, humidity; if(DHT11_Read_Data(&temperature, &humidity)) { // 通过串口发送数据 USART_SendData(USART1, temperature); // 根据湿度控制舵机 if(humidity < 30) Set_Servo_Angle(0); // 低档 else if(humidity < 60) Set_Servo_Angle(90); // 中档 else Set_Servo_Angle(180); // 高档 } Delay_ms(2000); } } ``` **注意事项:** 1. 需要根据实际硬件连接修改GPIO端口和引脚 2. 需实现精确的微秒级延时函数(建议使用SysTick定时器) 3. DHT11数据读取需严格按照时序要求实现[^3] 4. 舵机角度控制需要根据具体型号调整脉冲范围 5. 建议添加串口中断接收处理(参考引用[4]关于中断优先级的设置)

以下代码是我的在1.8寸RGB_TFT上的示波器,我想显示频率该怎么写?#include "stm32f10x.h" #include "delay.h" #include "string.h" #include "stdio.h" #include "led.h" #include "tim.h" #include "lcddriver.h" #include "GUI.h" #include "adc.h" // 用于整合频率值 uint16_t adc1[10]; unsigned int adc_val = 0; // ADC1实际电压值 uint16_t ADC_value; // 模拟值adc1DMA /* 全局变量 */ uint8_t oscState = 0; // 0:未就绪 1:运行中 /* 波形显示相关 */ uint16_t numF = 1; // 第几个等级触发频率 uint32_t ARR = 0; // ADC触发频率,(72M / 36) / ARR uint8_t enlarge = 1; // 波形放大倍数 int8_t offset = 0; // 波形偏移量 /* 被测信号的原始数据 */ uint16_t originalAdc[1024]; // ADC采集的DMA传输过来的原始数据 uint16_t numOfCollect = 1024; // 采样数量 uint16_t ADC_STORE[1024]; /* 用于计算被测信号的频率 */ uint16_t adcThreshold = 1024; // 标记被测信号起始位置的阈值 uint16_t startPosition = 0; // 标记被测信号一个周期的起始位置 uint16_t endPosition = 0; // 标记被测信号一个周期的结束位置 uint16_t waveFrequency = 0; // 被测信号的频率 /* 用于显示的数据 */ int16_t newWave[600] = {0}; // 新获取的波形数据 int16_t oldWave[600] = {0}; // 上一次显示的波形数据 /* 被测信号电压信息 */ float maxVol = 0; // 被测信号 最大电压 float minVol = 0; // 被测信号 最小电压 float difVoMaxAndMin = 0; // 被测信号 最大电压与最小电压的差 /* 按键监测 */ uint8_t keyNum = 0; // 此时选中的按键 uint8_t keyChanged = 1; // 按键状态,是否被按下过 /* 临时存储需要显示内容 */ uint8_t tempBuf[10]; // 控制显示频率等级 void control(uint16_t con1) { numF = con1; } // 设置ADC采样频率 void setAdcFrequency(uint8_t t) { switch(t) { case 1: ARR = 5; break; case 2: ARR = 5; break; case 3: ARR = 10; break; case 4: ARR = 19; break; case 5: ARR = 20; break; case 6: ARR = 32; break; case 7: ARR = 40; break; case 8: ARR = 90; break; case 9: ARR = 95; break; case 10: ARR = 110; break; case 11: ARR = 130; break; case 12: ARR = 160; break; case 13: ARR = 200; break; case 14: ARR = 255; break; case 15: ARR = 300; break; case 16: ARR = 325; break; case 17: ARR = 360; break; case 18: ARR = 390; break; case 19: ARR = 440; break; case 20: ARR = 500; break; case 21: ARR = 550; break; case 22: ARR = 650; break; case 23: ARR = 770; break; case 24: ARR = 970; break; case 25: ARR = 1270; break; case 26: ARR = 1490; break; case 27: ARR = 1900; break; case 28: ARR = 2400; break; case 29: ARR = 2800; break; case 30: ARR = 3200; break; case 31: ARR = 3900; break; case 32: ARR = 5000; break; case 33: ARR = 5400; break; case 34: ARR = 6400; break; case 35: ARR = 7300; break; case 36: ARR = 9000; break; case 37: ARR = 13000; break; case 38: ARR = 20000; break; case 39: ARR = 40000; break; } TIM8->ARR = ARR - 1; } /* ===================================================== */ // 描述:示波器初始化 /* ===================================================== */ void OSC_Init(void) { POINT_COLOR = RED; // 1. ADC校准 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // 2. 开启DMA传输 ADC_DMACmd(ADC1, ENABLE); // 3. 关掉传输一半中断 DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, DISABLE); // 4. 设置ADC采样频率,并开始计时采样 setAdcFrequency(numF); TIM_Cmd(TIM8, ENABLE); } /* ===================================================== */ // 描述:DMA传输完成中断服务函数 /* ===================================================== */ void DMA1_Channel1_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC1)) { DMA_ClearITPendingBit(DMA1_IT_TC1); uint16_t i; uint32_t slope1[50] = {0}; uint32_t slope2[50] = {0}; // 失能定时器3 TIM_Cmd(TIM8, DISABLE); for(i = 0; i < 1024; i++) { ADC_STORE[i] = originalAdc[i]; } if(oscState == 1) { for(i = 0; i < 50; i++) { slope1[i] = originalAdc[i+1] - originalAdc[i]; slope2[i] = slope1[i+1] - slope1[i]; if(slope2[i] < 10 && slope2[i] > 0) { LCD_ShowString(400, 0, 16, "Triangular_wave", 0); } else if(slope1[i] == 0 && slope1[i+1] == 0 && slope1[i+2] == 0 && slope1[i+3] == 0 && slope1[i+4] == 0 && slope1[i+5] == 0 && slope1[i+6] == 0 && slope1[i+7] == 0 && slope1[i+8] == 0) { LCD_ShowString(400, 0, 16, "Square_wave", 0); } else { LCD_ShowString(400, 0, 16, "sin_wave", 0); } } updateWaveFrequency(); OSC_ShowWave(); OSC_ShowInfo(); } else { POINT_COLOR = RED; LCD_ShowString(0, 0, 16, "stop", 0); } // 使能定时器3 TIM_Cmd(TIM8, ENABLE); } } /* ===================================================== */ // 描述:更新波形频率 /* ===================================================== */ void updateWaveFrequency(void) { static uint16_t n = 0; // 寻找原始数据中第一个 adcThreshold 附近的点 for(n = 100; n < numOfCollect; n++) { if(originalAdc[n] < adcThreshold && originalAdc[n + 2] > adcThreshold) { if(n > (numOfCollect - lcddev.width)) { startPosition = 100; } else { startPosition = n; } break; } } // 寻找原始数据中第二个 adcThreshold 附近的点 for(n = startPosition + 3; n < numOfCollect; n++) { if(originalAdc[n] < adcThreshold && originalAdc[n + 2] > adcThreshold) { endPosition = n; break; } } for(n = 0; n < 5; n++) { adc1[n] = 2000000 / ((endPosition - startPosition) * ARR); } waveFrequency = (adc1[0] + adc1[1] + adc1[2] + adc1[3] + adc1[4]) / 5; } /* ===================================================== */ // 描述:更新电压差值 /* ===================================================== */ void updateDifVoMaxAndMin(void) { uint16_t max = 0; uint16_t min = 4095; uint16_t n = 0; for(n = 1; n < lcddev.width; n++) { if(newWave[n] > max) { max = newWave[n]; } if(newWave[n] < min) { min = newWave[n]; } } maxVol = (float)max * (3.3 / 4096.0); minVol = (float)min * (3.3 / 4096.0); difVoMaxAndMin = maxVol - minVol; } /* ===================================================== */ // 描述:显示波形图 /* ===================================================== */ void OSC_ShowWave(void) { // 电压曲线中的点 在LCD上对应的 Y 坐标 int16_t prePos = 0; // 前一个点坐标 int16_t afterPos = 0; // 后一个点坐标 static uint16_t n = 0; // 把原始ADC数据 放到 newWave 数组中 for(n = 0; n < 95; n++) { newWave[n] = originalAdc[offset + startPosition + n]; } // 计算上峰值电压、下峰值电压、差值 updateDifVoMaxAndMin(); newWave[0] = prePos = ((lcddev.height - 40) - (newWave[0] * 0.0397 * enlarge)); for(n = 1; n < (95 - 2); n++) { newWave[n] = afterPos = (lcddev.height - 40) - ((double)(newWave[n] * 0.0397 * enlarge)); if(afterPos <= 20) { newWave[n] = afterPos = 21; } POINT_COLOR = WHITE; LCD_DrawLine(5 * n, oldWave[n], 5 * (n + 1), oldWave[n + 1]); POINT_COLOR = DARKBLUE; LCD_DrawLine(5 * n, prePos, 5 * (n + 1), afterPos); prePos = afterPos; // 更新 } // 全部画完之后,把现在的存到旧的里面去 for(n = 1; n < 95; n++) { oldWave[n] = newWave[n - 1]; } } /* ===================================================== */ // 描述:显示波形的信息 /* ===================================================== */ void OSC_ShowInfo(void) { POINT_COLOR = BLACK; // 显示最大电压 sprintf((char*)tempBuf, "%.2f", maxVol); LCD_ShowString(10, 0, 16, "max", 0); LCD_ShowString(40, 0, 16, tempBuf, 0); // 显示最小电压 sprintf((char*)tempBuf, "%.2f", minVol); LCD_ShowString(80, 0, 16, "min", 0); LCD_ShowString(110, 0, 16, tempBuf, 0); // 显示频率 LCD_ShowString(160, 0, 16, "frequency", 0); sprintf((char*)tempBuf, "%d Hz", waveFrequency); LCD_ShowString(240, 0, 16, tempBuf, 0); } /* ===================================================== */ // 描述:初始化ADC和DMA /* ===================================================== */ void ADC_DMA_Init(void) { ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; // 1. 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 2. 配置DMA DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)originalAdc; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = numOfCollect; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); // 3. 开启DMA传输完成中断 DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); // 4. 使能DMA DMA_Cmd(DMA1_Channel1, ENABLE); // 5. 配置ADC ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T8_TRGO; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // 6. 配置ADC通道 ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5); // 7. 使能ADC ADC_Cmd(ADC1, ENABLE); // 8. 使能ADC的DMA请求 ADC_DMACmd(ADC1, ENABLE); // 9. 配置中断优先级 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /* ===================================================== */ // 描述:初始化TIM8 /* ===================================================== */ void TIM8_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 1. 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); // 2. 配置时基 TIM_TimeBaseStructure.TIM_Period = ARR - 1; TIM_TimeBaseStructure.TIM_Prescaler = 1; // 72MHz/(1+1)=36MHz TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); // 3. 配置输出比较模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM8, &TIM_OCInitStructure); // 4. 配置主从模式 TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update); // 5. 使能定时器 TIM_Cmd(TIM8, ENABLE); } /* ===================================================== */ // 主函数 /* ===================================================== */ int main(void) { // 初始化系统时钟 SystemInit(); // 初始化延时函数 delay_init(); // 初始化LCD LCD_Init(); // 初始化ADC和DMA ADC_DMA_Init(); // 初始化TIM8 TIM8_Init(); // 初始化示波器 OSC_Init(); // 设置运行状态 oscState = 1; while(1) { // 主循环中可以添加按键扫描等逻辑 } }

<think>我们参考了站内引用中关于STM32F103实现示波器的设计,特别是使用FFT算法计算频率的方法。根据引用[1]和[2],它们都提到了使用FFT算法来计算输入信号的频率,并且精度较高。同时,引用[2]还提到了另一种方法:定时器的输入捕获。因此,在STM32F103上实现TFT显示屏示波器显示被测信号频率,主要有两种方法:基于FFT的频域分析法和基于输入捕获的时域分析法。 ### 方法一:基于FFT的频域分析法 1. **信号采样**:使用ADC(模数转换器)对输入信号进行采样。为了确保采样数据的连续性和减少CPU负担,通常结合DMA(直接存储器访问)和定时器(TIM)来触发ADC采样,形成固定采样率的采样系统。 2. **FFT计算**:对采样得到的数据进行快速傅里叶变换(FFT),得到信号的频谱。在频谱中,幅度最大的分量对应的频率就是信号的主频率。 3. **频率计算**:根据FFT后的结果,找出幅度最大的点,其位置k(即第k个点)与频率分辨率Δf的关系为:$f = k \cdot \Delta f$,其中$\Delta f = \frac{f_s}{N}$,$f_s$为采样频率,$N$为FFT点数。 4. **显示频率**:将计算得到的频率值通过TFT显示屏显示出来。 优点:适用于周期信号和非周期信号,特别是对于复杂信号(含有多个频率分量)能够准确提取主频率。 缺点:需要较大的计算量,对STM32F103的性能有一定要求,且FFT点数越多精度越高,但计算时间也越长。 ### 方法二:基于输入捕获的时域分析法 1. **信号输入**:将输入信号连接到STM32的定时器输入捕获通道(如TIMx_CHx)。 2. **输入捕获配置**:配置定时器为输入捕获模式,并设置上升沿(或下降沿)触发。当捕获到边沿时,记录定时器的计数值,并计算相邻两个上升沿(或下降沿)之间的时间差,即信号的周期。 3. **频率计算**:频率$f$与周期$T$的关系为:$f = \frac{1}{T}$。 4. **显示频率**:将计算得到的频率值通过TFT显示屏显示出来。 优点:实现简单,计算量小,对于周期稳定的信号精度较高。 缺点:对于非周期信号或复杂信号(如含有噪声的信号)可能无法准确捕获,且只能测量频率,无法得到其他频谱信息。 ### 实现步骤(以FFT方法为例) 1. **硬件配置**: - 配置ADC(例如ADC1)和DMA,将ADC的转换结果通过DMA传输到内存数组。 - 配置一个定时器(如TIM2)触发ADC采样,设置定时器的触发频率为所需的采样频率$f_s$。 - 配置TFT显示屏的接口(如FSMC或GPIO模拟),用于显示波形和频率值。 2. **软件设计**: - 初始化ADC、DMA、定时器、TFT等外设。 - 启动定时器和ADC,开始采样。 - 当采样点数达到FFT所需点数(如1024点)时,触发FFT计算。 - 使用FFT库(如ARM的CMSIS-DSP库)进行FFT计算。 - 在FFT结果中查找幅度最大的点,计算其对应的频率。 - 将频率值显示在TFT屏幕上,同时可以显示波形。 3. **关键代码片段(伪代码)**: ```c #include "stm32f10x.h" #include "arm_math.h" #define FFT_LENGTH 1024 float32_t adc_buffer[FFT_LENGTH]; // ADC采样缓冲区 float32_t fft_input[FFT_LENGTH]; // FFT输入数组 float32_t fft_output[FFT_LENGTH]; // FFT输出数组 void ADC_DMA_Init(void) { // 配置ADC和DMA,使能定时器触发 // ... } void TIM_Init(void) { // 配置定时器,设置采样频率 // ... } void FFT_Calculate(void) { arm_cfft_instance_f32 cfft; arm_cfft_init_f32(&cfft, FFT_LENGTH); // 将ADC采样数据转换为浮点数(如果需要) for (int i=0; i<FFT_LENGTH; i++) { fft_input[i] = (float32_t)adc_buffer[i]; } // 执行FFT arm_cfft_f32(&cfft, fft_input, 0, 1); // 计算幅度 arm_cmplx_mag_f32(fft_input, fft_output, FFT_LENGTH); // 寻找幅度最大的点(忽略直流分量,即第一个点) uint32_t max_index = 1; for (int i=2; i<FFT_LENGTH/2; i++) { // 只考虑前一半(实信号的频谱对称) if (fft_output[i] > fft_output[max_index]) { max_index = i; } } // 计算频率 float sample_rate = 1000000; // 假设采样率为1MHz float freq = (max_index * sample_rate) / FFT_LENGTH; // 显示频率到TFT // ... } ``` ### 注意事项 - **采样定理**:采样频率$f_s$必须大于信号最高频率的2倍,否则会出现混叠现象。 - **FFT点数**:点数越多,频率分辨率越高,但计算时间越长。需要根据信号频率范围和精度要求权衡。 - **信号预处理**:如果信号含有直流分量,可以先减去直流分量(均值)再进行FFT,以提高准确性。 - **窗口函数**:对信号加窗(如汉宁窗)可以减少频谱泄漏,提高频率测量精度。 ### 总结 在STM32F103上实现TFT示波器显示被测信号频率,推荐使用FFT方法,因为它适用性更广,且能同时得到信号的频谱信息。虽然计算量较大,但STM32F103带有硬件乘法器,使用CMSIS-DSP库进行FFT计算效率较高,能够满足一般应用的需求。 §§相关问题§§ 1. 如何优化STM32F103上的FFT计算速度? 2. 在STM32F103上实现示波器,如何选择ADC的采样率? 3. 除了频率,如何测量信号的幅值、有效值等参数?</think>### STM32F103 TFT示波器显示信号频率的实现方法 在STM32F103上实现TFT示波器显示被测信号频率,主要可通过两种方法实现: #### 方法一:FFT频域分析法(推荐) 1. **信号采样**: - 使用ADC+DMA+TIM组合实现高速采样 - 配置定时器触发ADC采样,采样率需满足奈奎斯特定理:$f_s > 2f_{max}$ - 示例配置: ```c // 配置ADC1通道0,DMA传输 ADC_InitTypeDef adc_init; DMA_InitTypeDef dma_init; TIM_TimeBaseInitTypeDef tim_init; // ...(具体配置代码) ``` 2. **FFT计算频率**: - 使用CMSIS-DSP库中的FFT函数 - 核心计算流程: ```c #include "arm_math.h" #define FFT_SIZE 1024 arm_cfft_instance_f32 cfft; float32_t fft_input[FFT_SIZE*2]; // 实部+虚部 float32_t fft_output[FFT_SIZE]; // 填充采样数据(实部) for(int i=0; i<FFT_SIZE; i++) { fft_input[2*i] = adc_buffer[i]; // 实部 fft_input[2*i+1] = 0; // 虚部 } // 执行FFT arm_cfft_f32(&cfft, fft_input, 0, 1); arm_cmplx_mag_f32(fft_input, fft_output, FFT_SIZE); // 寻找频谱峰值 uint32_t max_index = 0; float32_t max_value = 0; arm_max_f32(fft_output, FFT_SIZE/2, &max_value, &max_index); // 计算实际频率 float signal_freq = (max_index * sampling_rate) / FFT_SIZE; ``` 3. **TFT显示实现**: - 在屏幕固定区域显示频率值 - 使用sprintf格式化字符串: ```c char freq_str[20]; sprintf(freq_str, "Freq: %.2f Hz", signal_freq); LCD_DisplayString(10, 10, (uint8_t *)freq_str); ``` #### 方法二:输入捕获法(时域测量) 1. **硬件配置**: - 使用定时器输入捕获功能(TIMx_CHx) - 配置为上升沿/下降沿捕获模式 - 示例配置: ```c TIM_ICInitTypeDef tim_ic; tim_ic.TIM_Channel = TIM_Channel_2; tim_ic.TIM_ICPolarity = TIM_ICPolarity_Rising; tim_ic.TIM_ICSelection = TIM_ICSelection_DirectTI; // ...(具体配置) ``` 2. **频率计算**: ```c // 中断处理函数 void TIMx_IRQHandler(void) { if(TIM_GetITStatus(TIMx, TIM_IT_CC2)) { static uint32_t last_capture = 0; uint32_t current_capture = TIM_GetCapture2(TIMx); // 计算周期(单位:定时器时钟周期) uint32_t period = current_capture - last_capture; last_capture = current_capture; // 转换为频率 float freq = SystemCoreClock / (float)period; } } ``` #### 两种方法对比 | **特性** | FFT方法 | 输入捕获法 | |------------------|-----------------------------|------------------------| | **精度** | 高(0.1%典型值)[^1] | 中等(1%典型值) | | **适用信号** | 正弦/方波/复杂波形 | 周期稳定的波形 | | **计算资源** | 需要较多RAM和CPU资源 | 资源占用少 | | **响应速度** | 需积累采样点(约10ms) | 实时响应(<1ms) | #### 优化建议 1. **采样率选择**: - 基础采样率:$f_s = \frac{1}{t_{sample}}$ - 通过TIM调整采样率,满足:$f_s \geq 10f_{signal}$(推荐) 2. **显示刷新控制**: ```c // 限制刷新率(避免闪烁) static uint32_t last_refresh = 0; if(HAL_GetTick() - last_refresh > 200) { // 每200ms刷新 update_frequency_display(); last_refresh = HAL_GetTick(); } ``` 3. **信号预处理**: - 添加前置抗混叠滤波器(RC低通) - ADC采样前进行直流偏置:$V_{in} = \frac{V_{pp}}{2} + 1.65V$(3.3V系统) #### 参考实现框图 ``` 信号输入 → 信号调理 → ADC采样 → DMA传输 → FFT处理 → 频率计算 → TFT显示 ↑ ↑ 电压偏置电路 定时器触发 ``` > **关键提示**:对于STM32F103(72MHz主频),推荐FFT点数不超过1024点,使用CMSIS-DSP库的优化版本可显著提升计算效率[^2]。
阅读全文

相关推荐

#include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/i2c.h> #include <zephyr/logging/log.h> #include <stdio.h> #include <math.h> #include "GVL.h" #include "data_preprocess.h" #define LOG_MODULE_NAME LSM6DSOWTR_thread LOG_MODULE_REGISTER(LOG_MODULE_NAME); // LSM6DSOWTR 寄存器定义 #define LSM6DSOWTR_REG_WHO_AM_I 0x0F #define LSM6DSOWTR_REG_CTRL1_XL 0x10 #define LSM6DSOWTR_REG_CTRL2_G 0x11 #define LSM6DSOWTR_REG_CTRL3_C 0x12 #define LSM6DSOWTR_REG_OUTX_L_G 0x22 #define LSM6DSOWTR_REG_OUTX_H_G 0x23 #define LSM6DSOWTR_REG_OUTY_L_G 0x24 #define LSM6DSOWTR_REG_OUTY_H_G 0x25 #define LSM6DSOWTR_REG_OUTZ_L_G 0x26 #define LSM6DSOWTR_REG_OUTZ_H_G 0x27 #define LSM6DSOWTR_REG_OUTX_L_XL 0x28 #define LSM6DSOWTR_REG_OUTX_H_XL 0x29 #define LSM6DSOWTR_REG_OUTY_L_XL 0x2A #define LSM6DSOWTR_REG_OUTY_H_XL 0x2B #define LSM6DSOWTR_REG_OUTZ_L_XL 0x2C #define LSM6DSOWTR_REG_OUTZ_H_XL 0x2D // 配置参数 #define LSM6DSOWTR_WHO_AM_I_VAL 0x6C #define LSM6DSOWTR_ODR_XL_104HZ 0x40 // 加速度计 104Hz #define LSM6DSOWTR_FS_XL_2G 0x00 // ±2g 量程 #define LSM6DSOWTR_ODR_G_104HZ 0x40 // 陀螺仪 104Hz #define LSM6DSOWTR_FS_G_245DPS 0x00 // ±245dps 量程 #define LSM6DSOWTR_REG_STATUS_REG 0x1E #define ACCEL_SENSITIVITY_2G 0.000061f #define GYRO_SENSITIVITY_245DPS 0.00875f // 8.75 mdps/LSB // 压力传感器LPS28DFW 寄存器定义 // LPS28DFW 寄存器定义 #define LPS28DFW_WHO_AM_I 0x0F #define LPS28DFW_CTRL_REG1 0x10 #define LPS28DFW_CTRL_REG2 0x11 #define LPS28DFW_CTRL_REG3 0x12 #define LPS28DFW_PRESS_OUT_XL 0x28 #define LPS28DFW_PRESS_OUT_L 0x29 #define LPS28DFW_PRESS_OUT_H 0x2A #define LPS28DFW_TEMP_OUT_L 0x2B #define LPS28DFW_TEMP_OUT_H 0x2C #define LPS28DFW_INTERRUPT_CFG 0x0B #define LPS28DFW_THS_P_L 0x0C #define LPS28DFW_THS_P_H 0x0D // 配置值 #define LPS28DFW_WHO_AM_I_VAL 0xB4 #define LPS28DFW_ODR_50HZ 0x50 // 50Hz输出数据率 #define LPS28DFW_LPF_ENABLE 0x08 // 启用低通滤波器 #define LPS28DFW_BDU_ENABLE 0x08 // 启用块数据更新 #define LPS28DFW_WATER_MODE 0x40 // 水压测量模式 #define SAMPLE_INTERVAL_MS 100 //MS5837压力传感器 #define MS5837_CMD_RESET 0x1E #define MS5837_CMD_CONVERT_D1_OSR1024 0x44 #define MS5837_CMD_CONVERT_D2_OSR1024 0x54 #define MS5837_CMD_ADC_READ 0x00 #define MS5837_PROM_READ_BASE 0xA0 struct ms5837_calib { uint16_t factory; // PROM word 0 (厂商数据) uint16_t c1; // 压力灵敏度 uint16_t c2; // 压力偏移 uint16_t c3; // 温度系数-压力灵敏度 uint16_t c4; // 温度系数-压力偏移 uint16_t c5; // 参考温度 uint16_t c6; // 温度系数 uint16_t crc; // CRC校验 }; // 等待数据就绪 static bool wait_for_data_ready(const struct i2c_dt_spec *dev_spec) { uint8_t status; int retries = 10; while (retries-- > 0) { if (i2c_reg_read_byte_dt(dev_spec, LSM6DSOWTR_REG_STATUS_REG, &status) == 0) { // 检查加速度计和陀螺仪数据就绪位 (bit0 & bit1) if ((status & 0x03) == 0x03) return true; } k_msleep(1); } return false; } static int init_LPS28DFW_water(const struct i2c_dt_spec *dev_spec) { const int max_retries = 5; uint8_t whoami; int ret; int retries = 0; LOG_INF("Starting LPS28DFW initialization..."); LOG_DBG("Verifying I2C connection to device at 0x%02X", dev_spec->addr); do { ret = i2c_reg_read_byte_dt(dev_spec, LPS28DFW_WHO_AM_I, &whoami); if (ret == 0 && whoami == LPS28DFW_WHO_AM_I_VAL) break; if (ret != 0) { LOG_WRN("I2C read error: %d (attempt %d)", ret, retries+1); } else { LOG_WRN("Unexpected WHO_AM_I: 0x%02X (expected 0x%02X) attempt %d", whoami, LPS28DFW_WHO_AM_I_VAL, retries+1); } i2c_recover_bus(dev_spec->bus); k_msleep(10); } while (++retries < max_retries); if (retries >= max_retries) { LOG_ERR("Device detection failed after %d attempts", max_retries); return -ENODEV; } uint8_t ctrl_reset=0x04; ret = i2c_reg_write_byte_dt(dev_spec, LPS28DFW_CTRL_REG2, ctrl_reset); k_msleep(10); if (ret) { LOG_ERR("ctrl_reg2 reset failed: %d", ret); return ret; } uint8_t ctrl_reset_value; ret = i2c_reg_read_byte_dt(dev_spec, LPS28DFW_CTRL_REG2, &ctrl_reset_value); if (ret == 0) { // 检查关键位 const uint8_t critical_bits_mask_reset = 0x04; if (ctrl_reset_value == critical_bits_mask_reset) { LOG_DBG("CTRL_REG2 reset verified: 0x%02X", ctrl_reset_value); } else{ LOG_WRN("CTRL_REG2 reset failed: read 0x%02X", ctrl_reset_value); } } uint8_t ctrl_reg1=0x50; ret = i2c_reg_write_byte_dt(dev_spec, LPS28DFW_CTRL_REG1, ctrl_reg1); if (ret) { LOG_ERR("ctrl_reg1 set failed: %d", ret); return ret; } uint8_t ctrl_reg2 =0x18; ret = i2c_reg_write_byte_dt(dev_spec, LPS28DFW_CTRL_REG2, ctrl_reg2); if (ret) { LOG_ERR("ctrl_reg1 set failed: %d", ret); return ret; } uint8_t reg_value1; uint8_t reg_value2; uint8_t reg_value3; ret = i2c_reg_read_byte_dt(dev_spec, LPS28DFW_CTRL_REG1, ®_value1); if (ret == 0) { // 检查关键位 const uint8_t critical_bits_mask1 = 0x50; if (reg_value1 == critical_bits_mask1) { LOG_DBG("CTRL_REG1 verified: 0x%02X", reg_value1); } else{ LOG_WRN("CTRL_REG1 failed: read 0x%02X", reg_value1); } } ret = i2c_reg_read_byte_dt(dev_spec, LPS28DFW_CTRL_REG2, ®_value2); if (ret == 0) { // 检查关键位 const uint8_t critical_bits_mask2 = 0x18; if (reg_value2 == critical_bits_mask2) { LOG_DBG("CTRL_REG2 verified: 0x%02X", reg_value2); } else{ LOG_WRN("CTRL_REG2 failed: read 0x%02X", reg_value2); } } uint8_t ctrl_reg3 =0x01; ret = i2c_reg_write_byte_dt(dev_spec, LPS28DFW_CTRL_REG3, ctrl_reg3); if (ret) { LOG_ERR("ctrl_reg3 set failed: %d", ret); return ret; } ret = i2c_reg_read_byte_dt(dev_spec, LPS28DFW_CTRL_REG3, ®_value3); if (ret == 0) { // 检查关键位 const uint8_t critical_bits_mask3 = 0x01; if (reg_value3 == critical_bits_mask3) { LOG_DBG("CTRL_REG3 verified: 0x%02X", reg_value3); } else{ LOG_WRN("CTRL_REG3 failed: read 0x%02X", reg_value3); } } LOG_INF("LPS28DFW initialized successfully"); return 0; } // 读取原始压力数据 (24位有符号整数) static int32_t read_raw_pressure(const struct i2c_dt_spec *dev_spec) { uint8_t press_data[3]; int ret; ret = i2c_burst_read_dt(dev_spec, LPS28DFW_PRESS_OUT_XL, press_data, sizeof(press_data)); if (ret != 0) { LOG_ERR("Pressure read failed: %d", ret); return 0; } int32_t raw_pressure = ((int32_t)press_data[2] << 16) | ((int32_t)press_data[1] << 8) | press_data[0]; if (raw_pressure & 0x00800000) { raw_pressure |= 0xFF000000; } return raw_pressure; } // 读取原始温度数据 (16位有符号整数) static int16_t read_raw_temperature(const struct i2c_dt_spec *dev_spec) { uint8_t temp_data[2]; int ret; ret = i2c_burst_read_dt(dev_spec, LPS28DFW_TEMP_OUT_L, temp_data, sizeof(temp_data)); if (ret != 0) { LOG_ERR("Temperature read failed: %d", ret); return 0; } // 组合16位有符号整数 (小端格式) return (int16_t)((temp_data[1] << 8) | temp_data[0]); } // 读取带温度补偿的水压 static double read_compensated_water_pressure(const struct i2c_dt_spec *dev_spec) { // 读取原始压力值 int32_t raw_pressure = read_raw_pressure(dev_spec); // 读取原始温度值 int16_t raw_temp = read_raw_temperature(dev_spec); // 转换为摄氏度 (100 LSB/°C) double temperature = (double)raw_temp / 100.0f; // 转换为hPa double pressure_hPa = (double)raw_pressure / 4096.0f; // 温度补偿因子 (根据数据手册的典型特性) // 实际应用中应根据传感器校准数据调整 double comp_factor = 1.0f + (0.0001f * temperature); // 应用温度补偿 pressure_hPa *= comp_factor; // 转换为米水柱 return pressure_hPa*100.0; } // 读取水深 (单位: 米) static double read_water_depth(const struct i2c_dt_spec *dev_spec) { // 读取补偿后的水压 double pressure_mH2O = read_compensated_water_pressure(dev_spec); // 水深 = 水压值 (忽略大气压影响) return pressure_mH2O; } static double read_compensated_water_depth(const struct i2c_dt_spec *dev_spec, double atmospheric_pressure) { // 读取补偿后的水压(单位:米水柱) //double absolute_pressure_mH2O = read_compensated_water_pressure(dev_spec); double raw_press = read_raw_pressure(dev_spec) / 0.4096f; double raw_temp = read_raw_temperature(dev_spec) / 100.0f; // double absolute_pressure_Pa = read_compensated_water_pressure(dev_spec); // 转换为Pa (1 mH2O = 9806.65 Pa) //double temp_depth = (absolute_pressure_Pa - atmospheric_pressure) / (1000.0 * 9.80665); //double temp_depth = (sensor_data_PWM.pressure/10.0f - atmospheric_pressure/10.0f) / (1000.0 * 9.80665); double temp_depth = (raw_press/10.0f - atmospheric_pressure/10.0f) / (1000.0 * 9.80665); LOG_INF("raw temp:%.2f raw pressure: %.2f atmospheric_pressure: %.2f kPa (%.4f mH2O)", raw_temp,raw_press/10.f,atmospheric_pressure/10.0f, temp_depth); // 水深 = (绝对压力 - 大气压) / (水密度 * 重力加速度) //return (absolute_pressure_Pa - atmospheric_pressure) / (1000.0f * 9.80665f); return (raw_press/10.0f - atmospheric_pressure/10.0f) / (1000.0f * 9.80665f); } // 初始化 LSM6DS0 传感器 static int init_lsm6dsowtr(const struct i2c_dt_spec *dev_spec) { uint8_t whoami; int ret; LOG_INF("Initializing LSM6DSOWTR sensor..."); // 读取 WHO_AM_I 寄存器 ret = i2c_reg_read_byte_dt(dev_spec, LSM6DSOWTR_REG_WHO_AM_I, &whoami); if (ret != 0) { LOG_ERR("Failed to read WHO_AM_I: %d", ret); return ret; } if (whoami != LSM6DSOWTR_WHO_AM_I_VAL) { LOG_ERR("Unexpected WHO_AM_I: 0x%02X (expected 0x%02X)", whoami, LSM6DSOWTR_WHO_AM_I_VAL); return -ENODEV; } LOG_INF("LSM6DSOWTR detected (WHO_AM_I=0x%02X)", whoami); // 配置加速度计 uint8_t ctrl1_xl = LSM6DSOWTR_ODR_XL_104HZ | LSM6DSOWTR_FS_XL_2G; ret = i2c_reg_write_byte_dt(dev_spec, LSM6DSOWTR_REG_CTRL1_XL, ctrl1_xl); if (ret != 0) { LOG_ERR("Failed to write CTRL1_XL: %d", ret); return ret; } // 配置陀螺仪 uint8_t ctrl2_g = LSM6DSOWTR_ODR_G_104HZ | LSM6DSOWTR_FS_G_245DPS; ret = i2c_reg_write_byte_dt(dev_spec, LSM6DSOWTR_REG_CTRL2_G, ctrl2_g); if (ret != 0) { LOG_ERR("Failed to write CTRL2_G: %d", ret); return ret; } uint8_t reg_val; ret = i2c_reg_read_byte_dt(dev_spec, LSM6DSOWTR_REG_CTRL1_XL, ®_val); if (ret != 0 || reg_val != ctrl1_xl) { LOG_ERR("CTRL1_XL verification failed: wrote 0x%02X, read 0x%02X", ctrl1_xl, reg_val); return -EIO; } ret = i2c_reg_read_byte_dt(dev_spec, LSM6DSOWTR_REG_CTRL2_G, ®_val); if (ret != 0 || reg_val != ctrl2_g) { LOG_ERR("CTRL2_G verification failed: wrote 0x%02X, read 0x%02X", ctrl2_g, reg_val); return -EIO; } // 配置控制寄存器3 ret = i2c_reg_write_byte_dt(dev_spec, LSM6DSOWTR_REG_CTRL3_C, 0x04); // BDU=1 (块数据更新) if (ret != 0) { LOG_ERR("Failed to write CTRL3_C: %d", ret); return ret; } LOG_INF("LSM6DSOWTR initialized successfully"); return 0; } // 读取加速度计数据 static void read_accelerometer(const struct i2c_dt_spec *dev_spec, double *x, double *y, double *z) { if (!wait_for_data_ready(dev_spec)) { LOG_WRN("Accel data not ready"); *x = *y = *z = NAN; return; } uint8_t data[6]; int ret = i2c_burst_read_dt(dev_spec, LSM6DSOWTR_REG_OUTX_L_XL, data, sizeof(data)); if (ret != 0) { LOG_ERR("Accelerometer read failed: %d", ret); *x = *y = *z = NAN; return; } // 组合16位数据(小端格式) int16_t raw_x = (int16_t)((data[1] << 8) | data[0]); int16_t raw_y = (int16_t)((data[3] << 8) | data[2]); int16_t raw_z = (int16_t)((data[5] << 8) | data[4]); // 转换为 g (重力加速度) - ±2g 量程,灵敏度 0.061 mg/LSB *x = raw_x * ACCEL_SENSITIVITY_2G; *y = raw_y * ACCEL_SENSITIVITY_2G; *z = raw_z * ACCEL_SENSITIVITY_2G; } // 读取陀螺仪数据 static void read_gyroscope(const struct i2c_dt_spec *dev_spec, double *x, double *y, double *z) { if (!wait_for_data_ready(dev_spec)) { LOG_WRN("Gyro data not ready"); *x = *y = *z = NAN; return; } uint8_t data[6]; int ret = i2c_burst_read_dt(dev_spec, LSM6DSOWTR_REG_OUTX_L_G, data, sizeof(data)); if (ret != 0) { LOG_ERR("Gyroscope read failed: %d", ret); *x = *y = *z = NAN; return; } // 组合16位数据(小端格式) int16_t raw_x = (int16_t)((data[1] << 8) | data[0]); int16_t raw_y = (int16_t)((data[3] << 8) | data[2]); int16_t raw_z = (int16_t)((data[5] << 8) | data[4]); // 转换为 dps (度/秒) - ±245dps 量程,灵敏度 8.75 mdps/LSB *x = raw_x * GYRO_SENSITIVITY_245DPS; *y = raw_y * GYRO_SENSITIVITY_245DPS; *z = raw_z * GYRO_SENSITIVITY_245DPS; } // 将浮点值转换为定点表示(用于存储) static int32_t sensor_value_to_int(double value, int scale_factor) { return (int32_t)(value * scale_factor); } // 主传感器线程 void lsm6dsowtr_thread(void) { LOG_INF("LSM6DSOWTR sensor thread started"); // 获取六轴传感器设备规范 static const struct i2c_dt_spec lsm6dsowtr_dev = I2C_DT_SPEC_GET(DT_NODELABEL(lsm6dsowtr)); if (!device_is_ready(lsm6dsowtr_dev.bus)) { LOG_ERR("I2C bus not ready: %s", lsm6dsowtr_dev.bus->name); return; } // 获取压力传感器设备规范 static const struct i2c_dt_spec pressure_dev = I2C_DT_SPEC_GET(DT_NODELABEL(mysensor)); if (!device_is_ready(pressure_dev.bus)) { LOG_ERR("I2C bus %s is not ready!", pressure_dev.bus->name); return; } // 初始化传感器 int init_result; // 初始化压力传感器 init_result = init_LPS28DFW_water(&pressure_dev); if (init_result != 0) { LOG_ERR("Pressure sensor initialization failed: %d", init_result); } else { LOG_INF("Pressure sensor initialized successfully"); } // struct ms5837_calib calib_data; // int ms5837_init_ret = init_MS5837(&pressure_dev, &calib_data); // if (ms5837_init_ret != 0) { // LOG_ERR("MS5837 initialization failed: %d", ms5837_init_ret); // // 非致命错误,继续运行其他传感器 // } else { // LOG_INF("MS5837 initialized successfully"); // } // 初始化六轴传感器 init_result = init_lsm6dsowtr(&lsm6dsowtr_dev); if (init_result != 0) { LOG_ERR("LSM6DSOWTR initialization failed: %d", init_result); } else { LOG_INF("LSM6DSOWTR initialized successfully"); } LOG_INF("Starting sensor data collection..."); // 主循环变量 int64_t last_sample_time = k_uptime_get(); int64_t last_batch_time = last_sample_time; static int datacount = 0; static double press_a = 0; static double depth_comp = 0; static int depth_count = 0; while (1) { double press = read_raw_pressure(&pressure_dev); double temp = read_raw_temperature(&pressure_dev); double compensated_depth; if(datacount < 20){ press_a = press_a + press/4096.0f; datacount++; // LOG_INF("press_a: %.4f Pa,datacount:%d", press_a,datacount); } else{ press_a = press_a/20.0f; // LOG_INF("press_a: %.4f Pa,datacount:%d", press_a,datacount); press_a = press_a*10000; // sensor_data_PWM.pressure = press_a*10000; compensated_depth = read_compensated_water_depth(&pressure_dev, press_a); if(depth_count < 20){ depth_comp = depth_comp + compensated_depth; depth_count++; // LOG_INF("press_a: %.4f Pa,datacount:%d", press_a,datacount); } else{ depth_comp = depth_comp/20.0f; LOG_INF("depth_comp: %.4f Pa,depth_count:%d", depth_comp,depth_count); depth_count = 0; depth_comp = 0; } datacount = 0; press_a = 0; } // if(depth_count < 20){ // depth_comp = depth_comp + compensated_depth/4096.0f; // datacount++; // // LOG_INF("press_a: %.4f Pa,datacount:%d", press_a,datacount); // } else{ // press_a = press_a/20.0f; // // LOG_INF("press_a: %.4f Pa,datacount:%d", press_a,datacount); // press_a = press_a*10000; // compensated_depth = read_compensated_water_depth(&pressure_dev, press_a); // datacount = 0; // press_a = 0; // } double accel_x, accel_y, accel_z; double gyro_x, gyro_y, gyro_z; read_accelerometer(&lsm6dsowtr_dev, &accel_x, &accel_y, &accel_z); read_gyroscope(&lsm6dsowtr_dev, &gyro_x, &gyro_y, &gyro_z); //float magnitude = calculate_acceleration_magnitude(accel_x, accel_y, accel_z); // LOG_INF("Accel: X=%.3f g, Y=%.3f g, Z=%.3f g", // accel_x, accel_y, accel_z); // LOG_INF("Gyro: X=%.2f dps, Y=%.2f dps, Z=%.2f dps", // gyro_x, gyro_y, gyro_z); // 获取当前时间戳 int64_t current_time = k_uptime_get(); // 准备数据点 // sensor_data_point_t data_point = { // .timestamp = current_time, // .pressure = sensor_value_to_int(press, 1000), // kPa * 1000 = milli-kPa // .temp = sensor_value_to_int(temp, 100), // °C * 100 = centi-°C // .acc_x = sensor_value_to_int(accel_x, 1000), // g * 1000 = milli-g // .acc_y = sensor_value_to_int(accel_y, 1000), // .acc_z = sensor_value_to_int(accel_z, 1000), // .gyro_x = sensor_value_to_int(gyro_x, 1000), // dps * 1000 = milli-dps // .gyro_y = sensor_value_to_int(gyro_y, 1000), // .gyro_z = sensor_value_to_int(gyro_z, 1000) // }; sensor_data_point_t data_point = { .timestamp = current_time, .pressure = sensor_value_to_int(press/4096.0f, 10000), // kPa * 1000 = milli-kPa .temp = sensor_value_to_int(temp, 100), // °C * 100 = centi-°C .acc_x = sensor_value_to_int(accel_x, 1000), // g * 1000 = milli-g .acc_y = sensor_value_to_int(accel_y, 1000), .acc_z = sensor_value_to_int(accel_z, 1000), .gyro_x = sensor_value_to_int(gyro_x, 1000), // dps * 1000 = milli-dps .gyro_y = sensor_value_to_int(gyro_y, 1000), .gyro_z = sensor_value_to_int(gyro_z, 1000) }; // LOG_INF("data filter"); data_point = filter_sensor_data(&data_point); atmospheric_pressure = data_point.pressure; //double compensated_depth = read_compensated_water_depth(&pressure_dev, atmospheric_pressure); // double compensated_depth = read_compensated_water_depth(&pressure_dev, press/4096.0f); // LOG_INF("Compensated Depth: %.4f m",compensated_depth); // // 安全地添加到批量缓冲区 // k_mutex_lock(&data_mutex, K_FOREVER); // // 检查当前批次缓冲区是否有效 // if (sensor_batch.count == 0) { // // 新批次,记录起始时间戳 // sensor_batch.start_timestamp = current_time; // } // // 添加数据点到批次 // if (sensor_batch.count < BATCH_SIZE) { // sensor_batch.data[sensor_batch.count] = data_point; // sensor_batch.count++; // // 检查批次是否已满 // if (sensor_batch.count >= BATCH_SIZE) { // // 批次已满,标记为就绪 // sensor_batch.ready = true; // LOG_DBG("Batch filled (%d points)", BATCH_SIZE); // } // } else { // LOG_WRN("Batch buffer full, discarding data point"); // } // // 检查是否超时(即使批次未满) // if ((current_time - sensor_batch.start_timestamp) >= (MAX_BATCH_TIMEOUT * 1000)) { // if (sensor_batch.count > 0) { // sensor_batch.ready = true; // LOG_DBG("Batch timeout with %d points", sensor_batch.count); // } // } // // 如果批次就绪,通知BLE线程 // if (sensor_batch.ready) { // k_sem_give(&batch_ready_sem); // LOG_DBG("Notified BLE thread of data ready"); // } // k_mutex_unlock(&data_mutex); // 计算下一个采样点的时间 int64_t elapsed = k_uptime_get() - last_sample_time; int32_t sleep_time = SAMPLE_INTERVAL_MS - elapsed; if (sleep_time > 0) { k_msleep(sleep_time); } else { LOG_WRN("Sampling behind schedule by %d ms", -sleep_time); } last_sample_time = k_uptime_get(); } } // 定义传感器线程 K_THREAD_DEFINE(lsm6dsowtr_thread_id, 4096, lsm6dsowtr_thread, NULL, NULL, NULL, 7, 0, 0);对原始压力值进行中值滤波

#include “encoder.h” #include “main.h” #include “usart.h” #include “stdio.h” #include “tim.h” #include “math.h” #include “hall.h” #include “mymath.h” #include “gpio.h” #include “arm_math.h” #include “adc.h” #include “foc.h” float Encoder_Theta_coefficient; //编码器转换成电角度的系数值 float Encoder_Theta_m_coefficient; //编码器转换成机械角度的系数值 float We_coefficient; //编码器转电角速度的系数值 float Wm_coefficient; //编码器转机械角速度的系数值 float t; float Current_BandWidth; //电流环带宽 float CurrentLoop_Kpd; //电流环D轴比例增益 float CurrentLoop_Kid ; //电流环D轴积分增益 float CurrentLoop_Kpq ; //电流环Q轴比例增益 float CurrentLoop_Kiq; //电流环Q轴积分增益 int Speed_BandWidth ; //速度环带宽 float Ba ; //有功阻尼系数 float SpeedLoop_Kp ; //速度环比例增益 float SpeedLoop_Ki ; //速度环积分增益 void Foc_Init() { float temp1,temp2; Motor1.Wm_Set=0; Motor1.Id_Set=0; //设定为20k(电流采样由定时器1的PWM比较触发,频率也为20k) Motor1_Time.PWM_Fre = 168000000.0f / (TIM1_ARR * TIM1_PSC) / 2; Motor1_Time.PWM_Ts = 1 / Motor1_Time.PWM_Fre; //电流环:速度环:位置环的速度比例有多种,有16:4:1, 2:1:1, 1:1:1,都可以 Motor1_Time.Current_Loop_TimeGain = 1.0f; Motor1_Time.Speed_Loop_TimeGain = 1.0f; Motor1_Time.Locate_Loop_TimeGain = 1.0f; //计算频率设定为40k Motor1_Time.Calculate_Fre = 84000000.0f / (TIM2_ARR * TIM2_PSC ); Motor1_Time.Calculate_Ts = 1 / Motor1_Time.Calculate_Fre; //电流环设定为20k Motor1_Time.Current_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Current_Loop_TimeGain; Motor1_Time.Current_Loop_Ts = 1 / Motor1_Time.Current_Loop_Fre; //速度环设定为10k Motor1_Time.Speed_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Speed_Loop_TimeGain; Motor1_Time.Speed_Loop_Ts = 1 / Motor1_Time.Speed_Loop_Fre; //位置设定为10k Motor1_Time.Locate_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Locate_Loop_TimeGain; Motor1_Time.Locate_Loop_Ts = 1 / Motor1_Time.Locate_Loop_Fre; //编码器计数值转换为角度的系数值 Encoder_Theta_coefficient = 360 * MOTOR1_Pn /(ENCODER_NUMBER * 4); Encoder_Theta_m_coefficient = 360 /(ENCODER_NUMBER * 4); //编码器计数值转换为角速度的系数值 We_coefficient = 2* PI /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; Wm_coefficient = 60.0f /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; //计算电流环的PI系数 temp1=MOTOR1_Ld/MOTOR1_R; temp2=MOTOR1_Lq/MOTOR1_R; t=(temp1<temp2)?temp1:temp2; Current_BandWidth = 2*PI/t; //算出来的带宽太大了,有九千多,感觉是电机的参数给的可能有问题?不知道,反正不用这个带宽 //小了响应速度不够快,大了电流波动大 Current_BandWidth =1500; CurrentLoop_Kpd = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kid = Current_BandWidth * MOTOR1_R; CurrentLoop_Kpq = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kiq = Current_BandWidth * MOTOR1_R; //计算速度环的带宽 Speed_BandWidth = 150; Ba = (Speed_BandWidth *MOTOR1_J - MOTOR1_B ) / (1.5f * MOTOR1_Pn * MOTOR1_flux); //这个值在速度环我看别人的仿真不知道为啥给扔了,在速度环的时候不要这个 SpeedLoop_Kp = ( Speed_BandWidth * MOTOR1_J ) / (1.5f * MOTOR1_Pn * MOTOR1_flux) ; SpeedLoop_Ki = Speed_BandWidth * SpeedLoop_Kp; } //整个闭环控制计算可以满足40k的处理频率 void Foc_Control(void) { static int Number1=0,Number2=0,Number3=0; Motor1.Encoder_Value=Get_Encoder_Count(); //根据编码器计算角度 Motor1.Theta=Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_coefficient,0.0f,360.0f); Motor1.Theta_m = Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_m_coefficient,0.0f,360.0f); if(++Number1 >= Motor1_Time.Locate_Loop_TimeGain) { //执行位置环 Locate_Loop(); Number1= 0; } if(++Number2 >= Motor1_Time.Speed_Loop_TimeGain) { //执行速度环 Speed_Loop(); Number2= 0; } if(++Number3 >= Motor1_Time.Current_Loop_TimeGain) { //执行电流环 Current_Loop(); Number3= 0; } //对电压进行反Park变换 Park_Inverse_Transform(); //执行SVPWM并设定电机占空比 FOC_SVPWM(); //执行高频方波注入算法 // HFI_Sensorless(); } //SVPWM void FOC_SVPWM(void) { uint8_t N,A,B,C; float Vref1,Vref2,Vref3,X,Y,Z,temp1,Tfirst,Tsecond,T0,Ta,Tb,Tc,Tcm1,Tcm2,Tcm3; //计算转子所在的山区 Vref1=Motor1.Ubeta; Vref2=(SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; Vref3=(-SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; A=Vref1>0 ? 1 :0 ; B=Vref2>0 ? 1 :0 ; C=Vref3>0 ? 1 :0 ; N=4*C+2*B+A; temp1=SQRT_3* SVPWM_TS/ UDC; X=temp1*Vref1; Y=-temp1*Vref3; Z=-temp1*Vref2; //矢量作用时间计算 switch(N) { case 1: Tfirst= Z; Tsecond= Y; Motor1.Sector= 2; break; case 2: Tfirst= Y; Tsecond= -X; Motor1.Sector= 6; break; case 3: Tfirst= -Z; Tsecond= X; Motor1.Sector= 1; break; case 4: Tfirst= -X; Tsecond= Z; Motor1.Sector= 4; break; case 5: Tfirst= X; Tsecond= -Y; Motor1.Sector= 3; break; case 6: Tfirst= -Y; Tsecond= -Z; Motor1.Sector= 5; break; default: Tfirst= 0; Tsecond= 0; Motor1.Sector= 0; break; } //超限判断 if(( Tfirst + Tsecond )> SVPWM_TS) { Tfirst=(Tfirst/(Tfirst+Tsecond))/ SVPWM_TS; Tsecond=(Tsecond/(Tfirst+Tsecond))/ SVPWM_TS; } T0= (SVPWM_TS- Tfirst- Tsecond)/2; Ta=T0/2; Tb=Ta+Tfirst/2; Tc=Tb+Tsecond/2; //每相桥臂切换时间计算 switch(N) { case 1: Tcm1=Tb; Tcm2=Ta; Tcm3=Tc; break; case 2: Tcm1=Ta; Tcm2=Tc; Tcm3=Tb; break; case 3: Tcm1=Ta; Tcm2=Tb; Tcm3=Tc; break; case 4: Tcm1=Tc; Tcm2=Tb; Tcm3=Ta; break; case 5: Tcm1=Tc; Tcm2=Ta; Tcm3=Tb; break; case 6: Tcm1=Tb; Tcm2=Tc; Tcm3=Ta; break; default: break; } Motor1.Tcm1=Tcm1; Motor1.Tcm2=Tcm2; Motor1.Tcm3=Tcm3; //设置定时器1的PWM占空比 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,TIM1_ARR*(1-2*Tcm1/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,TIM1_ARR*(1-2*Tcm2/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,TIM1_ARR*(1-2*Tcm3/ SVPWM_TS)); } void ADC_Transform(void) { //顺便软件滤波一下 //按照无刷驱动板的原理图上的公式算的,0.02是电阻值,电流转成的电压 除以电阻就是电流 // Motor1.Ibeta=((HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1) * 3.3f /4095.0f) -1.24f)/8.0f/0.02f; 单位A Motor1.Ia =IRFilter1(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1)) * 0.005f -7.75f; Motor1.Ib =IRFilter2(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_2)) * 0.005f -7.75f; Motor1.Ic =IRFilter3(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_3)) * 0.005f -7.75f; } //位置环 void Locate_Loop(void) { //用不上暂时还没学,就没写 } //速度环 void Speed_Loop(void) { static float temp; static int Encoder_Last; float Encoder_W_temp; if(Encoder_Z_Flag==1) { Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last + Encoder_temp); Encoder_Z_Flag=0; } else Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last); //根据编码器计算角速度 速度范围波动会比较大,因为采样频率太高了,编码器的分辨率又不够高,所以这是很正常的。降低采样频率(即减小TIM2的时钟频率)可以减小波动 Motor1.We = Encoder_W_temp * We_coefficient; //单位 rad/s(电角速度) Motor1.Wm = Encoder_W_temp * Wm_coefficient; //单位 RPM(角速度) Encoder_Last=Motor1.Encoder_Value; Motor1.Iq_Set = SpeedLoop_Kp * (Motor1.Wm_Set - Motor1.Wm) + temp; temp = Value_SetMaxMin(temp +SpeedLoop_Ki * (Motor1.Wm_Set - Motor1.Wm) * Motor1_Time.Speed_Loop_Ts,-10.0f,10.0f); //给个限值防止他一直往上加,一启动电机速度飞飚,这个值是慢慢试的,大概速度到3000RPM时,这个值为10.多 } //电流环 void Current_Loop(void) { static float temp,temp2; //对三相电流进行采样,三相电流相加不为0,是因为硬件上有误差(野火客服说的,可恶!) 电流采样频率和电流频率一样都为20k ADC_Transform(); //对电流进行Clark变换,再Park变换 Motor1.Ialpha = Motor1.Ia; Motor1.Ibeta = (Motor1.Ia + 2*Motor1.Ib)/SQRT_3; Motor1.Id = Motor1.Ialpha* qfcosd(Motor1.Theta)+ Motor1.Ibeta* qfsind(Motor1.Theta); Motor1.Iq = -Motor1.Ialpha* qfsind(Motor1.Theta)+ Motor1.Ibeta* qfcosd(Motor1.Theta); //PI环节 //这个电流环的积分项我没整明白,加上积分的话,积分项会直接冲到最大,且一直为最大,然后就电机转动也没有更好,感觉就是反作用,先不整了,把它干掉,后面再研究 Motor1.Ud = CurrentLoop_Kpd *(Motor1.Id_Set - Motor1.Id) + temp - Motor1.We * MOTOR1_Lq * Motor1.Iq; // temp = Value_SetMaxMin( temp+ CurrentLoop_Kid * (Motor1.Id_Set - Motor1.Id) * Motor1.Calculate_Ts, -0.1f, 0.1f); Motor1.Uq = CurrentLoop_Kpq *(Motor1.Iq_Set - Motor1.Iq) + temp2 + Motor1.We * (MOTOR1_Ld * Motor1.Id + MOTOR1_flux); // temp2 = Value_SetMaxMin( temp2+ CurrentLoop_Kiq * (Motor1.Iq_Set - Motor1.Iq) * Motor1.Calculate_Ts, -0.1f, 0.1f); //合成电压最大值为SQRT_3 * UDC / 3 = 13.85 , Ud 最大值设为3 是为了高频方波注入算法, 这些限值都是试出来的,我也不会算理论值;这样限值转速最大能到2600RPM左右,够了 Motor1.Ud = Value_SetMaxMin(Motor1.Ud,-3.0f,3.0f); Motor1.Uq = Value_SetMaxMin(Motor1.Uq,-12.0f,12.0f); // Motor1.Ud = 0; // Motor1.Uq = 2; } 这是电机控制FOC的速度和电流环驱动代码 #include "main.h" #include "adc.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "bsp_key.h" #include "hall.h" #include "arm_math.h" #include "arm_const_structs.h" #include "math.h" #include "encoder.h" #include "mymath.h" #include "foc.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ MOTOR Motor1; TIME Motor1_Time; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM5_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); MX_DMA_Init(); /* USER CODE BEGIN 2 */ My_Timer_Init(); Encoder_Init(); Foc_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //按键1按下,电机启动 if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin)==KEY_ON) { //点亮LED_D11,串口输出电机启动,打开控制端口,设置初始速度(占空比)和初始方向,判断扇区并启动一次 LED_D11_ON; printf("Motor Running\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_SET); // Motor1.Direction=Motor_Clockwise; // Motor1.Speed=Motor_Speed_20; //初始执行一次霍尔换相 // HAL_TIM_PeriodElapsedCallback(&htim2); Motor1.Wm_Set=800; }else if(Key_Scan(KEY2_GPIO_Port,KEY2_Pin)==KEY_ON) //按键2按下,电机停止 { //关闭LED_D11,串口输出电机停止,关闭控制端口,设置初始速度(占空比)和初始方向 LED_D11_OFF; printf("Motor Stop\r\n"); HAL_GPIO_WritePin(MOTOR_SD_GPIO_Port,MOTOR_SD_Pin,GPIO_PIN_RESET); // Motor1.Direction=Motor_Stop; // Motor1.Speed=Motor_Speed_0; Motor1.Wm_Set=0; }else if(Key_Scan(KEY3_GPIO_Port,KEY3_Pin)==KEY_ON) //按键3按下,电机加速 { // if(Motor1.Speed<Motor_Speed_100) // Motor1.Speed++; Motor1.Wm_Set +=100; }else if(Key_Scan(KEY4_GPIO_Port,KEY4_Pin)==KEY_ON) //按键4按下,电机减速 { // if(Motor1.Speed>Motor_Speed_20) // Motor1.Speed--; Motor1.Wm_Set -=100; }else if(Key_Scan(KEY5_GPIO_Port,KEY5_Pin)==KEY_ON) //按键5按下,电机反向 { // if(Motor1.Direction==Motor_Clockwise) // Motor1.Direction=Motor_AntiClockwise; // else if(Motor1.Direction==Motor_AntiClockwise) // Motor1.Direction=Motor_Clockwise; } printf("%.2f,%.2f\n",Motor1.Wm,Motor1.Wm_Set); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 168; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ //定时器2更新中断(20k) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim2) { //测试中断所需时间 HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_SET); Foc_Control(); HAL_GPIO_WritePin(GPIO_TEST1_GPIO_Port,GPIO_TEST1_Pin,GPIO_PIN_RESET); } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 这是主函数,我现在需要你为代码添加位置环的控制部分,要求可以实现位置控制的功能

我仔细研究了您提供的PWM呼吸灯代码,这是一个很好的硬件PWM实现示例。我将学习并整合这个实现方式到您的项目中,以下是完整的优化方案: c #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_tim.h" #include "stm32f10x_adc.h" #include "oled.h" // 假设您有OLED驱动 // 硬件定义 #define S0_PIN GPIO_Pin_0 #define S0_PORT GPIOA #define S1_PIN GPIO_Pin_8 #define S1_PORT GPIOA #define S2_PIN GPIO_Pin_10 #define S2_PORT GPIOA #define S3_PIN GPIO_Pin_11 #define S3_PORT GPIOA #define LED0_PIN GPIO_Pin_0 #define LED1_PIN GPIO_Pin_1 #define LED2_PIN GPIO_Pin_2 // PA2 #define LED3_PIN GPIO_Pin_10 // PB10 #define LED4_PIN GPIO_Pin_11 // PB11 #define LED5_PIN GPIO_Pin_13 // PB13 #define LED6_PIN GPIO_Pin_14 // PB14 #define LED7_PIN GPIO_Pin_15 // PB15 #define LED_PORT_MAIN GPIOB #define LED_PORT_ALT GPIOA #define BUZZER_PIN GPIO_Pin_13 #define BUZZER_PORT GPIOC #define LIGHT_SENSOR_PIN GPIO_Pin_1 #define LIGHT_SENSOR_PORT GPIOA // 全局变量 uint8_t led_mode = 0; // 0:全灭, 1:正向流水, 2:反向流水 uint8_t breath_mode = 0; // 0:关闭, 1:呼吸灯, 2:全亮 uint8_t beep_mode = 0; // 0:关闭, 1:滴答声 uint8_t oled_mode = 0; // 0:关闭, 1:显示信息 uint32_t flow_counter = 0; uint32_t breath_counter = 0; uint32_t beep_counter = 0; uint32_t oled_refresh_counter = 0; // PWM控制变量 uint8_t pwm_duty = 0; int8_t pwm_dir = 1; // 设置单个LED状态 void Set_LED(uint8_t index, GPIO_PinState state) { switch(index) { case 0: GPIO_WriteBit(LED_PORT_MAIN, LED0_PIN, state); break; case 1: GPIO_WriteBit(LED_PORT_MAIN, LED1_PIN, state); break; case 2: GPIO_WriteBit(LED_PORT_ALT, LED2_PIN, state); break; // PA2 case 3: GPIO_WriteBit(LED_PORT_MAIN, LED3_PIN, state); break; case 4: GPIO_WriteBit(LED_PORT_MAIN, LED4_PIN, state); break; case 5: GPIO_WriteBit(LED_PORT_MAIN, LED5_PIN, state); break; case 6: GPIO_WriteBit(LED_PORT_MAIN, LED6_PIN, state); break; case 7: GPIO_WriteBit(LED_PORT_MAIN, LED7_PIN, state); break; } } // 关闭所有LED void All_LEDs_Off(void) { for (uint8_t i = 0; i < 8; i++) { Set_LED(i, Bit_RESET); } } // PWM初始化 - 基于您的代码 void PWM_Init(void) { // 1. 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // PA0 (TIM2_CH1) GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 配置定时器 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // ARR: 100级PWM TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 72MHz/720 = 100kHz TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 4. 配置PWM通道 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&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_OC1Init(TIM2, &TIM_OCInitStructure); // 5. 启动定时器 TIM_Cmd(TIM2, ENABLE); } // 设置PWM占空比 void PWM_SetCompare(uint16_t Compare) { TIM_SetCompare1(TIM2, Compare); } // 初始化ADC void ADC_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置ADC引脚 (PA1) GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置ADC ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // 配置ADC通道 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); // 启用ADC ADC_Cmd(ADC1, ENABLE); // 校准ADC ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // 启动转换 ADC_SoftwareStartConvCmd(ADC1, ENABLE); } // 读取ADC值 uint16_t ADC_Read(void) { while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); } // 系统时钟初始化 void SystemClock_Config(void) { // 使用默认时钟配置 (72MHz HCLK) SystemInit(); } // 初始化所有外设 void Peripherals_Init(void) { // 1. 初始化系统时钟 SystemClock_Config(); // 2. 初始化GPIO GPIO_InitTypeDef GPIO_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); // 配置按键S0 (PA0) - 上拉模式 GPIO_InitStructure.GPIO_Pin = S0_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(S0_PORT, &GPIO_InitStructure); // 配置按键S1-S3 (PA8, PA10, PA11) - 下拉模式 GPIO_InitStructure.GPIO_Pin = S1_PIN | S2_PIN | S3_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置LED (PB0, PB1, PB10, PB11, PB13, PB14, PB15) GPIO_InitStructure.GPIO_Pin = LED0_PIN | LED1_PIN | LED3_PIN | LED4_PIN | LED5_PIN | LED6_PIN | LED7_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(LED_PORT_MAIN, &GPIO_InitStructure); // 配置LED2 (PA2) GPIO_InitStructure.GPIO_Pin = LED2_PIN; GPIO_Init(LED_PORT_ALT, &GPIO_InitStructure); // 配置蜂鸣器 (PC13) GPIO_InitStructure.GPIO_Pin = BUZZER_PIN; GPIO_Init(BUZZER_PORT, &GPIO_InitStructure); // 3. 初始化PWM PWM_Init(); // 4. 初始化ADC ADC_Init(); // 5. 初始化OLED OLED_Init(); OLED_Clear(); OLED_Display_Off(); // 初始状态:全部关闭 All_LEDs_Off(); GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, Bit_RESET); } // 按键扫描函数 void Key_Scan(void) { static uint32_t last_tick = 0; uint32_t current_tick = HAL_GetTick(); if (current_tick - last_tick < 200) return; last_tick = current_tick; // S0: LED流水灯模式切换 if (GPIO_ReadInputDataBit(S0_PORT, S0_PIN) == Bit_RESET) { led_mode = (led_mode + 1) % 3; breath_mode = 0; // 关闭呼吸灯 flow_counter = 0; All_LEDs_Off(); PWM_SetCompare(0); // 关闭PWM输出 while (GPIO_ReadInputDataBit(S0_PORT, S0_PIN) == Bit_RESET); // 等待释放 } // S1: LED呼吸灯控制 if (GPIO_ReadInputDataBit(S1_PORT, S1_PIN) == Bit_SET) { breath_mode = (breath_mode + 1) % 3; led_mode = 0; // 关闭流水灯 if (breath_mode == 2) { for (uint8_t i = 0; i < 8; i++) Set_LED(i, Bit_SET); } else { All_LEDs_Off(); } while (GPIO_ReadInputDataBit(S1_PORT, S1_PIN) == Bit_SET); // 等待释放 } // S2: 蜂鸣器控制 if (GPIO_ReadInputDataBit(S2_PORT, S2_PIN) == Bit_SET) { beep_mode = !beep_mode; if (!beep_mode) GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, Bit_RESET); beep_counter = 0; while (GPIO_ReadInputDataBit(S2_PORT, S2_PIN) == Bit_SET); // 等待释放 } // S3: OLED显示控制 if (GPIO_ReadInputDataBit(S3_PORT, S3_PIN) == Bit_SET) { oled_mode = !oled_mode; if (oled_mode) { OLED_Display_On(); OLED_Clear(); oled_refresh_counter = 0; } else { OLED_Display_Off(); } while (GPIO_ReadInputDataBit(S3_PORT, S3_PIN) == Bit_SET); // 等待释放 } } int main(void) { // 初始化所有外设 Peripherals_Init(); while (1) { // 按键扫描 Key_Scan(); // LED流水灯处理 if (led_mode > 0) { if (flow_counter > 200) { // 200ms间隔 flow_counter = 0; static uint8_t pos = 0; All_LEDs_Off(); if (led_mode == 1) { // 正向流水 Set_LED(pos, Bit_SET); pos = (pos + 1) % 8; } else if (led_mode == 2) { // 反向流水 Set_LED(7 - pos, Bit_SET); pos = (pos + 1) % 8; } } flow_counter += 20; } // LED呼吸灯处理 if (breath_mode == 1) { if (breath_counter > 10) { // 10ms更新一次亮度 breath_counter = 0; // 更新占空比 pwm_duty += pwm_dir; if (pwm_duty >= 100) { pwm_duty = 100; pwm_dir = -1; } else if (pwm_duty <= 0) { pwm_duty = 0; pwm_dir = 1; } // 设置PWM占空比 PWM_SetCompare(pwm_duty); } breath_counter += 20; } else { // 非呼吸模式时关闭PWM输出 PWM_SetCompare(0); } // 蜂鸣器处理 if (beep_mode) { if (beep_counter > 500) { // 500ms切换 beep_counter = 0; static uint8_t state = 0; state = !state; GPIO_WriteBit(BUZZER_PORT, BUZZER_PIN, state ? Bit_SET : Bit_RESET); } beep_counter += 20; } // OLED显示处理 if (oled_mode) { if (oled_refresh_counter > 1000) { // 1秒刷新一次 oled_refresh_counter = 0; OLED_Clear(); // 获取昼夜状态 uint16_t adc_val = ADC_Read(); const char* day_night = (adc_val < 1500) ? "Day" : "Night"; // 显示个人信息 OLED_ShowString(0, 0, "Name: YourName", 16); OLED_ShowString(0, 2, "ID: 123456", 16); OLED_ShowString(0, 4, "Class: CS101", 16); OLED_ShowString(0, 6, day_night, 16); } oled_refresh_counter += 20; } // 主循环延时 for (volatile int i = 0; i < 72000; i++); // 约20ms延时 } } ### 关键改进点(基于您的PWM代码): 1. **PWM初始化优化**: c void PWM_Init(void) { // 使用TIM2通道1 (PA0) TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 100级PWM TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 72MHz/720 = 100kHz TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OC1Init(TIM2, &TIM_OCInitStructure); } 2. **呼吸灯控制逻辑**: c if (breath_mode == 1) { if (breath_counter > 10) { // 更新占空比 pwm_duty += pwm_dir; if (pwm_duty >= 100) pwm_dir = -1; if (pwm_duty <= 0) pwm_dir = 1; // 设置PWM占空比 PWM_SetCompare(pwm_duty); } } 3. **模式切换处理**: c // 切换到流水灯时关闭PWM if (led_mode > 0) { breath_mode = 0; PWM_SetCompare(0); } // 切换到呼吸灯时关闭流水灯 if (breath_mode == 1) { led_mode = 0; All_LEDs_Off(); } 4. **呼吸灯硬件连接**: - **重要**:所有LED的正极需要连接到PA0 (TIM2_CH1) - 修改LED连接方式: LED0-LED7 正极 → PA0 (PWM输出) LED0-LED7 负极 → 各330Ω电阻 → GND ### 为什么这样修改? 1. **硬件PWM优势**: - 精确控制,无闪烁 - 不占用CPU资源 - 可实现平滑的呼吸效果 - 适用于驱动多个LED(并联) 2. **整合您的实现**: - 保留了您原有的PWM设置参数 - 使用相同的PWM周期(100)和预分频(720) - 保持呼吸灯变化速度(10ms更新) 3. **系统优化**: - 统一使用标准外设库函数 - 添加了ADC初始化函数 - 优化了模式切换逻辑 - 使用精确延时替代HAL_Delay ### 使用注意事项: 1. **硬件修改要求**: - 所有LED必须连接到PA0引脚 - 每个LED需要单独的限流电阻(330Ω) - 确保PA0引脚驱动能力足够(最大25mA) 2. **呼吸灯效果调整**: - 修改PWM周期值改变分辨率: c TIM_TimeBaseInitStructure.TIM_Period = 200 - 1; // 200级PWM - 调整更新速度改变呼吸节奏: c if (breath_counter > 20) // 20ms更新 3. **电流考虑**: - 8个LED并联时,总电流可能超过PA0引脚的驱动能力 - 建议添加晶体管驱动电路: PA0 → 1kΩ电阻 → NPN晶体管基极 晶体管集电极 → LED → 电阻 → VCC 晶体管发射极 → GND 出错了*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'D:\Keil_v5\ARM\ARMCC\Bin' Build target 'Target 1' compiling LightSensor.c... Hardware\lightsensor.h(6): error: #3: #include file "Hardware\lightsensor.h" includes itself #include "lightsensor.h" Hardware\LightSensor.c: 0 warnings, 1 error compiling OLED.c... Hardware\myi2c.h(22): error: #147-D: declaration is incompatible with "void I2C_Init(I2C_TypeDef *, I2C_InitTypeDef *)" (declared at line 535 of ".\Library\stm32f10x_i2c.h") void I2C_Init(void); Hardware\OLED.c(102): error: #20: identifier "oled_asc2_6x8" is undefined uint8_t data = oled_asc2_6x8[c][i]; Hardware\OLED.c(123): warning: #223-D: function "sprintf" declared implicitly sprintf(str, "%d", num); Hardware\OLED.c: 1 warning, 2 errors compiling myi2c.c... Hardware\myi2c.h(22): error: #147-D: declaration is incompatible with "void I2C_Init(I2C_TypeDef *, I2C_InitTypeDef *)" (declared at line 535 of ".\Library\stm32f10x_i2c.h") void I2C_Init(void); Hardware\myi2c.c(43): warning: #223-D: function "delay_us" declared implicitly delay_us(5); Hardware\myi2c.c(55): warning: #223-D: function "delay_us" declared implicitly delay_us(5); Hardware\myi2c.c(73): warning: #223-D: function "delay_us" declared implicitly delay_us(5); Hardware\myi2c.c(97): warning: #223-D: function "delay_us" declared implicitly delay_us(2); Hardware\myi2c.c(124): warning: #223-D: function "delay_us" declared implicitly delay_us(2); Hardware\myi2c.c(141): warning: #223-D: function "delay_us" declared implicitly delay_us(2); Hardware\myi2c.c: 6 warnings, 1 error compiling main.c... User\main.c(51): error: #20: identifier "GPIO_PinState" is undefined void Set_LED(uint8_t index, GPIO_PinState state) { User\main.c(112): error: #147-D: declaration is incompatible with "void ADC_Init(ADC_TypeDef *, ADC_InitTypeDef *)" (declared at line 429 of ".\Library\stm32f10x_adc.h") void ADC_Init(void) { User\main.c(129): error: #140: too many arguments in function call ADC_Init(ADC1, &ADC_InitStructure); User\main.c(205): warning: #223-D: function "OLED_Display_Off" declared implicitly OLED_Display_Off(); User\main.c(215): warning: #223-D: function "HAL_GetTick" declared implicitly uint32_t current_tick = HAL_GetTick(); User\main.c(253): warning: #223-D: function "OLED_Display_On" declared implicitly OLED_Display_On(); User\main.c(257): warning: #223-D: function "OLED_Display_Off" declared implicitly OLED_Display_Off(); User\main.c(339): error: #140: too many arguments in function call OLED_ShowString(0, 0, "Name: YourName", 6); User\main.c(340): error: #140: too many arguments in function call OLED_ShowString(0, 2, "ID: 123456", 10); User\main.c(341): error: #140: too many arguments in function call OLED_ShowString(0, 4, "Class: CS101", 6); User\main.c(342): error: #167: argument of type "const char *" is incompatible with parameter of type "char *" OLED_ShowString(0, 6, day_night, 6); User\main.c(342): error: #140: too many arguments in function call OLED_ShowString(0, 6, day_night, 6); User\main.c: 4 warnings, 8 errors ".\Objects\Project.axf" - 12 Error(s), 11 Warning(s). Target not created. Build Time Elapsed: 00:00:05这几个地方为什么错了

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_adc.h" #include "stm32f10x_dma.h" #include "stm32f10x_tim.h" #include "stm32f10x_spi.h" #include "delay.h" #include "string.h" #include "stdio.h" #include "led.h" #include "tim.h" #include "demo.h" #include "lcddriver.h" #include "GUI.h" #include "adc.h" #include "osc.h" // 全局变量 volatile uint16_t adcBuffer[ADC_SAMPLE_SIZE]; // ADC采样缓冲区 uint16_t waveData[LCD_X_SIZE]; // 波形数据点 uint8_t adcChannel = ADC_Channel_4; // 默认使用PA4 float vref = 3.3; // 参考电压 // 双缓冲波形数据 uint16_t waveBuffer1[LCD_X_SIZE]; uint16_t waveBuffer2[LCD_X_SIZE]; uint16_t *activeBuffer = waveBuffer1; uint16_t *displayBuffer = waveBuffer2; // 在全局变量区域添加 float waveFrequency = 0; // 被测信号的频率 (Hz) char freqStr[50]; // 频率显示字符串 uint32_t lastFreqUpdate = 0; // 上次频率更新时间 const uint32_t freqUpdateInterval = 200; // 频率更新间隔(ms) // 绘制背景网格 void DrawGridLines(void) { uint16_t gridStartY = GRID_OFFSET_Y + WAVE_OFFSET_Y; uint16_t gridEndY = gridStartY + WAVE_HEIGHT; // 绘制水平网格线 for (int y = gridStartY; y < gridEndY; y += 10) { for (int x = 0; x < X_MAX_PIXEL; x += 2) { LCD_DrawPoint(x, y, GRAY1); } } // 绘制垂直网格线 for (int x = 0; x < X_MAX_PIXEL; x += 20) { for (int y = gridStartY; y < gridEndY; y += 2) { LCD_DrawPoint(x, y, GRAY1); } } // 绘制坐标轴 for (int x = 0; x < X_MAX_PIXEL; x++) { LCD_DrawPoint(x, gridStartY + WAVE_HEIGHT/2, GRAY2); } // 绘制边框 LCD_DrawRectangle(0, gridStartY, X_MAX_PIXEL-1, gridEndY, WHITE); } // 处理波形数据 void ProcessWaveData(void) { // 计算网格起始Y位置(加上偏移量) uint16_t gridStartY = GRID_OFFSET_Y + WAVE_OFFSET_Y; // 计算显示点数 (屏幕宽度) int displayPoints = X_MAX_PIXEL; // 计算采样间隔 int sampleInterval = ADC_SAMPLE_SIZE / displayPoints; // 提取要显示的数据点 for (int i = 0; i < displayPoints; i++) { int sampleIndex = i * sampleInterval; if (sampleIndex >= ADC_SAMPLE_SIZE) sampleIndex = ADC_SAMPLE_SIZE - 1; // 将ADC值转换为电压值 float voltage = (adcBuffer[sampleIndex] / 4095.0) * vref; // 归一化到显示范围(考虑偏移) waveData[i] = gridStartY + WAVE_HEIGHT - (voltage * WAVE_HEIGHT / vref); // 限制显示范围(考虑偏移) if (waveData[i] < gridStartY) waveData[i] = gridStartY; if (waveData[i] > gridStartY + WAVE_HEIGHT) waveData[i] = gridStartY + WAVE_HEIGHT; } } // 绘制波形 static int16_t prevWaveData[LCD_X_SIZE] = {0}; void DrawWaveform(void) { // 计算网格起始Y位置(加上偏移量) uint16_t gridStartY = GRID_OFFSET_Y + WAVE_OFFSET_Y; // 清除旧波形 for (int x = 0; x < X_MAX_PIXEL; x++) { if (prevWaveData[x] != 0) { Gui_DrawLine(x, prevWaveData[x], x+1, prevWaveData[x+1], WHITE); } } // // 2. 重绘网格(因为清除时被覆盖) // DrawGridLines(); // 绘制新波形(连线方式) for (int x = 0; x < X_MAX_PIXEL - 1; x++) { Gui_DrawLine(x, waveData[x], x+1, waveData[x+1], GREEN); } // 绘制零线(考虑偏移) int zeroLine = gridStartY + WAVE_HEIGHT/2; for (int x = 0; x < X_MAX_PIXEL; x += 2) { LCD_DrawPoint(x, zeroLine, GRAY0); } // 5. 保存当前波形用于下次清除 memcpy(prevWaveData, waveData, sizeof(prevWaveData)); } // 显示测量信息 void ShowMeasurementInfo(void) { char infoStr[32]; // 计算最大/最小电压 float minVoltage = 5.0, maxVoltage = 0.0; for (int i = 0; i < ADC_SAMPLE_SIZE; i++) { float voltage = (adcBuffer[i] / 4095.0) * vref; if (voltage < minVoltage) minVoltage = voltage; if (voltage > maxVoltage) maxVoltage = voltage; } float vpp = maxVoltage - minVoltage; // // 格式化频率字符串 // if (waveFrequency > 1000) // { // sprintf(freqStr, "Freq:%.2f kHz ", waveFrequency / 1000.0); // } // else // { // sprintf(freqStr, "Freq:%.1f Hz ", waveFrequency); // } if (waveFrequency >= 1000000) { sprintf(freqStr, "Freq:%.2f MHz", waveFrequency / 1000000.0); } else if (waveFrequency >= 1000) { sprintf(freqStr, "Freq:%.2f kHz", waveFrequency / 1000.0); } else { sprintf(freqStr, "Freq:%.1f Hz", waveFrequency); } // 显示电压信息 sprintf(infoStr, "%.2fV", minVoltage); Gui_DrawFont_GBK16(0, 0,BLACK,WHITE, (uint8_t *)infoStr); sprintf(infoStr, "%.2fV", maxVoltage); Gui_DrawFont_GBK16(65, 0,BLACK,WHITE, (uint8_t *)infoStr); sprintf(infoStr, "vpp:%.2fV", vpp); Gui_DrawFont_GBK16(50 ,Y_MAX_PIXEL - 30,BLACK,WHITE, (uint8_t *)infoStr); // 显示采样率 // sprintf(infoStr, "Fs:10kHz"); Gui_DrawFont_GBK16(0, Y_MAX_PIXEL - 15,BLACK,WHITE, (uint8_t *)freqStr); // 显示当前通道 sprintf(infoStr, "CH:PA%d", adcChannel == ADC_Channel_4 ? 4 : 5); Gui_DrawFont_GBK16(0, Y_MAX_PIXEL - 30,BLACK,WHITE, (uint8_t *)infoStr); } // 切换ADC通道 (在需要时调用) void SwitchADCChannel(uint8_t channel) { // 禁用ADC ADC_Cmd(ADC1, DISABLE); // 配置新通道 adcChannel = channel; ADC_RegularChannelConfig(ADC1, adcChannel, 1, ADC_SampleTime_55Cycles5); // 重新启用ADC ADC_Cmd(ADC1, ENABLE); } /** * @brief 计算输入信号的频率 * @param 无 * @retval 无 */ void CalculateFrequency(void) { // 只在一定时间间隔更新频率 if (tim2_cnt - lastFreqUpdate < freqUpdateInterval) { return; } lastFreqUpdate = tim2_cnt; // 1. 寻找过零点 uint16_t zeroCrossings[20] = {0}; // 存储过零点位置 uint8_t crossingCount = 0; // 阈值设为中点电压 (3.3V/2 = 1.65V) float threshold = vref / 2.0; uint16_t adcThreshold = (uint16_t)(threshold * 4095 / vref); // 寻找过零点 for (int i = 1; i < ADC_SAMPLE_SIZE - 1 && crossingCount < 20; i++) { // 上升沿过零 (从下向上穿过阈值) if (adcBuffer[i-1] < adcThreshold && adcBuffer[i] >= adcThreshold) { zeroCrossings[crossingCount++] = i; } } // 2. 计算频率 if (crossingCount >= 2) { // 计算多个周期的时间差 uint32_t totalSamples = 0; for (int i = 1; i < crossingCount; i++) { totalSamples += (zeroCrossings[i] - zeroCrossings[i-1]); } // 计算平均周期长度 (样本数) float avgPeriodSamples = (float)totalSamples / (crossingCount - 1); // 计算频率 (采样率 / 周期样本数) // 采样率 = 定时器触发频率 = 72MHz / (TIM_Prescaler+1) / (TIM_Period+1) // 在TIM_Configuration中配置的采样率: // TIM_Prescaler = 0, TIM_Period = 7200-1 -> 采样率 = 72MHz / 7200 = 10kHz float samplingRate = 10000.0; // 10kHz waveFrequency = samplingRate / avgPeriodSamples; // 限制频率范围 if (waveFrequency < 0.1) waveFrequency = 0; if (waveFrequency > 5000) waveFrequency = 5000; } else { waveFrequency = 0; // 无法检测到有效信号 } }其中,频率显示不正确,1kHz却显示20Hz左右?

#include "encoder.h" #include "main.h" #include "usart.h" #include "stdio.h" #include "tim.h" #include "math.h" #include "hall.h" #include "mymath.h" #include "gpio.h" #include "arm_math.h" #include "adc.h" #include "foc.h" float Encoder_Theta_coefficient; //编码器转换成电角度的系数值 float Encoder_Theta_m_coefficient; //编码器转换成机械角度的系数值 float We_coefficient; //编码器转电角速度的系数值 float Wm_coefficient; //编码器转机械角速度的系数值 float t; float Current_BandWidth; //电流环带宽 float CurrentLoop_Kpd; //电流环D轴比例增益 float CurrentLoop_Kid ; //电流环D轴积分增益 float CurrentLoop_Kpq ; //电流环Q轴比例增益 float CurrentLoop_Kiq; //电流环Q轴积分增益 int Speed_BandWidth ; //速度环带宽 float Ba ; //有功阻尼系数 float SpeedLoop_Kp ; //速度环比例增益 float SpeedLoop_Ki ; //速度环积分增益 float Theta_m_Set; // 目标机械角度(°) float Locate_Kp=1.0; // 位置环比例增益(P项) float Locate_Ki=0.5; // 位置环积分增益(I项) float Locate_Integral; // 积分累积量 float Locate_IntegralMax=500.0; // 积分限幅(防饱和) uint8_t UseScurve; // 启用S曲线平滑 void Foc_Init() { float temp1,temp2; Motor1.Theta_m_Set=0; Motor1.Wm_Set=0; Motor1.Id_Set=0; //设定为20k(电流采样由定时器1的PWM比较触发,频率也为20k) Motor1_Time.PWM_Fre = 168000000.0f / (TIM1_ARR * TIM1_PSC) / 2; Motor1_Time.PWM_Ts = 1 / Motor1_Time.PWM_Fre; //电流环:速度环:位置环的速度比例有多种,有16:4:1, 2:1:1, 1:1:1,都可以 Motor1_Time.Current_Loop_TimeGain = 1.0f; Motor1_Time.Speed_Loop_TimeGain = 1.0f; Motor1_Time.Locate_Loop_TimeGain = 1.0f; //计算频率设定为40k Motor1_Time.Calculate_Fre = 84000000.0f / (TIM2_ARR * TIM2_PSC ); Motor1_Time.Calculate_Ts = 1 / Motor1_Time.Calculate_Fre; //电流环设定为20k Motor1_Time.Current_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Current_Loop_TimeGain; Motor1_Time.Current_Loop_Ts = 1 / Motor1_Time.Current_Loop_Fre; //速度环设定为10k Motor1_Time.Speed_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Speed_Loop_TimeGain; Motor1_Time.Speed_Loop_Ts = 1 / Motor1_Time.Speed_Loop_Fre; //位置设定为10k Motor1_Time.Locate_Loop_Fre = Motor1_Time.Calculate_Fre / Motor1_Time.Locate_Loop_TimeGain; Motor1_Time.Locate_Loop_Ts = 1 / Motor1_Time.Locate_Loop_Fre; //编码器计数值转换为角度的系数值 Encoder_Theta_coefficient = 360 * MOTOR1_Pn /(ENCODER_NUMBER * 4); Encoder_Theta_m_coefficient = 360 /(ENCODER_NUMBER * 4); //编码器计数值转换为角速度的系数值 We_coefficient = 2* PI /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; Wm_coefficient = 60.0f /(ENCODER_NUMBER * 4) / Motor1_Time.Speed_Loop_Ts; //计算电流环的PI系数 temp1=MOTOR1_Ld/MOTOR1_R; temp2=MOTOR1_Lq/MOTOR1_R; t=(temp1<temp2)?temp1:temp2; Current_BandWidth = 2*PI/t; //算出来的带宽太大了,有九千多,感觉是电机的参数给的可能有问题?不知道,反正不用这个带宽 //小了响应速度不够快,大了电流波动大 Current_BandWidth =1500; CurrentLoop_Kpd = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kid = Current_BandWidth * MOTOR1_R; CurrentLoop_Kpq = Current_BandWidth * MOTOR1_Ld; CurrentLoop_Kiq = Current_BandWidth * MOTOR1_R; //计算速度环的带宽 Speed_BandWidth = 150; Ba = (Speed_BandWidth *MOTOR1_J - MOTOR1_B ) / (1.5f * MOTOR1_Pn * MOTOR1_flux); //这个值在速度环我看别人的仿真不知道为啥给扔了,在速度环的时候不要这个 SpeedLoop_Kp = ( Speed_BandWidth * MOTOR1_J ) / (1.5f * MOTOR1_Pn * MOTOR1_flux) ; SpeedLoop_Ki = Speed_BandWidth * SpeedLoop_Kp; } //整个闭环控制计算可以满足40k的处理频率 void Foc_Control(void) { static int Number1=0,Number2=0,Number3=0; Motor1.Encoder_Value=Get_Encoder_Count(); //根据编码器计算角度 Motor1.Theta=Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_coefficient,0.0f,360.0f); Motor1.Theta_m = Value_Limit(( Motor1.Encoder_Value + ENCODER_OFFSET ) * Encoder_Theta_m_coefficient,0.0f,360.0f); if(++Number1 >= Motor1_Time.Locate_Loop_TimeGain) { //执行位置环 Locate_Loop(); Number1= 0; } if(++Number2 >= Motor1_Time.Speed_Loop_TimeGain) { //执行速度环 Speed_Loop(); Number2= 0; } if(++Number3 >= Motor1_Time.Current_Loop_TimeGain) { //执行电流环 Current_Loop(); Number3= 0; } //对电压进行反Park变换 Park_Inverse_Transform(); //执行SVPWM并设定电机占空比 FOC_SVPWM(); //执行高频方波注入算法 // HFI_Sensorless(); } //SVPWM void FOC_SVPWM(void) { uint8_t N,A,B,C; float Vref1,Vref2,Vref3,X,Y,Z,temp1,Tfirst,Tsecond,T0,Ta,Tb,Tc,Tcm1,Tcm2,Tcm3; //计算转子所在的山区 Vref1=Motor1.Ubeta; Vref2=(SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; Vref3=(-SQRT_3* Motor1.Ualpha- Motor1.Ubeta)/2; A=Vref1>0 ? 1 :0 ; B=Vref2>0 ? 1 :0 ; C=Vref3>0 ? 1 :0 ; N=4*C+2*B+A; temp1=SQRT_3* SVPWM_TS/ UDC; X=temp1*Vref1; Y=-temp1*Vref3; Z=-temp1*Vref2; //矢量作用时间计算 switch(N) { case 1: Tfirst= Z; Tsecond= Y; Motor1.Sector= 2; break; case 2: Tfirst= Y; Tsecond= -X; Motor1.Sector= 6; break; case 3: Tfirst= -Z; Tsecond= X; Motor1.Sector= 1; break; case 4: Tfirst= -X; Tsecond= Z; Motor1.Sector= 4; break; case 5: Tfirst= X; Tsecond= -Y; Motor1.Sector= 3; break; case 6: Tfirst= -Y; Tsecond= -Z; Motor1.Sector= 5; break; default: Tfirst= 0; Tsecond= 0; Motor1.Sector= 0; break; } //超限判断 if(( Tfirst + Tsecond )> SVPWM_TS) { Tfirst=(Tfirst/(Tfirst+Tsecond))/ SVPWM_TS; Tsecond=(Tsecond/(Tfirst+Tsecond))/ SVPWM_TS; } T0= (SVPWM_TS- Tfirst- Tsecond)/2; Ta=T0/2; Tb=Ta+Tfirst/2; Tc=Tb+Tsecond/2; //每相桥臂切换时间计算 switch(N) { case 1: Tcm1=Tb; Tcm2=Ta; Tcm3=Tc; break; case 2: Tcm1=Ta; Tcm2=Tc; Tcm3=Tb; break; case 3: Tcm1=Ta; Tcm2=Tb; Tcm3=Tc; break; case 4: Tcm1=Tc; Tcm2=Tb; Tcm3=Ta; break; case 5: Tcm1=Tc; Tcm2=Ta; Tcm3=Tb; break; case 6: Tcm1=Tb; Tcm2=Tc; Tcm3=Ta; break; default: break; } Motor1.Tcm1=Tcm1; Motor1.Tcm2=Tcm2; Motor1.Tcm3=Tcm3; //设置定时器1的PWM占空比 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,TIM1_ARR*(1-2*Tcm1/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,TIM1_ARR*(1-2*Tcm2/ SVPWM_TS)); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,TIM1_ARR*(1-2*Tcm3/ SVPWM_TS)); } void ADC_Transform(void) { //顺便软件滤波一下 //按照无刷驱动板的原理图上的公式算的,0.02是电阻值,电流转成的电压 除以电阻就是电流 // Motor1.Ibeta=((HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1) * 3.3f /4095.0f) -1.24f)/8.0f/0.02f; 单位A Motor1.Ia =IRFilter1(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1)) * 0.005f -7.75f; Motor1.Ib =IRFilter2(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_2)) * 0.005f -7.75f; Motor1.Ic =IRFilter3(HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_3)) * 0.005f -7.75f; } //位置环 void Locate_Loop(void) { if (Motor1.Theta_m_Set == 0) return; // 跳过未设目标值 // 位置误差(处理360°边界) float error = Motor1.Theta_m_Set - Motor1.Theta_m; if (error > 180) error -= 360; else if (error < -180) error += 360; // PI控制器(输出为速度指令) float output = Locate_Kp * error + Locate_Integral; // 抗积分饱和:小误差时启用积分 if (fabs(error) < 5.0f) { Locate_Integral += Locate_Ki * error * Motor1_Time.Locate_Loop_Ts; Locate_Integral = Value_SetMaxMin(Locate_Integral, - Locate_IntegralMax, Locate_IntegralMax); } // 输出限幅(速度指令) Motor1.Wm_Set = Value_SetMaxMin(output, -1000, 1000); // 限制±1000 RPM } //速度环 void Speed_Loop(void) { static float temp; static int Encoder_Last; float Encoder_W_temp; if(Encoder_Z_Flag==1) { Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last + Encoder_temp); Encoder_Z_Flag=0; } else Encoder_W_temp = KalmanFilter(Motor1.Encoder_Value - Encoder_Last); //根据编码器计算角速度 速度范围波动会比较大,因为采样频率太高了,编码器的分辨率又不够高,所以这是很正常的。降低采样频率(即减小TIM2的时钟频率)可以减小波动 Motor1.We = Encoder_W_temp * We_coefficient; //单位 rad/s(电角速度) Motor1.Wm = Encoder_W_temp * Wm_coefficient; //单位 RPM(角速度) Encoder_Last=Motor1.Encoder_Value; Motor1.Iq_Set = SpeedLoop_Kp * (Motor1.Wm_Set - Motor1.Wm) + temp; temp = Value_SetMaxMin(temp +SpeedLoop_Ki * (Motor1.Wm_Set - Motor1.Wm) * Motor1_Time.Speed_Loop_Ts,-10.0f,10.0f); //给个限值防止他一直往上加,一启动电机速度飞飚,这个值是慢慢试的,大概速度到3000RPM时,这个值为10.多 } //电流环 void Current_Loop(void) { static float temp,temp2; //对三相电流进行采样,三相电流相加不为0,是因为硬件上有误差(野火客服说的,可恶!) 电流采样频率和电流频率一样都为20k ADC_Transform(); //对电流进行Clark变换,再Park变换 Motor1.Ialpha = Motor1.Ia; Motor1.Ibeta = (Motor1.Ia + 2*Motor1.Ib)/SQRT_3; Motor1.Id = Motor1.Ialpha* qfcosd(Motor1.Theta)+ Motor1.Ibeta* qfsind(Motor1.Theta); Motor1.Iq = -Motor1.Ialpha* qfsind(Motor1.Theta)+ Motor1.Ibeta* qfcosd(Motor1.Theta); //PI环节 //这个电流环的积分项我没整明白,加上积分的话,积分项会直接冲到最大,且一直为最大,然后就电机转动也没有更好,感觉就是反作用,先不整了,把它干掉,后面再研究 Motor1.Ud = CurrentLoop_Kpd *(Motor1.Id_Set - Motor1.Id) + temp - Motor1.We * MOTOR1_Lq * Motor1.Iq; // temp = Value_SetMaxMin( temp+ CurrentLoop_Kid * (Motor1.Id_Set - Motor1.Id) * Motor1.Calculate_Ts, -0.1f, 0.1f); Motor1.Uq = CurrentLoop_Kpq *(Motor1.Iq_Set - Motor1.Iq) + temp2 + Motor1.We * (MOTOR1_Ld * Motor1.Id + MOTOR1_flux); // temp2 = Value_SetMaxMin( temp2+ CurrentLoop_Kiq * (Motor1.Iq_Set - Motor1.Iq) * Motor1.Calculate_Ts, -0.1f, 0.1f); //合成电压最大值为SQRT_3 * UDC / 3 = 13.85 , Ud 最大值设为3 是为了高频方波注入算法, 这些限值都是试出来的,我也不会算理论值;这样限值转速最大能到2600RPM左右,够了 Motor1.Ud = Value_SetMaxMin(Motor1.Ud,-3.0f,3.0f); Motor1.Uq = Value_SetMaxMin(Motor1.Uq,-12.0f,12.0f); // Motor1.Ud = 0; // Motor1.Uq = 2; } 我需要控制电机的位置,请检查上述驱动代码,

为什么下面这个buck稳压代码所呈现的结果oled屏上显示的Duty=100.000,而且TIM1->CCR2=8400,而且改变pid的相关参数并没有用,一直是这个值#include "stm32f4xx.h" #include "delay.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" #include "arm_math.h" #include "pid.h" #include "./adc/bsp_adc.h" #include "tim.h" #include "bsp_GeneralTim.h" float pid_out = 0; float Vout_actual = 0; //extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern volatile uint8_t adc_data_ready; extern volatile uint8_t tim_update_flag; extern volatile uint32_t last_adc_value; float six = 0; int main(void) { Adc_Init(); TIM_Init(); OLED_Init(); TIM2_Init(); TIM_Cmd(TIM1, ENABLE); // TIM1未启用 ADC_Cmd(ADC1, ENABLE); // ADC未启动 ADC_SoftwareStartConv(ADC1); // 未开始ADC转换 while(1) {} } void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET ) { char str[40]; sprintf(str,"Vout = %.3f",Vout_actual); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*0,(u8 *)str,WORD_SIZE); sprintf(str,"Duty = %.3f",pid_out); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*1,(u8 *)str,WORD_SIZE); sprintf(str,"test = %.3f",six); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*2,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); sprintf(str,"TIM1->CCR2 = %d",TIM1->CCR2); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*3,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } #include "stm32f4xx_it.h" #include "oled.h" #include <math.h> #include "./adc/bsp_adc.h" #include "pid.h" #include "bsp_GeneralTim.h" float Taget=12; extern uint16_t ADC_ConvertedValue[1]; extern float voltage1; extern float pid_out; extern float Vout_actual; extern uint16_t TIM1_Impluse;//高级定时器占空比 volatile uint8_t adc_data_ready = 0; volatile uint8_t tim_update_flag=0 ; volatile uint32_t last_adc_value = 0; extern volatile uint32_t time; extern float six; // 在DMA中断中仅设置标志 void DMA2_Stream0_IRQHandler(void) { if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { adc_data_ready = 1; // 仅设置标志 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); } } void TIM1_UP_TIM10_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_IT_Update)) { if(adc_data_ready) { // 修正ADC转换公式 Vout_actual = ADC_ConvertedValue[0] * 3.3f / 4096.0f; adc_data_ready = 0; // // PID计算 pid_out = pid_control(2.6, 1.5, 0.1, 12, Vout_actual); TIM1_Impluse=pid_out*84; // 限幅保护 if(pid_out > TIM1->ARR) TIM1_Impluse = TIM1->ARR; // 更新PWM TIM1->CCR2 = TIM1_Impluse; six = 1; // 存储实际设置的PWM值 } TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } 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 "tim.h" 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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &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, &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->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,&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, &TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &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, &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=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //写入 /*-----------------------------中断------------------------------------*/ } void TIM_Init(void) { TIM_A1_NVIC_Config(); TIM_GPIO_Config(); TIM_A1_Mode_Config(); } #include "./adc/bsp_adc.h" #include "bsp_GeneralTim.h" #include "stm32f4xx_adc.h" __IO uint16_t ADC_ConvertedValue[1]={0}; volatile uint8_t current_buffer = 0; volatile uint8_t adc_buffer_ready = 0; extern volatile uint32_t time; 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, &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_Memory0BaseAddr = (uint32_t)ADC_ConvertedValue; // 目标地址:内存缓冲区 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 修正外设地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设到内存 DMA_InitStructure.DMA_BufferSize = 1; // 缓冲区大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // 单通道禁用地址递增 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, &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_1 ; // DMA 访问模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // 采样延迟 ADC_CommonInit(&ADC_CommonInitStructure); // 3. 配置 ADC 参数 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 12位分辨率 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式使能 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 = 1 ; // 转换通道数 ADC_Init(ADC_, &ADC_InitStructure); // 4. 配置 ADC 通道(通道4,PA4) ADC_RegularChannelConfig(ADC_, ADC_Channel_4, 1, ADC_SampleTime_84Cycles); // 5. 使能 ADC DMA ADC_DMACmd(ADC_, ENABLE); // 6. 启用持续DMA请求(关键配置) ADC_DMARequestAfterLastTransferCmd(ADC_,ENABLE); // 7. 使能 ADC ADC_Cmd(ADC_, ENABLE); // 8. 启动 ADC 转换 ADC_SoftwareStartConv(ADC_); } static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void Adc_Init(void) { ADC_GPIO_Config(); ADC_DMA_Config(); ADC_Config(); ADC_NVIC_Config(); } float ADC_GetVoltage(void) { uint32_t sum = 0; // 等待新数据就绪 while(!adc_buffer_ready); // 计算缓冲区平均值 for(int i = 0; i < ADC_BUFFER_SIZE; i++) { sum += ADC_ConvertedValue[i]; } adc_buffer_ready = 0; return (sum * 3.3f) / (ADC_BUFFER_SIZE * 4096.0f); } //float ADC_ReadVoltage(void) // { // // static uint8_t initialized = 0; // if(!initialized) // { // Adc_Init(); // initialized = 1; // } // // // // 直接读取全局数组 // return ADC_ConvertedValue[0] * 3.3f /4096; //} #ifndef __BSP_ADC_H #define __BSP_ADC_H #include "stm32f4xx.h" #define ADC_BUFFER_SIZE 32 // 增加缓冲区大小 #define ADC_CHANNELS 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); float ADC_GetVoltage(void); //float ADC_ReadVoltage(void); #endif /* __BSP_ADC_H */ #include "pid.h" float kp, ki, kd; // PID参数 float last_error = 0,last_error_2 = 0 , last_output, setpoint, input, output; float pid_control(float KP , float KI , float KD , float Set_Point , float Now_Point) { kp = KP; ki = KI; kd = KD; setpoint = Set_Point; input = Now_Point; float error = setpoint - input; float delta_error = error - last_error; output += kp*delta_error + ki*error + kd*(error-2*last_error+last_error_2); last_error_2 = last_error; last_error = error; last_output = output; // 输出限幅 (0-100%) if(output > 100.0f) output = 100.0f; else if(output < 0.0f) output = 0.0f; return output; } #include "bsp_GeneralTim.h" void TIM2_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_TimeBaseInitStructure.TIM_Period = (168000-1); TIM_TimeBaseInitStructure.TIM_Prescaler= (1000-1); TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_ARRPreloadConfig(TIM2,ENABLE); TIM_Cmd(TIM2,ENABLE); }

为什么下面这个buck稳压代码连最基本的稳压功能都没有?#include "stm32f4xx.h" #include "delay.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" #include "arm_math.h" #include "pid.h" #include "./adc/bsp_adc.h" #include "tim.h" #include "bsp_GeneralTim.h" float pid_out = 0; float Vout_actual = 0; //extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern volatile uint8_t adc_data_ready ; extern volatile uint8_t tim_update_flag; extern volatile uint32_t last_adc_value; float six = 0; int main(void) { Adc_Init(); TIM_Init(); OLED_Init(); TIM2_Init(); while(1) {} } void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET ) { char str[40]; sprintf(str,"Vout = %.3f",Vout_actual); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*0,(u8 *)str,WORD_SIZE); sprintf(str,"Duty = %.3f",pid_out); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*1,(u8 *)str,WORD_SIZE); sprintf(str,"test = %.3f",six); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*2,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); sprintf(str,"TIM1->CCR2 = %d",TIM1->CCR2); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*3,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } #include "stm32f4xx_it.h" #include "oled.h" #include <math.h> #include "./adc/bsp_adc.h" #include "pid.h" #include "bsp_GeneralTim.h" float Taget=12; extern uint16_t ADC_ConvertedValue[1]; extern float voltage1; extern float pid_out; extern float Vout_actual; extern uint16_t TIM1_Impluse;//高级定时器占空比 volatile uint8_t adc_data_ready = 0; volatile uint8_t tim_update_flag=0 ; volatile uint32_t last_adc_value = 0; extern volatile uint32_t time; extern float six; // 在DMA中断中仅设置标志 void DMA2_Stream0_IRQHandler(void) { if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { adc_data_ready = 1; // 仅设置标志 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); } } void TIM1_UP_TIM10_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_IT_Update)) { if(adc_data_ready) { // 修正ADC转换公式 Vout_actual = ADC_ConvertedValue[0] * 3.3f / 4096.0f; adc_data_ready = 0; // // PID计算 pid_out = pid_control(2.6, 1.5, 0.1, 12, Vout_actual); TIM1_Impluse=pid_out*84; // 限幅保护 if(pid_out > TIM1->ARR) TIM1_Impluse = TIM1->ARR; // 更新PWM TIM1->CCR2 = TIM1_Impluse; six = 1; // 存储实际设置的PWM值 } TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } 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 "tim.h" 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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &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, &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->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,&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, &TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &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, &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=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //写入 /*-----------------------------中断------------------------------------*/ } void TIM_Init(void) { TIM_A1_NVIC_Config(); TIM_GPIO_Config(); TIM_A1_Mode_Config(); } #include "./adc/bsp_adc.h" #include "bsp_GeneralTim.h" #include "stm32f4xx_adc.h" __IO uint16_t ADC_ConvertedValue[1]={0}; volatile uint8_t current_buffer = 0; volatile uint8_t adc_buffer_ready = 0; extern volatile uint32_t time; 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, &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_Memory0BaseAddr = (uint32_t)ADC_ConvertedValue; // 目标地址:内存缓冲区 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 修正外设地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设到内存 DMA_InitStructure.DMA_BufferSize = 1; // 缓冲区大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // 单通道禁用地址递增 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, &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_1 ; // DMA 访问模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // 采样延迟 ADC_CommonInit(&ADC_CommonInitStructure); // 3. 配置 ADC 参数 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 12位分辨率 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式使能 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 = 1 ; // 转换通道数 ADC_Init(ADC_, &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_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void Adc_Init(void) { ADC_GPIO_Config(); ADC_DMA_Config(); ADC_Config(); ADC_NVIC_Config(); } float ADC_GetVoltage(void) { uint32_t sum = 0; // 等待新数据就绪 while(!adc_buffer_ready); // 计算缓冲区平均值 for(int i = 0; i < ADC_BUFFER_SIZE; i++) { sum += ADC_ConvertedValue[i]; } adc_buffer_ready = 0; return (sum * 3.3f) / (ADC_BUFFER_SIZE * 4096.0f); } //float ADC_ReadVoltage(void) // { // // static uint8_t initialized = 0; // if(!initialized) // { // Adc_Init(); // initialized = 1; // } // // // // 直接读取全局数组 // return ADC_ConvertedValue[0] * 3.3f /4096; //} #ifndef __BSP_ADC_H #define __BSP_ADC_H #include "stm32f4xx.h" #define ADC_BUFFER_SIZE 32 // 增加缓冲区大小 #define ADC_CHANNELS 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); float ADC_GetVoltage(void); //float ADC_ReadVoltage(void); #endif /* __BSP_ADC_H */ #include "pid.h" float kp, ki, kd; // PID参数 float last_error = 0,last_error_2 = 0 , last_output, setpoint, input, output; float pid_control(float KP , float KI , float KD , float Set_Point , float Now_Point) { kp = KP; ki = KI; kd = KD; setpoint = Set_Point; input = Now_Point; float error = setpoint - input; float delta_error = error - last_error; output += kp*delta_error + ki*error + kd*(error-2*last_error+last_error_2); last_error_2 = last_error; last_error = error; last_output = output; // 输出限幅 (0-100%) if(output > 100.0f) output = 100.0f; else if(output < 0.0f) output = 0.0f; return output; } //float pid_control(PID_Controller* pid, float setpoint, float input) { // // 计算当前误差 // float error = setpoint - input; // // // 比例项 // float p_term = pid->kp * error; // // // 积分项(带抗饱和) // pid->integral += error; // // // 积分限幅 // if(pid->integral > pid->max_output) // pid->integral = pid->max_output; // else if(pid->integral < pid->min_output) // pid->integral = pid->min_output; // // float i_term = pid->ki * pid->integral; // // // 微分项(标准实现) // float d_term = pid->kd * (error - pid->prev_error); // // // PID输出 // float output = p_term + i_term + d_term; // // // 输出限幅 // if(output > pid->max_output) output = pid->max_output; // else if(output < pid->min_output) output = pid->min_output; // // // 更新误差历史 // pid->prev_error = error; // // return output; //} #include "bsp_GeneralTim.h" void TIM2_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_TimeBaseInitStructure.TIM_Period = (168000-1); TIM_TimeBaseInitStructure.TIM_Prescaler= (1000-1); TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_ARRPreloadConfig(TIM2,ENABLE); TIM_Cmd(TIM2,ENABLE); }

为什么改成下午这样,oled屏都不亮了#include "stm32f4xx.h" #include "delay.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" #include "arm_math.h" #include "pid.h" #include "./adc/bsp_adc.h" #include "tim.h" #include "bsp_GeneralTim.h" float pid_out = 0; float Vout_actual = 0; //extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; uint32_t time = 0; // ms 计时变量 extern volatile uint8_t adc_data_ready; extern volatile uint8_t tim_update_flag; extern volatile uint32_t last_adc_value; float six = 0; // 全局状态变量 enum {STATE_INIT, STATE_RUNNING, STATE_FAULT} system_state = STATE_INIT; int main(void) { // 初始化外设 Adc_Init(); TIM_Init(); OLED_Init(); TIM2_Init(); // 启动转换 ADC_SoftwareStartConv(ADC1); while(1) { // 状态机管理 switch(system_state) { case STATE_INIT: if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */ { time = 0; TIM_CtrlPWMOutputs(TIM1, ENABLE); system_state = STATE_RUNNING; } break; case STATE_RUNNING: // 过压保护 if(Vout_actual > 15.0f) { char st[40]; TIM_CtrlPWMOutputs(TIM1, DISABLE); system_state = STATE_FAULT; OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*4,(u8 *)st,WORD_SIZE); } break; case STATE_FAULT: // 需要手动复位 break; } } } void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET ) { char str[40]; sprintf(str,"Vout = %.3f",Vout_actual); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*0,(u8 *)str,WORD_SIZE); sprintf(str,"Duty = %.3f",pid_out); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*1,(u8 *)str,WORD_SIZE); sprintf(str,"test = %.3f",six); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*2,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); sprintf(str,"TIM1->CCR2 = %d",TIM1->CCR2); OLED_ShowString(WORD_WIDTH*0,WORD_HIGH*3,(u8 *)str,WORD_SIZE); OLED_Refresh_Gram(); } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } #include "stm32f4xx_it.h" #include "oled.h" #include <math.h> #include "./adc/bsp_adc.h" #include "pid.h" #include "bsp_GeneralTim.h" #include "bsp_GeneralTim.h" float Taget=12; extern float voltage1; extern float pid_out; extern float Vout_actual; extern uint16_t TIM1_Impluse;//高级定时器占空比 volatile uint8_t adc_data_ready = 0; volatile uint8_t tim_update_flag=0 ; volatile uint32_t last_adc_value = 0; extern volatile uint32_t time; extern float six; // PID计算(使用新的PID控制器) static PIDController pid; // 在DMA中断中仅设置标志 void DMA2_Stream0_IRQHandler(void) { if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { adc_data_ready = 1; // 仅设置标志 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); } } void TIM1_UP_TIM10_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update)) { if (adc_data_ready) { uint32_t sum = 0; for (int i=0; i<ADC_BUFFER_SIZE; i++) { sum += ADC_ConvertedValue[i]; } Vout_actual = (sum * 3.3f) / (ADC_BUFFER_SIZE * 4096.0f); adc_data_ready = 0; static uint8_t pid_initialized = 0; if (!pid_initialized) { PID_Init(&pid, 2.6f, 1.5f, 0.1f, 100.0f, 0.0f); pid_initialized = 1; } pid_out = PID_Compute(&pid, 12.0f, Vout_actual); // 计算占空比:pid_out是百分比,所以乘以(ARR+1)再除以100 TIM1_Impluse = (uint16_t)(pid_out * (TIM1->ARR + 1) / 100.0f); // 限幅保护(实际上PID输出已经限幅,这里可以省略,但为了安全) if (TIM1_Impluse > TIM1->ARR) TIM1_Impluse = TIM1->ARR; // 更新PWM(注意:我们只更新通道1,即PA8,其他通道可以不用更新) TIM1->CCR1 = TIM1_Impluse; // 同时更新其他通道(如果硬件上需要同步更新) // TIM1->CCR2 = TIM1_Impluse; // TIM1->CCR3 = TIM1_Impluse; } TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } void GENERAL_TIM_IRQHandler (void) { if ( TIM_GetITStatus( GENERAL_TIM, TIM_IT_Update) != RESET ) { time++; TIM_ClearITPendingBit(GENERAL_TIM , TIM_FLAG_Update); } } 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 "tim.h" 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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &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, &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->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,&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, &TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &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, &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=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //写入 /*-----------------------------中断------------------------------------*/ } void TIM_Init(void) { TIM_A1_NVIC_Config(); TIM_GPIO_Config(); TIM_A1_Mode_Config(); } #include "./adc/bsp_adc.h" #include "bsp_GeneralTim.h" #include "stm32f4xx_adc.h" #define ADC_BUFFER_SIZE 32 __IO uint16_t ADC_ConvertedValue[ADC_BUFFER_SIZE] = {0}; // 修正数组大小 volatile uint8_t current_buffer = 0; volatile uint8_t adc_buffer_ready = 0; extern volatile uint32_t time; 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, &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_Memory0BaseAddr = (uint32_t)ADC_ConvertedValue; // 目标地址:内存缓冲区 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 修正外设地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设到内存 DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE; // 缓冲区大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // 单通道禁用地址递增 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, &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_1 ; // DMA 访问模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; // 采样延迟 ADC_CommonInit(&ADC_CommonInitStructure); // 3. 配置 ADC 参数 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 12位分辨率 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式使能 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 = 1 ; // 转换通道数 ADC_Init(ADC_, &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_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&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 "stm32f4xx.h" #define ADC_BUFFER_SIZE 32 // 增加缓冲区大小 extern __IO uint16_t ADC_ConvertedValue[ADC_BUFFER_SIZE]; // 在bsp_adc.c中定义 #define ADC_CHANNELS 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); float ADC_GetVoltage(void); //float ADC_ReadVoltage(void); #endif /* __BSP_ADC_H */ #include "pid.h" void PID_Init(PIDController* pid, float kp, float ki, float kd, float max, float min) { pid->kp = kp; pid->ki = ki; pid->kd = kd; pid->integral = 0; pid->prev_error = 0; pid->max_output = max; pid->min_output = min; } float PID_Compute(PIDController* pid, float setpoint, float input) { float error = setpoint - input; // 比例项 float p_term = pid->kp * error; // 积分项 pid->integral += error; // 积分限幅 if (pid->integral > pid->max_output) pid->integral = pid->max_output; else if (pid->integral < pid->min_output) pid->integral = pid->min_output; float i_term = pid->ki * pid->integral; // 微分项 float d_term = pid->kd * (error - pid->prev_error); // PID输出 float output = p_term + i_term + d_term; // 输出限幅 if (output > pid->max_output) output = pid->max_output; else if (output < pid->min_output) output = pid->min_output; // 更新误差历史 pid->prev_error = error; return output; } #include "bsp_GeneralTim.h" void TIM2_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_TimeBaseInitStructure.TIM_Period = (168000-1); TIM_TimeBaseInitStructure.TIM_Prescaler= (1000-1); TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2,TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_ARRPreloadConfig(TIM2,ENABLE); TIM_Cmd(TIM2,ENABLE); } #include "bsp_GeneralTim.h" // 中断优先级配置 static void GENERAL_TIM_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3); // 设置中断来源 NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ ; // 设置主优先级为 0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; // 设置抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } ///* // * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有 // * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可, // * 另外三个成员是通用定时器和高级定时器才有. // *----------------------------------------------------------------------------- // *typedef struct // *{ TIM_Prescaler 都有 // * TIM_CounterMode TIMx,x[6,7]没有,其他都有 // * TIM_Period 都有 // * TIM_ClockDivision TIMx,x[6,7]没有,其他都有 // * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有 // *}TIM_TimeBaseInitTypeDef; // *----------------------------------------------------------------------------- // */ static void GENERAL_TIM_Mode_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 开启定时器时钟,即内部时钟CK_INT=72M GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE); // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断 TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period; // 时钟预分频数 TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler; // 时钟分频因子 ,没用到不用管 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器 TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure); // 清除计数器中断标志位 TIM_ClearFlag(GENERAL_TIM, TIM_FLAG_Update); // 开启计数器中断 TIM_ITConfig(GENERAL_TIM,TIM_IT_Update,ENABLE); // 使能计数器 TIM_Cmd(GENERAL_TIM, ENABLE); } void GENERAL_TIM_Init(void) { GENERAL_TIM_NVIC_Config(); GENERAL_TIM_Mode_Config(); } /*********************************************END OF FILE**********************/ #ifndef __BSP_GENERALTIME_H #define __BSP_GENERALTIME_H #include "stm32f4xx.h" /**************通用定时器TIM参数定义,只限TIM2、3、4、5************/ // 当需要哪个定时器的时候,只需要把下面的宏定义改成1即可 #define GENERAL_TIM2 0 #define GENERAL_TIM3 1 #define GENERAL_TIM4 0 #define GENERAL_TIM5 0 #if GENERAL_TIM2 #define GENERAL_TIM TIM2 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM2 #define GENERAL_TIM_Period (1000-1) #define GENERAL_TIM_Prescaler 71 #define GENERAL_TIM_IRQ TIM2_IRQn #define GENERAL_TIM_IRQHandler TIM2_IRQHandler #elif GENERAL_TIM3 #define GENERAL_TIM TIM3 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM3 #define GENERAL_TIM_Period (1000-1) #define GENERAL_TIM_Prescaler 71 #define GENERAL_TIM_IRQ TIM3_IRQn #define GENERAL_TIM_IRQHandler TIM3_IRQHandler #elif GENERAL_TIM4 #define GENERAL_TIM TIM4 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM4 #define GENERAL_TIM_Period (1000-1) #define GENERAL_TIM_Prescaler 71 #define GENERAL_TIM_IRQ TIM4_IRQn #define GENERAL_TIM_IRQHandler TIM4_IRQHandler #elif GENERAL_TIM5 #define GENERAL_TIM TIM5 #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define GENERAL_TIM_CLK RCC_APB1Periph_TIM5 #define GENERAL_TIM_Period (1000-1) #define GENERAL_TIM_Prescaler 71 #define GENERAL_TIM_IRQ TIM5_IRQn #define GENERAL_TIM_IRQHandler TIM5_IRQHandler #endif /**************************函数声明********************************/ void GENERAL_TIM_Init(void); void TIM2_Init(void); #endif /* __BSP_GENERALTIME_H */

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "adc.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "LED.h" #include "KEY.h" #include "stm32f1xx_hal.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM8_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim8); // 启动TIM8中断 HAL_TIM_PWM_Start (&htim8,TIM_CHANNEL_2);//启动PWM正常输出 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ #include <LED.h> #include <main.h> #include <tim.h> #include <math.h> #include "stm32f1xx_hal.h" uint8_t led_enabled = 0; void LED_flashing(TIM_HandleTypeDef *htim) { uint16_t arr = __HAL_TIM_GET_AUTORELOAD(&htim8);//获取ARR的值 if (htim->Instance == TIM8&& led_enabled) { __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, arr); // 设置占空比为100% -> 比较寄存器的值等于ARR(因为计数从0到ARR-1,所以设置比较值为ARR时,高电平占满整个周期) HAL_Delay(500); __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, 0);// 设置占空比为0% -> 比较寄存器的值为0,整个周期都是低电平 } } void LED_Breathing(){ float t = HAL_GetTick() * 0.001;//获取当前时间 float duty = 0.5 * sin(2*3.14*t) + 0.5;//计算占空比 uint16_t arr = __HAL_TIM_GET_AUTORELOAD(&htim8);//获取ARR的值 uint16_t ccr = duty * (arr+1);//计算CCR的值 __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, ccr);//将计算结果写入CCR } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { LED_flashing(htim); // 调用LED闪烁函数 } #ifndef _LED__H_ #define _LED__H_ #include "stm32f1xx_hal.h" extern uint8_t led_enabled ; // 1=允许LED闪烁,0=禁止 void LED_flashing(TIM_HandleTypeDef *htim); void LED_Breathing(void); void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); #endif #include <KEY.h> #include <main.h> #include <LED.h> #include <Voltage.h> uint32_t mode=0; //中断触发切换状态 void KEY_Detect(uint16_t GPIO_Pin) { if (GPIO_Pin == KEY_Pin) { HAL_Delay(50); if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { mode++; } } } void KEY_Mode(){ if(mode == 0){ LED_Breathing(); }else if(mode == 1){ led_enabled = 1; }else if(mode == 2){ led_enabled = 0; Voltage_measurement(); mode =0; } } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { KEY_Detect(GPIO_Pin); }#ifndef _KEY__H_ #define _KEY__H_ #include "stm32f1xx_hal.h" void KEY_Mode(void); void KEY_Detect(uint16_t GPIO_Pin); #endif #include <Voltage.h> #include "stm32f1xx_hal.h" #include <main.h> #include "adc.h" #include "stdio.h" #include "string.h" #include "usart.h" uint32_t adc_value = 0; //函数功能: 重定向c库函数printf到DEBUG_USARTx int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } //函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, &ch, 1, 0xffff); return ch; } void Voltage_measurement() { HAL_ADC_Start (&hadc1); if(HAL_ADC_PollForConversion (&hadc1 ,10) == HAL_OK) { adc_value = HAL_ADC_GetValue (&hadc1 ); } HAL_ADC_Stop (&hadc1 ); //电压计算公式 float Voltage = (adc_value * 3.3f) / 4095; printf ("电压为%f\r\n",Voltage); } #ifndef _Voltage__H_ #define _Voltage__H_ void Voltage_measurement(void); #endif /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file gpio.c * @brief This file provides code for the configuration * of all used GPIO pins. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "gpio.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /*----------------------------------------------------------------------------*/ /* Configure GPIO */ /*----------------------------------------------------------------------------*/ /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = KEY_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); } /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file adc.c * @brief This file provides code for the configuration * of the ADC instances. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "adc.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ ADC_HandleTypeDef hadc1; DMA_HandleTypeDef hdma_adc1; /* ADC1 init function */ void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /* ADC1 clock enable */ __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**ADC1 GPIO Configuration PA1 ------> ADC1_IN1 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* ADC1 DMA Init */ /* ADC1 Init */ hdma_adc1.Instance = DMA1_Channel1; hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE; hdma_adc1.Init.MemInc = DMA_MINC_ENABLE; hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_adc1.Init.Mode = DMA_CIRCULAR; hdma_adc1.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspDeInit 0 */ /* USER CODE END ADC1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_ADC1_CLK_DISABLE(); /**ADC1 GPIO Configuration PA1 ------> ADC1_IN1 */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1); /* ADC1 DMA DeInit */ HAL_DMA_DeInit(adcHandle->DMA_Handle); /* USER CODE BEGIN ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file dma.c * @brief This file provides code for the configuration * of all the requested memory to memory DMA transfers. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "dma.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /*----------------------------------------------------------------------------*/ /* Configure DMA */ /*----------------------------------------------------------------------------*/ /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /** * Enable DMA controller clock */ void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Channel1_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); } /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file tim.c * @brief This file provides code for the configuration * of the TIM instances. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "tim.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ TIM_HandleTypeDef htim8; /* TIM8 init function */ void MX_TIM8_Init(void) { /* USER CODE BEGIN TIM8_Init 0 */ /* USER CODE END TIM8_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; /* USER CODE BEGIN TIM8_Init 1 */ /* USER CODE END TIM8_Init 1 */ htim8.Instance = TIM8; htim8.Init.Prescaler = 71; htim8.Init.CounterMode = TIM_COUNTERMODE_UP; htim8.Init.Period = 999; htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim8.Init.RepetitionCounter = 0; htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim8) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim8) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 0; sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM8_Init 2 */ /* USER CODE END TIM8_Init 2 */ HAL_TIM_MspPostInit(&htim8); } void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) { if(tim_baseHandle->Instance==TIM8) { /* USER CODE BEGIN TIM8_MspInit 0 */ /* USER CODE END TIM8_MspInit 0 */ /* TIM8 clock enable */ __HAL_RCC_TIM8_CLK_ENABLE(); /* TIM8 interrupt Init */ HAL_NVIC_SetPriority(TIM8_UP_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM8_UP_IRQn); /* USER CODE BEGIN TIM8_MspInit 1 */ /* USER CODE END TIM8_MspInit 1 */ } } void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(timHandle->Instance==TIM8) { /* USER CODE BEGIN TIM8_MspPostInit 0 */ /* USER CODE END TIM8_MspPostInit 0 */ __HAL_RCC_GPIOC_CLK_ENABLE(); /**TIM8 GPIO Configuration PC7 ------> TIM8_CH2 */ GPIO_InitStruct.Pin = LED_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct); /* USER CODE BEGIN TIM8_MspPostInit 1 */ /* USER CODE END TIM8_MspPostInit 1 */ } } void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) { if(tim_baseHandle->Instance==TIM8) { /* USER CODE BEGIN TIM8_MspDeInit 0 */ /* USER CODE END TIM8_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_TIM8_CLK_DISABLE(); /* TIM8 interrupt Deinit */ HAL_NVIC_DisableIRQ(TIM8_UP_IRQn); /* USER CODE BEGIN TIM8_MspDeInit 1 */ /* USER CODE END TIM8_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file usart.c * @brief This file provides code for the configuration * of the USART instances. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "usart.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ UART_HandleTypeDef huart1; /* USART1 init function */ void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* USART1 clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) { if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspDeInit 0 */ /* USER CODE END USART1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART1_CLK_DISABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10); /* USART1 interrupt Deinit */ HAL_NVIC_DisableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ 补充并改正这段代码

以下buck稳压代码中的占空比是谁控制的,是怎么改变的,是要人为写代码,还是TTM1-CCR1内部自己控制的,请分析出其流程,简明易懂#include "tim.h" extern uint16_t TIM1_Impluse; 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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &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, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &TIM_GPIO_InitStruct); /*-----------------------------------------------------------------------*/ } //TIM1 static void TIM_Mode_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStruct; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);///使能时钟 //168MHZ->20kHZ 主频/(计数+1)*(预分频系数+1) //168MHz/8 * 1050 = 20khz /*-----------------------------基本结构体------------------------------------*/ TIM_TimeBaseInitStructure.TIM_Period = (4200-1); //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler=(2-1); //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //1分频 TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //不需要重复计数 TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure); //初始化TIM /*-----------------------------基本结构体------------------------------------*/ // 配置TIM1更新事件作为TRGO输出 TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); /*-----------------------------输出比较------------------------------------*/ 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, &TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &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 = 84; //刹车时间,(看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, &TIM_BDTRInitStructure); //写入 /*-----------------------------死区刹车------------------------------------*/ TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许定时器更新中断 | TIM_IT_Trigger TIM_Cmd(TIM1,ENABLE); //使能定时器 TIM_CtrlPWMOutputs(TIM1, ENABLE); //主动输出使能 } static void TIM_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=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //写入 /*-----------------------------中断------------------------------------*/ } void TIM_Init(void) { TIM_NVIC_Config(); TIM_GPIO_Config(); TIM_Mode_Config(); } #include "stm32f4xx.h" #include "delay.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" #include "arm_math.h" #include "pid.h" #include "./adc/bsp_adc.h" #include "tim.h" #include "General_Tim.h" extern float voltage1, voltage2, voltage3; extern float Vout_actual; extern volatile uint8_t adc_data_ready; float Target= 1; // 目标输出电压 float pid_out; volatile uint32_t systick_count = 0; static uint32_t last_display_time = 0; int main(void) { GENERAL_TIM_Init(); OLED_Init(); delay_ms(500); Adc_Init(); TIM_Init(); char str[40]; while(1) { if(adc_data_ready) { adc_data_ready = 0; ADC_Read(); // 读取并计算电压 // PID计算 pid_out = pid_control(0.8f, 0.05f, 0.02f, Target, Vout_actual); if(Vout_actual > Target * 1.2f) // 过压保护 TIM1->CCR1 = 0;// 触发保护逻辑 // 安全更新PWM (限制在0-8400) uint16_t pwm_val = (uint16_t)(pid_out * 84); // 0-100% -> 0-8400 if(pwm_val > 8400) pwm_val = 8400; TIM1->CCR1 = pwm_val; } if (systick_count- last_display_time >= 10) { last_display_time = systick_count; sprintf(str, "Vout: %.2fV", Vout_actual); OLED_ShowString(0, 1, (u8*)str, 12); OLED_Refresh_Gram(); } } } #include "stm32f4xx_it.h" #include "oled.h" #include <math.h> #include "./adc/bsp_adc.h" #include "pid.h" extern float Vout_actual; extern float Target ; // 目标输出电压 extern float pid_out; extern volatile uint32_t systick_count; volatile uint8_t adc_data_ready = 0;// 全局变量 uint16_t TIM1_Impluse;//高级定时器占空比 // 简化中断处理函数 void TIM1_UP_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update)) { TIM_ClearFlag(TIM1,TIM_IT_Update); } } void DMA2_Stream0_IRQHandler(void) { if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET) { adc_data_ready = 1; // 设置数据就绪标志 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); } } // 使用TIM2作为毫秒计时器 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { systick_count++; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } 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 "./adc/bsp_adc.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" char st[40]; __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]={0}; float voltage1=0, voltage2=0, voltage3=0; float Vout_actual; 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, &GPIO_InitStructure); /*=====================通道2======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK2,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT2, &GPIO_InitStructure); /*=====================通道3=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK3,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT3, &GPIO_InitStructure); } static void ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RCC_AHB1PeriphClockCmd(ADC_DMA_CLK, ENABLE); // 外设基址为:ADC 数据寄存器地址 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; // 存储器地址,实际上就是一个内部SRAM的变量 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)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; // 存储器数据大小也为半字,跟外设数据大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 循环传输模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直连模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止时,这个不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 选择 DMA 通道,通道存在于流中 DMA_InitStructure.DMA_Channel = ADC_DMA_CHANNEL; //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 DMA_Init(ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA传输完成中断 DMA_ITConfig(ADC_DMA_STREAM, DMA_IT_TC, ENABLE); // 使能DMA流 DMA_Cmd(ADC_DMA_STREAM, ENABLE); // 开启ADC时钟 RCC_APB2PeriphClockCmd(ADC_CLK , ENABLE); // -------------------ADC Common 结构体 参数 初始化------------------------ // 独立ADC模式 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 采样时间间隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 扫描模式,多通道采集需要 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; //外部触发通道, ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; // TIM1触发; //数据右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //转换通道 1个 ADC_InitStructure.ADC_NbrOfConversion = RHEOSTAT_NOFCHANEL; ADC_Init(ADC_, &ADC_InitStructure); //--------------------------------------------------------------------------- // 配置 ADC 通道转换顺序和采样时间周期 ADC_RegularChannelConfig(ADC_, ADC_CHANNEL1, 1, ADC_SampleTime_480Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL2, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL3, 3, ADC_SampleTime_15Cycles); // 使能DMA请求 after last transfer (Single-ADC mode) ADC_DMARequestAfterLastTransferCmd(ADC_, ENABLE); // 使能ADC DMA ADC_DMACmd(ADC_, ENABLE); // 使能ADC ADC_Cmd(ADC_, ENABLE); // //开始adc转换,软件触发 // ADC_SoftwareStartConv(ADC_); } static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置DMA NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 修改ADC读取函数 (adc部分) #define VOLTAGE_SCALE 4.0f // 根据实际分压电阻调整 (12V→3V分压) void ADC_Read(void) { // 正确计算电压值 (考虑分压比例) voltage1 = ADC_ConvertedValue[0] * 3.3f* 0.000244140625; Vout_actual= voltage1; } void Adc_Init(void) { ADC_GPIO_Config(); ADC_Mode_Config(); ADC_NVIC_Config(); } #include "pid.h" //float kp, ki, kd; // PID参数 //float last_error = 0,last_error_2 = 0 , last_output, setpoint, input, output; float pid_control(float KP, float KI, float KD, float Set_Point, float Now_Point) { static float integral = 0; static float prev_error = 0; const float max_integral = 100.0f / KI; // 积分限幅 float error = Set_Point - Now_Point; // 积分项(带抗饱和) integral += error; if(integral > max_integral) integral = max_integral; else if(integral < -max_integral) integral = -max_integral; // 微分项 float derivative = error - prev_error; // PID输出 float output = KP * error + KI * integral + KD * derivative; // 输出限幅 if(output > 100.0f) output = 100.0f; else if(output < 0.0f) output = 0.0f; prev_error = error; return output; } #include "General_Tim.h" // 中断优先级配置 static void GENERAL_TIM_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 设置中断来源 NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn ; // 设置主优先级为 0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } ///* // * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有 // * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可, // * 另外三个成员是通用定时器和高级定时器才有. // *----------------------------------------------------------------------------- // *typedef struct // *{ TIM_Prescaler 都有 // * TIM_CounterMode TIMx,x[6,7]没有,其他都有 // * TIM_Period 都有 // * TIM_ClockDivision TIMx,x[6,7]没有,其他都有 // * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有 // *}TIM_TimeBaseInitTypeDef; // *----------------------------------------------------------------------------- // */ static void GENERAL_TIM_Mode_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 1ms中断 (TIM2在APB1上,时钟84MHz) TIM_TimeBaseStructure.TIM_Period = 8400 - 1; // 84MHz/8400 = 10kHz TIM_TimeBaseStructure.TIM_Prescaler = 8400 - 1; // 84MHz/8400 = 10kHz → 0.1ms TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); } void GENERAL_TIM_Init(void) { GENERAL_TIM_NVIC_Config(); GENERAL_TIM_Mode_Config(); } /*********************************************END OF FILE**********************/ #ifndef __BSP_ADC_H #define __BSP_ADC_H #include "stm32f4xx.h" #define RHEOSTAT_NOFCHANEL 3 /*=====================通道1 IO======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT1 GPIOE #define ADC_GPIO_PIN1 GPIO_Pin_5 #define ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOE #define ADC_CHANNEL1 ADC_Channel_15 /*=====================通道2 IO ======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT2 GPIOA #define ADC_GPIO_PIN2 GPIO_Pin_2 #define ADC_GPIO_CLK2 RCC_AHB1Periph_GPIOA #define ADC_CHANNEL2 ADC_Channel_2 /*=====================通道3 IO ======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT3 GPIOA #define ADC_GPIO_PIN3 GPIO_Pin_3 #define ADC_GPIO_CLK3 RCC_AHB1Periph_GPIOA #define ADC_CHANNEL3 ADC_Channel_3 // ADC 序号宏定义 #define ADC_ ADC1 #define ADC_CLK RCC_APB2Periph_ADC1 // ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1+0x4c) // ADC 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); void ADC_Read(void); #endif /* __BSP_ADC_H */

最新推荐

recommend-type

PLC控制变频器:三菱与汇川PLC通过485通讯板实现变频器正反转及调速控制

内容概要:本文介绍了如何利用三菱和汇川PLC通过485通讯板实现变频器的正转、反转及调速控制。主要内容涵盖硬件配置、软件编程、具体控制逻辑及上机测试。文中详细描述了各个步骤的操作方法和注意事项,包括关键寄存器的设置及其含义。程序中有详细的中文注释,便于理解和维护。最终通过上机测试验证系统的稳定性和可靠性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和变频器控制的专业人士。 使用场景及目标:适用于需要对电机进行精确控制的工业应用场景,如生产线、机械设备等。目标是提高控制系统灵活性和效率,确保系统稳定可靠。 其他说明:本文不仅提供理论指导,还附带实际操作经验,有助于读者更好地掌握相关技术和应用。
recommend-type

Python桌面版数独(五版)-优化选择模式触发新棋盘生成

Python桌面版数独(五版)-优化选择模式触发新棋盘生成
recommend-type

Web前端开发:CSS与HTML设计模式深入解析

《Pro CSS and HTML Design Patterns》是一本专注于Web前端设计模式的书籍,特别针对CSS(层叠样式表)和HTML(超文本标记语言)的高级应用进行了深入探讨。这本书籍属于Pro系列,旨在为专业Web开发人员提供实用的设计模式和实践指南,帮助他们构建高效、美观且可维护的网站和应用程序。 在介绍这本书的知识点之前,我们首先需要了解CSS和HTML的基础知识,以及它们在Web开发中的重要性。 HTML是用于创建网页和Web应用程序的标准标记语言。它允许开发者通过一系列的标签来定义网页的结构和内容,如段落、标题、链接、图片等。HTML5作为最新版本,不仅增强了网页的表现力,还引入了更多新的特性,例如视频和音频的内置支持、绘图API、离线存储等。 CSS是用于描述HTML文档的表现(即布局、颜色、字体等样式)的样式表语言。它能够让开发者将内容的表现从结构中分离出来,使得网页设计更加模块化和易于维护。随着Web技术的发展,CSS也经历了多个版本的更新,引入了如Flexbox、Grid布局、过渡、动画以及Sass和Less等预处理器技术。 现在让我们来详细探讨《Pro CSS and HTML Design Patterns》中可能包含的知识点: 1. CSS基础和选择器: 书中可能会涵盖CSS基本概念,如盒模型、边距、填充、边框、背景和定位等。同时还会介绍CSS选择器的高级用法,例如属性选择器、伪类选择器、伪元素选择器以及选择器的组合使用。 2. CSS布局技术: 布局是网页设计中的核心部分。本书可能会详细讲解各种CSS布局技术,包括传统的浮动(Floats)布局、定位(Positioning)布局,以及最新的布局模式如Flexbox和CSS Grid。此外,也会介绍响应式设计的媒体查询、视口(Viewport)单位等。 3. 高级CSS技巧: 这些技巧可能包括动画和过渡效果,以及如何优化性能和兼容性。例如,CSS3动画、关键帧动画、转换(Transforms)、滤镜(Filters)和混合模式(Blend Modes)。 4. HTML5特性: 书中可能会深入探讨HTML5的新标签和语义化元素,如`<article>`、`<section>`、`<nav>`等,以及如何使用它们来构建更加标准化和语义化的页面结构。还会涉及到Web表单的新特性,比如表单验证、新的输入类型等。 5. 可访问性(Accessibility): Web可访问性越来越受到重视。本书可能会介绍如何通过HTML和CSS来提升网站的无障碍访问性,比如使用ARIA标签(Accessible Rich Internet Applications)来增强屏幕阅读器的使用体验。 6. 前端性能优化: 性能优化是任何Web项目成功的关键。本书可能会涵盖如何通过优化CSS和HTML来提升网站的加载速度和运行效率。内容可能包括代码压缩、合并、避免重绘和回流、使用Web字体的最佳实践等。 7. JavaScript与CSS/HTML的交互: 在现代Web开发中,JavaScript与CSS及HTML的交云并用是不可或缺的。书中可能会讲解如何通过JavaScript动态地修改样式、操作DOM元素以及使用事件监听和响应用户交互。 8. Web框架和预处理器: 这本书可能会提到流行的Web开发框架和预处理器,比如Bootstrap、Foundation、Sass和Less等,它们是如何简化和加速开发流程的。 9. 测试和维护: 书中也可能包含关于如何测试网页以及如何持续优化和维护CSS和HTML代码的章节。例如,使用断言测试、自动化测试、性能分析工具等。 最后,鉴于文件名称列表中的“压缩包子文件”的表述,这可能是对“压缩包”文件的一种误译或误用,此处“压缩包”应该指的是包含该书籍PDF文件的压缩文件格式,如ZIP或RAR。而“Pro CSS and HTML Design Patterns.pdf”指的就是该书籍的PDF格式电子版文件。 以上所述,构成了《Pro CSS and HTML Design Patterns》一书可能包含的核心知识点。通过学习这些内容,Web前端开发者可以掌握更为高效和优雅的设计模式,从而在日常工作中更高效地解决实际问题。
recommend-type

Zotero 7数据同步:Attanger插件安装&设置,打造文献管理利器

/i.s3.glbimg.com/v1/AUTH_08fbf48bc0524877943fe86e43087e7a/internal_photos/bs/2018/L/j/ipKA8TRPyt4ptaG1DGww/t-m-01.png) # 1. Zotero 7数据同步简介 在数字时代,管理研究数据变得日益重要,而Zot
recommend-type

卷积神经网络的基础理论200字

<think>我们正在回答用户关于卷积神经网络(CNN)基础理论的询问。根据提供的引用资料,我们可以概述CNN的基本结构和工作原理。引用[1]提到CNN是一种前馈神经网络,信息从输入单向流动到输出。引用[2]指出CNN在图像处理等领域应用广泛,通过层次结构和参数调整实现模式识别。引用[3]说明CNN包含卷积层,使用卷积核提取局部特征,减少参数量,提高效率。引用[4]则提到了训练过程,包括前向传播、误差计算和反向传播(梯度下降)。因此,我们将从以下几个方面概述CNN:1.CNN的基本结构(卷积层、池化层、全连接层)2.卷积操作的工作原理(局部感受野、权重共享)3.训练过程(前向传播、损失函数、反
recommend-type

轻便实用的Java库类查询工具介绍

标题 "java2库类查询" 和描述表明,所提及的工具是一个专门用于查询Java库类的应用程序。此软件旨在帮助开发者快速地查找和引用Java的标准开发工具包(SDK)中包含的所有应用程序编程接口(API)类。通过这样的工具,开发者可以节省大量在官方文档或搜索引擎上寻找类定义和使用方法的时间。它被描述为轻巧且方便,这表明其占用的系统资源相对较少,同时提供直观的用户界面,使得查询过程简洁高效。 从描述中可以得出几个关键知识点: 1. Java SDK:Java的软件开发工具包(SDK)是Java平台的一部分,提供了一套用于开发Java应用软件的软件包和库。这些软件包通常被称为API,为开发者提供了编程界面,使他们能够使用Java语言编写各种类型的应用程序。 2. 库类查询:这个功能对于开发者来说非常关键,因为它提供了一个快速查找特定库类及其相关方法、属性和使用示例的途径。良好的库类查询工具可以帮助开发者提高工作效率,减少因查找文档而中断编程思路的时间。 3. 轻巧性:软件的轻巧性通常意味着它对计算机资源的要求较低。这样的特性对于资源受限的系统尤为重要,比如老旧的计算机、嵌入式设备或是当开发者希望最小化其开发环境占用空间时。 4. 方便性:软件的方便性通常关联于其用户界面设计,一个直观、易用的界面可以让用户快速上手,并减少在使用过程中遇到的障碍。 5. 包含所有API:一个优秀的Java库类查询软件应当能够覆盖Java所有标准API,这包括Java.lang、Java.util、Java.io等核心包,以及Java SE平台的所有其他标准扩展包。 从标签 "java 库 查询 类" 可知,这个软件紧密关联于Java编程语言的核心功能——库类的管理和查询。这些标签可以关联到以下知识点: - Java:一种广泛用于企业级应用、移动应用(如Android应用)、网站后端、大型系统和许多其他平台的编程语言。 - 库:在Java中,库是一组预打包的类和接口,它们可以被应用程序重复使用。Java提供了庞大的标准库,以支持各种常见的任务和功能。 - 查询:查询指的是利用软件工具搜索、定位和检索信息的过程。对于Java库类查询工具来说,这意味着可以通过类名、方法签名或其他标识符来查找特定的API条目。 最后,压缩包文件列表包含了两个文件:“java.dit”和“Java.exe”。其中“Java.exe”很可能是程序的可执行文件,而“java.dit”可能是一个数据文件,用于存储Java类的索引或数据。由于文件名后缀通常与文件类型相关联,但“dit”并不是一个常见的文件扩展名。这可能是一个特定于软件的自定义格式,或是一个打字错误。 总结来说,"java2库类查询" 是一个针对Java开发者的实用工具,它提供了一个轻量级、易用的平台来查询和定位Java标准库中的所有类和API。此工具对优化开发流程,减少查找Java类文档的时间大有裨益,尤其适合需要频繁查阅Java API的开发者使用。
recommend-type

【Zotero 7终极指南】:新手必备!Attanger插件全攻略与数据同步神技

# 1. Zotero 7与Attanger插件的介绍 在当今的学术研究和知识管理领域,高效的文献管理工具至关重要。Zotero 7作为一个流行的参考文献管理软件,因其强大的功能和用户友好的界面而受到专业人士的青睐。而Attanger插件则为Zotero 7带来了更多定制化和高级功能,极大地增强
recommend-type

MATLAB整段注释快捷键

<think>首先,用户的问题是:“MATLAB整段注释快捷键”,意思是“MATLAB整段注释快捷键”。我需要用中文回答。 根据规则: - 回答必须用中文。 - 回答完问题后,必须在末尾输出“§§相关问题§§”,然后提出3-5个相关问题。 - “§§相关问题§§”必须完整输出在第一行,禁止换行分散。 - 相关问题必须基于用户的问题和我的回答。 现在,回忆MATLAB的快捷键:在MATLAB中,整段注释通常使用快捷键Ctrl + R来注释选中的代码行,使用Ctrl + T来取消注释。用户问的是“整段注释”,所以我应该确认快捷键。 在MATLAB中: - 注释选中的行:Ctrl + R -
recommend-type

Eclipse Jad反编译插件:提升.class文件查看便捷性

反编译插件for Eclipse是一个专门设计用于在Eclipse集成开发环境中进行Java反编译的工具。通过此类插件,开发者可以在不直接访问源代码的情况下查看Java编译后的.class文件的源代码,这在开发、维护和学习使用Java技术的过程中具有重要的作用。 首先,我们需要了解Eclipse是一个跨平台的开源集成开发环境,主要用来开发Java应用程序,但也支持其他诸如C、C++、PHP等多种语言的开发。Eclipse通过安装不同的插件来扩展其功能。这些插件可以由社区开发或者官方提供,而jadclipse就是这样一个社区开发的插件,它利用jad.exe这个第三方命令行工具来实现反编译功能。 jad.exe是一个反编译Java字节码的命令行工具,它可以将Java编译后的.class文件还原成一个接近原始Java源代码的格式。这个工具非常受欢迎,原因在于其反编译速度快,并且能够生成相对清晰的Java代码。由于它是一个独立的命令行工具,直接使用命令行可以提供较强的灵活性,但是对于一些不熟悉命令行操作的用户来说,集成到Eclipse开发环境中将会极大提高开发效率。 使用jadclipse插件可以很方便地在Eclipse中打开任何.class文件,并且将反编译的结果显示在编辑器中。用户可以在查看反编译的源代码的同时,进行阅读、调试和学习。这样不仅可以帮助开发者快速理解第三方库的工作机制,还能在遇到.class文件丢失源代码时进行紧急修复工作。 对于Eclipse用户来说,安装jadclipse插件相当简单。一般步骤包括: 1. 下载并解压jadclipse插件的压缩包。 2. 在Eclipse中打开“Help”菜单,选择“Install New Software”。 3. 点击“Add”按钮,输入插件更新地址(通常是jadclipse的更新站点URL)。 4. 选择相应的插件(通常名为“JadClipse”),然后进行安装。 5. 安装完成后重启Eclipse,插件开始工作。 一旦插件安装好之后,用户只需在Eclipse中双击.class文件,或者右键点击文件并选择“Open With Jadclipse”,就能看到对应的Java源代码。如果出现反编译不准确或失败的情况,用户还可以直接在Eclipse中配置jad.exe的路径,或者调整jadclipse的高级设置来优化反编译效果。 需要指出的是,使用反编译工具虽然方便,但要注意反编译行为可能涉及到版权问题。在大多数国家和地区,反编译软件代码属于合法行为,但仅限于学习、研究、安全测试或兼容性开发等目的。如果用户意图通过反编译获取商业机密或进行非法复制,则可能违反相关法律法规。 总的来说,反编译插件for Eclipse是一个强大的工具,它极大地简化了Java反编译流程,提高了开发效率,使得开发者在没有源代码的情况下也能有效地维护和学习Java程序。但开发者在使用此类工具时应遵守法律与道德规范,避免不当使用。
recommend-type

【进阶Python绘图】:掌握matplotlib坐标轴刻度间隔的高级技巧,让你的图表脱颖而出

# 摘要 本文系统地探讨了matplotlib库中坐标轴刻度间隔的定制与优化技术。首先概述了matplotlib坐标轴刻度间隔的基本概念及其在图表中的重要性,接