摘要
本设计是基于STM32F103RCT6为主控核心的数字示波器的设计。包AD转换,波形处理,LCD液晶显示模块及外围按键,完成了简单的示波器功能,可以实时采样显示波形、振幅大小和频率大小,本设计实现了电压信号包括周期信号和非周期信号波形的显示,实时采样速率最高可达为1M。采用显示器件是LCD(240*320)动态显示。另外,还实现了对模拟信号的调理,让其电压值符合 AD的采样范围,通过数值处理计算出波形的相关参数,并显示在 LCD 中等功能。因此,整个系统更加完整、完善,从而达到预期目的。文中介绍了硬件的工作原理、模块组成、软件程序的设计方法,并给出了相关设计文档与实物结果。且该示波器,体积小,价格低廉,低功耗,方便携带,适用范围广泛,基本上满足了某些场合的需要,同时克服了传统示波器体积庞大的缺点,减小成本。
关键词:STM32;AD;数字示波器;实时采样
目 录
1. 选题背景与意义 4
1.1 选题依据 4
1.2 设计目的 4
1.3 设计意义 4
1.4 应用分析 4
1.5 国内外研究现状 5
1.6 设计任务和要求 5
1.7 方案选择 5
1.8 工艺要求 6
2. 系统设计 7
2.2硬件结构 7
2.3芯片模块 9
2.3.1STM32处理器介绍 9
2.4LCD液晶显示模块 11
2.5按键电路模块 12
3. 软件设计与分析 14
3.1系统介绍 15
3.2信号采集和显示部分 16
3.2.1显示部分 16
3.2.2数值计算部分 17
3.3A/D采样设置 17
3.3.1信号采集部分 17
3.3.2设置采样时间 17
4. 软件编程与分析 19
4.1ADC的初始化函数 19
4.2按键控制程序设计 21
4.3峰峰值测量程序设计 22
4.4LCD显示程序设计 23
4.5动态刷新及显示波形 24
5. 性能测试与分析 26
6. 结论 28
致谢 29
参考文献 30
附录 31
- 选题背景与意义
- 选题依据
本设计是基于 ARM Cortex-M3 的数字示波器设计可以通过 AD 采样和数值处理 显示周期信号和非周期信号并且将信号的峰峰值、频率和动态的实时波形显示在 LCD液晶屏上。系统的设计包括前端信号调理AD采样控制、J-TLink仿真接口、LCD液晶屏显示实时波形,其优点是方便携带,能够实时对数据进行采集。
-
- 设计目的
本设计的目的是通过接收任意电压信号并且能够将显示所接收到的实时波形, 并且能够测量出波形的峰峰值和频率。并完整的显示动态波形,显示出波形的频率和幅度特性本设计还附加信号发生器功能让系统更加完整。
-
- 设计意义
本设计实现了对信号包括周期信号和瞬时信号的采集和显示,去除了模拟传统 示波器只能显示周期信号的弊端,并且由于采用嵌入式设计这样使得设计的体积很 小,有利于便携和数据的实时采集。本设计中采用 STM32 系列 ARM 芯片为主控制 器,最大实时采样速率可以达到1MHz,并且采用LCD液晶屏(分辨率为 240*320)液晶显示,出波形的频率和幅度特性,得到波形的峰值和频率。 此外,本设计是基于嵌入式的数字示波器并且控制器内部集成 A/D 和 D/A,具有 小体积、高可靠性能优点,对于信号的便携式采集有着重要意义,能够满足本设计的要求。
-
- 应用分析
本设计是采用 STM32 芯片完成数据的采样量化、A/D 转换、 波形分析、波形 显示和触摸屏操作等多部分设计,STM32 处理器是 ST 公司面向于低价位市场设计 的一种基于 ARM-M3 内核的微处理器,凭借其性价比高、性能强悍的优势,非常适 合于嵌入式开发的设计需求。本设计所采用是 STM32 芯片,具体是采用嵌入式数字 示波器主要基于最新 Cortex-M3 内核的微控制器是 STM32F103RCT6 芯片 ARM 作为当今嵌入式时代的主流的嵌入式控制器产品, 随着嵌入式技术的发 展, 对于嵌入式示波器功能也越来越强大,显示输出等功能,并且系统能够稳定的运行,采样的数据实时准确。
-
- 国内外研究现状
随着集成电路的发展和数字信号处理技术的采用,数字示波器已成为集显示、测量、运算、分析、记录等各种功能于一体的智能化测量仪器。数字示波器在性能上也逐渐超越模拟示波器,并有取而代之的趋势。与模拟示波器相比,数字示波器不仅具有可存储波形、体积小、功耗低,使用方便等优点,而且还具有强大的信号实时处理分析功能。因此,数字示波器的使用越来越广泛。目前我国国内自主研发的高性能数字示波器还是比较少,广泛使用的仍是国外产品。因此,有必要对高性能数字示波器进行广泛和深入研究。
-
- 设计任务和要求
(1)采用 STM32F103单片机和2.8寸液晶屏显示;
(2)能显示外部输入的波形,频率1KHz,幅度1000mv;
(3)显示的波形在 x、y方向上可以调整。
(4)LCD 显示清晰,波形无明显失真,不能有明显的残影,乱码、抖动。
-
- 方案选择
1.5.1 主控制芯片的选择
本设计是通过将输入的电压信号进行信号调理满足于ADC输入的条件并将信号输入ADC引脚进行采样并量化,在所接触过的微处理器包含有C51、DSP以及ARM处理器,首先由于C51内部资源有限,包括寄存器、Flash都不满足要求,此放弃使用C51单片机,设计中如果选择的是DSP,由于DSP对外部信号要求很高,那么设计中用到芯片就会增多,并且因为DSP造价高于ARM若在信号的采集时如果应用DSP对于信号放大芯片有很多限制,不易选取。最新的STM32是ST意法半导体公司采用的基于ARM-CortexM3内核所设计的处理器,无论是在性价比还是易操作性上都是优先选择使用的对象,由于STM32F103处理器内部集成自带的ADC并且可以直接调用内部硬件DMA能够最大减少对系统资源的占用,所以若采用ARM处理器在AD采样方面可优先考虑使用内部的ADC,减少电路设计的复杂程度。此外STM32系统资源丰富,功能性强,用于嵌入式开发也是不错的选择。因此,本设计采样STM32处理器作为控制器。本设计所使用的是STM32系列中增强型的系列,内部含有ADC、DAC以及硬件DMA那么这样就简化的外围电路的设计时就会变的简单,干扰也会比较小,并且系统时钟频率可达到72MHz,在数据的处理能力上适合于本设计相关计算使用,而且内部的Flash对与数据的存储也能够满足,由于GPIO的配置比较灵活在控制显示器件时候更加灵活方便。综上所述,本设计采用的是STM32中性能比较强的RC系列,在数据的处理能力上符合要求。
-
- 工艺要求
本课程设计使用正点原子mini版套件。
- 系统设计
示波器的设计分为硬件设计和软件设计两部分。示波器的控制核心采用ARM9,由于STM32芯片里有自带的AD,采样速率最高为500KSPS,分辨率为10位,供电电压为3.3V,基本上能满足本设计要求,显示部分用2.8寸TFTLCD(分率:320*240)模块。软件部分采用C语言进行设计,设计环境为Keil4。 2.1.示波器的工作原理
数字示波器的工作原理,当输入被测信号从无源探头进入到数字示波器,首先通过的是示波器的信号调理模块,由于后续的A/D转换器对其测量电压有一个规定的量程范围,所以,示波器的信号调理模块就是负责对输入信号的预先处理,通过放大器放大或者通过衰减网络衰减到一定合适的幅度,然后才进入A/D转换器。在这一阶段,微控制器可设置放大和衰减的倍数来让用户选择调整信号的幅度和位置范围。
在A/D采样模块阶段,信号实时在离散点采样,采样位置的信号电压转换为数字值,而这些数字值成为采样点。该处理过程称为信号数字化。A/D采为采样速率,表示为样值每秒(S/s)。A/D转换器最终将输入信号转换为二进制数据,传送给捕获存储区。被测的模拟信号在显示之前要通过微处理器的处理,微处理器处理信号,包括获取信号的电压峰峰值、有效值、周期、频率、上升时间、相位、延迟、占空比、均方值等信息,然后调整显示运行。最后,信号通过显示器的显存显示在屏幕上。
2.2硬件结构
图1:系统框图
该示波器由4部分电路构成,分别是:
- 输入程控放大衰减电路;
- 极性转换电路;
- AD转换电路;
- 显示控制电路;
(5)按键控制电路; 设计思路是:信号从探头输入,进入程控放大衰减电路进行放大衰减,程控放大器对电压大的信号进行衰减,对电压小信号进行放大以符合AD的测量范围,经过处理后信号进入极性转换电路进行电平调整成0一3. 3V电压,因为被测信号可能是交流信号,而AD只能测量正极性电信号,经调整后送入AD转换电器对信号进行采样,采样所得数据送入LCD显示,这样实现了波形的显示。按键控制可以通过不同的按键来控制波形的放大和缩小,同时也可以改变采样间隔,以测量更大频率范围的信号。该设计采用正点原子mini版套件,根据系统功能把整个系统分成不同的具有特定功能的模块,硬件整体框图如下图所示。
图2:系统总体框图
2.3芯片模块
2.3.1STM32处理器介绍 STM32系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex-M3内核。按性能分成两个不同的系列: STM32F103“增强型”系列和STM32F101“基本型”系列。增强型系列时钟频率达到72MHz,是同类产品中性能最高的产品;基本型时钟频率为36MHz,以16位产品的价格得到比16 位产品大幅提升的性能,是16位产品用户的最佳选择。两个系列都内置32K到128K的闪存,不同的是SRAM的最大容量和外设接口的组合。时钟频率72MHz时,从闪存执行代码,STM32功耗36mA,是32位市场上功耗最低的产品。 本设计所用的STM32F103RCT6 集成的片上功能如下: (1)1.2v内核供电,1.8V/2.5V/3.3/V存储器供电,3.3V外部I/0供电
(2)外部存储控制器
(3)LCD控制器 (4)4通道DNA并有外部请求引脚
(5)3通道UART (6)2通道SPI (7)1通道IIC总线接口1通道IIS总线接口(8)AC’97编解码器接口 (9)兼容SD主接口协议1.0版和MMC卡协议2.11兼容版
(10)2通道USB主机1通道USB设备 (11)4通道PWM定时器和1通道内部定时器/看门狗定时器
(12)8通道10位ADC和触摸屏接口 (13)80个通用I/0和24通道外部中断源
ALIENTEKMiniSTM32V3.0版开发板选的是STM32F103RCT6作MCU。它拥有的资源包括48KSRAM、256KFLASH、2个基本定时器、4个通用定时器、2个高级定时器、2个DMA控制器(共12个通道)3个SP、2个IIC、5个串口、1个USB、l个CAN、3个12位ADC、1个2位DAC、1个SD10接口及51个通用l0口。该芯片性价比高,STM32F103芯片原理图如下图
图3 stm32f103RTC6芯片原理图
上图中中上部的BOOT1用于设置STM32的启动方式,其对应启动模
式如下表所示:
表1:启动模式表
一般情况下(即标准的ISP下载步骤)如果我们想用串口下载代码,则必须先配置BOOT0为1,B00T1为0然后按复位键,最后再通过程序下裁代码,下裁完以后又需要将BOOT0设置为GND,以使每次复位后都可以运行用户代码。可以看到,这个标准的ISP是很繁琐的,跳线要跳来跳去,还要手动复位。所以AUENTEK为STM32的串口下载专门设计了一键下载电路。通过串口的DTR和RTS信号,来自动控制RST(复位)和B00T0,因此不需要用户来手动切换状态,可以非常方便地下载代码。这是其他开发扱所不具备的。这STM32的VBAT采用CR1220纽扣电池和VCC3.3混合供电的方式.在有外部电源(VCC3.3)的时候,CR1220不给VBAT供电.面在外部电源断开的时候.则由CR1220给VBAT供电。这样,VBAT总是有电的,以保证RTC的走时以及后各备存器的内容不丢失,该部分还有JTAG,JTAG部分电路如下图:
图4JTAG原理图
这里采用的是标准的JTAG接法,但是STM32还有SWD接口,SWD只需要最少2根线(SWCLK和SWDIO)就可以下载并调试代码了,这同我们使用串口下载代码差不多,而且速度更快,能调试。所以建议大家在设计产品的时候,可以留出SWD来下载调试代码,而放弃JTAG。STM32的SWD接口与JTAG是共用的,只要接上JTAG,你就可以使用SWD模式了(其实SWD并不需要JTAG这么多线),JLINKV8/JLINKV7/ULINK2以及STLINK等都支持SWD[2]。
2.4LCD液晶显示模块
LCD液晶显示器构造是在两片平行的玻璃当中放置液态晶体,两片玻璃中间有许多垂直和水平的细小电线,通过通电与否来控制杆状水晶分子改变方向,将光线折射出来产生画面。
LCD技术参数有: 1.对比度 LCD制造时选用的控制IC、滤光片和定向膜等配件,与面板的对比度有关,对于一般用户而言,对比度能够达到350:1就足够了,但在专业领域这样的对比度还不能满足用户的要求。对比值定义是最大亮度值(全白)除以最小亮度值(全黑)的比值。 2.亮度 LCD是一种介于固态与液态之间的物质,本身是不能发光的,需要借助于额外的光源才行。因此,灯管数目关系着液晶显示器亮度。液晶显示器的最大亮度,通常由冷阴极涉嫌管来决定,亮度值一般都在200~250cd/m2间。 3.可视面积 液晶显示器所表示的尺寸就是与实际可以使用的屏幕范围一致。
4.可视角度 当背光源通过偏极片、液晶和去向层之后,输出的光线变具有了方向性。也就是说大多说光都是从屏幕中垂直射出来的,所以从某一个较大的角度观看液晶显示时,便不能看到原来的颜色,甚至是只能看到全白或者全黑。为了解决这个问题,制造商们也着手开发广角技术,到目前为止有三种比较流行的技术,分别是:TN FILM、IPS和MVA。 5.色彩度 任何一种色彩都是由红、绿、蓝三种基本色组成的。LCD 面板上是由480X272个像素点组成现象的,每个独立的像素色彩是由红、绿、蓝(R、G、B)三种基本色来控制。其原理图如下:
图5:液晶显示模块原理图
2.5按键电路模块
ALIENTEKMiniSTM32开发板总有3个按键,其原理图如下:
图6:按键输入原理图
KEY0和KEY1用作普通按键输入,分别连接在PC5和PA15上,其中PA15和JTDI共用了,所以,在使用KEY0和KEY1的时候,就不能使用JTAG来调试了,但是可以用SWD调试,这点在使用的时候要注意。KEY0和KEY1还和PS/2的DLK线共用。WK_UP按键连接到PA0(STM32的WKUP引脚),他除了可以用作普通输入按键外,还可以用作STM32的唤醒输入。该按键是高电平触发的。由于PA0还是DS18B20的输入引脚,而18B20是上拉电阻的,所以在使用WK_UP按键的时候,请一定要断开PA0和DS18B20的跳线帽。
- 软件设计与分析 整体设计思路是:信号从探头输入,进入程控放大衰减电路进行放大衰减,程控放大器对电压大的信号进行衰减,对电压小信号进行放大以符合AD的测量范围,经过处理后信号进入极性转换电路进行电平调整成0一3V电压,因为被测信号可能是交流信号,而AD只能测量正极性电信号,经调整后送入AD转换电器对信号进行采样,采样所得数据送入LCD显示,实现波形的显示按键控制可以通过不同的按键来控制波形的放大和缩小,同时也可以控制程控放大器,选择放大和衰减的倍数。该示波器软件开发环境为Keil4,代码采用C语言编写。ARM中软件完成的功能: (1)输入波形显示和峰峰值测量。
- LCD的初始化和显示控制。
(3)按键的检测和控制。
下面是示波器代码整体流程图,主要是一个循环内不断进行背景绘制、得到ADC值、打印波形、扫描按键。
图7:示波器代码流程图
3.1系统介绍
系统软件设计包括如下几个部分:获取波形频率、获取峰峰值、示波器界面、按键变换波形、得到ADC转换值并转换为坐标、绘制波形、波形产生函数、主函数这几个部分,如图。
图8:软件结构模型图
3.2信号采集和显示部分
本设计中将输入的模拟信号经过运放进行衰减和放大后使得电压值满足0~3.3V的要求,能够实现采集、数值的处理和显示波形,本设计应用STM32F103RCT6完成模数转换芯片功能以及数值运算、计数显示等功能。这样要比DSP利用多个芯片配合完成目标所要的电路简洁的多。并且若是采用STM32处理器那么在信息采集部分首先要做的就是要把模拟电路信号转换为数字信号,在A/D转换部分之后,就是要对数据计算以及显示部分。STM32F103RCT6增强型系列是ARM-M3中,性价比和功能最强的一个,其内部资源非常丰富,无论是硬件条件或者操作的难易性来说都足够满足设计需求。
3.2.1显示部分
在显示部分所采用的显示器件为320*240的LCD显示屏,由于STM32的引脚很多并且引脚的配置也很多,可以较方便的控制LCD屏的处理操作,应用软件对LCD使能使其开启开始画面。LCD显示器件的优势在于能够稳定的显示动态的波形,不需要和你繁琐的操作稳定性高。与传统的CRT相比,LCD显示屏有着色彩丰富的面板不但体积更小,厚度更薄、重量更轻、耗能更少、工作电压低且无辐射、无闪烁现象,而且外部驱动电路简单便于设计,经过简单的连接驱动就能够达到波形的动态显示功能。LCD液晶屏作为比较新的显示器件,根据分辨率的不同,像素点的分布也不同,本设计采用的是240*320的屏幕。
3.2.2数值计算部分
数据采集完后,就开始对波形显示的参数进行处理,所设计中要计算峰峰值,频率,控制传输使能。应用通用复用硬件完成寄存器计数功能。通用和复用部分应用寄存器计数。对于STM32中每个GPIO端口都有两个32位寄存器组成。每个端口位可以进行灵活的根据所需进行配置。
AD转换时间=采样时间 12.5个周期,定时器溢出中断时间=((arr 1)*(psc 1))/Tclk
3.3A/D采样设置
3.3.1信号采集部分
信号采集最先做的就是A/D转换。使用STM32RCT6内部AD,虽然能够达到预期要求但是它的工作温度比较小,完成这部分还要完成芯片所需的温度补偿,然而由于STM32内部AD采样和数据处理时处的环境温度是一样的,这样就去除了需要温度可以减少系统资源的占用。使用STM32内部的AD可以测量2个内部信号和16个外部信号,并且在各个通道的AD转换模式又包含有单次、连续和间断等多种操作模式的执行。并且将转换的结果保存到16位数据寄存器中。将输入电压-5~5V,进行信号调理后为0~2V,在ADC的范围内进行采样,根据不同的频率范围,选择定时器定时触发采样。并且配合DMA使得最大采样频率可以达到1M。
3.3.2设置采样时间
模拟信号经调理后,根据频率范围,分为高速采样、中速采样、低速采样。
STM32内部的AD是逐次逼近型额AD转换器。最多包含有18个通道。STM32中的AD可以进行多种模式的转换。所需要强调的是STM32的ADC输入时钟不得超过14M,并且只由PCLK2分频产生[2]。在使用ADC的时候,需要配置相关参数。
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子672M/6=12,ADC最大时间不能超过14M
ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_1Cycles5);
//ADC1,ADC通道,采样时间为239.5周期 T=(5 12.5)=72M
- A/D的模式,本设计中我们设置为独立模式
- A/D的通道设置,由于是单通道的AD采样所以我们设置成DISABLE
- A/D的转换设置,由于我们设置了定时器定时出发采用所以我们设置ENABLE
- AD的触发选择,我们设置为定时器触发
5.最后使能ADC。
具体是采用通过预设的采样时间设置定时器的预分频值和重载值。对于AD采样中需要将AD的采样值转化为电压值:对于ADC的采样值所对应的电压值信号的计算在STM32手册中给出:Volue=Vpp*3300/4096(单位:mV)也可以理解为当前AD的电压值所对应的AD的数字量
3.3频率和参数设置
在程序设计中预设了DA的幅值为0~3300mV,频率最大设置为20kHz,输出时,只要控制其输出滤波就能达到预期的目的,因此在程序设计时通过修改分频值、重载值、计数值来改变DAC的输出频率。
TIM3_Int_Init(24,71);//((24 1)*(71 1))/72*1000000=21us
TIM_TimeBaseStructure.TIM_Period=arr; TIM_TimeBaseStructure.TIM_Prescaler=psc;
- 软件编程与分析
设计思路是:经过程控放大衰减和极性转换后的电压作为AD转换的输入电压,然后通过不同的按键来控制波形的放大和缩小,最后在LCD屏上显示出大小适中的波形。同时测出电压峰峰值,并显示。以下是根据整个系统进行的软件设计
图9:软件设计流程图
4.1ADC的初始化函数
VoidAdc_Init(void)
{
ADC_InitTypeDefADC_InitStructure;
GPIO_InitTypeDefGPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1 ,ENABLE); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子672M/6=12,ADC最大时间不能超过14M
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//PA1作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA,&GPIO_InitStructure);
ADC_DeInit(ADC1);//复位ADC1,将外设ADC1的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel=1;//顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1,&ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1,ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//使能指定的ADC1的软件转换启动功能
}
4.2按键控制程序设计 利用2个按键K0, K1来选择波形的放大和缩小,按键采用外部中断方式。其中通过K0来调整波形显示的幅度,通过K1来改变采样间隔增加或减少一个周期内采样点数,达到控制水平扫描速度,使低频率波形能完整显示。按键中断程序流程图如下图所示。
图10:按键中断流程图
voidEXTI9_5_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0) {
POINT_COLOR=RED;
switch(ch1AmpTimes){
case1:ch1AmpTimes=2;break;//通过改变采样变频来改变X轴上的变化
case2:ch1AmpTimes=3;break;
case3:ch1AmpTimes=4;break;
case4:ch1AmpTimes=1;break;
}
}
EXTI_ClearITPendingBit(EXTI_Line5);}
voidEXTI15_10_IRQHandler(void)
{
delay_ms(10);
if(KEY1==0) {
switch(sampTimes){
case1:sampTimes=2;TIM3->ARR=49;break;//通过改变定时器的重装载值来改 变,Y轴上的变化
case2:sampTimes=4;TIM3->ARR=99;break;
case4:sampTimes=8;TIM3->ARR=199;break;
case8:sampTimes=1;TIM3->ARR=24;break;
}
}
EXTI_ClearITPendingBit(EXTI_Line15);}
程序分析:
通过按键KEY0来改变采样倍频,从而改变X轴上的变化,按键KYE1,改变定时器的重装载值来改变Y轴上的变化。
4.3峰峰值测量程序设计 通过遍历AD转换结果,取出最大值和最小值求差,结果即为电压峰 值。
voidTIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET){
wave1[w1count]=Get_Adc(ADC_Channel_1);
if(wave1[w1count]>vmax1)
{
vmax1=wave1[w1count];
vmaxc=w1count;
}
if(wave1[w1count]<vmin1)
{
vmin1=wave1[w1count];
vminc=w1count;
} vpp1=vmax1-vmin1; //求差值
vpp1=3300*vpp1/4096;
LCD_ShowNum(153,0,vpp1);//显示位置
程序分析:
通过定时器来使能AD转换,并且在定时器中断函数里,获取AD值,并存储在wave1[i]数组里,遍历求出最高值和最低值,求出它们的差,就是波形的峰峰值,
4.4LCD显示程序设计
本设计所使用的是2.8寸320X240图形点阵LCD,该部分程序主要有LCD初始化,AD转换过来的数据转换成显示数据。用数组连续存储AD转换结果,存满后依次在LCD上显示,依次循环。显示过程中由于STM32处理器频率较低,导致显示一屏的时间较长,从而使刷屏速度较慢,效果不好。这里采用每次刷一-列的的算法,即每次显示下一列点之前将此列初始化为屏幕底色,从而改善视觉效果。
图形界面显示 voidGUI_Init()
{
u16x,y;
BACK_COLOR=BLACK;
for(x=25;x<276;x=x 25)//画列,从第25个像素点开始
for(y=25;y<226;y=y 5)
{
LCD_Fast_DrawPoint(x,y,GRAY);
}
for(y=25;y<226;y=y 25)//画行
for(x=25;x<276;x=x 5)
{
LCD_Fast_DrawPoint(x,y,GRAY);
}
//画坐标轴
POINT_COLOR=YELLOW;//颜色为黄色
LCD_DrawLine(25,25,275,25);//横上
LCD_DrawLine(275,25,275,225);//竖右
LCD_DrawLine(25,25,25,225); //竖左
LCD_DrawLine(25,225,275,225);//横下
LCD_ShowString(150,0,140,16,16,”Frequency=Hz”);
LCD_ShowString(0,15,40,16,12,”Volt”);
LCD_ShowString(275,225,50,16,12,”Time”);
}
4.5动态刷新及显示波形
voidshow_wave(){
u8i,j;
Get_wave();
for(i=1;i<250;i ){
for(j=1;j<200;j ){
if((j%5==0&&i%==0)||(j%==0&&i%5==0))LCD_Fast_DrawPoint(i 25,j 25,GRAY);
else(LCD_Fast_DrawPoint(i 25,j 25,BLACK));
}
POINT_COLOR=YELLOW; LCD_DrawLine(i 25,200-wave1[i]*0.03*ch1AmpTimes,i 24,200-wave1[i-1]*ch1A
mpTimes*0.03);
}
程序分析:动态刷新整个界面及波形的显示,波形的显示,给出波形开始显示的坐标,X轴的坐标确定,Y轴根据AD转化的值显示,以及Y上的变化,跟采样倍频有关
- 性能测试与分析 在最初的安装调试中,由于没有使用过函数发生器导致一些问题,波形始终不能正常显示,在老师和同学的指导下最终正确显示。但硬件的缺陷与软件的实现仍然有些小弊端。 1、当采样间隔与波形频率不太匹配时并不能很好显示出波形。但适当调整采样时间仍可准确测量1KHz的信号,并显示电压峰峰值。 2、当信号频率大于1KHz时正弦波波形不能很好分辨出来,当信号频率大于500KHZ时波形失真,发生重叠。 3、使用刷屏显示时会出现一点残影,这里采用刷列显示。但当信号频率较低时,由于处理器的速度较慢,使得刷列显示出的波形连续性不好,目前没找到更好的解决办法。总体来讲,所有预期技术参数都能正确实现。
4.测试结果
方波
正弦波
三角波
- 结论
经过努力,终于完成了该题目的设计,本次的课程设计采用STM32ADC采样和LCD屏幕驱动显示和的核心内容,属于嵌入式系统项目设计。设计使用C语言编程,结合KeiluVision4对源程序进行编译、连接和运行,利用STM32F103RCT6mini板实现多种波形示波器的制作,使用J-link在线编译调试,完成设计要求。个人总结以下几个方面:
1.综述了现阶段数字存储示波器技术及产品的国内外发展状况,对数字存储示波器的原理、工作方式、显示方式等的基本概念及技术发展进行了介绍。
2.针对设计的任务和要求,确定了存储示波器波形采样和数据处理及波形重组的硬件和软件方案。
3.对整机各部分关键电路进行相关理论分析、计算和设计。
4.完成了样机的制作与调试;论述了仪器的测试方法,完成数据测试及测试结果分析。
5.通过本次设计,学习并掌握了ARM的硬件结构,编程方式和技巧,为以后使用打下了坚实的基础,同时也体会到了学以致用的意义,对嵌入式产生了更加浓厚的兴趣。ARM等高性能、低成本微处理器的出现,为高性能智能化电子测试仪器的设计提供了良好的平台。这次设计也得到了老师的很大帮助,在这里表示衷心的感谢
致谢
首先,感谢张健老师诲人不倦,在本次课程设计中给予我的指导与帮助。张老师学识渊博,谆谆教导,桃李满园。不论是平时的课程讲授还是本次的课程设计环节始终不辞辛劳如一日,传其道,授其业,为我们打开关于嵌入式开发技术的新天地。秦岭巍巍,八水泱泱,师恩难忘,地久天长。在这里还要同时感谢与我一起齐心协力合作完成本次多种波形示波器制作的课程设计的队员,我们三人分工明确,拾遗查缺,一起奋斗,一起解决课程设计中存在的种种问题,每当有疑问的时候,我们都能够互帮互助,尽全力为对方解答。长路虽有阻且长,但,上下求索创新篇;百尺竿头须进步,十方世界乃大千。再次感谢他们在本次课程设计中为我解答疑惑,耐心讲解,使我受益匪浅。
参考文献
[1].王文应.检定数字示波器和模拟示波器主要区别[J].电子测量技术,2002(04):33.
[2].周富相.etal.基于STM32的数字示波器设计与实现.山西电子技术,2011,2:8-10.
[3].周嘉伟.简易数字控制仪用表的技术研究[J].电子技术.2016(10)
[4]张洪润等.单片机应用设计200例上册.北京:北京航空航天大学出版社,2006 [5]潘新民,王燕芳.微型计算机控制技术.北京:人民邮电出版社,1999 [6]潘新民,王燕芳.微型计算机控制技术使用教程.北京:电子工业出版社,2006
[7]周立功.ARM嵌入式系统基础教程,北京:北京航空航天大学出版社,2005
[8]范圣一.ARM原理与嵌入式系统实战.北京:机械工业出版社,2007
附录
main.c:
#include “led.h”
#include “delay.h”
#include “sys.h”
#include “usart.h”
#include “lcd.h”
#include “adc.h”
#include “timer.h”
#include “exti.h”
u16 wave1[250];//wave2[250];//双通采样数据保存
float vpp1,vpp2;//通道1和通道2的峰峰值
u8 ch1AmpTimes;//通道1采样倍频参数
u8 sampTimes;//两个通道的幅度比例参数
Float vmax1,vmin1;//vmax2,vmin1,vmin2;
int vmaxc=0,vminc=0;
int main(void){
u32 freq=0;
delay_init(); //延时函数初始化
uart_init(9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();
LCD_Scan_Dir(D2U_L2R);
Adc_Init(); //ADC初始化
LCD_Clear(BLACK);
GUI_Init();
EXTIX_Init();
TIM3_Int_Init(24,71);
ch1AmpTimes=1;
sampTimes=1;
while (1){
LED0=!LED0;//判断程序是否正常
delay_ms(1000);
vpp1=vmax1-vmin1; //100
vpp1=3300*vpp1/4096;//330/4095
if((vmaxc-vminc)<0)
{
freq=(1000000.0/((vminc-vmaxc)*50));
}
else
{
freq=(1000000.0/((vmaxc-vminc)*50));
}
LCD_ShowNum(93,0,vpp1,4,16);
LCD_ShowNum(243,0,freq,4,16);
vminc=0;
vmaxc=0;
vmax1=0;
vmin1=2000;
show_wave();
}
}
ADC.c
#include “timer.h”
#include “adc.h”
#include “delay.h”
#include “lcd.h”
#include “usart.h”
u8 w1count,w2count;
_Bool finish_flag=0;
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5); //ADC1,ADC通道,采样时间为5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t )
{
temp_val =Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
void Get_wave(){
w1count=0;
finish_flag=0;
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
while(!finish_flag);
}
EXTI.c
#include “exti.h”
#include “led.h”
#include “key.h”
#include “delay.h”
#include “usart.h”
#include “lcd.h”
extern float times;
u8 pressTimes=0;
extern u8 ch1AmpTimes;
extern u8 sampTimes;
extern u8 ch2AmpTimes;
extern _Bool chSetChoose;
//外部中断初始化函数
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
KEY_Init();//初始化按键对应io模式
//GPIOC.5 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.15 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
EXTI_InitStructure.EXTI_Line=EXTI_Line15;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) {
POINT_COLOR=YELLOW;
switch(ch1AmpTimes){
case 1:ch1AmpTimes=2;break;
case 2:ch1AmpTimes=3;break;
case 3:ch1AmpTimes=4;break;
case 4:ch1AmpTimes=1;break;
}
}
EXTI_ClearITPendingBit(EXTI_Line5); //清除LINE5上的中断标志位
}
void EXTI15_10_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY1==0) {
switch(sampTimes){
case 1:sampTimes=2;TIM3->ARR=49;break;
case 2:sampTimes=4;TIM3->ARR=99;break;
case 4:sampTimes=8;TIM3->ARR=199;break;
case 8:sampTimes=1;TIM3->ARR=24;break;
}
} EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE15线路挂起位
}
KEY.c
#include “key.h”
#include “delay.h”
//按键初始化函数
//PA0.15和PC5 设置成输入
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA15
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PC5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(WK_UP==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}
LCD.c
void GUI_Init()
{
u16 x,y;
BACK_COLOR=BLACK;
for(x=25;x<276;x=x 25)//画列,从第25个像素点开始
for(y=25;y<226;y=y 5)
{
LCD_Fast_DrawPoint(x,y,GRAY);
}
for(y=25;y<226;y=y 25)//画行
for(x=25;x<276;x=x 5)
{
LCD_Fast_DrawPoint(x,y,GRAY);
}
//画坐标轴
POINT_COLOR=YELLOW ;//颜色为黄色
LCD_DrawLine(25,25,275,25);//横上
LCD_DrawLine(275,25,275,225);//竖右
LCD_DrawLine(25,25,25,225); //竖左
LCD_DrawLine(25,225,275,225);//横下
POINT_COLOR=GRAY;
LCD_ShowString(50,0,90,16,16,”Vpp1= mv”);
LCD_ShowString(160,0,150,16,16,”Frequency= Hz”);
LCD_ShowString(0,15,40,16,12,”Volt”);
LCD_ShowString(275,225,50,16,12,”Time”);
}
void show_wave(){
u8 i,j;
Get_wave();
for(i=1;i<250;i ){
for(j=1;j<200;j ){
if((j%5==0&&i%==0)||(j%==0&&i%5==0))LCD_Fast_DrawPoint(i 25,j 25,GRAY);
else(LCD_Fast_DrawPoint(i 25,j 25,BLACK));
}
POINT_COLOR=YELLOW;
LCD_DrawLine(i 25,200-wave1[i]*0.03*ch1AmpTimes,i 24,200-wave1[i-1]*ch1AmpTimes*0.03);
}
Timer.c
#include “timer.h”
#include “led.h”
#include “adc.h”
#include “usart.h”
extern u16 wave1[250],wave2[250];
extern u8 w1count,w2count;
extern _Bool finish_flag;
extern float vpp1,vpp2;
extern float vmax1,vmax2,vmin1,vmin2;
extern int vmaxc,vminc;
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
//static u8 timer_fr=0,Vcc_max_mun=0,fluent=0;
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){
wave1[w1count]=Get_Adc(ADC_Channel_1);
if(wave1[w1count]>vmax1)
{
vmax1=wave1[w1count];
vmaxc=w1count;
}
if(wave1[w1count]<vmin1)
{
vmin1=wave1[w1count];
vminc=w1count;
}
w1count ;
if(w1count>250){
finish_flag=1;
LED1=0;
TIM_ITConfig(TIM3,TIM_IT_Update,DISABLE);
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/181700.html原文链接:https://javaforall.cn