9.2 普通触发 普通触发的实现是基于ADC的模拟看门狗功能,通过设置不同看门狗阀值实现不同的触发电压。由于使能了看门狗中断,检测到外部触发电压后会进入ADC模拟看门狗中断。在中断里面判断是否是上升沿触发,如果是的话,会关闭模拟看门狗中断并开启一个定时器计数功能,目的是为了采集这个触发电压前后各1024个ADC数据,基本的实现思路就是这个样子的。 下面把实际的实现为大家做个说明: 第1步:将ADC3配置使能模拟看门狗功能。 - /*ADC3的配置*****************************************************************/
- ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
- ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3;
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
- ADC_InitStructure.ADC_NbrOfConversion = 1;
-
- /* ADC3 规则通道配置 */
- ADC_Init(ADC3, &ADC_InitStructure);
- ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 1, ADC_SampleTime_3Cycles);
-
- /* 使能 ADC3 DMA */
- ADC_DMACmd(ADC3, ENABLE);
-
- /* 配置模拟看门狗的阀值 注意别配置反了,要不一直进入中断 */
- ADC_AnalogWatchdogThresholdsConfig(ADC3, 4095, 0);
-
- /* 配置模拟看门狗监测ADC3的通道10 */
- ADC_AnalogWatchdogSingleChannelConfig(ADC3, ADC_Channel_10);
-
- /* 使能一个规则通道的看门狗 */
- ADC_AnalogWatchdogCmd(ADC3, ADC_AnalogWatchdog_SingleRegEnable);
-
- /* 使能模拟看门狗中断 */
- ADC_ITConfig(ADC3, ADC_IT_AWD, ENABLE);
-
- /* 使能DMA请求 -------------------------------------------------------------*/
- ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
-
- /* Enable ADC3 --------------------------------------------------------------*/
- ADC_Cmd(ADC3, ENABLE);
复制代码特别注意函数ADC_AnalogWatchdogThresholdsConfig的设置,因为是12位分辨率的ADC,最大值就是2^12 – 1 = 4095,这里设置为4095表示超过4095才会触发模拟看门狗中断,由于已经是最大值了,所以不会触发模拟看门狗中断。
第2步:模拟看门狗中断。 达到设置的模拟看门狗触发值会进入到这个中断里面。 - /*
- *********************************************************************************************************
- * 函 数 名: ADC_IRQHandler
- * 功能说明: 模拟看门狗中断服务程序。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void ADC_IRQHandler()
- {
- /* 读取DMA剩余要传输的数目 */
- g_usTrigCount = DMA2_Stream1->NDTR;
-
- /* 确认是否是ADC3的看门狗中断 */
- if((ADC3->SR)&0x01)
- {
- /* 取触发值的前一个点,查看是否是在阀值范围内,从而判断是上升沿还是下降沿 */
- if(g_usTrigCount == 10240)
- {
- /* 剩余10240表示触发值是ADC3ConvertedValue[10239]是触发值,那么上一个点就是10238 */
- g_usTrigTempFlag = ADC3ConvertedValue[10238];
- }
- else if(g_usTrigCount == 10239)
- {
- /* 剩余10239表示触发值是ADC3ConvertedValue[0]是触发值,那么上一个点就是10239 */
- g_usTrigTempFlag = ADC3ConvertedValue[10239];
- }
- else
- {
- /* 剩余10239表示触发值是ADC3ConvertedValue[0]是触发值,那么上一个点就是10239 */
- g_usTrigTempFlag = ADC3ConvertedValue[10238 - g_usTrigCount];
- }
-
- /* 判断是否是上升沿,是的话开启定时器记录ADC数据 */
- if(g_usTrigTempFlag <= g_TrigVol->usTrigValue)
- {
- /* 关闭ADC3的看门狗中断 */
- ADC3->CR1 &= 0xffffffbf;
- TriggerFlag = 1;
-
- /* 启动定时器计数 */
- TIM8->CR1 |= 0x01;
- }
- /* 清除挂起标志 */
- ADC3->SR &= 0xfe;
- }
- }
复制代码进入到这个中断后,主要做了一件事,判断是否是上升沿,如果是上升沿的话,将关闭模拟看门狗并开启定时器测量功能。也就是下面第3步要讲解的。
第3步:初始化一个定时器做时间测量,表示检测到触发值后记录一段时间的波形。 - /*
- *********************************************************************************************************
- * 函 数 名: TIM8_MeasureTrigConfig
- * 功能说明: 使用TIM8为普通触发模式下数据采集计时,定时采集触发值前后的1024个ADC数据
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- /*
- 每次捕获采集触发值前后的1024个ADC数据(单通道)。
- */
- const uint32_t g_TrigFreqTable[][2] =
- {
- {60, 1024}, //2.8Msps 168000000/2800000 = 60 => 60 * 1024
- {84, 1024}, //2Msps 168000000/2000000 = 84 => 84 * 1024
- {168, 1024}, //1Msps 168000000/1000000 = 168 => 168 * 1024
- {336, 1024}, //500Ksps 168000000/500000 = 336 => 336 * 1024
- {840, 1024}, //200Ksps 168000000/200000 = 840 => 840 * 1024
-
- {1680, 1024}, //100Ksps 168000000/100000 = 1680 => 1680 * 1024
- {3360, 1024}, //50Ksps 168000000/50000 = 3360 => 3360 * 1024
- {8400, 1024}, //20Ksps 168000000/20000 = 8400 => 8400 * 1024
- {16800, 1024}, //10Ksps 168000000/10000 = 16800 => 16800 * 1024
- {33600, 1024}, //5Ksps 168000000/5000 = 33600 => 33600 * 1024
-
- /* 下面5种采样频率下刷新较慢,因为采集前后1024个ADC的时间较长 */
- {42000, 2048}, //2Ksps 168000000/2000 = 84000 => 84000 * 1024
- {42000, 4096}, //1Ksps 168000000/1000 = 168000 => 168000 * 1024
- {42000, 8192}, //500sps 168000000/500 = 336000 => 336000 * 1024
- {42000, 20480}, //200sps 168000000/200 = 840000 => 840000 * 1024
- {42000, 40960}, //100sps 168000000/100 = 1680000 => 1680000 * 1024
-
- /* 下面这几种采样率不做触发支持 */
- {42000, 40960}, //50sps
- {42000, 40960}, //20sps
- {42000, 40960}, //10sps
- {42000, 40960}, //5sps
- {42000, 40960}, //2sps
- {42000, 40960}, //1sps
- };
-
- void TIM8_MeasureTrigConfig(void)
- {
- TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- /* 开启时钟 */
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
-
- /* 加上第一次进入中断的标志,进入中断后将其置1 */
- g_usFirstTimeIRQFlag = 0;
-
- /* 使能定时器8中断 */
- NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- /* 定时器配置 */
- TimeBaseId = 1; /* 开机后按照ADC单通道1Msps进行配置 */
- TIM_DeInit(TIM8);
- TIM_BaseInitStructure.TIM_Period = g_TrigFreqTable[TimeBaseId][0] - 1;
- TIM_BaseInitStructure.TIM_Prescaler = g_TrigFreqTable[TimeBaseId][1] - 1;
- TIM_BaseInitStructure.TIM_ClockDivision = 0;
- TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM8, &TIM_BaseInitStructure);
- TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE);
- TIM_Cmd(TIM8, DISABLE);
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: Time8Recorder
- * 功能说明: 使用TIM8为普通触发模式下数据采集计时,定时采集触发值前后的1024个ADC数据
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void Time8Recorder(void)
- {
- TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
-
- /* 加上第一次进入中断的标志 */
- g_usFirstTimeIRQFlag = 0;
-
- TIM_DeInit(TIM8);
- TIM_BaseInitStructure.TIM_Period = g_TrigFreqTable[TimeBaseId][0] - 1;
- TIM_BaseInitStructure.TIM_Prescaler = g_TrigFreqTable[TimeBaseId][1] - 1;
- TIM_BaseInitStructure.TIM_ClockDivision = 0;
- TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM8, &TIM_BaseInitStructure);
- TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE);
- TIM_Cmd(TIM8, DISABLE);
- }
复制代码 首先讲解数组g_TrigFreqTable的作用,因为不同采样率下,采样1024个数据时间是不同的,所以我们就要将定时器设置为不同的参数配置,以此实现这段时间内采集1024个数据(也许有读者会问,我们不是要采集2048个数据吗,为什么这里只采集了1024个。原因在于另外1024个数据直接从这个触发值所在位置的前面获取即可,也就是已经采集的,最近的1024个数据)。比如第一组是60和1024,他的作用就是将定时器结构体成员配置为: TIM_BaseInitStructure.TIM_Period = 60 – 1 TIM_BaseInitStructure.TIM_Prescaler = 1024 – 1 那么定时器的更新周期= TIMCLK / (TIM_Period + 1)/(TIM_Prescaler + 1)=TIMCLK/60/1024。到了这个时间后,就会进入到定时器中断,也就是在这个采样率下,已经完成了1024个数据的采集。其它采样率的参数配置同理。具体取值不限制,只要大家的配置满足采集了1024个数据即可。 这个理解了,其它的就是TIM8的配置,这个就相对容易些,我们这里就不做介绍了。另外特别注意变量标志g_usFirstTimeIRQFlag,下一步里面会说明为什么要做这个。
第4步:也是最后一步,测量时间到后,进入到定时器中断。 进入到定时器中断后,要关闭定时器,并将触发值前后各1024个采样数据从缓冲区中提取出来。具体实现代码如下: 变量标志g_usFirstTimeIRQFlag的作用就是代码前面的注释,第一次进入TIM8中断执行退出操作,因为定时器更新中断有个小bug,一旦初始化了定时器并且使能中断就会有立即进入一次中断的情况,为了防止这种情况而做的这个变量。 定时器中断里面最重要的功能还是从10240大小的缓冲区里面提取2048个采样数据,代码里面分为三种情况进行实现: 1. 当前的位置 < 1024。 2. 1024 <= 当前的位置 <= 10240 - 1024(9216)。 3. 当前位置 > 10240 - 1024(9216)。 具体实现需要大家拿笔实际计算下,捋一遍基本就都明白了,我们这里就不做说明了。
|