+= *= /= %= 运算的先后顺序

这篇博客通过一个简单的C语言代码示例,解释了运算符的优先级规则。在示例中,`b *= a + 5` 先计算 `a + 5`,然后将结果乘以 `b`,最终输出21。这展示了在C语言中,乘法和除法运算符在加法和减法之前进行计算的规则。

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

# include <stdio.h>

/*
2022年4月13日17:17:20
+= *= /= %=  
先计算运算符右边的 

*/
int main() {
	int a = 2;
	int b = 3;
	b *= a + 5;  //先是 2+5  再 乘以 3   等于21 
	
	printf("%d", b);
	
	return 0;
} 

/*
在dev中运行的结果为
21
--------------------------------
Process exited after 0.01038 seconds with return value 0
请按任意键继续. . .
先计算运算符右边的 

由此 
*/
#include "./adc/bsp_adc.h" __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]={0}; float voltage1=0, voltage2=0, voltage3=0; static void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*=====================通道1======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK1,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT1, &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_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 = ENABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //数据右对齐 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_3Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL3, 3, ADC_SampleTime_3Cycles); // 使能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_1); NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void ADC_Read(void) { voltage1=(float)ADC_ConvertedValue[0]/4096*(float)3; voltage2=(float)ADC_ConvertedValue[1]/4096*(float)3; voltage3=(float)ADC_ConvertedValue[2]/4096*(float)3; } void Adc_Init(void) { ADC_GPIO_Config(); ADC_Mode_Config(); ADC_NVIC_Config(); } 中的以下代码干什么的,怎么移入中断采电压 void ADC_Read(void) { voltage1=(float)ADC_ConvertedValue[0]/4096*(float)3; voltage2=(float)ADC_ConvertedValue[1]/4096*(float)3; voltage3=(float)ADC_ConvertedValue[2]/4096*(float)3; } 中断函数这样extern uint16_t ADC_ConvertedValue; uint16_t TIM_Advance_Impulse ;//高级定时器占空比 float Vout_actual; float Vout_set; // 目标输出电压 float pid_out; void TIM1_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM1,TIM_IT_Update) == SET) { Vout_actual = ADC_ConvertedValue * 3.3f / 4096; pid_out = PID_DC( Vout_set ,Vout_actual, 20.0f ); TIM1->CCR1 = pid_out; TIM_ClearFlag(TIM1,TIM_IT_Update); } } void ADC_IRQHandler(void) { if(ADC_GetITStatus(ADC_,ADC_IT_EOC) == SET) { ADC_ConvertedValue = ADC_GetConversionValue(ADC_); } ADC_ClearITPendingBit(ADC_,ADC_IT_EOC); }
最新发布
07-05
<think>我们分析一下提供的代码片段,并回答两个问题: 1. 函数`ADC_Read(void)`的作用是什么? 2. 如何将ADC采样移到中断中,并修改中断函数以采集三个通道的电压? 首先,我们来看`ADC_Read`函数: ```c void ADC_Read(void) { voltage1=(float)ADC_ConvertedValue[0]/4096*(float)3; voltage2=(float)ADC_ConvertedValue[1]/4096*(float)3; voltage3=(float)ADC_ConvertedValue[2]/4096*(float)3; } ``` 这个函数的作用是将ADC转换的原始值转换为电压值。ADC是12位的,所以最大值为4095(2^12-1)。这里假设参考电压为3V(也可能是3.3V,但代码中用了3,可能是笔误,通常STM32的参考电压是3.3V)。因此,每个通道的转换公式为: 电压 = (ADC原始值 / 4096) * 参考电压 注意:这里参考电压用了3,但实际上很多STM32开发板的VDDA是3.3V,所以通常用3.3。如果参考电压确实是3V,则没问题。 该函数读取全局数组`ADC_ConvertedValue`(该数组有3个元素,分别对应3个通道)的值,然后计算每个通道的电压,并分别赋值给全局变量voltage1, voltage2, voltage3。 接下来,我们看第二个问题:如何移入中断采电压? 在提供的代码中,已经有一个ADC中断函数`ADC_IRQHandler`和一个定时器中断函数`TIM1_UP_IRQHandler`。但是,当前的ADC中断函数只读取了一个通道的值(因为`ADC_ConvertedValue`被当作单个变量使用,而不是数组),而且定时器中断中读取的`ADC_ConvertedValue`也是当作单个变量。 然而,在ADC初始化代码中,我们配置了3个通道,并且使用了DMA传输。DMA传输会将三个通道的值依次存放到数组`ADC_ConvertedValue`中(即`ADC_ConvertedValue[0]`、`ADC_ConvertedValue[1]`、`ADC_ConvertedValue[2]`)。因此,在ADC中断中,我们不需要读取转换值,因为DMA已经自动将转换结果存放到数组中了。 实际上,在DMA模式下,通常不需要使用ADC中断来读取数据,因为DMA会自动搬运数据。但是,如果我们希望在每个通道转换完成后得到通知,或者转换序列完成后触发中断,则可以使用中断。 观察初始化函数`ADC_Mode_Config`,它使能了DMA,并且配置为循环模式。因此,DMA会不断将ADC转换结果搬运到数组`ADC_ConvertedValue`中。所以,我们不需要在中断中读取ADC的值,只需要在需要的时候直接读取数组即可。 但是,如果我们希望在每个转换序列完成(即三个通道都转换完)时触发中断,可以配置ADC的EOC(转换结束)选择为序列结束(在ADC初始化中,默认是每个通道转换结束就产生EOC,但我们可以配置为序列结束产生一次中断)。 然而,在提供的初始化代码中,并没有配置EOC选择,因此默认是每个通道转换结束产生EOC。而且,在初始化代码中,我们使能了DMA,所以每个通道转换结束后,DMA请求会触发,将数据搬运到内存。因此,我们不需要在EOC中断中读取数据,因为DMA已经做了。 但是,如果我们想利用中断来通知一次扫描完成(即三个通道都转换完成),我们可以配置ADC在序列转换完成时产生中断,并在这个中断中处理数据(比如计算电压)。但注意,DMA已经搬运了数据,所以中断中只需要读取数组即可。 然而,在初始化代码中,并没有使能ADC的序列转换完成中断(即ADC_IT_EOC或ADC_IT_EOS)。所以,我们需要修改初始化代码,使能序列转换完成中断,并在中断中读取数据(实际上数据已经在数组里了,我们只需要调用`ADC_Read`函数计算电压即可)。 但是,请注意:在DMA模式下,通常我们使用DMA传输完成中断(即DMA中断)来通知数据已经准备好,而不是ADC中断。所以,我们有两种选择: 方案1:使用DMA传输完成中断(推荐) - 配置DMA传输完成中断(即传输3个数据后产生中断) - 在DMA中断中调用`ADC_Read`函数计算电压 方案2:使用ADC序列转换完成中断(EOS) - 配置ADC在序列转换完成时产生中断(注意:每个序列转换完成,而不是每个通道) - 在ADC中断中调用`ADC_Read`函数 由于我们使用了DMA循环模式,所以每次DMA传输完3个数据就会产生一次传输完成中断(如果使能了中断)。或者,如果使用ADC序列完成中断,那么每次序列转换完成就会产生中断。 但是,在初始化代码中,我们并没有配置ADC产生序列完成中断,也没有配置DMA传输完成中断。因此,我们需要修改初始化代码。 这里,我们采用方案1(DMA传输完成中断)来实现,因为DMA传输完成正好对应三个通道的数据都搬运完成。 步骤: 1. 在ADC_Mode_Config函数中,配置DMA传输完成中断。 2. 编写DMA中断服务函数,在中断中调用ADC_Read函数。 3. 修改现有的ADC_Read函数,使其计算三个通道的电压。 但是,注意:我们使用的是DMA2的Stream0(根据宏定义,但代码中宏没有给出,我们假设是DMA2_Stream0),它的传输完成中断是`DMA2_Stream0_IRQn`。 修改`ADC_Mode_Config`函数,在DMA初始化后使能传输完成中断: ```c static void ADC_Mode_Config(void) { // ... 其他代码不变 DMA_Init(ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA传输完成中断 DMA_ITConfig(ADC_DMA_STREAM, DMA_IT_TC, ENABLE); // 使能DMA流 DMA_Cmd(ADC_DMA_STREAM, ENABLE); // ... 其他代码不变 } ``` 然后,我们需要配置DMA中断的NVIC: 在`ADC_NVIC_Config`函数中,我们原来配置的是ADC中断,现在改为配置DMA中断(注意:我们可能还需要保留ADC中断?实际上,如果使用DMA传输完成中断,就不需要ADC中断了,所以可以去掉ADC中断配置,改为配置DMA中断)。 修改`ADC_NVIC_Config`函数: ```c static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置DMA中断(假设使用DMA2 Stream0) NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; // 根据实际使用的DMA和Stream修改 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } ``` 然后,编写DMA中断服务函数: ```c void DMA2_Stream0_IRQHandler(void) { // 检查传输完成中断标志 if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET) { // 清除中断标志 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); // 调用ADC_Read计算电压 ADC_Read(); } } ``` 注意:DMA2_Stream0的传输完成中断标志是`DMA_IT_TCIF0`,即数据流0的传输完成中断标志。其他流的中断标志位不同,请参考手册。 这样,每次DMA传输完3个通道的数据(即完成一次循环),就会进入DMA中断,然后调用`ADC_Read`计算电压。 另外,注意全局数组`ADC_ConvertedValue`在DMA传输中会被不断更新,而我们在DMA传输完成中断中读取,此时数据是刚更新完的,所以计算出的电压是最新的。 但是,请注意:DMA传输完成中断是在每次传输完指定数据量(这里为3)后触发。由于我们配置为循环模式,所以传输完3个数据后,DMA会自动重置,重新开始传输,因此每次完成都会触发中断。 最后,我们再看一下定时器中断函数`TIM1_UP_IRQHandler`,它里面也使用了`ADC_ConvertedValue`,但它是当作单个变量使用的,而且它计算的是`Vout_actual`,这显然与三个通道的数据不符。所以,我们需要根据实际需求修改定时器中断中的代码。比如,如果我们只需要第一个通道的电压,那么可以这样修改: ```c void TIM1_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM1,TIM_IT_Update) == SET) { // 注意:这里我们使用第一个通道的电压值 Vout_actual = (float)ADC_ConvertedValue[0] * 3.3f / 4096.0f; pid_out = PID_DC( Vout_set, Vout_actual, 20.0f ); TIM1->CCR1 = pid_out; TIM_ClearFlag(TIM1,TIM_IT_Update); } } ``` 但是,如果定时器中断中需要的电压值已经在全局变量voltage1中(因为DMA中断会更新voltage1),那么我们可以直接使用voltage1,而不用重新计算。但是要注意,voltage1是在DMA中断中更新的,而定时器中断中读取voltage1可能会发生数据竞争(因为两个中断可能同时发生)。为了避免这种情况,我们可以使用临界区保护(关中断)或者确保两个中断的优先级关系。 另外,如果定时器中断中需要的是最新的第一个通道的电压,而DMA中断中计算的是三个通道的电压,那么我们可以考虑在定时器中断中直接使用数组`ADC_ConvertedValue[0]`来计算,这样避免使用全局变量voltage1(因为voltage1可能在定时器中断发生时正在被DMA中断更新)。或者,我们可以在DMA中断中更新voltage1,voltage2,voltage3,然后在定时器中断中读取voltage1,但要注意数据同步。 由于DMA中断和定时器中断是异步的,我们可以将volatile关键字用于voltage1等变量,以确保每次读取都是最新的。同时,如果这两个中断的优先级相同,它们不会互相抢占,所以在一个中断执行时,另一个中断不会发生。因此,在定时器中断中读取voltage1时,DMA中断不会打断定时器中断(如果DMA中断优先级低于定时器中断),所以不会发生数据不一致。但是,如果DMA中断优先级更高,则可能在定时器中断执行时被DMA中断打断,导致读取到不完整的值(但float的赋值在32位系统是原子的,所以通常不会出现中间值)。不过,为了安全,我们可以使用临界区(在读取voltage1前关中断,读取后开中断)来确保读取的完整性。 但是,由于voltage1的计算在DMA中断中完成,而DMA中断的频率(即ADC转换的频率)和定时器中断的频率可能不同,所以定时器中断中读取的voltage1可能是之前的值,也可能是最新的值,这取决于两者发生的先后顺序。这通常是可以接受的,因为两次读取之间相差一个ADC采样周期。 总结: 1. `ADC_Read`函数的作用是将ADC原始值转换为电压值,并更新三个全局电压变量。 2. 为了在中断中采集电压,我们使用DMA传输完成中断,并在中断中调用`ADC_Read`函数。同时,修改了NVIC配置和DMA初始化以启用中断。 注意:以上代码是基于假设的(如DMA2_Stream0),实际使用时需要根据硬件调整。 最后,修改中断函数: 我们添加了DMA中断服务函数,并修改了定时器中断函数中读取ADC值的方式(改为使用数组第一个元素或全局变量voltage1)。 但是,原代码中的ADC中断服务函数`ADC_IRQHandler`可以删除,因为我们已经不需要它了(除非有其他用途)。另外,原初始化函数`Adc_Init`中调用了`ADC_NVIC_Config`,这个函数现在被修改为配置DMA中断,所以ADC中断没有被使能,因此不会进入ADC中断。 因此,我们可以删除原来的ADC中断服务函数,或者保留但不再使用(不使能ADC中断)。 如果保留ADC中断服务函数,但实际没有使能ADC中断,则不会执行。 建议删除没有使用的中断服务函数。 相关代码修改总结: 1. 修改`ADC_NVIC_Config`函数,配置DMA中断。 2. 在`ADC_Mode_Config`中使能DMA传输完成中断。 3. 编写DMA中断服务函数,调用`ADC_Read`。 4. 修改定时器中断函数,使用正确的ADC值(比如voltage1或直接计算数组第一个元素)。 由于代码较长,这里只给出修改的部分。 注意:参考电压是3.3V还是3V?根据实际硬件修改。在`ADC_Read`函数中,我们使用了3,但通常为3.3。所以建议修改为3.3。 修改`ADC_Read`: ```c #define VREF 3.3f void ADC_Read(void) { voltage1 = (float)ADC_ConvertedValue[0] / 4096.0f * VREF; voltage2 = (float)ADC_ConvertedValue[1] / 4096.0f * VREF; voltage3 = (float)ADC_ConvertedValue[2] / 4096.0f * VREF; } ``` 在定时器中断中,同样使用3.3f。 以上是详细的解决方案。 现在,按照要求,在最后提出相关问题。</think>### `ADC_Read`函数的作用与中断采电压实现 #### 1. `ADC_Read`函数解析 ```c void ADC_Read(void) { voltage1 = (float)ADC_ConvertedValue[0] / 4096 * (float)3; voltage2 = (float)ADC_ConvertedValue[1] / 4096 * (float)3; voltage3 = (float)ADC_ConvertedValue[2] / 4096 * (float)3; } ``` - **功能**:将ADC原始值转换为实际电压值 - **计算原理**: - `ADC_ConvertedValue[]`:存储ADC原始值的数组(12位ADC,范围0-4095) - `4096`:2¹²(12位ADC的最大值+1) - `*3`:假设参考电压为3V(实际开发中通常是3.3V) - 公式:`电压值 = (ADC原始值 / 4096) × 参考电压` - **输出**:更新全局变量`voltage1/2/3` #### 2. 移入中断采电压的实现 需修改中断处理函数,在ADC转换完成中断中直接读取电压值: ##### 修改后的ADC中断服务函数 ```c // 全局变量声明(在头文件中) extern float voltage1, voltage2, voltage3; void ADC_IRQHandler(void) { if(ADC_GetITStatus(ADC_, ADC_IT_EOC) == SET) { // 读取当前转换值(单通道模式) // uint16_t adc_val = ADC_GetConversionValue(ADC_); // 多通道DMA模式直接使用数组值 voltage1 = (float)ADC_ConvertedValue[0] / 4096 * 3.3f; voltage2 = (float)ADC_ConvertedValue[1] / 4096 * 3.3f; voltage3 = (float)ADC_ConvertedValue[2] / 4096 * 3.3f; ADC_ClearITPendingBit(ADC_, ADC_IT_EOC); } } ``` ##### 关键修改说明: 1. **参考电压修正**: - 将`(float)3`改为`3.3f`(STM32通常使用3.3V参考电压) - 若实际VDDA电压不同,需调整此值 2. **中断触发配置**: - 在`ADC_Mode_Config()`中添加: ```c // 使能序列转换完成中断 ADC_ITConfig(ADC_, ADC_IT_EOC, ENABLE); ``` 3. **DMA同步机制**: - 由于使用DMA传输,`ADC_ConvertedValue`数组会自动更新 - 中断中直接使用数组值,无需调用`ADC_GetConversionValue()` 4. **多通道处理**: - 确保`ADC_InitStructure.ADC_NbrOfConversion = 3` - 通道配置顺序需与数组索引一致: ```c ADC_RegularChannelConfig(ADC_, ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL3, 3, ADC_SampleTime_3Cycles); ``` #### 3. 定时器中断中的电压读取优化 在`TIM1_UP_IRQHandler`中直接使用已计算的全局电压值: ```c void TIM1_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_IT_Update) == SET) { // 直接使用全局变量(已在ADC中断中更新) Vout_actual = voltage1; // 假设使用通道1 pid_out = PID_DC(Vout_set, Vout_actual, 20.0f); TIM1->CCR1 = pid_out; TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } ``` ### 中断采电压的优势 1. **实时性**:电压转换在ADC转换完成后立即执行 2. **资源节约**:避免在主循环中频繁调用`ADC_Read()` 3. **同步性**:确保PID控制使用最新的电压值 4. **稳定性**:消除主循环执行时间对采样间隔的影响 ### 注意事项 1. **参考电压校准**:实际VDDA电压需通过`VREFINT`校准 2. **中断优先级**: - ADC中断优先级应高于定时器中断 - 避免PID计算被ADC中断打断 3. **浮点运算**:确保启用FPU(浮点运算单元) 4. **数组越界**:确认`RHEOSTAT_NOFCHANEL=3`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值