【安富莱二代示波器教程】第9章 示波器设计—自动触发和普通触发

第9章        示波器设计—自动触发和普通触发

自带触发和普通触发是示波器设计中比较重要的两个功能,本章节为大家讲解二代示波器中自动触发和普通触发的实现。

9.1    自动触发

9.2    普通触发

9.3     总结

9.1  自动触发

由于示波器模拟前端模块稍有些问题,所以自动触发功能是用软件实现的。软件实现自动触发比较容易实现,具体的实现代码如下:

/*  通过软件检测实现上升沿触发,并保留最后600的数据不做检测,用于直接显示 2048-600 = 1448; */

j = 0;

for(i = 0; i < 1448; i++)

{

     j++;

     if((g_DSO1->usWaveBufTemp[i] > g_TrigVol->usTrigValue) &&

        (g_DSO1->usWaveBufTemp[i+1] < g_TrigVol->usTrigValue))

     {   

         break;

     }

}

g_DSO1->usWaveBufTemp是2048个ADC数据的缓冲区,g_TrigVol->usTrigValue是上升沿触发值。for循环的作用就是从1448个数据中找到满足触发值的位置,判断方法也比较简单,大于前一个值小于后一个值即可。

保留600个数据是因为这个大小是波形显示区一次可以显示的波形个数。如果从前2048-600 = 1448个数据中检索不到满足要求的数据将不再检索,直接显示末尾的600个数据,如果检测到将直接从for循环里面退出。

这个方法在实际测试中比较好用,另外上升沿阀值的判断还不够严谨,大家有兴趣可以继续完善下。

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个采样数据从缓冲区中提取出来。具体实现代码如下:

/*

*********************************************************************************************************

*    函 数 名: TIM8_UP_TIM13_IRQHandler

*    功能说明: 定时器中断函数

*    形    参: 无

*    返 回 值: 无              

*********************************************************************************************************

*/

void TIM8_UP_TIM13_IRQHandler(void)

{

     uint16_t i, k, usCurPos;

     /* 第一次进入TIM8中断执行退出操作,因为定时器更新中断有个小bug,

        一旦初始化了定时器并且使能中断就会有立即进入一次中断的情况,为了防止这种情况的

        出现,作如下操作:

     */

     if(g_usFirstTimeIRQFlag == 0)

     {

         g_usFirstTimeIRQFlag = 1;

         TIM_ClearITPendingBit(TIM8, TIM_IT_Update);

         /* 如果是第一次进入就退出 */

         return;

     }

     /* 定时器时间中断 */

     if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET)

     {

         /* 先关闭定时器 */

         TIM_Cmd(TIM8, DISABLE);

         /* 确保开启的定时器计时采集ADC数据 */

         if(TriggerFlag == 1)

         {

              /* 定时器采集ADC数据结束 */

              TriggerFlag = 2;

              /* 开启定时器计数前DMA传输数据的位置 */

              usCurPos = 10240 - g_usTrigCount;

              /*

                   下面分为三种情况获取2K的数据(三通道,单通道就是前后1024个ADC数据):

                   1. 当前的位置 < 1024.

                   2. 1024 <= 当前的位置 <= 10240 - 1024(9216)

                    3. 当前位置 > 10240 - 1024(9216)

              */

              /* 第一种情况:当前的位置 < 1024. */

              if(usCurPos < 1024)

              {

                   k = 1024 - usCurPos;

                   usCurPos = 10240 - k;

                   /* 前部分数据 */

                   for(i = 0; i < k; i++)

                   {

                       g_DSO1->usWaveBufTemp[i] = ADC3ConvertedValue[i+usCurPos];

                   }

                   usCurPos = i;

                   k = 10240 - g_usTrigCount + 1024;

                   /* 后部分数据 */

                   for(i = 0; i < k; i++)

                   {

                       g_DSO1->usWaveBufTemp[i + usCurPos] = ADC3ConvertedValue[i];

                   }

              }

              /* 第三种情况:当前位置 > 10240 - 1024(9216) */

              else if(usCurPos > 9216)

              {

                   usCurPos = usCurPos - 1024;

                   /* 采集前1024+g_usTrigCount的数据 */

                   for(i = 0; i < (g_usTrigCount + 1024); i++)

                   {

                       g_DSO1->usWaveBufTemp[i] = ADC3ConvertedValue[i+usCurPos];

                   }

                   k = i;

                   usCurPos = (1024 - g_usTrigCount);

                   /* 剩余数据的采集 */

                   for(i = 0; i < usCurPos; i++)

                   {

                       g_DSO1->usWaveBufTemp[i + k] = ADC3ConvertedValue[i];

                   }

              }

              /* 第二种情况:1024 <= 当前的位置 <= 10240 - 1024(9216)  */

              else

              {

                   usCurPos = usCurPos - 1024;

                   for(i = 0; i < 2048; i++)

                   {

                       g_DSO1->usWaveBufTemp[i] = ADC3ConvertedValue[i+usCurPos];

                   }

              }

         }

         TIM_ClearITPendingBit(TIM8, TIM_IT_Update);

     }

}

变量标志g_usFirstTimeIRQFlag的作用就是代码前面的注释,第一次进入TIM8中断执行退出操作,因为定时器更新中断有个小bug,一旦初始化了定时器并且使能中断就会有立即进入一次中断的情况,为了防止这种情况而做的这个变量。

定时器中断里面最重要的功能还是从10240大小的缓冲区里面提取2048个采样数据,代码里面分为三种情况进行实现:

1. 当前的位置 < 1024。

2. 1024 <= 当前的位置 <= 10240 - 1024(9216)。

3. 当前位置 > 10240 - 1024(9216)。

具体实现需要大家拿笔实际计算下,捋一遍基本就都明白了,我们这里就不做说明了。

9.3   总结

本章为大家讲解的普通触发更适合做波形故障记录,如果波形触发速度太快了,有点处理不过来,因为我们需要记录2048个数据,大家可以根据实际需要调整记录的大小。

原文地址:https://www.cnblogs.com/armfly/p/9258897.html

时间: 2024-08-29 15:36:43

【安富莱二代示波器教程】第9章 示波器设计—自动触发和普通触发的相关文章

【安富莱二代示波器教程】第2章示波器操作说明及其介绍

教程完整下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=45785 第2章       示波器操作说明及其介绍 本章节主要讲解示波器的操作说明及其相关介绍. 2.1  示波器使用前注意事项 2.2  示波器的按键操作说明 2.3  示波器滑动操作说明 2.4  对话框操作说明 2.5  示波器功能介绍 2.6  波形显示效果 2.8   2048点浮点FFT计算的幅频显示 2.8   水平测量和垂直测量功能 2.9   普通触发

【安富莱二代示波器教程】第1章 示波器基础知识

第1章   示波器基础知识 本章的内容整理自网络,主要讲解示波器的基础知识.如果初学的话非常有必要对这部分知识有一个了解.因为示波器是硬件调试必不可少的设备. 1.1 什么是示波器 1.2 示波器的发展史 1.3 示波器的基础概念 1.4 触发系统 1.5 李沙育图形 1.6 窗函数选择 1.7 总结 1.1  什么是示波器 示波器是形象地显示信号幅度随时间变化的波形显示仪器,是一种综合的信号特性测试仪,是电子测量仪器的基本种类. 自然界运行着各种形式的正弦波,比如海浪.地震.声波.爆破.空气中

【安富莱】RTX嵌入式操作系统教程发布,支持F103,F407和F429,含81个配套例程(2017-10-17)

前言说明:1. 首先感谢大家对我们安富莱电子一年来的支持,2016年我们会再接再厉推出更好的教程. 2. 估计也有网友会问RTX的优势在那里,针对这个问题,教程中第一章分为6条专门回答了这个问题,有兴趣可以看看.    这些优势里面最欣赏的还是这条,基本KEIL在每个中间件里面都说了类似的话,    The library handles the low-level USB requests without the need to write the hardware layer code.  

【安富莱】各种开发板和模块的资料下载汇总贴(2019-07-30)

开发板类: 1.STM32-V7开发板(2019-07-28更新): 详情看此贴:https://www.cnblogs.com/armfly/p/11255864.html 2.STM32-V6开发板(2019-07-28更新): (1)[安富莱]V6,V5开发板用户手册,重在BSP驱动包设计方法,HAL库的框架学习,授人以渔 https://www.cnblogs.com/armfly/p/11254157.html (2)  链接:https://pan.baidu.com/s/1beZV

mySQL教程 第1章 数据库设计

第1章 数据库设计 E-R设计 很多同学在学SQL语句时,觉得非常困难,那是因为你在学一个你根本不了解的数据库,数据库中的表不是你设计的,表与表之间的关系你不明白.因此在学SQL语句之前,先介绍一下数据库设计. 下面举例说明数据库设计: 学校需要开发一个系统记录有学生.课程和成绩信息.数据库如何设计? 这里面涉及到两个实体,学生表.课程,这些表为实体表. 这些表之间有什么关系呢?.学生考试出成绩,成绩记录在成绩表. 一个学生可以参加多门课程,关系是1对多. 数据库设计实例 设计数据库和表 安装m

【安富莱专题教程第3期】开发板搭建Web服务器,利用花生壳让电脑和手机可以外网远程监控

说明:1.  开发板Web服务器的设计可以看我们之前发布的史诗级网络教程:链接.2.  需要复杂些的Web设计模板,可以使用我们V6开发板发布的综合Demo:链接.3.  教程中使用的是花生壳免费版,免费版仅支持电信用户,每个月1GB的流量,实际测试几天,稳定性还行.收费版没有这些限制.4.  现在已经用了快两年的花生壳收费版,比较稳定,基本没有死机现象.5.  不管是免费版本的花生壳还是收费版的,有时候会提示需要实名认证,可以不用管.现在还没有强制必须执行.如果长期使用的话,建议做一下认证,认

【安富莱专题教程第5期】工程调试利器RTT实时数据传输组件,替代串口调试,速度飞快,可以在中断和多任务中随意调用

说明:1.串口作为经典的调试方式已经存在好多年了,缺点是需要一个专门的硬件接口.现在有了SEGGER的RTT(已经发布有几年了),无需占用系统额外的硬件资源,而且速度超快,是替代串口调试的绝佳方式.2.RTT的API可以在中断和多任务环境中正常调用.并且JLINK处于MDK或者IAR的调试状态,RTT功能依然可以正常使用.最重要的是速度非常快,普通的JLINK也可以飙到几百KB/S.3.当前版本的JLINK都支持此功能,含V8,V9和V10.4.RTT Viewer小软件支持多个虚拟端口消息展示

【安富莱专题教程第1期】基于STM32的硬件RGB888接口实现emWin的快速刷新方案,32位色或24

说明:1. 首先感谢ST终于推出了ARGB格式的emWin库,可谓千呼万唤始出来,使用STM32的硬件RGB888接口刷新图片慢的问题终于得到解决.2. 这个问题由来已久,是之前为我们的STM32-V6板子制作emWin模板时发现的.V6板子的硬件配置是STM32F429BIT6 + 32位带宽的SDRAM + 硬件RGB888接口.实际测试中发现,将F429配置为16位色的RGB565,刷新800*480分辨率的图片可以做到15ms左右一帧,而测试24位色的RGB888或者32位色的ARGB8

【安富莱专题教程第2期】uC/Probe简易使用说明,含MDK和IAR,支持F103,F407和F429开发板

说明:1. 在uCOS工程调试时,这个软件还是非常给力的,方便查看各种信息,可以认为是MDK或者IAR调试功能的图形化版本,使用JLINK连接可以随时连接查看,无需目标端代码.2. 当前教程中,我们使用的是教育版,这个版本可以免费使用,但部分功能有限制.3. 教程是采用的是 JLINK 实现开发板与 uC/Probe 的连接通信.4. 其它需要注意的问题,在专题教程开头进行了说明.================================专题教程下载: 专题教程第2期:uC-Probe简易