本帖最后由 xiaxuepiaopiao 于 2013-8-5 21:35 编辑
闲来无事,玩玩去年申请过来的STM32f0Discovery,决定试试 ADC+TIM+DMA方式。具体方法是每隔10s钟TIM触发一次AD转换,之后从DMA读走数据。讲讲ADC中最重要编程的部分,看看下面这张ADC结构图:
ADC可选择外部触发,有5个信号,TIM1_TRGO,TIM1_CC4,...TIM15_TRGO,TIM1_CC4容易理解,就是TIM1输出捕获4通道触发(PWM模式下)。那TRGO是什么呢?Trigger Output,触发输出,定时器均有Trigger Output信号(自己看看定时器的结构图),以下事情可以触发TRGO信号:(摘自stm32f0xx.tim.c TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)函数)
* - For all TIMx
* @arg TIM_TRGOSource_Reset: The UG bit in the TIM_EGR register is used as the trigger output (TRGO).
* @arg TIM_TRGOSource_Enable: The Counter Enable CEN is used as the trigger output (TRGO).
* @arg TIM_TRGOSource_Update: The update event is selected as the trigger output (TRGO).
*
* - For all TIMx except TIM6
* @arg TIM_TRGOSource_OC1: The trigger output sends a positive pulse when the CC1IF flag
* is to be set, as soon as a capture or compare match occurs (TRGO).
* @arg TIM_TRGOSource_OC1Ref: OC1REF signal is used as the trigger output (TRGO).
* @arg TIM_TRGOSource_OC2Ref: OC2REF signal is used as the trigger output (TRGO).
* @arg TIM_TRGOSource_OC3Ref: OC3REF signal is used as the trigger output (TRGO).
* @arg TIM_TRGOSource_OC4Ref: OC4REF signal is used as the trigger output (TRGO).
即复位、使能、输出捕获均能引起TRGO事件。所以若想利用TIM的TRGO引发ADC转换,记得必须加上:
TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)函数选择定时器的TRGO。当如果你想利用TIM1_CC4触发,就不必使用该函数,因为CC4不在TRGO行列内!
上面有TIM_TRGOSource_OC1和TIM_TRGOSource_OC1Ref,OCxRef和OCx有啥区别,这里,有许多PWM模式知识要将。看看下面这张结构图:
OCxRef参看信号由PWM模式决定,OCxRef再经过极性选择得到OCx,OCx必须被使能输出才得到PWM。PWM有模式1和模式2。PWM mode1为:当计数值cnt<预定值ccr时,输出有效电平(可简单视为高电平),cnt=ccr时,触发PWM,输出无效电平(低电平);mode2相反,cnt<ccr,输出高,cnt>ccr,输出低;必须说说有两种计数模式,向上计数和向下计数;向上计数,从0向上递增;向下计数,从0xfff向下递减。所以若选择PWM模式1且向上计数,输出极性的变化必定是从高到低,即OCxRef是从有效电平到无效电平跳变。其他组合方式自己算算。极性选择,若选择TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High,表明输出OCx与OCxRef同相,若为Low表明反向。总输出极性=PWM模式选择 && PWM计数方式 && 极性选择。
讲程序,TIM15 10s触发一次ADC有3种方法。1法:10s进入TIM中断,在中断中软件触发ADC。技术含量低,不采用,效率也低。2法:定时器配置为10s计时,ADC配置为ADC_ExternalTrigConv_T15_TRGO,TIM15的TRGO选择为TIM_TRGOSource_Update(即计数溢出事件);3法:TIM15配置为PWM,周期10s,向上计数,PWM pulse值设定为1,ADC配置为ADC_ExternalTrigConv_T15_TRGO,TIM15的TRGO选择为TIM_TRGOSource_OC1。
2法如下:
(1)GPIO设置
void GPIOConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//使能外设GPIO时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1;//ADC_CH1 ADC_CH2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模式为模拟输入输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉设置
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
(2)ADC配置
void ADCConfig()
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_DeInit(ADC1);
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;//上升沿触发
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T15_TRGO;//TIM15触发
// ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC4;//选择TIM1 Compare&Capture4 作为触发源
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续模式禁止 要由定时器触发
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; //CH0-18
ADC_Init(ADC1, &ADC_InitStructure);
ADC_OverrunModeCmd(ADC1,ENABLE);//数据覆盖方式,只保留最新的转换数据
ADC_ChannelConfig(ADC1,ADC_Channel_0|ADC_Channel_1,ADC_SampleTime_239_5Cycles);
ADC_GetCalibrationFactor(ADC1); // 开始ADC校准
ADC_Cmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)==RESET); /* 等待ADC准备好 */
ADC_DMACmd(ADC1,ENABLE);//使能DMA
ADC_DMARequestModeConfig(ADC1,ADC_DMAMode_Circular);//DMA循环模式
}
(3)TIM15配置
void TIM15Config()
{
//(1)选择TIM15 Update事件作为触发源(计数器溢出)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
//配置基础
TIM_DeInit(TIM15);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_ClockDivision=0;//48M
TIM_TimeBaseStructure.TIM_Prescaler=48000;//48k 1Tick=1ms
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period=1000*10;//10s周期
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;//发生RepetitionCounter+1次溢出事件后中断
TIM_TimeBaseInit(TIM15,&TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM15,TIM_TRGOSource_Update);//TIM15的Update事件作为外部TRGO
TIM_ClearFlag(TIM15,TIM_FLAG_Update); //防止一上电进入中断 貌似可以不用??
TIM_Cmd(TIM15,ENABLE);//使能TIM3
}
(4)DMA配置
void DMAConfig()
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);//使能DMA时钟
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (INT32U)&(ADC1->DR);//外设基地址 ADC1数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr =(INT32U)ADCResult;//内存基地址:数组ADCResult地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//方向:外设为源地址 即外设-》内存
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;//BUFFER_SIZE是宏定义,为2
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//DMA外设地址不自加
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);
/* DMA1 Channel1 enable */
DMA_Cmd(DMA1_Channel1, ENABLE);
}
(5)main函数
#define BUFFER_SIZE 2
INT16U ADCResult[BUFFER_SIZE];
INT16U ADC1ConvertedValue[BUFFER_SIZE];
void main()
{
int i;
GPIOConfig();
ADCConfig();
DMAConfig();
TIM15Config();
ADC_StartOfConversion(ADC1);
//A conversion either starts immediately (software trigger configuration)or once a hardware trigger event
// occurs (hardware trigger configuration).
while (1)
{
while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET );
DMA_ClearFlag(DMA1_FLAG_TC1);
for(i=0;i<BUFFER_SIZE;i++)
{
ADC1ConvertedValue=ADCResult*3000/0x0fff;
}
asm("nop");
}
}
ADC_StartOfConversion(ADC1)必须加入,看看注释就知道以及本帖图一ADC结构图就知道。
#################################################################
3法程序如下:
只有TIM15配置不同,多了PWM配置。
void TIM15Config()
{
//(2)选择T15 TIM_TRGOSource_OC1 作为触发
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_DeInit(TIM15);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
// Time base configuration /
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //48M
TIM_TimeBaseStructure.TIM_Prescaler = 48000; //48k 1Tick=1ms
TIM_TimeBaseStructure.TIM_Period = 1000*10;//周期10s
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM15, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM15,TIM_TRGOSource_OC1);//TIM15的OC1事件作为外部TRGO
TIM_ClearFlag(TIM15,TIM_FLAG_CC1);
// Output Compare PWM Mode configuration
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;
//PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道2为无效电平,否则为有效电平;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//OC与OCRef同相
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0x01;
TIM_OC1Init(TIM15, &TIM_OCInitStructure);
//TIM1 enable counter //
TIM_Cmd(TIM15, ENABLE);
//Main Output Enable //
TIM_CtrlPWMOutputs(TIM15, ENABLE);
}
#########################################################
如果选择TIM1_CC4触发ADC,TIM配置中TIM_SelectOutputTrigger函数不必有(也不应该有,第二个参数没合适的)。。。官网例程中TIM就有CC4触发的例子 |