当前位置:   article > 正文

基于STM32F103RCT6的简易示波器详解(ADC,TIM,LCD)_stem32f103rct6控制lcd

stem32f103rct6控制lcd

最近在做2019年的电赛D题,其中有一个要求是‘“自动测量并显示该放大器的频幅特性曲线。显示上限频率值,相对误 差的绝对值不超过 25%”  所以学习了一下简易示波器的制作

目录

硬件部分

器材

原理

软件部分(注释详解)

ADC.c部分

ADC.h

TIMER.c部分

TIMER.h部分

main部分

文件需知

实际调测图片

1.等倍率放大

2.正常状况

提示(讲解):

最重要的一点:

资源分享:


硬件部分

器材:STM32F103RCT6(正点原子mini版)  +   输入信号源(我采用了学校的信号发生器)  若干杜邦线  调试器(ST-link  J-link  或者CMSIS-DAP都行)

原理:采用STM32F103RCT6的ADC采集功能,获取并显示输入信号的电压在TFTLCD显示屏上,根据功能需要,做出不同变化。

软件部分(注释详解)

ADC.c部分

  1. #include "stm32f10x.h" // Device header
  2. /*基础配置*/
  3. #define GPIO_Pin_ADC GPIO_Pin_1
  4. #define GPIO_Port_ADC GPIOA
  5. #define RCC_Clock RCC_APB2Periph_GPIOA
  6. #define ADC_Use ADC1
  7. #define RCC_ADC RCC_APB2Periph_ADC1
  8. /*ADC初始化*/
  9. /**************************************
  10. * 函 数 名: ADC_init
  11. * 功能说明: ADC初始化
  12. * 形 参: 无
  13. * 返 回 值: 无
  14. *************************************/
  15. void ADC_init(void)
  16. {
  17. RCC_APB2PeriphClockCmd (RCC_Clock|RCC_APB2Periph_ADC1,ENABLE); //时钟使能
  18. GPIO_InitTypeDef GPIO_InitStructure;
  19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN ; //模拟输入模式
  20. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_ADC; //GPIO口配置
  21. GPIO_Init (GPIO_Port_ADC,&GPIO_InitStructure); //GPIO配置完成
  22. ADC_DeInit(ADC_Use);
  23. RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 时钟分频
  24. ADC_InitTypeDef ADC_InitStructure;
  25. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ; //独立模式
  26. ADC_InitStructure.ADC_ContinuousConvMode = DISABLE ; //单次转换模式
  27. ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式
  28. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;//转换由软件启动而不是外部
  29. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ; //右对齐
  30. ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数量1
  31. ADC_Init (ADC_Use,&ADC_InitStructure);
  32. ADC_Cmd(ADC_Use,ENABLE ); //ADC使能
  33. ADC_ResetCalibration(ADC_Use); //复位校准
  34. while(ADC_GetResetCalibrationStatus(ADC_Use)); //等待复位校准结束
  35. ADC_StartCalibration(ADC_Use);
  36. while(ADC_GetCalibrationStatus(ADC_Use)); //等待校准结束
  37. }
  38. /*获取模拟量*/
  39. /**************************************
  40. * 函 数 名: Get_A
  41. * 功能说明: 获取模拟量 用于转换成电压
  42. * 形 参: 无
  43. * 返 回 值: 模拟量
  44. *************************************/
  45. uint16_t Get_A(uint8_t ADC_Channel)
  46. {
  47. ADC_RegularChannelConfig(ADC_Use, ADC_Channel, 1, ADC_SampleTime_239Cycles5 ); //ADC_Use,ADC通道,采样时间为239.5周期
  48. ADC_SoftwareStartConvCmd(ADC_Use, ENABLE); //使能指定的ADC_Use的软件转换启动功能
  49. while(!ADC_GetFlagStatus(ADC_Use, ADC_FLAG_EOC ));//等待转换结束
  50. return ADC_GetConversionValue(ADC_Use); //返回最近一次ADC_Use规则组的转换结果
  51. }

ADC.h

  1. #ifndef __adc_H
  2. #define __adc_H
  3. void ADC_init(void);
  4. uint16_t Get_A(uint8_t ADC_Channel);
  5. #endif

TIMER.c部分

  1. #include "stm32f10x.h" // Device header
  2. /*基础配置*/
  3. #define TIM_Use TIM3 //(如果这里的TIM改了,下面的中断函数也要需要改)
  4. #define RCC_TIM RCC_APB1Periph_TIM3
  5. #define IRQn_TIM TIM3_IRQn
  6. /*定时器初始化*/
  7. /**************************************
  8. * 函 数 名: TIM_Init
  9. * 功能说明:定时器初始化
  10. * 形 参: arr
  11. * 形 参: psc
  12. * 返 回 值: 无
  13. *************************************/
  14. void TIM_Init(u16 arr,u16 psc)
  15. {
  16. RCC_APB1PeriphClockCmd (RCC_TIM ,ENABLE); //定时器时钟使能
  17. TIM_TimeBaseInitTypeDef TIM_InitStructure;
  18. TIM_InitStructure.TIM_Period = arr; //计数值
  19. TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上计数模式
  20. TIM_InitStructure.TIM_Prescaler = psc; // 预分频,此值+1为分频的除数
  21. TIM_InitStructure .TIM_ClockDivision = 0 ; //时钟因子
  22. TIM_TimeBaseInit (TIM_Use,&TIM_InitStructure ); // 将配置应用到定时器中
  23. NVIC_InitTypeDef NVIC_InitStructure; //中断配置
  24. NVIC_InitStructure .NVIC_IRQChannel = IRQn_TIM ;
  25. NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级
  26. NVIC_InitStructure .NVIC_IRQChannelSubPriority = 3; // 设置相应优先级
  27. NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE ; // 通道使能
  28. NVIC_Init (&NVIC_InitStructure); // 应用配置
  29. TIM_ITConfig (TIM_Use,TIM_IT_Update ,ENABLE );//允许更新中断
  30. TIM_Cmd (TIM_Use,ENABLE); //定时器使能
  31. }
  32. /*定时器中断*/
  33. /**************************************
  34. * 函 数 名: TIM_Use_IRQHandler
  35. * 功能说明:定时器中断
  36. * 形 参: 无
  37. * 返 回 值: flag_of_IRQHandler//时钟是否中断的标志flag
  38. * 返 回 值: ms//记录数据个数(中断次数)
  39. *************************************/
  40. int flag_of_IRQHandler=1;//时钟是否中断的标志flag
  41. int ms=0;//记录数据个数(中断次数)
  42. void TIM3_IRQHandler(void) //TIM_Use中断
  43. {
  44. if (TIM_GetITStatus(TIM_Use, TIM_IT_Update) != RESET) //检查TIM_Use更新中断发生与否
  45. {
  46. TIM_ClearITPendingBit(TIM_Use, TIM_IT_Update ); //清除TIMx更新中断标志
  47. ms++;
  48. flag_of_IRQHandler=1;
  49. //每次中断 ms++ 电压数据量加一
  50. }
  51. }

TIMER.h部分

  1. #ifndef __TIMER_H
  2. #define __TIMER_H
  3. void TIM_Init(u16 arr,u16 psc);
  4. void TIM3_IRQHandler(void);
  5. #endif

main部分

  1. #include "stm32f10x.h" // Device header
  2. #include "delay.h"
  3. #include "lcd.h"
  4. #include "usart.h"
  5. #include "fontupd.h"//字库检查用
  6. #include "adc.h"
  7. #include "TIMER.h"
  8. #define lcd_width 240 //LCD的宽
  9. #define lcd_height 320 //LCD的高
  10. #define VCC_fatual 3300 //ADC基准电压 不一定都是3300mv
  11. /*画网格线和坐标轴*/
  12. /**************************************
  13. * 函 数 名: net
  14. * 功能说明: 画网格线和坐标轴
  15. * 形 参: 无
  16. * 返 回 值: 无
  17. *************************************/
  18. void net(void)
  19. {
  20. LCD_Clear(WHITE); //清屏
  21. Brush_Color=GREEN; //绿色笔刷画网格线
  22. for(int t = 5;t<lcd_height;t=t+20)//画竖着的网格
  23. {LCD_DrawLine(0, t, lcd_height, t);}
  24. for(int t = 5;t<lcd_height;t=t+20)//画横着的网格
  25. {LCD_DrawLine(t,0, t,lcd_height );}
  26. Brush_Color=RED;//红色笔刷画坐标轴
  27. LCD_Draw_Circle(10,lcd_height-10,2);//用圆圈标注原点
  28. //为了凸显出坐标轴 坐标轴离LCD的最大宽和最大高都差10
  29. LCD_DrawLine(10,0,10,lcd_height );//y轴
  30. LCD_DrawLine(0,lcd_height-10,lcd_width,lcd_height-10 );//x轴
  31. Show_Str(230,305,12,12,"T",12,0);
  32. Show_Str(0,0,12,12,"V",12,0);
  33. }
  34. //主函数
  35. int main(void)
  36. {
  37. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  38. uart_init(9600); //串口初始化为9600
  39. LCD_Init();
  40. ADC_init();
  41. font_init();//检查汉字库
  42. TIM_Init(100-1,7200-1); //10Khz的计数频率(最小能测到10khz 低于10k会不准 1ms 一个点
  43. extern int flag_of_IRQHandler,ms;//adc.h中的中断标志和数据下标
  44. float D_arr[lcd_width];//存放D 电压数据的数组
  45. float X_arr[lcd_width];//用于计算的数据数组
  46. float max,min,difference; //电压量的最大值,最小值,最大最小的差值
  47. LCD_Clear(Background_Color);//清屏
  48. while(1)
  49. {
  50. if(flag_of_IRQHandler==1)//如果被中断 则记录一次数据
  51. {
  52. flag_of_IRQHandler = 0;//0 等待下次中断
  53. D_arr[ms] = Get_A(ADC_Channel_1)*VCC_fatual/4096;//模拟量换成数字量电压 以1mV为最小量度
  54. }
  55. if(ms>=lcd_width-10)//如果显示的个数达到要求,(建议:最大数据个数<=LDC宽度或者高度)
  56. {
  57. TIM_Cmd (TIM3,DISABLE);//定时器暂时关闭,先处理数据
  58. net();//画出网格线
  59. for(ms=0;ms<lcd_width-10;ms++)//进行最大值和最小值等处理
  60. {
  61. if(ms==0){max=D_arr[ms];min=D_arr[ms];}
  62. X_arr[ms]=D_arr[ms];
  63. if(D_arr[ms]>max){max=D_arr[ms];}
  64. if(D_arr[ms]<min){min=D_arr[ms];}
  65. }
  66. difference=max-min;//最大值和最小值的差值计算
  67. //最大值和最小值显示
  68. Brush_Color=BLACK;//设置画笔为黑色
  69. Show_Str(0,15,20,12,"max",12,0);
  70. LCD_ShowNum(0,27,max,4,12);
  71. Show_Str(0,lcd_height-30-12,20,12,"min",12,0);
  72. LCD_ShowNum(0,lcd_height-30,min,4,12);
  73. //显示处理 看需要等倍率放大显示,还是按照最大值为3.3v显示(二选一)
  74. for(ms=0;ms<lcd_width-10;ms++)
  75. {
  76. //倍率可调 这边选了280 是调试出来的,比较适合我使用的 可以放小点或者放大点
  77. //X_arr[ms]=D_arr[ms]/difference*280;//等倍率放大(比如3.3V能到LCD最顶部, 换成最高2V的波也能到最顶部
  78. X_arr[ms]=D_arr[ms]/10/330*(lcd_height-10);//不按倍率放大 最顶部3.3v 最下面是0v 其余在中间
  79. }
  80. for(ms=0;ms<lcd_width-10;ms++)//数据显示在LCD上
  81. {
  82. LCD_DrawLine(ms+10,lcd_height-10-X_arr[ms],ms+11,lcd_height-10-X_arr[ms+1]);
  83. }
  84. ms = 0;//重新进行数据获取
  85. TIM_Cmd (TIM3,ENABLE);//定时器重新启动
  86. }
  87. } }

文件需知

需要"delay.h"  "lcd.h"   "usart.h"   "fontupd.h"  "adc.h"  "TIMER.h"这几个文件

其中delay.h   lcd   usart  fontupd是采用正点原子的,请自行前往原子哥的网站下载。

delay 延时用    lcd  用于显示lcd屏    usart  用于串口通信  fontupd是汉字库文件(如果不需要显示汉字,可以不要)

adc和TIMER是这篇文章发出来的部分   

实际调测图片

1.等倍率放大(在测量范围内(设定的是0~3.3v) 都能把把最高值显示在最上方,最低值呈现在最下方,等比例放大)

 采用10khz的正弦波,高电平1v  低电平为0v的显示图像

                                         采用10khz的正弦波,高电平2v  低电平为0v的显示图像




2.正常状况(最上方是3.3v  最下方0v  其他的显示在中间)

采用10khz的正弦波,高电平1.1v  低电平为0v的显示图像

 采用10khz的正弦波,高电平2.2v  低电平为0v的显示图像 采用10khz的正弦波,高电平3.3v  低电平为0v的显示图像

提示(讲解):

1.在main文件里  定义了lcd_width和lcd_height  是LCD的宽高,不同尺寸的LCD需要更改

2.如果不要显示汉字的话,可以删去fontupd.h文件和font_init()等语句  就应该不会报错了

3.在main文件里  VCC_fatual是测量的基准电压  不一定都是3.3V  需要根据实际更改,不然显示会有问题的

4.在main文件里  用了TIM_Init(100-1,7200-1);  这里使用的是10khz的频率,所以本简易示波器,适用范围需要大于10khz  本人实测能到200khz左右,200k以上会失真比较严重,10k一下测不准

如果对频率有要求的,自行调整TIM_Init(100-1,7200-1); 的数值

5.adc和timer文件  只需要把上面的基础配置部分更改就可以用,注意TIMER.c中私用TIM3  如果更改了TIM_USE  记得把下面的TIM3_IRQHandler函数也更改一下  

  1. //adc.c
  2. /*基础配置*/
  3. #define GPIO_Pin_ADC GPIO_Pin_1
  4. #define GPIO_Port_ADC GPIOA
  5. #define RCC_Clock RCC_APB2Periph_GPIOA
  6. #define ADC_Use ADC1
  7. #define RCC_ADC RCC_APB2Periph_ADC1
  8. //TIMER.c
  9. /*基础配置*/
  10. #define TIM_Use TIM3
  11. #define RCC_TIM RCC_APB1Periph_TIM3
  12. #define IRQn_TIM TIM3_IRQn

最重要的一点:

如果看完上面,觉得对你有帮助的,麻烦点个赞,谢谢!!!祝您生活愉快!

资源分享:

如果需要源文件,可以私信我的账号 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/690740
推荐阅读
相关标签
  

闽ICP备14008679号