本设计基于2019电赛 电路特性测试仪一题,目的在于显示放大电路输入输出波形,保证不会出现失真,从而使电路的增益测量结果更加准确。
这里使用定时器TIM3输出PWM波触发ADC采样,根据需要采样的波形频率为1kHz设置合适的定时器的预分频系数以及自动重装载值。
STM32F4xx 通用定时器相关知识
STM32F4定时器系统包括,高级控制定时器 TIM1和 TIM8、通用定时器TIM2~TIM5、通用定时器TIM9 ~TIM14、基本定时器TIM6和TIM7。我们重点关注通用定时器的相关配置。
通用定时器TIM2~TIM5包含一个 16 位或 32 位自动重载计数器,该计数器由可编程预分频器驱动。它们可用于多种用途,包括测量输入信号的脉冲宽度(输入捕获)或生成输出波形(输出比较和 PWM)。使用定时器预分频器和 RCC 时钟控制器预分频器,可将脉冲宽度和波形周期从几微秒调制 到几毫秒。这些定时器彼此完全独立,不共享任何资源。
TIM3配置函数如下,注意GPIO复用,自动重装载值和分频系数的设置。定时器溢出时间Tout和重装载值arr以及预分频系数psc的关系如下:
- Tout=((arr+1)*(psc+1))/TCLK
//初始化定时器
void ADC12_TIM3_Mode_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTC时钟
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOC6(USART)复用为定时器3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOC6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC6
TIM_TimeBaseStructure.TIM_Prescaler = 83;//psc=83
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period =49;//arr=49
TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=25;//自动重装载值为49,占空比设置为50%,则这里应赋值25
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能
TIM_Cmd(TIM3, ENABLE);
}
DMA相关配置
在配置DMA时要注意以下几点
- 由于我们采用双重ADC模式采样,必须使用ADC1和ADC2,ADC1为主,ADC2为从,因此根据STM32F4xx中文参考手册中的DMA请求映射表
我们应选择ADC1对应的DMA2的通道0和数据流0。 - 由于我们采用的是双重ADC规则同步模式,ADC采集的数据并没有存储在ADC1的数据寄存器中,而是在ADC->CDR寄存器中,所以DMA传输的源地址要改写成ADC->CDR
- DMA目的地址为自己设定的数组ADC_ConvertedValue[]
- 另外,双重ADC 模式下,ADC1和2的数据都存储在同一个32位的寄存器中,低半字为ADC1的数据,高半字为ADC2的数据,所以要设置源和目的数据大小为一个字。
DMA相关配置函数如下:
//初始化DMA2
void ADC12_DMA2_Mode_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //使能DMA2时钟
//DMA是一种快速的数据传送方式
while(DMA_GetCmdStatus(DMA2_Stream0)!=DISABLE);
DMA_InitStructure.DMA_BufferSize=NUM;
DMA_InitStructure.DMA_Channel=DMA_Channel_0;
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Memory0BaseAddr=(u32)ADC_ConvertedValue;
DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_MemoryDataSize=DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&(ADC->CDR);
DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority=DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
//使能DMA通道2
DMA_Cmd(DMA2_Stream0, ENABLE);
}
ADC1和ADC2的配置
- ADC通用设置中要将模式设置为双重模式ADC_DualMode_RegSimult
- 注意ADC的时钟最好不要超过36Mhz,否则ADC采集的数据将会不理想。
- 要设置ADC1为定时器触发,ADC2要设置为软件触发,使用ADC_SoftwareStartConv(ADC2)函数实现。
- DMA设置为多重模式 ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE)
配置函数如下:
/********************************
配置ADC1,ADC2和DMA,利用DMA来读取ADC采
集到的数据
********************************/
void ADC12_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE); //使能ADC2时钟
ADC12_GPIO_Config();
ADC12_DMA2_Mode_Config();
ADC12_TIM3_Mode_Config();
//ADC通用配置
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;//双重模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟 5 个时钟,影响不大
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2; //DMA 模式选择
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频 4 分频。
//ADCCLK=PCLK2/4=84/4=21Mhz,ADC 时钟最好不要超过 36Mhz
ADC_CommonInit(&ADC_CommonInitStructure);//初始化
//配置ADC1
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //使用12 位分辨率
ADC_InitStructure.ADC_ScanConvMode =DISABLE; //非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //只采集一次,等待下次触发
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;//上升沿触发
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T3_CC1; //定时器触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐方式
ADC_InitStructure.ADC_NbrOfConversion = 1; // 用来设置规则序列的长度,我们只对一个ADC通道进行转换
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_84Cycles); //
// ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE);
//ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA
//配置ADC2
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //使用12 位分辨率
ADC_InitStructure.ADC_ScanConvMode =DISABLE; //非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //只采集一次,等待下次触发
//ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;//上升沿触发
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConvEdge_None; //从ADC不设置外部触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐方式
ADC_InitStructure.ADC_NbrOfConversion = 1; // 用来设置规则序列的长度,我们只对一个ADC通道进行转换
ADC_Init(ADC2, &ADC_InitStructure);
ADC_Cmd(ADC2, ENABLE); //使能ADC2
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_84Cycles); //
// ADC_DMARequestAfterLastTransferCmd(ADC2,ENABLE);
//ADC_DMACmd(ADC2, ENABLE); //使能ADC1的DMA
ADC_SoftwareStartConv(ADC2);
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
}
数据转换
将采集到的数据转化为两组数据,输入波形数据和输出波形数据
/******************************************************************
将采集到的数据转化为两组数据,输入数据和输出数据
*******************************************************************/
void zhuanhua(void)
{int i=0;
for(i=0;i<2000;i++)
{ ADC2_ConvertedValue[i]=(u16)((ADC_ConvertedValue[i]&0xFFFF0000)>>16);
ADC1_ConvertedValue[i]=(u16)(ADC_ConvertedValue[i]&0xFFFF);
}
}