赞
踩
最近在做2019年的电赛D题,其中有一个要求是‘“自动测量并显示该放大器的频幅特性曲线。显示上限频率值,相对误 差的绝对值不超过 25%。 ” 所以学习了一下简易示波器的制作
目录
- #include "stm32f10x.h" // Device header
-
- /*基础配置*/
- #define GPIO_Pin_ADC GPIO_Pin_1
- #define GPIO_Port_ADC GPIOA
- #define RCC_Clock RCC_APB2Periph_GPIOA
- #define ADC_Use ADC1
- #define RCC_ADC RCC_APB2Periph_ADC1
-
-
- /*ADC初始化*/
- /**************************************
- * 函 数 名: ADC_init
- * 功能说明: ADC初始化
- * 形 参: 无
- * 返 回 值: 无
- *************************************/
- void ADC_init(void)
- {
- RCC_APB2PeriphClockCmd (RCC_Clock|RCC_APB2Periph_ADC1,ENABLE); //时钟使能
-
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN ; //模拟输入模式
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_ADC; //GPIO口配置
- GPIO_Init (GPIO_Port_ADC,&GPIO_InitStructure); //GPIO配置完成
-
- ADC_DeInit(ADC_Use);
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 时钟分频
-
- ADC_InitTypeDef ADC_InitStructure;
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ; //独立模式
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE ; //单次转换模式
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;//转换由软件启动而不是外部
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ; //右对齐
- ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数量1
- ADC_Init (ADC_Use,&ADC_InitStructure);
-
- ADC_Cmd(ADC_Use,ENABLE ); //ADC使能
-
- ADC_ResetCalibration(ADC_Use); //复位校准
- while(ADC_GetResetCalibrationStatus(ADC_Use)); //等待复位校准结束
- ADC_StartCalibration(ADC_Use);
- while(ADC_GetCalibrationStatus(ADC_Use)); //等待校准结束
- }
- /*获取模拟量*/
- /**************************************
- * 函 数 名: Get_A
- * 功能说明: 获取模拟量 用于转换成电压
- * 形 参: 无
- * 返 回 值: 模拟量
- *************************************/
- uint16_t Get_A(uint8_t ADC_Channel)
- {
- ADC_RegularChannelConfig(ADC_Use, ADC_Channel, 1, ADC_SampleTime_239Cycles5 ); //ADC_Use,ADC通道,采样时间为239.5周期
- ADC_SoftwareStartConvCmd(ADC_Use, ENABLE); //使能指定的ADC_Use的软件转换启动功能
- while(!ADC_GetFlagStatus(ADC_Use, ADC_FLAG_EOC ));//等待转换结束
- return ADC_GetConversionValue(ADC_Use); //返回最近一次ADC_Use规则组的转换结果
- }
- #ifndef __adc_H
- #define __adc_H
-
- void ADC_init(void);
- uint16_t Get_A(uint8_t ADC_Channel);
-
-
- #endif
- #include "stm32f10x.h" // Device header
-
- /*基础配置*/
- #define TIM_Use TIM3 //(如果这里的TIM改了,下面的中断函数也要需要改)
- #define RCC_TIM RCC_APB1Periph_TIM3
- #define IRQn_TIM TIM3_IRQn
-
- /*定时器初始化*/
- /**************************************
- * 函 数 名: TIM_Init
- * 功能说明:定时器初始化
- * 形 参: arr
- * 形 参: psc
- * 返 回 值: 无
- *************************************/
- void TIM_Init(u16 arr,u16 psc)
- {
-
- RCC_APB1PeriphClockCmd (RCC_TIM ,ENABLE); //定时器时钟使能
-
- TIM_TimeBaseInitTypeDef TIM_InitStructure;
- TIM_InitStructure.TIM_Period = arr; //计数值
- TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上计数模式
- TIM_InitStructure.TIM_Prescaler = psc; // 预分频,此值+1为分频的除数
- TIM_InitStructure .TIM_ClockDivision = 0 ; //时钟因子
- TIM_TimeBaseInit (TIM_Use,&TIM_InitStructure ); // 将配置应用到定时器中
-
- NVIC_InitTypeDef NVIC_InitStructure; //中断配置
- NVIC_InitStructure .NVIC_IRQChannel = IRQn_TIM ;
- NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级
- NVIC_InitStructure .NVIC_IRQChannelSubPriority = 3; // 设置相应优先级
- NVIC_InitStructure .NVIC_IRQChannelCmd = ENABLE ; // 通道使能
- NVIC_Init (&NVIC_InitStructure); // 应用配置
-
- TIM_ITConfig (TIM_Use,TIM_IT_Update ,ENABLE );//允许更新中断
- TIM_Cmd (TIM_Use,ENABLE); //定时器使能
- }
-
-
- /*定时器中断*/
- /**************************************
- * 函 数 名: TIM_Use_IRQHandler
- * 功能说明:定时器中断
- * 形 参: 无
- * 返 回 值: flag_of_IRQHandler//时钟是否中断的标志flag
- * 返 回 值: ms//记录数据个数(中断次数)
- *************************************/
- int flag_of_IRQHandler=1;//时钟是否中断的标志flag
- int ms=0;//记录数据个数(中断次数)
-
- void TIM3_IRQHandler(void) //TIM_Use中断
- {
- if (TIM_GetITStatus(TIM_Use, TIM_IT_Update) != RESET) //检查TIM_Use更新中断发生与否
- {
- TIM_ClearITPendingBit(TIM_Use, TIM_IT_Update ); //清除TIMx更新中断标志
- ms++;
- flag_of_IRQHandler=1;
- //每次中断 ms++ 电压数据量加一
- }
- }
- #ifndef __TIMER_H
- #define __TIMER_H
-
- void TIM_Init(u16 arr,u16 psc);
- void TIM3_IRQHandler(void);
-
- #endif
- #include "stm32f10x.h" // Device header
- #include "delay.h"
- #include "lcd.h"
- #include "usart.h"
- #include "fontupd.h"//字库检查用
- #include "adc.h"
- #include "TIMER.h"
-
- #define lcd_width 240 //LCD的宽
- #define lcd_height 320 //LCD的高
- #define VCC_fatual 3300 //ADC基准电压 不一定都是3300mv
- /*画网格线和坐标轴*/
- /**************************************
- * 函 数 名: net
- * 功能说明: 画网格线和坐标轴
- * 形 参: 无
- * 返 回 值: 无
- *************************************/
- void net(void)
- {
- LCD_Clear(WHITE); //清屏
- Brush_Color=GREEN; //绿色笔刷画网格线
-
- for(int t = 5;t<lcd_height;t=t+20)//画竖着的网格
- {LCD_DrawLine(0, t, lcd_height, t);}
- for(int t = 5;t<lcd_height;t=t+20)//画横着的网格
- {LCD_DrawLine(t,0, t,lcd_height );}
-
- Brush_Color=RED;//红色笔刷画坐标轴
- LCD_Draw_Circle(10,lcd_height-10,2);//用圆圈标注原点
- //为了凸显出坐标轴 坐标轴离LCD的最大宽和最大高都差10
- LCD_DrawLine(10,0,10,lcd_height );//y轴
- LCD_DrawLine(0,lcd_height-10,lcd_width,lcd_height-10 );//x轴
- Show_Str(230,305,12,12,"T",12,0);
- Show_Str(0,0,12,12,"V",12,0);
- }
-
- //主函数
-
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
- uart_init(9600); //串口初始化为9600
- LCD_Init();
- ADC_init();
- font_init();//检查汉字库
-
- TIM_Init(100-1,7200-1); //10Khz的计数频率(最小能测到10khz 低于10k会不准 1ms 一个点
-
- extern int flag_of_IRQHandler,ms;//adc.h中的中断标志和数据下标
- float D_arr[lcd_width];//存放D 电压数据的数组
- float X_arr[lcd_width];//用于计算的数据数组
- float max,min,difference; //电压量的最大值,最小值,最大最小的差值
-
- LCD_Clear(Background_Color);//清屏
- while(1)
- {
-
- if(flag_of_IRQHandler==1)//如果被中断 则记录一次数据
- {
- flag_of_IRQHandler = 0;//归0 等待下次中断
- D_arr[ms] = Get_A(ADC_Channel_1)*VCC_fatual/4096;//模拟量换成数字量电压 以1mV为最小量度
- }
- if(ms>=lcd_width-10)//如果显示的个数达到要求,(建议:最大数据个数<=LDC宽度或者高度)
- {
- TIM_Cmd (TIM3,DISABLE);//定时器暂时关闭,先处理数据
- net();//画出网格线
- for(ms=0;ms<lcd_width-10;ms++)//进行最大值和最小值等处理
- {
- if(ms==0){max=D_arr[ms];min=D_arr[ms];}
- X_arr[ms]=D_arr[ms];
- if(D_arr[ms]>max){max=D_arr[ms];}
- if(D_arr[ms]<min){min=D_arr[ms];}
- }
- difference=max-min;//最大值和最小值的差值计算
-
- //最大值和最小值显示
- Brush_Color=BLACK;//设置画笔为黑色
- Show_Str(0,15,20,12,"max",12,0);
- LCD_ShowNum(0,27,max,4,12);
- Show_Str(0,lcd_height-30-12,20,12,"min",12,0);
- LCD_ShowNum(0,lcd_height-30,min,4,12);
-
-
- //显示处理 看需要等倍率放大显示,还是按照最大值为3.3v显示(二选一)
- for(ms=0;ms<lcd_width-10;ms++)
- {
- //倍率可调 这边选了280 是调试出来的,比较适合我使用的 可以放小点或者放大点
- //X_arr[ms]=D_arr[ms]/difference*280;//等倍率放大(比如3.3V能到LCD最顶部, 换成最高2V的波也能到最顶部
- X_arr[ms]=D_arr[ms]/10/330*(lcd_height-10);//不按倍率放大 最顶部3.3v 最下面是0v 其余在中间
- }
- for(ms=0;ms<lcd_width-10;ms++)//数据显示在LCD上
- {
- LCD_DrawLine(ms+10,lcd_height-10-X_arr[ms],ms+11,lcd_height-10-X_arr[ms+1]);
- }
- ms = 0;//重新进行数据获取
- TIM_Cmd (TIM3,ENABLE);//定时器重新启动
- }
- } }
-
需要"delay.h" "lcd.h" "usart.h" "fontupd.h" "adc.h" "TIMER.h"这几个文件
其中delay.h lcd usart fontupd是采用正点原子的,请自行前往原子哥的网站下载。
delay 延时用 lcd 用于显示lcd屏 usart 用于串口通信 fontupd是汉字库文件(如果不需要显示汉字,可以不要)
adc和TIMER是这篇文章发出来的部分
采用10khz的正弦波,高电平1v 低电平为0v的显示图像
采用10khz的正弦波,高电平2v 低电平为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函数也更改一下
- //adc.c
- /*基础配置*/
- #define GPIO_Pin_ADC GPIO_Pin_1
- #define GPIO_Port_ADC GPIOA
- #define RCC_Clock RCC_APB2Periph_GPIOA
- #define ADC_Use ADC1
- #define RCC_ADC RCC_APB2Periph_ADC1
-
- //TIMER.c
- /*基础配置*/
- #define TIM_Use TIM3
- #define RCC_TIM RCC_APB1Periph_TIM3
- #define IRQn_TIM TIM3_IRQn
如果看完上面,觉得对你有帮助的,麻烦点个赞,谢谢!!!祝您生活愉快!
如果需要源文件,可以私信我的账号
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。