你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32定时器+ADC制作简易示波器

[复制链接]
STMCU小助手 发布时间:2022-8-22 18:17
一、方案
先用 LCD制作 一个格子图形,然后定时器触发ADC采样。再将采集到的数据绘制成曲线显示在格子图形上,通过读取 图形上的点来测量信号。 本文使用的是原子哥的F103ZET6的战舰开发板。

二、具体实施

1.LCD初步显示
LCD的配置代码我是直接复制原子哥的,直接调用了它里面的函数。
因为我的屏幕是480*800,所以为了布局采用了横屏显示。
首先根据方案,我要先制作一个格子图。并且为了观察波形的数据,加入了时间和数值显示。绘制格子是在函数display里面。格子的话时 每隔20绘制一条线,可以根据 自己情况 修改。

  1. void display(void)
  2. {
  3.         uint16_t t;
  4.         LCD_Fill(0,0,800,340,WHITE);
  5.         POINT_COLOR=GREEN;
  6.         for(t  = 0;t<360;t=t+20)
  7.                 LCD_DrawLine(0, t, 800, t);
  8.         for(t  = 0;t<800;t=t+20)
  9.                 LCD_DrawLine(t,0, t,340 );
  10.         POINT_COLOR=BLUE;
  11.         LCD_DrawLine(0, 180, 800, 180);
  12.         LCD_DrawLine(400,0, 400,340 );
  13.         LCD_ShowNum(84,376,k,2,24);
  14. }
  15. void main(void)
  16. {
  17.           ...
  18.         LCD_Init();
  19.         LCD_Clear(WHITE);
  20.         LCD_Display_Dir(1);
  21.         POINT_COLOR=BLUE;
  22.         LCD_ShowString(60,400,24*3,24,24,"t1:");
  23.         LCD_ShowString(24,376,24*2,24,24,"move:");
  24.         LCD_ShowString(60,424,24*3,24,24,"t2:");
  25.         LCD_ShowString(0,448,24*4,24,24,"|t1-t2|:");
  26.         LCD_ShowString(146,400,24*2,24,24,"ms");
  27.         LCD_ShowString(146,424,24*2,24,24,"ms");
  28.         LCD_ShowString(146,448,24*2,24,24,"ms");
  29.         LCD_ShowString(260,400,24*3,24,24,"V1:");
  30.         LCD_ShowString(260,424,24*3,24,24,"V2:");
  31.         LCD_ShowString(200,448,24*4,24,24,"|V1-V2|:");
  32.         LCD_ShowString(358,400,24*3,24,24,"mV");
  33.         LCD_ShowString(358,424,24*3,24,24,"mV");
  34.         LCD_ShowString(358,448,24*3,24,24,"mV");
  35.         display ();
  36.         ...
  37. }
复制代码

效果展示:

b99f471e6f0f4adba6a01dff2c858fa3.jpg

2.ADC配置

接下来就是ADC配置 。先上代码:

  1. void adc_init(void)
  2. {
  3.         GPIO_InitTypeDef adc1io;
  4.         ADC_InitTypeDef adc1;
  5.         
  6.         RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
  7.         
  8.         adc1io.GPIO_Mode = GPIO_Mode_AIN ;
  9.         adc1io.GPIO_Pin = GPIO_Pin_1 ;
  10.         GPIO_Init (GPIOA,&adc1io);
  11.         
  12.         ADC_DeInit(ADC1);
  13.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  14.         
  15.         adc1.ADC_Mode =  ADC_Mode_Independent   ;
  16.         adc1.ADC_ContinuousConvMode =  ENABLE  ;//连续转换模式
  17.         adc1.ADC_ScanConvMode  = DISABLE;//扫描模式 ,在于单通道还是双通道
  18.         adc1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None  ;//转换由软件启动而不是外部
  19.         adc1.ADC_DataAlign = ADC_DataAlign_Right  ;
  20.         adc1.ADC_NbrOfChannel = 1;
  21.         ADC_Init (ADC1,&adc1);
  22.         
  23.         ADC_Cmd(ADC1,ENABLE );
  24.         ADC_ResetCalibration(ADC1);
  25.         while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
  26.         ADC_StartCalibration(ADC1);
  27.         while(ADC_GetCalibrationStatus(ADC1)); //等待校 AD 准结束

  28.         ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5 );//通道 1,规则采样顺序值为 1,采样时间为 239.5 周期
  29.         ADC_SoftwareStartConvCmd  (ADC1,ENABLE);//使能软件转换功能
  30.         
  31. }

  32. uint16_t Get_adc()
  33. {
  34.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
  35.         return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果
  36. }

  37. uint16_t Get_adcAverage(uint8_t times)//最高255,最少3
  38. {
  39.         uint32_t t;
  40.         uint16_t a[255]={0};
  41.         int i,g1,g2;
  42.         for(i=0;i<times;i++)
  43.         {
  44.                 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
  45.                 a<i> = ADC_GetConversionValue(ADC1);
  46.         }
  47.         g1= g2  = a[0];
  48.         for(i = 0;i<times-1;i++)
  49.         {
  50.                 if(g1>a[i+1])g1 = a[i+1];//让 g1为最小值
  51.                 if(g2<a[i+1])g2 = a[i+1];//让 g2为最大值
  52.         }
  53.         for(i = 0;i<times;i++)
  54.         {
  55.                 t += a<i>;
  56.         }
  57.         return t = (t-g1-g2)/(times-2);
  58. }
  59. </i></i>
复制代码

ADC配置简单介绍:
因为只打算做一个通道,所以配置成独立模式 ,开启连续转换模式。这里 的ADC采集我打算放在 主函数里面,所以也是配置成由软件触发。 这里使用了ADC1的通道1。为了采样准确率上升我把 采样时间 配置 239.5个周期 。因为是72M6分频,所以ADCCLK是12M。总转换时间公式是:Tcovn=采样时间+12.5 个周期,计算出来我采样一次时间是18us。计算这个只要是因为使用定时器触发,防止触发时间低于采样时间。
然后我编了一个提高采样精准的函数:连续采样多次(至少3次),去掉最大值和最小值,然后取平均。

3.定时器配置
同样先上代码:

  1. void TIM_Init(void)
  2. {
  3.         TIM_TimeBaseInitTypeDef Tim3;
  4.         NVIC_InitTypeDef NVIC_Tim;


  5.         RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3 ,ENABLE);
  6.         Tim3.TIM_Period = 10;
  7.         Tim3.TIM_CounterMode = TIM_CounterMode_Up ;
  8.         Tim3.TIM_Prescaler = 7199;
  9.         Tim3 .TIM_ClockDivision = TIM_CKD_DIV1 ;
  10.         TIM_TimeBaseInit (TIM3,&Tim3 );
  11.                
  12.         TIM_ITConfig (TIM3,TIM_IT_Update ,ENABLE );//允许更新中断
  13.         
  14.         NVIC_Tim .NVIC_IRQChannel = TIM3_IRQn ;
  15.         NVIC_Tim .NVIC_IRQChannelPreemptionPriority = 0;
  16.         NVIC_Tim .NVIC_IRQChannelSubPriority = 0;
  17.         NVIC_Tim .NVIC_IRQChannelCmd = ENABLE ;
  18.         NVIC_Init (&NVIC_Tim);
  19.         
  20.         TIM_Cmd (TIM3,ENABLE);

  21. }


  22. void TIM3_IRQHandler(void)   //TIM3中断
  23. {
  24.         if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
  25.                 {
  26.                         Z=1;
  27.                         TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志
  28.                         ms++;
  29.                 }
  30. }
复制代码

我这里是打算 1ms采集一个点 ,所以我的采集信号频率只能在 1KHz以下,由于是绘图,所以在一个周期采集越多的点效果回越好。
因为中断不好执行占用CPU过长的任务,所以我在主函数 里面使用ADC采集,这里 只是 给它一个判断标志。

5.按键配置

为了测量周期和峰峰值,我用可移动的两根线在波形上测量。这里就需要 用到按键。

  1. void EXTI0_IRQHandler(void)
  2. {
  3.         for(time = 0;time<40000;time++);
  4.         if(GPIO_ReadInputDataBit (WKUP_GPIO ,WKUP_GPIO_PIN ) == SET)                  //WK_UP按键
  5.         {                                 
  6.                 mode ++;
  7.                 if(mode ==5)mode  =0;
  8.         }
  9.         EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
  10. }

  11. void EXTI2_IRQHandler(void)
  12. {
  13.         for(time = 0;time<40000;time++);
  14.         if(GPIO_ReadInputDataBit (KEY2_GPIO ,KEY2_GPIO_PIN ) == RESET)
  15.         {
  16.                 node  =1;
  17.         }
  18.         EXTI_ClearITPendingBit(EXTI_Line2); //清除 LINE2 上的中断标志位
  19. }

  20. void EXTI3_IRQHandler(void)
  21. {
  22.         for(time = 0;time<40000;time++);
  23.         if(GPIO_ReadInputDataBit (KEY1_GPIO ,KEY1_GPIO_PIN ) == RESET)
  24.         {
  25.                 k++;
  26.                 if(k>10)k=1;
  27.                
  28.         }
  29.         EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE3 上的中断标志位
  30. }

  31. void EXTI4_IRQHandler(void)
  32. {
  33.         for(time = 0;time<40000;time++);
  34.         if(GPIO_ReadInputDataBit (KEY0_GPIO ,KEY0_GPIO_PIN ) == RESET)
  35.         {
  36.                 node = 2;
  37.         }
  38.         EXTI_ClearITPendingBit(EXTI_Line4); //清除 LINE4 上的中断标志位
  39. }
复制代码

这里也是通过标志来让单片机运行相应功能。
KEY_UP:第一次按下 :暂停波形,选定第一个竖直线。
第二次按下 :选定第二个数值线。
第三次按下 :选定第一个水平线。
第四次按下 :选定第二 个水平线。
第五次按下 :恢复运行波形。
KEY1:修改线移动的格数,这里设置是1~10。
KEY0:**当选定水平线时:向下移动k。
当选定竖直线时:向左移动k。
KEY2:当选定水平线时:向上移动k。
当选定竖直线时:向右移动k。

6.整合
首先是先采集并且 绘制波形 ,这里的 方法 是1ms采集一个点并且采集800个点后开始绘制波形。

  1. void  main(void)
  2. {
  3.         ...
  4.         while(1)
  5.         {
  6.                 while( mode  ==0)
  7.                 {
  8.                         if(Z==1)
  9.                         {
  10.                                 Z = 0;
  11.                                 adcx = Get_adcAverage (5);
  12.                                 temp[ms] = adcx*(3.3/4096)*100;
  13.                                 while(temp[ms]>330)
  14.                                 {
  15.                                         adcx = Get_adcAverage (3);
  16.                                         temp[ms] =adcx*(3.3/4096)*100;
  17.                                 }
  18.                         }
  19.                         if(ms == 799)
  20.                         {
  21.                                 TIM_Cmd (TIM3,DISABLE);
  22.                                 display ();
  23.                                 for(ms=0;ms<800;ms++)xemp[ms]=temp[ms];
  24.                                 POINT_COLOR=BLACK;
  25.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  26.                                 ms = 0;
  27.                                 TIM_Cmd (TIM3,ENABLE);
  28.                         }
  29.                 }
  30.         ...
  31.         }
  32. }
复制代码

这里的话就是1ms触发定时器中断,然后Z置1,并且变量ms++。Z为 1开启ADC采集 ,将采集的数据存储在数组temp中。当ms为 799时,暂时关闭定时器,将数组temp中的数据转移到xemp中,再根据 xemp绘制曲线(转移到xemp中是为了保证后面绘制的曲线数据 在同一采集周期内)。因为波形需要更新 所以先调用 display更新下次波形。然后调用LCD_DrawLine绘制波形。这样就能将上个0.8s的信号数据绘制出来。将 变为 0并且再次开启定时器。至于要把这些放在while(mode==0)里,是为了后面服务的。
再看后面的代码:

  1.        if(mode == 1)
  2.                 {
  3.                         TIM_Cmd (TIM3,DISABLE);
  4.                         while(mode == 1)
  5.                         {
  6.                                 if(node ==1)
  7.                                 {
  8.                                         display  ();
  9.                                         POINT_COLOR=BLACK;
  10.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  11.                                         node = 0;
  12.                                         POINT_COLOR=RED;
  13.                                         X=X+k;
  14.                                         if(X  >  800)X = 0;
  15.                                         LCD_DrawLine(X, 0, X, 340);
  16.                                         LCD_ShowNum(110,400,(u32)X,3,24);
  17.                                 }
  18.                                 if(node ==2)
  19.                                 {        
  20.                                         display  ();
  21.                                         POINT_COLOR=BLACK;
  22.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  23.                                         node = 0;
  24.                                         POINT_COLOR=RED;
  25.                                         X=X-k;
  26.                                         if(X >  800)X = 800;
  27.                                         LCD_DrawLine(X, 0, X, 340);
  28.                                         LCD_ShowNum(110,400,(u32)X,3,24);
  29.                                 }
  30.                         }
  31.                 }
  32.                 while(mode == 2)
  33.                 {
  34.                         if(node ==1)
  35.                         {
  36.                                 display  ();
  37.                                 POINT_COLOR=BLACK;
  38.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  39.                                 POINT_COLOR=RED;
  40.                                 LCD_DrawLine(X, 0, X, 340);
  41.                                 node = 0;
  42.                                 X1=X1+k;
  43.                                 if(X1  >  800)X1 = 0;
  44.                                 LCD_DrawLine(X1, 0, X1, 340);
  45.                                 LCD_ShowNum(110,424,(u32)X1,3,24);
  46.                         }
  47.                         if(node ==2)
  48.                         {        
  49.                                 display  ();
  50.                                 POINT_COLOR=BLACK;
  51.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  52.                                 POINT_COLOR=RED;
  53.                                 LCD_DrawLine(X, 0, X, 340);
  54.                                 node = 0;
  55.                                 X1=X1-k;
  56.                                 if(X1 >  800)X1 = 800;
  57.                                 LCD_DrawLine(X1, 0, X1, 340);
  58.                                 LCD_ShowNum(110,424,(u32)X1,3,24);
  59.                         }
  60.                         if(X1>X)LCD_ShowNum(110,448,(u32)X1-X,3,24);
  61.                         else LCD_ShowNum(110,448,(u32)X-X1,3,24);
  62.                 }
  63.                 while(mode == 3)
  64.                 {
  65.                         if(node ==1)
  66.                         {
  67.                                 display  ();
  68.                                 POINT_COLOR=BLACK;
  69.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  70.                                 node = 0;
  71.                                 POINT_COLOR=RED;
  72.                                 X=X+k;
  73.                                 if(X  >  340)X = 0;
  74.                                 LCD_DrawLine(0,X,  800,X);
  75.                                 LCD_ShowNum(310,400,(u32)(340-X)*10,4,24);
  76.                         
  77.                         }
  78.                         if(node ==2)
  79.                         {        
  80.                                 display  ();
  81.                                 POINT_COLOR=BLACK;
  82.                                 for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  83.                                 node = 0;
  84.         
  85.                                 POINT_COLOR=RED;
  86.                                 X=X-k;
  87.                                 if(X >  340)X = 340;
  88.                                 LCD_DrawLine(0,X,800,X);
  89.                                 LCD_ShowNum(310,400,(u32)(340-X)*10,4,24);
  90.                         }
  91.                 }
  92.                 if(mode == 4)
  93.                 {
  94.                         while(mode == 4)
  95.                         {
  96.                                 if(node ==1)
  97.                                 {
  98.                                         display  ();
  99.                                         POINT_COLOR=BLACK;
  100.                                         for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  101.                                         POINT_COLOR=RED;
  102.                                         LCD_DrawLine(0, X, 800, X);
  103.                                         node = 0;
  104.                                         X1=X1+k;
  105.                                         if(X1  >  340)X1 = 0;
  106.                                         LCD_DrawLine(0,X1, 800, X1);
  107.                                         LCD_ShowNum(310,424,(u32)(340-X1)*10,4,24);
  108.                                 
  109.                                 }
  110.                                 if(node ==2)
  111.                                 {        
  112.                                         display  ();
  113.                                         POINT_COLOR=BLACK;
  114.                                         for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
  115.                                         POINT_COLOR=RED;
  116.                                         LCD_DrawLine(0, X, 800, X);
  117.                                         node = 0;
  118.                                         X1=X1-k;
  119.                                         if(X1 >  340)X1 = 340;
  120.                                         LCD_DrawLine(0,X1, 800, X1);
  121.                                         LCD_ShowNum(310,424,(u32)(340-X1)*10,4,24);
  122.                                 }
  123.                         if(X1>X)LCD_ShowNum(310,448,(u32)(X1-X)*10,4,24);
  124.                                 else LCD_ShowNum(310,448,(u32)(X-X1)*10,4,24);
  125.                         }
  126.                         ms = 0;
  127.                         TIM_Cmd (TIM3,ENABLE);
  128.                 }
复制代码

这部分代码思路其实都一样,因 为要关闭和开启 定时器,所以再在mode为1和mode为4外面先加了if语句。这里介绍一下mode为一的情况:
当按下第一次WK_UP的时候,mode为1,主函数进入while(mode==1)的循环。当你不 进行人格 操作 时,此时LCD页面 时 静止的,因为 不会进入里面 的if语句,while相当于空内容。当你按下相应功能按键,比如 KEY0,变量node变为2,进入if:先将 node置0。然后调用 display,更新显示,绘制波形,显示线和数值。图中红线 既是选定 的线。可按KEY2和 KEY0移动,move表示移动的大小。其他mode差不多 是一样的情况。

c14573a952404f8392f73dec740cc2d6.jpg

7.实测效果
用信号发生器产生10Hz的峰峰值为2.8V的正弦波形(因为没搞采集负电压所以需要将低电平调为0):

118e016499214a02866080932ad306e0.jpg
f8ac5bf1882b43bf99c8febad0e990aa.jpg


5Hz1.5V方波:

124559555d994e3099156c37c2caafeb.jpg

1659cd6983924cfdb92431a3ae45d1b9.jpg

2Hz三角波1V

49768440b75d4d31befe273f8ef42edf.jpg

d1455b4732d74304a45e9d3f306b8220.jpg


总结
这个代码我也只是测了几组,不知道还有没有问题,有的话欢迎大家指出。 然后误差也有,这跟32采集的精准度以及对他的处理还有我设置的分辨率都有关系,再者这里是将采集的两个点直接连线连起来,所以图形也会有误差。不过这个代码可改进的 方面有很多,例如用DMA传输,改变触发采集 时间(应该只要保证采样时间小于触发时间就行) 等等 。



收藏 评论0 发布时间:2022-8-22 18:17

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版