【立创·GD32E230C8T6】简易数字示波器设计
开源地址
项目开源地址:https://oshwhub.com/course-examples/yi-qi-yi-biao-jian-yi-shu-zi-shi-bo-qi-she-ji-cha-jian-ban
一、设计背景
示波器是电子工程师必不可少的工具,按照信号的不同分为模拟示波器以及数字示波器两种,模拟示波器采用模拟电路和电子枪,通过电子束打在屏幕上显示波形,而数字示波器采用数字电路和数字存储技术,将波形数字化并显示出来。
在示波器升级迭代过程中,传统的模拟示波器以及难以满足现代电子测量的需求,相比于模拟示波器,数字示波器的测量进度更高、显示稳定以及信号。随着技术的发展,示波器的性能和功能不断提高。现代示波器采用高速ADC和FPGA等技术,具有高带宽、高采样率和深存储深度等特点。此外,数字示波器还支持多种触发方式和信号分析功能,如FFT变换、频谱分析等功能。
学习设计和制作一个数字示波器对于个人综合能力的培养是非常有帮助的,数字示波器项目包括单片机电路的设计与开发、信号调理电路的计算、人机交互的设计以及外壳模型的设计等内容,综合了模拟电路、单片机设计、电路与PCB设计、外壳设计等综合知识。结合初学者对知识的接受程度,为此专门设计出这一个入门级数字示波器项目,适用于电子入门实训以及单片机入门学习。
项目特点
- 采用核心板加扩展板设计理念,采用插件器件设计,让学习更能简单;
- 核心板选用国产GD32为主控,同时兼容同类型其它款式开发板;
- 项目综合程度高,实用性强,设计完成后可作为桌面日常仪表使用;
- 项目学习资料丰富,包括电路设计教学、PCB设计以及代码编程的学习。
二、电路原理分析
数字示波器是一种用于显示电信号波形的仪器,主要由模拟前端处理电路、单片机电路、电源电路、控制电路、触发电路、校准电路等电路组成。由于该项目为示波器入门项目,在电路设计上选择了一些核心电路,帮助初学者更好的了解示波器的原理和设计方法,主要包括了以下电路:
- 模拟前端处理电路: 负责将输入的检测模拟信号进行处理后给单片机进行识别,具体电路包括了交直流耦合选择电路、电压衰减电路、信号处理电路以及频率检测电路,是整个电路的核心。
- 电源电路: 负责给运放提供正负电源以及系统供电,是保障电路正常运行的基础;
- 单片机电路: 给系统提供控制核心,负责对输入信号的采集与处理输出工作;
- 人机交互电路: 用于控制示波器功能,包括按键、旋钮、LED灯、显示屏以及其它输入输出接口,为示波器功能的开发提供基础。
一个优秀的数字示波器设计及工作原理非常复杂,其中还包含了许多细节和技术内容,各个电路相互配合,使得数字示波器能够准确地获取、存储和显示输入信号的波形。
2.1. 模拟前端处理电路
在整个示波器电路设计过程中,模拟前端处理电路是最为重要的,其中大量应用了模拟电路的知识,其中包括输入交直流耦合切换电路、输入信号衰减电路、以及信号调理电路所组成,在讲解模拟输入采集电路前,我们来思考以下几个问题:
- 为什么要加这个电路,直接将输入信号接到单片机引脚不行吗?
- 输入信号处为什么要加一个电容,有什么作用?
- 信号调理电路的作用是什么,该如何设计?
交直流耦合切换电路
信号类型可以分为直流信号和交流信号,现实中的信号往往都是都不是理想波形。比如直流电源信号应该是一条水平的直流信号,但都会存在电源纹波(交流信号);在采集交流信号时也可能混入直流信号对波形的峰峰值造成影响。为了保障对输入交流信号的准确测量,利用电容通交隔直的特性,将电容串联到电路中就可以过滤到信号中的直流分量,这就是交流耦合的概念。而直流耦合就是不对输入信号做任何处理。
举个例子:一个电压为1V的直流电平上叠加了一个1Vpp的正弦波信号,如果使用直流耦合模式,测试看到的是一个向上抬升1V的正弦波信号,但如果使用交流耦合模式,直流信号被滤除,则输出正弦波基准电压为0V。
电路中通过一个拨动开关SW2对输入交直流耦合信号进行切换,当开关2与1接到一起时为直流耦合,当开关2接到3时为交流耦合。那么问题来了,这里的电容该如何取值呢?是不是任意一个电容都行?
要解决这个问题,那就需要了解电容的频率特性,理想状态下隔直电容的选择应该越大越好,但是由于不同容值的自谐振频率不同,低于自谐振频率时电容呈现容性状态,高于该频率时存在感性状态。电容越大,其自谐振频率越低,简单来说就是大电容通低频,小电容通高频。如上右图可知当瓷片电容为0.1uF(100nF)时,其自谐振频率为4Mhz,一般要求电容的截止频率:
其中fo为电路中工作频率。由此可知在该项目中100nF是足够应用的,但如果输入信号频率更高,就应该选择更小的电容。
输入信号衰减电路
信号经过交直流耦合选择电路后由开关SW3选择两个通道,开关2和3接到一起时,输入信号直接流入后级的电压跟随器电路;当开关2和1接到一起时,输入信号经过R7、R11、R14三个电阻构成的电阻分压网络后将信号衰减到了1/50倍,即:
- 当SW2开关2和3接到一起时,可以测量的输入信号幅值为-1.6V-5V
- 当SW2开关2和1接到一起时,可以测量的输入信号幅值为-80V-250V
由此可知,当输入信号幅值较小时,可优先选择低压档位,如果测量时不确定输入信号幅值可先用高压档位测量后如满足低压范围内,可用低压档位测量以得到更为精准的测量结果,同时保护电路。
补充说明:
该项目中使用的探头为BNC转鳄鱼夹探头,非专业示波器探头,仅对电路进行简单分析,此处不进行展开说明更多关于探头选择与阻抗说明。如使用专业示波器的无源探头进行测量,由于探头上有一个x1和x10的档位选择,当档位为x10时可以通过调整探头上的补偿电容或者是电路中的C10进行匹配已得到准确的测量效果,其中C10一般为可调电容,实际大小与各板间电容特性有关。
信号调理电路
在信号调理电路中包含了一个电压跟随器以及由运放构成的信号放大电路,在分析该这部分电路时需要掌握运放的虚断与虚短原理。
- 也可以用欧姆定律 U=I∗R 来理解,当电压一定时,电流与电阻成反比,电阻无限大那电流也就无限小接近为0。
- 在负反馈中,运放的输出信号的一部分被取出并反馈到输入端。这种反馈作用使得运放的两个输入端(正输入和负输入)的电压差趋近于零,、两个输入端的电压几乎相等。因为尽管运放的两个输入端在电气上并没有被直接短路,但由于负反馈的作用,两个输入端的电压却几乎相等,就好像它们被短路了一样,故称为虚短。
(1) 电压跟随器电路
在电路U5.2芯片中运放反向输入引脚2脚接到运放输出引脚1脚,结合运放的虚短特性,V+=V-Vout。根据虚断可知运算放大器的输入阻抗比较大,所以运算放大器正向输入电流很小,运放输出阻抗小所以输出的电流很大,说这里的电压跟随器起着一个阻抗匹配的作用。
(2) 比例放大电路
在对U5.1运放构成的电路进行分析时,可以将其拆解为一个同相比例放大电路和一个反相比例放大电路进行单独分析后合到一起。
假设运放正向输入端接地,构成反相比例放大电路,由运放的虚断特性可知反向输入引脚流入运放的电流为0,可以R13和R15可以看做串联,所以流经电流相同。由虚短特性可知V+=V-=0,由此可得:
将Vi=1V,R13=20K,R15=10K带入得Vo=-0.5V,与仿真结果一致。
假设运放反向输入端接地,构成正相比例放大电路,由运放的虚断特性可知运算放大器两个输入引脚流过的电流为0,即流过R4的电流为0,Vin=V+。可以看做R13和R15串联,所以流过的电流也相同。由虚短特性可知两个输入引脚的电压相同,即V+=V-,所以有Vin=V+=V-。Vo与R13和R15构成回路,则流过的电流为:
单独对R13来看,流过的电流也等于它两端的电压除以它的电阻值,代入Vin=V+=V-得到以下公式:
合并两公式可得出:
将以上两个公式合到一起,将Vin = 5/3 * V,R13=20K,R15=10K带入得Vo=2.5V,与仿真结果一致。
信号调理电路仿真图 将以上同相比例放大电路与反相比例放大电路结合起来得到以下公式:
将Vin=1代入公式得Vo=2V,与仿真结果保持一致。
Vo信号将直接接到单片机的ADC引脚,由于单片机采集到ADC的电压范围值为0~3.3V,由此可以计算出该示波器输入电压范围。
当输入信号Vin不衰减时,将Vo=0、Vo=3.3V分别代入公式得:
当输入信号Vin衰减 1/50 时,将Vo=0、Vo=3.3V分别代入公式得:
得到以下结论:
低压档位测量范围:-1.6V~5V,高压档位测量范围:-80V~250V
比较器测频电路
为了实现频率检测的功能,将ADC输入信号通过一个滞回比较器对输入信号进行比较,实现频率的测量功能。滞回比较器是属于电压比较器中的一种,常规的电压比较器是一个单限比较器,电路中只有一个阈值电压,但在输入电压在阈值附近有微小变化时都会引起输出电压的越变。为了增强电路的抗干扰能力,在单限比较器的基础上引入了正反馈,保障了在一定范围内信号的稳定性。通过滞回比较器电路后输出一个方波信号,使用单片机的定时器捕获功能计算出输入波形的周期大小。
滞回比较器电路的阈值电压需单独对运放输出结果进行分析,当输出为高电平时,输出端上拉到高电平,这时等效电路如下左图所示,算得Uth=U+=2.214V。当运放输出为低电平时,输出端接地,等效电路如下右图所示,算得Utl=U-=2.172V。
下图绿色线代表信号输入电压变化情况,从0电位上升,初始输出状态为高电平,输入电压达到2.214V时,输出信号变成低电平,直到输入信号低于下限阈值2.172V时输出变为高电平。可以根据比较器当前的输出状态来确定下一个变化电平的阈值,当输出为高电平时使用的是高阈值Uth,输出为低电平时使用低阈值Ttl。之所以将阈值设置接近是为了避免信号干扰造成的误识别。
注意事项: 此处阈值比较器运放正向输入信号为固定电平,若使用带有DAC输出的单片机,可自由配置该点电位大小,从而改变阈值电压,实现对触发模式的设置。
2.2. 电源控制电路
该项目使用GD32最小系统板为核心,板载了5V转3.3V降压电路,所以在设计扩展板时只需要设计一个5V电源输入电路即可,这里选用了主流的Type-C为输入接口 ,且该接口只有两根线,插件封装,方便新手焊接学习,但需要注意的是这个Type-C接口仅用于供电,不能传输数据,如果需要使用传输数据,可以使用核心板上的Type-C接口。SW1为电源总开关、C1为输入滤波电容,R1是LED1的限流电阻。
除了电源输入电路,为保障运算放大器对于负电压的测量性能,使用了XD7660负压产生电路得到一个 负电压,该芯片外围电路简单,只需要两个电容和一个二极管即可工作,理论上输入电压为+5V,也可以输出一个-5V的电压,由于芯片内部存在一定压降及转换效率,实际测量负电压为-4.3V左右,也能满足运算放大器的要求。
2.3. 单片机电路
该项目使用了由立创开发板团队推出的GD32最小系统板为主控,这款开发板是由立创开发板团队联合兆易创新推出的一款全国产的开发板,板载CH340下载芯片,只需要一根数据线就可以对板子进行烧录与串口调试,同时兼容STM32最小系统板的尺寸与引脚配置,可以直接进行替换。
在配置核心板引脚与单片机功能时需结合引脚特性,比如SPI液晶屏驱动需要连接到对应的SPI引脚,ADC检测需要接到到ADC功能的引脚上,这里的引脚配置不唯一,可重新配置其他的方案,以数据手册中的引脚功能说明为准。
2.4 人机交互电路
2.4.1 液晶屏显示电路
1.8 TFT 是一款彩色显示屏,具有 128 x 160 个彩色像素,使用四线SPI通信方式与单片机进行连接,一共有八根引脚,模块引脚说明及与单片机连接情况如下所示:
2.4.2 旋转编码器电路
旋转编码器属于一种特殊的按键,该项目使用的EC11旋转编码器有五个引脚,其中DE两个引脚类似于普通按键引脚,按下导通,松手断开,其余ABC三个引脚用于检测旋钮的转动方向,C脚为公共端,直接接地就行。
在旋转编码器时,A和B两个信号引脚存在相位差,也就是有一个引脚信号变化后另一个引脚信号再跟着变化,即两个引脚不同时变化,通过检测哪个引脚先变就能判断是正转还是反转功能。
2.4.3 LED灯指示电路
LED指示电路设计比较简单,采用低电平驱动的方式,当单片机引脚输出为低电平时,LED两端存在电势差,LED点亮;当单片机引脚输出为高电平时,LED灯熄灭。
2.4.4 按键检测电路
除了旋转编码器外,该项目还使用了三个独立按键对系统进行控制,三个按键一侧直接接地,另一侧连接到单片机引脚,当单片机引脚检测到按键按下时,单片机引脚直接接到GND接地,单片机收到该引脚接地信号的反馈后再去实现对应的功能,为节约硬件成本,可以在软件设计时引入消抖功能,避免机械按键抖动时的误触发。
2.4.5 其他电路
除了示波器检测功能外,单独引出了一个PWM信号用于模拟一个简易的函数发生器功能,可以通过改变输出PWM的频率和占空比输出一个简易方波信号输出。
三、原理图与PCB设计
在了解完示波器硬件电路原理后接下来进行原理图与PCB设计环节,原理图设计部分包含了元器件选型、元器件搜索以及原理图整理的内容;PCB设计部分包含边框设计、元器件分类布局、PCB走线与设计检查、PCB生产与打样等内容。
3.1 原理图设计
3.1.1 工程创建
打开嘉立创EDA专业版软件(https://pro.lceda.cn/editor),登录账号后选择创建工程, 输入工程名称:【仪器仪表】基于GD32的简易示波器设计,系统会自动创建一个工程项目,接下来就在该项目中完成示波器的原理图与PCB、3D外壳与面板设计等内容。
创建好工程后打开左侧工程列表中的Schematic1
图页,在右侧画布区域放置元器件进行连接,底下的PCB1是用来绘制PCB图的页面,设计流程是先完成原理图的设计后再到PCB设计。
3.1.2 元器件搜索
元器件搜索的方式有三种途径,第一种可以在左侧的常用库中找到官方提供的参考库进行放置,优点是比较方便,缺点是库种类较少;第二种方式是通过顶部菜单栏中的放置按钮选项,可以看到器件实物图与参考价格、数据手册等信息;第三种方式是在软件底部面板中搜索放置器件,这种方式的好处是可以看到所选器件的符号、封装与3D模型效果图。
使用技巧:熟悉使用快捷键可以大大提升设计效率哦~
3.1.3 元器件放置
为了方便初学者学习,该项目提供了完成电路图中所需的元器件清单,可以直接根据器件清单中提供的器件编号及备注信息进行元器件的搜索和放置。以底部面板搜索器件为例,在原理图工作区打开底部库面板,选择器件类目,在搜索栏中输入器件编号,进行搜索找到对应元器件点击放置在画布中即可。里面的M3铜柱在EDA左侧常用库的最后一项可以找到并放置。
序号 | 器件名称 | 器件参数 | 数量 | 器件封装 | 器件编号 |
---|---|---|---|---|---|
1 | 单片机模块 | GD32核心板 | 1 | GD32E230C8T6最小系统 | C20068939 |
2 | USB连接器 | TYPE-C-2P | 1 | USB-SMD_KH-TYPE-C-2P | C2919656 |
3 | 拨动开关 | 12E12-G5 | 3 | SW-TH_SK-12E12-G5 | C136720 |
4 | 电解电容 | 10uF | 3 | CAP-TH_BD5.0-P2.00-D0.8 | C503219 |
5 | 直插电阻 | 1kΩ | 4 | RES-TH_BD2.5-L6.5-P10.50 | C57435 |
6 | 发光二极管 | 3mm 红色 | 1 | LED_TH-3mm | C2895470 |
7 | 电源芯片 | XD7660 | 1 | DIP-8_L9.7-W6.4-P2.54 | C521200 |
8 | 二极管 | 1N4148 | 1 | DO-35_BD2.0-L4.2-P8.20 | C402212 |
9 | 瓷片电容 | 100nF | 5 | CAP-TH_L4.2-W3.8-P2.54 | C168753 |
10 | 连接器 | BNC接口 | 1 | ANT-TH_KH-BNC75-3511 | C2837587 |
11 | 直插电阻 | 510kΩ | 2 | RES-TH_BD2.3-L6.5-P10.50 | C119382 |
12 | 直插电阻 | 470kΩ | 1 | RES-TH_BD2.2-L6.5-P10.50 | C58640 |
13 | 直插电阻 | 20kΩ | 4 | RES-TH_BD2.3-L6.5-P10.50 | C119353 |
14 | 直插电阻 | 10kΩ | 4 | RES-TH_BD2.4-L6.3-P10.30 | C410695 |
15 | 瓷片电容 | 680pF/NC | 1 | CAP-TH_L5.5-W3.2-P5.00 | C377846 |
16 | 瓷片电容 | 10pF/NC | 1 | CAP-TH_L5.5-W2.5-P5.00 | C260724 |
17 | 运算放大器 | TL072IP | 1 | PDIP-8_L9.3-W6.4-P2.54 | C110329 |
18 | 比较器芯片 | LM393P | 1 | DIP-8_L10.1-W6.3-P2.54 | C725322 |
19 | 直插电阻 | 0Ω | 1 | RES-TH_BD2.5-L6.5-P10.50 | C410698 |
20 | 液晶芯片 | 1.8寸TFT屏 | 1 | SW-TH_EC11L1525E05 | C2991196 |
21 | 旋转编码器 | EC11 | 3 | SW-TH_4P-L6.0-W6.0 | C2845257 |
22 | 轻触开关 | 6x6x7 | 2 | LED_TH-3mm | C2895477 |
23 | 发光二极管 | 3mm 绿色 | 2 | HDR-TH_2P-P2.54-V-M-1 | C124375 |
24 | 显示屏 | 1.8 TFT | 1 | LCD-TH_8P-P2.54 | C9900080251 |
25 | M3螺丝 | M3 | 4 | M3螺丝接口 | EDA常用库 |
文件下载
通过网盘分享的文件:物料清单-简易数字示波器.xlsx
链接: https://pan.baidu.com/s/14vNClaaUS8o9pmHVfQ4Wxg?pwd=LCKF 提取码: LCKF
3.1.4 原理图整理
完成元器件放置后接下来进行电路图的连接与整理工作,参照以下电路图,完成元器件间的连接,使用网络标签可以替代导线连接,两个相同网络的位置放置相同网络标签即可。接下来按功能模块划分各个电路,使用矩形边框包围住各个电路模块,并用文本加上电路模块标识说明。最好使用设计菜单栏下的检查DRC功能查看电路连接是否有误。
3.2 PCB设计
3.2.1 电路模块分类
将原理图生成PCB后接下来进行元器件的布局与走线,刚转到PCB画布时元器件摆布是比较杂乱的,首先要做的是将元器件按电路功能进行分类,分类的方式是先在原理图页面对各个电路模块进行单独框选,然后选择“设计”菜单栏下的“布局传递”功能,传送到PCB将对应的元器件提取出来重新摆放,这一步是分类的关键。
3.2.2 边框设置
嘉立创可供免费PCB打样的尺寸是10cm10cm,结合该项目情况我们设为了70mm x 80mm,在放置菜单栏中选择放置-板框,在PCB画布中任意放置一个矩形,点击矩形框,在右侧属性栏中将尺寸改为7080mm,圆角尺寸设为2mm。
3.2.3 PCB布局
边框放置好后可以将四个螺丝孔分别放置在板子四周,布局时先将大尺寸器件放置在板子内部,进行初步布局,使整个板面电路模块清晰,布局合理,使用方便。布局时使用3D预览功能实时查看布局效果是否合适。
布局时元器件相互连接处有一根淡蓝色的线条,这根线叫做飞线,它起的作用是告诉我们那两个焊盘是相同网络,需要使用导线连接,所以飞线也叫做指引线。但是页面中飞线太多影响布局摆放,在布局走线时可以将GND网络的飞线隐藏,使页面更简洁。隐藏方式是:在左侧“工程设计”列表中选择网络,在搜索栏中搜索GND,在飞线列表中将AGND和GND前的眼睛关闭即可。走线完成后别忘了重新打开哦~
接下来布局时把相关模块电路放到一起,按照飞线的指引摆放,尽可能使飞线水平,走线时减少拐弯,开关接口靠边方便操作,最终布局效果如下所示:
3.2.4 PCB走线
好的布局已经成功了一半,接下来只需掌握以下几点走线基本要求即可:
- 走线以直线为主,如需拐弯时拐角以135°钝角或圆角优先,减少直角的使用;
- 走线线宽电源线宽大于信号线,该项目中信号线走线宽度为15mil,电源走线为20mil,GND和AGND网络使用铺铜的方式连接;
- 建议优先使用顶层走线,走不通的地方使用过孔建立顶层和底层的连接后转到底层继续走,底层走不通同样可以放置过孔换到顶层连线;
- 对AGND和GND需要以0欧姆电阻处为分界单独覆铜,这里需结合PCB布局情况来调整覆铜范围;
- 覆铜完成后如果还存在飞线,可通过在存在飞线的位置放置对应网络的过孔或者是调整走线位置使网络能够连接,也可以采用手动接线的方式消除飞线;
- 走线完成后可在“工具”菜单栏选择泪滴添加,加强焊盘与走线的连接,最后再进行覆铜操作,如果对走线有移动调整也应使用快捷键Shift+B进行重建覆铜。
完成走线后点击底部面板中的检查DRC按钮,若显示无报错警告,则代表该PCB设计完成。实际设计过程中往往可能出现各种错误,也可以通过检查DRC找到错误点进行修复,确保PCB板设计一板成功!
四、免费PCB打样
嘉立创自2019年起给全国电子工程师及爱好者普及PCB免费打样服务至今,伴随着电子工程师的成长,解决学习过程中打样贵、打样慢的问题。在完成PCB设计后我们可以大胆地将文件提交到嘉立创平台进行PCB的打样,每个月可以支持免费打样两次,每次可打一款板子,实际生产5片,重点是还全国包邮!接下来介绍如何在嘉立创平台进行免费的PCB制板服务。
4.1 优惠券领取
在进行PCB下单前需要先领取一张免费打样券,打开嘉立创下单平台的优惠券领券专区页面,嘉立创下单的账号与嘉立创EDA的账号通用,登录账号后即可领取优惠券: 嘉立创优惠券领取专区
进入领券专区后,选择PCB+SMT喷锡免费券进行领取,每个月可领取两张,领取后30天内有效。这里的PCB+SMT的意思是可用于PCB和SMT而不是一定要用SMT才能用券,系统会随机抽取幸运儿,有极小概率抽中的话还可以免费体验工厂帮忙焊接元器件(SMT)的服务,如果能抽中那也是极好的!
4.2 生成制造文件
领取优惠券后回到PCB设计页面,点击顶部菜单栏中的“文件”,选择“导出”选项,在里面可以选择导出物料清单(BOM)、PCB制板文件(Gerber)以及坐标文件,这三个文件是实际PCB设计生产中常用到的文件。其中物料清单是用于元器件采购以及SMT贴片时物料选择、坐标文件是用于SMT贴片时器件位置定位,而里面的PCB制板文件(Gerber)文件就是PCB生产用的文件。
点击导出PCB制板文件(gerber),在选项中选择“导出Gerber”,这时系统会提醒先检查DRC再进行导出,这里需要点击“是,检查DRC”,当检查无误后才会导出Gerber文件下载到本地。然后再将Gerber文件上传到嘉立创下单系统即可完成下单。
在导出Gerber文件的窗口还有一个“PCB下单”的选型,直接点击PCB下单的话可以不需要下载文件直接跳转到嘉立创平台上进行PCB的打样操作,这里给PCB下单提供了更多的便利。点击自动下单后 同样需要对DRC进行检查,检查无误后系统会讲下单数据生产,点击“确认”按钮即可跳转到嘉立创下单平台页面进行下单。
4.3 下单页面介绍
进入下单平台后,需要填写板子生产工艺参数,具体说明如下:
4.3.1 基本信息
- 板材类别:选择FR-4,另外FPC板材为柔性PCB、铝基板常用于做灯板、铜基板散热性较好、高频板用于设计制作多阻抗和信号要求较高的板子;
- 板子尺寸:默认会自动识别出来的,没有识别的话也可手动填写;
- 板子数量:免费打样数量为5片,如果多打需要自费;
- 板子层数:嘉立创现在支持1-4层的免费打样,板子设计是两层板,这里选择“2”;
- 产品类型:选择工业/消费/其他类电子产品,航空和医疗板精度设计要求较高;
- 确认生产稿:如果是批量生产那必须要确认生产稿,避免生产文件有误影响板子功能,免费打样则选择不需要生产稿即可。
4.3.2 PCB工艺
PCB工艺选项里面内容较多,仅需关注下图中框选出来的几个选项:
- 拼板款数:在进行批量打样时常将多个PCB拼在一个板子上生产,这样成本更低。由于目前仅做免费打样,拼板数量应为1,出货方式为单片;
- 板子厚度:默认板子厚度为1.6mm,无特殊要求建议是默认1.6mm ,选择其他厚度和颜色匹配时容易选到冷门工艺会额外添加工艺费;
- 阻焊颜色:即板子的颜色,嘉立创支持七种不同的阻焊颜色,其中绿色的生产周期最快,最快48小时内发货,其他颜色最快是72小时发货,可结合自身喜好和板子的着急程度选择合适的阻焊颜色。
4.3.3 个性化服务
个性化服务没有特殊需求选默认即可。
4.3.4 交期
交期与所选颜色有关,嘉立创最快支持12小时加急,但需额外支付加急费。若无特殊需求,选用默认交期即可。
4.3.5 SMT贴片与激光钢网
如需选择工厂代焊接元器件则在此选择需要SMT贴片,工厂生产PCB后会将元器件一同焊接好寄出,SMT属于收费服务,若不需要则选择不需要即可。钢网是用于SMT贴片刷锡膏用的,如果自己有贴片机,可在生产PCB时选择开钢网回来自己进行贴片焊接。
4.3.6 开票与支付
嘉立创是支持开票的,下单前需填写开票税号,免费打样无需开票,开票信息填写个人即可。在确认订单方式推荐选择“系统自动扣款并确认”选项,避免个人疏忽忘记确认订单影响生产。
4.3.7 发货与快递
在发货页面建议选择电子收据,发货方式可选不同交期是否一起发后,填写收货地址,下单联系人和技术联系人都可以填自己的联系方式,快递根据地区不同会由不同的快递显示,选择里面一个免费的快递即可。
4.3.8 使用优惠券下单
填写完下单数据后,在右侧结算页面选择使用优惠券下单即可获得减免,这里注意下优惠前面额应该是20元,如果超过20那可能是选了某个特殊工艺,应修改回默认工艺后再使用优惠券下单。
提交订单后还需确认订单,系统可能会自动审核通过,也有可能会有人工审核参与,这时需等待审核通过后进行确认订单才可以进入生产环节,如果前面选了“系统自动扣款并确认选项”在有余额的前提下系统就自动确认并进入生产啦~
生产中的板子可在嘉立创下单平台的PCB订单中查看进度以及快递情况,稍等几天就能拿到5块新鲜的电路板啦~
五、PCB焊接练习
准备好元器件与PCB板后下一步进入焊接调试焊接,需要掌握常用焊接工具的使用、插件元器件的焊接与拆卸方法。
5.1 焊接工具
需要用到的焊接工具有:电烙铁、焊锡丝、高温海绵、斜口钳、吸锡器、松香、洗板水等,其中电烙铁、焊锡丝、高温海绵、斜口钳这四项为必备工具,其余几个有最好,没有也可以。
电烙铁选择常规烙铁就行,有条件的可以选择焊台,升温快且稳定。烙铁加热时请勿触摸金属位置,避免烫伤,头发绑好避免烫到,桌面保持整洁。
焊锡建议是选用无铅焊锡,焊锡时较好比较好上锡。一般左手拿焊锡,右手拿烙铁,如果惯用左手的话反之。
高温海绵在使用前先用水浸湿,然后拧干后使用,海绵上不能沾太多水分,避免烙铁头高温沾水后急速降温损坏烙铁头;斜口钳使用时用手捏住多余引脚,然后再剪,免得引脚导出弹飞。
5.2 辅助焊接工具
在嘉立创EDA专业版中提供了一个焊接辅助工具,焊接时可在线打开该工具对照元器件的参数及位置,实时进行查看焊接情况。打开方式是该项目的PCB设计页面,点击“工具”菜单栏,选择“焊接辅助工具”;也可以直接点击下载以下离线文件,并在浏览器中打开。
文件下载
通过网盘分享的文件:PCB焊接辅助工具-简易数字示波器V1.2.html
链接: https://pan.baidu.com/s/18xxd8wsgcnm5FTbPdMplfQ?pwd=LCKF 提取码: LCKF
进入焊接辅助工具后,在显示模式中选择仅显示已焊接选项,左侧选择焊接时勾选对应器件,右侧的3D预览效果图显示当前器件焊接位置及情况,避免焊接错误。焊接的原则是从矮到高以此焊接。焊接时建议根据下表中的焊接顺序进行焊接。
焊接顺序 | 器件名称 | 参数 | 位号 | 数量 | 备注 |
---|---|---|---|---|---|
1 | 通用二极管 | 1N4148 | D1 | 1 | 黑色色环一侧为负极 |
2 | 插件电阻 | 1kΩ | R1,R2,R3,R6 | 4 | |
3 | 插件电阻 | 20kΩ | R4,R10,R13,R14 | 4 | |
4 | 插件电阻 | 10kΩ | R5,R8,R9,R15 | 4 | |
5 | 插件电阻 | 510kΩ | R7,R12 | 2 | |
6 | 插件电阻 | 470kΩ | R11 | 1 | |
7 | 插件电阻 | 0Ω | R16 | 1 | |
8 | 瓷片电容 | 100nF | C2,C5,C6,C8,C9 | 5 | |
9 | 瓷片电容 | 680pF/NC | C10 | 1 | 可不焊接 |
10 | 瓷片电容 | 10pF/NC | C11 | 1 | 可不焊接 |
11 | 发光二极管 | 3mm 红色 | LED1 | 1 | 长引脚一侧为正极 |
12 | 发光二极管 | 3mm 绿色 | LED2,LED3 | 2 | 长引脚一侧为正极 |
13 | USB连接器 | TYPE-C-2P | USB1 | 1 | |
14 | 比较器芯片 | LM393P | U4 | 1 | 先焊接DIP8芯片座再装芯片 |
15 | 运算放大器 | TL072IP | U5 | 1 | 先焊接DIP8芯片座再装芯片 |
16 | 电源芯片 | XD7660 | U2 | 1 | 先焊接DIP8芯片座再装芯片 |
17 | 拨动开关 | SK-12E12 | SW1,SW2,SW3 | 3 | |
18 | 排针 | 2.54*2P | H3,H4 | 2 | |
19 | 轻触开关 | 6x6x7 | KEY1-KEY3 | 3 | |
20 | 电解电容 | 10uF | C1,C3,C4 | 3 | 有白色边一侧为负极 |
21 | 同轴连接器 | BNC75 | RF1 | 1 | |
22 | 旋转编码器 | EC11 | SW4 | 1 | |
23 | 主控电路 | GD32E230C8T6板 | U1 | 1 | 板子上焊接排母,核心板插上去 |
24 | 显示模块 | 1.8寸TFT | U3 | 1 | 板子上焊接排母,屏幕插上去 |
25 | 两个M2的螺丝安装在原理屏幕的一侧,四个M3铜柱安装在板子四个角落 |
六、软件开发
6.1. 开发环境搭建
Keil社区版介绍
该版本有以下几个特点:
- 可供电子爱好者、学生、学者等群体非商业免费评估和使用。
- 没有代码大小限制。
- 支持Arm Compiler 6:可为所有基于Arm Cortex-M的产品提供精简的代码和强大的性能。
- 可访问超过9500款支持基于Cortex-M处理器的微控制器器件。
- 可用于基于Arm架构的微处理器的CMSIS标准框架。
- 大量可免费使用的中间件(keil RTX5、lwIP、CMSIS-FreeRTOS等)。
Keil社区版下载链接:https://www.keil.arm.com/mdk-community/
详细下载激活说明文档参考:【立创·GD32E230C8T6】入门手册
安装注意事项:安装目录下不能有中文、第一次进入Keil以管理员模式运行。
6.2 程序下载说明
GD32核心板支持串口下载以及DAP-Link下载两种方式,其中串口下载只需要一根数据线即可,而DAP-Link需要单独准备一个DAP-Link下载器。下面介绍两种下载方式的使用方法:
6.2.1 串口下载
开发板板载CH340N-USB转串口芯片,可直接通过串口DFU下载程序,下载程序前需要安装两个驱动一个软件。
下载CH340驱动
通过网盘分享的文件:CH340驱动.zip
链接: https://pan.baidu.com/s/1M1S43egi4EYaYdFd7Ixhzw?pwd=LCKF 提取码: LCKF
下载DFU驱动
通过网盘分享的文件:GD32DfuDrivers_V3.6.6.6167.zip
链接: https://pan.baidu.com/s/143_BQXuz6DEzR0ct6HLTqw?pwd=LCKF 提取码: LCKF
下载DFU软件
通过网盘分享的文件:GD32AllInOneProgrammer_V2.0.3.13854.zip
链接: https://pan.baidu.com/s/1l2yXsuEqaegmH39TI6nvgw?pwd=LCKF 提取码: LCKF
两个驱动一个软件下载完成,开始安装;
第一步:安装CH340驱动
鼠标右键以管理员身份进行安装;
如果出现安装失败,那可能是你电脑已经有了该驱动,你可以尝试先点击卸载,再次安装即可。
第二步:安装DFU驱动
根据自己电脑选择合适的版本进行安装,如今大部分电脑都是(X64-64位),一般选这个不会错。
点击鼠标右键以管理员身份运行;
直接安装即可。
第三步:配置Keil生成下载文件
第四步:开发板配置
将数据线插入USB口,注意数据线需要是具有数据传输功能的,如果不清楚是否具有数据传输功能,建议使用原装手机充电线进行尝试;
使用跳线帽将BOOT0与1进行连接,靠近TYPE-C接口的一侧,然后按一下复位按键。
现在开发板已经进入DFU下载模式了,双击打开GD32AllInOneProgrammer文件夹,运行DFU软件
进入DFU软件后,配置如下:选择模式为COM,系统会自动识别出端口号,我们选择即可,如果没有识别出端口号,可重新关掉再次打开即可识别,识别成功后,点击连接;
系统会自动将芯片型号以及内存大小识别出来
测试读保护和写保护是开启状态还是关闭状态。 在Erase选项栏,选择 Erase selected pages。会弹出一个信息框。
如果W和R是两排绿色的锁,则代表读保护与写保护是关闭的,直接点击OK即可,如果是锁上的,则选择Uncheck解锁;
第五步:下载代码测试
点击 Browse 进行文件路径选择;
选择工程路径的hex格式文件;
点击 Download 下载,注意hex文件路径不能太深,如果发现点击Download没有反应,也可重新插拔,然后先选择擦除再进行下载;
下载成功后发现程序好像并没有运行,这是因为此时是在DFU下载模式,我们需要使用跳线帽将BOOT0与0连接,也就是朝芯片的一侧,然后按一下复位键进行模式切换。此时程序就能正常运行了。
6.2.2 DAP-Link下载
GD32E230是ARMV8架构,使用AC6编译器,如果你手里的是旧版Jlink,可能无法进行下载,建议使用开源的DAP-Link下载器,可以直接进行SWD程序的下载与调试。
连接说明:如果底板或USB接口有连接电源,3V3电源接口可不用接,保证调试器与板子共地即可;
连接完成后,进入Keil进行配置下载(daplink的配置与串口下载配置并不影响);
注意使用DAP-Link下载时BOOT0建议还是与0相连接,也就是跳线帽插到靠近芯片的一侧,无需反复插拔;
此时两种程序下载方式已经全部介绍完成,接下来可以下载以下案例工程将代码下载到核心板中看看会有什么现象。下载成功会发现板载LED灯闪烁。
文件下载
通过网盘分享的文件:Demo1-LED控制实验.zip
链接: https://pan.baidu.com/s/1ctTotgoc5NPHaf4QiQTMfg?pwd=LCKF 提取码: LCKF
6.3. LED控制实验
6.3.1 LED灯结构组成
LED灯,也称发光二极管,是一种能够将电能转化为可见光的固态的半导体器件,它可以直接把电转化为光。LED的内部是一个半导体的晶片,晶片的一端附在一个支架上,一端是负极,另一端连接电源的正极,整个晶片被环氧树脂封装起来。
6.3.2 LED灯发光原理
半导体晶片由两部分组成,一部分是P型半导体,另一端是N型半导体。这两种半导体连接起来的时候,它们之间就形成了一个P-N结。当电流通过导线作用于这个晶片的时候,电子就会被推向P区,在P区里电子跟空穴复合,然后就会以光子的形式发出能量,这就是LED灯发光的原理。
6.3.3 LED灯驱动原理
驱动LED灯,也就是使LED灯点亮,首先查看原理图,查看LED灯引脚的连接关系,可通过对应单片机引脚输出高低电平,从而改变LED灯两侧电势差形成电流回路,点亮LED灯。
6.3.4 LED引脚原理图
核心板板载LED引脚图:
底板板载LED引脚图:
6.3.5 项目分析
本次项目完成点亮底板LED灯实验,核心板板载LED灯留给大家自行修改进行点亮; 通过前面的基础知识说明,我们已经知道LED灯点亮的原理与LED灯连接的引脚;
- LED2--->PC14
- LED3--->PC15 在LED灯的正极是连接3.3V,中间接入了一个1K的限流电阻防止LED灯烧坏; 当我们将对应引脚配置为高电平时,单片机引脚高电平输出(3.3V),此时没有电压差,没有电流流过,不能点亮LED;
当我们将对应引脚配置为低电平时,单片机引脚低电平输出(0V),此时有电压差,有电流流过,LED灯被点亮;
6.3.6. LED配置流程
一般我们使用GPIO的端口,都需要有以下几个步骤。
- 开启GPIO的端口时钟
- 配置GPIO的模式
- 配置GPIO的输出
开启GPIO端口时钟
GD32的所有外设资源时钟默认都是关闭的,在配置外设之前需要先开启对应的时钟。
时钟库函数在gd32e23x_rcu.h头文件中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
//GPIOC端口时钟使能
rcu_periph_clock_enable(RCU_GPIOC);
2
配置GPIO模式
在配置模式前,首先需要了解GPIO的模式,GD32E230GPIO模式有以下几种:
GPIO_MODE_INPUT-------输入模式
GPIO_MODE_OUTPUT-----输出模式
GPIO_MODE_AF-----------复用模式(引脚复用功能时使用)
GPIO_MODE_ANALOG----模拟模式(ADC读取模拟量时使用,可以读取细微变化的值)
当GPIO用作输入时,还会有以下几种情况可以设置:
GPIO_PUPD_NONE--------引脚浮空
GPIO_PUPD_PULLUP------引脚上拉
GPIO_PUPD_PULLDOWN-引脚下拉
相关GPIO操作库函数在gd32e23x_gpio.h中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
此函数有四个参数:
第一个参数是引脚端口,第二参数设置引脚模式;
第三个参数是设置上下拉情况,第四个参数是具体端口引脚号;
此处我们使用引脚的输出功能,无需上下拉设置,直接设置为浮空即可;
//设置输出模式,不上下拉
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_14);
//设置输出模式,不上下拉
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
2
3
4
配置GPIO的输出
在配置输出之前,也同样需要了解GPIO的输出类型,GD32E230GPIO输出类型有以下几种:
GPIO_OTYPE_PP----推挽输出(由两个MOS或者三极管进行互补控制的信号控制,可输出高低电平)
GPIO_OTYPE_OD---开漏输出(由单个MOS或者三极管进行信号控制,无法输出高电平,需要借助外部上拉电阻,常用作“线与”);
引脚配置为输出,GPIO可以进行速度的配置(IO电平翻转速度),输入因为是检测电平,所以IO翻转速度对其影响不大。
GPIO_OSPEED_2MHZ----速度最大2Mhz
GPIO_OSPEED_10MHZ---速度最大10Mhz
GPIO_OSPEED_50MHZ---速度最大50Mhz
相关GPIO操作库函数在gd32e23x_gpio.h中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
//设置输出类型,推挽输出,50Mhz
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
//设置输出类型,推挽输出,50Mhz
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
2
3
4
自此,GPIO引脚配置完成。
6.3.7. 输出高低电平
引脚配置完成后,现在可通过配置输出高低电平来实现LED的点亮和熄灭。相关GPIO操作库函数在gd32e23x_gpio.h中,也可通过附件资料中的《GD32固件库使用指南查看》该函数的详细说明。
输出低电平:
gpio_bit_reset(GPIOC,GPIO_PIN_14);
gpio_bit_reset(GPIOC,GPIO_PIN_15);
2
输出高电平:
gpio_bit_set(GPIOC,GPIO_PIN_14);
gpio_bit_set(GPIOC,GPIO_PIN_15);
2
此时可自行配置对应引脚进行点亮和熄灭LED灯。
6.3.8. 项目完整源码
下载成功后即可看到底板LED全部被点亮。
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "led.h"
#include "main.h"
int main(void)
{
//初始化滴答定时器
systick_config();
//使能时钟
rcu_periph_clock_enable(RCU_GPIOC);
//设置输出模式,不上下拉
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
//设置输出类型,推挽输出,50Mhz
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
//设置引脚输出低电平
gpio_bit_reset(GPIOC,GPIO_PIN_14);
gpio_bit_reset(GPIOC,GPIO_PIN_15);
while(1)
{
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
6.3.9 注意事项
(1)新建工程及模板创建
解决办法:
可通过立创GD32E230C8T6入门手册的第二章,工程模版新建教程,完成工程新建。
也可直接使用项目源码中的本次实验源码作为基础模版,重点是理解GPIO各库函数的作用以及如何进行查找。
(2)发现在其余.c/.h文件中跳转函数失败
解决办法:
参考此链接: https://blog.csdn.net/cao43215822/article/details/119594148
也可以直接使用Ctrl+F进行查找该函数;
在查找中选择在整个文档下进行查找,即可快速跳转到其余文件指定位置。
(3)编译过程中遇到一些奇怪的警告
解决办法:参考以下配置
(4)Keil无法编译或编译失败
注意GD32E230是ARMV8架构,默认就是AC6,无法切换回AC5,请确保keil版本不能太老,需要支持AC6。
6.4. 按键检测实验
6.4.1 按键结构组成
独立按键实际上是一个非自锁的轻触开关,有左右四个触点,当按下时左右四个触点闭合,当松开时左右四个触点断开,12触点与34触点在内部是连接在一起的。
6.4.2 独立按键原理
单片机通过检测按键按下前后的高低电平变化,来判断按键是否按下。通过程序的控制,就可以实现不同的功能与设置。机械式按键在按下或者释放时,由于机械弹性作用的影响,通常伴随有一定时间的触点机械抖动,然后其触点才稳定下来。抖动时间长短与开关的机械特性有关,一般为5-10ms。在触点抖动期间检测按键的按下与否,可能会导致判断错误,为了克服机械抖动所产生的影响,必须采取消抖措施,可分为硬件消抖和软件消抖。
硬件消抖一般会在按键检测引脚处加入电容与电阻,通过RC延迟电路将按键按下时的高频振荡吸收滤除掉。
软件消抖一般是通过延时。当检测到按键按下时,不会立即去检测电平,而是经过短暂的延时之后,再去检测当前引脚的电平,这样就可以去掉抖动过程中的干扰,一般抖动时间在10ms以内。
6.4.3 按键驱动原理
驱动独立按键,首先需要判断按键两端的引脚,根据按键两侧引脚接的不同,可分为两种情况。
按键一侧接电源,GPIO需要设置为下拉,也就是默认为低电平,通过判断对应GPIO引脚为高电平,从而判断按键是否按下。
按键一侧接GND,GPIO需要设置为上拉,也就是默认为高电平,通过判断对应GPIO引脚为低电平,从而判断按键是否按下。
6.4.4 板载按键原理图
6.4.5 项目分析
本次项目完成KEY1点亮两个LED灯,KEY2熄灭两个LED灯,KEY3留给大家自行发挥; 通过前面的知识,我们已经知道了按键检测的原理与按键连接的引脚; KEY1--->PB13
KEY2--->PB14
KEY3--->PB15
从原理图中可看出,按键一侧接GND,所以需要判断GPIO引脚为低电平,从而判断按键是否按下。
6.4.6 按键配置流程
配置流程
GD32的所有外设资源时钟默认都是关闭的,在配置外设之前需要先开启对应的时钟。
时钟库函数在gd32e23x_rcu.h头文件中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
//使能时钟
rcu_periph_clock_enable(RCU_GPIOB);
2
在配置模式前,首先需要了解GPIO的模式,GD32E230GPIO模式有以下几种:
GPIO_MODE_INPUT-------输入模式
GPIO_MODE_OUTPUT-----输出模式
GPIO_MODE_AF-----------复用模式(引脚复用功能时使用)
GPIO_MODE_ANALOG----模拟模式(ADC读取模拟量时使用,可以读取细微变化的值)
当GPIO用作输入时,还会有以下几种情况可以设置:
GPIO_PUPD_NONE--------引脚浮空
GPIO_PUPD_PULLUP------引脚上拉
GPIO_PUPD_PULLDOWN-引脚下拉
相关GPIO操作库函数在gd32e23x_gpio.h中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
此函数有四个参数:
第一个参数是引脚端口,第二参数设置引脚模式;
第三个参数是设置上下拉情况,第四个参数是具体端口引脚号;
此处我们使用引脚的输入功能,按照原理说明,设置为引脚上拉;可通过“|”语法一次配置多个引脚
//设置输出模式,上拉
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
2
引脚配置完成后,现在可通过检测GPIO是否为低电平来判断按键是否按下。相关GPIO操作库函数在gd32e23x_gpio.h中,也可通过附件资料中的《GD32固件库使用指南》查看该函数的详细说明。
//高电平返回SET,低电平返回RESET
gpio_input_bit_get(GPIOB,GPIO_PIN_13)
2
此时可自行配置对应按键引脚检测是否是低电平来判断按键是否按下。
6.4.7 项目完整源码
下载成功后即可看到,按下KEY1,板载LED灯点亮,按下KEY2,板载LED灯熄灭。
此处项目源码中,有一个小细节,当我们检测按键是低电平时,开始延迟10ms进行消抖,随后再次判断,如果还是低电平,那就确定是按键按下,但还会有另外一个问题出现,如果我们一直按着按键,那就会一直是低电平,就会造成反复判断。
所以,加入一个死循环,如果按键一直按下,就一直循环,当按下松手的时候,才算一次完整的按键按下。
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "led.h"
#include "main.h"
int main(void)
{
//初始化滴答定时器
systick_config();
//使能时钟
rcu_periph_clock_enable(RCU_GPIOC);
//使能时钟
rcu_periph_clock_enable(RCU_GPIOB);
//设置输出模式,不上下拉
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
//设置输出模式,上拉
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
//设置输出类型,推挽输出,50Mhz
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
//没有按键按下时引脚输出为高电平,不点亮
gpio_bit_set(GPIOC,GPIO_PIN_14);
gpio_bit_set(GPIOC,GPIO_PIN_15);
while(1)
{
//KEY1按下
if(gpio_input_bit_get(GPIOB,GPIO_PIN_13)==RESET)
{
//消抖
delay_1ms(10);
//判断是否是真的按下
if(gpio_input_bit_get(GPIOB,GPIO_PIN_13)==RESET)
{
//等待按键松手
while(gpio_input_bit_get(GPIOB,GPIO_PIN_13)==RESET);
//点亮LED
gpio_bit_reset(GPIOC,GPIO_PIN_14);
gpio_bit_reset(GPIOC,GPIO_PIN_15);
}
}
if(gpio_input_bit_get(GPIOB,GPIO_PIN_14)==RESET)
{
delay_1ms(10);
if(gpio_input_bit_get(GPIOB,GPIO_PIN_14)==RESET)
{
while(gpio_input_bit_get(GPIOB,GPIO_PIN_14)==RESET);
gpio_bit_set(GPIOC,GPIO_PIN_14);
gpio_bit_set(GPIOC,GPIO_PIN_15);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
6.4.8 注意事项
- 软件消抖延迟时间为经验值,可根据实际情况增大或减少。
- 此时我们按键扫描是在while(1)中,无间断的扫描,如果此时在循环中加入延迟函数,那么延迟函数在延时时也是死循环,意味着延时时间内你按键按下是没有任何判断的,所以不建议在循环中加入大延迟函数或做大延迟操作。
6.5. 串口调试实验
6.5.1 串口介绍
串口是指外设和处理器之间通过数据信号线、地线和控制线等,按位进行传输数据的一种通讯方式。尽管传输速度比并行传输低。但串口可以在使用一根线发送数据的同时用另一根线接收数据。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位,这些参数在两个通信端口之间必须一致。
6.5.2 串口通信参数介绍
- 波特率: 衡量通信速度的参数,它表示每秒钟传送的bit的个数。
- 数据位: 衡量通信中实际数据位的参数,表示一个信息包里包含的数据位的个数。
- 停止位: 用于表示单个信息包的最后位,典型值为1、1.5和2位。由于数据是在传输线上传输的,每个设备都有自己的时钟,很有可能在通信过程中出现不同步,停止位不仅仅表示传输的结束,还能提供校正时钟同步的机会。停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率也越慢。
- 奇偶检验位: 表示一种简单的检查错误的方式。
6.5.3 串口工作模式
串口可以工作在单工、半双工和全双工模式下。
- 单工:在通信的任意时刻,信息只能由A传到B。
- 半双工:在通信的任意时刻,信息即可由A传到B,又能由B传到A,但同时只能有一个方向上的传输存在。
- 全双工:在通信的任意时刻,通信线路上存在A到B和B到A的双向信号传输。
6.5.4 串口通信协议
串口在进行通信的时候会按照数据包的形式进行发送,帧格式如下所示。
串口通信是一位一位地传输,每传输一个字符总是以起始位开始,以停止位结束,字符之间没有固定的时间间隔要求。每一个字符的前面都有一位起始位(低电平),后面由7位数据位组成,接着是一位校验位,最后是停止位。停止位后面是不定长的空闲位,停止位和空闲位都规定为高电平。
6.5.5 串口接口原理图
在开发板上默认使用的串口是串口0,有两根数据线,也就是USART0_TX和USART0_RX。串口引脚和下载引脚连接在Type-C,插上Type-C就可以进行串口调试。关于串口原理图如下所示。
6.5.6 项目分析
本次项目实现串口发送“Hello World”到电脑,通过电脑串口助手进行显示查看。
通过前面的知识,我们已经知道了串口通信的原理与连接的引脚; USART0_TX--->PA9
USART0_RX--->PA10
6.5.7 串口配置流程
一般我们使用串口,都需要有以下几个步骤;
- 开启时钟(包括串口时钟和GPIO时钟)
- 配置GPIO复用模式
- 配置GPIO的模式
- 配置GPIO的输出
- 配置串口(配置串口参数)
- 使能串口(串口使能和发送使能)
串口0使用的是PA9,PA10引脚,首先需要开启GPIOA时钟,同时需要把串口外设时钟打开。
时钟库函数在gd32e23x_rcu.h头文件中,也可通过附件资料中的GD32固件库使用指南查看该函数的详细说明。
//使能时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
2
3
GPIO引脚默认是作为普通IO使用,当需要使用外设功能时,需要打开对应的复用模式,在gd32e23x_gpio.h中可以查找到设置复用功能的函数。也可通过附件资料中的《GD32固件库使用指南》与《GD32E230数据手册》查看该函数与复用功能的详细说明。
该函数有三个参数:
- 第一个参数是配置引脚端口;
- 第二个参数是复用功能,此处选择选择复用功能1,对应是串口0的USART0_TX,USART0_RX;
- 第三个参数是实际的引脚号;
//复用功能设置
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
2
3
GPIO模式配置函数在前两节已经详细介绍过了,这里就不在赘述了。与前面两节不同的是,这里模式配置参数需要设置为复用功能而不是输入或输出,第三个参数为上拉。
//端口模式设置
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
//端口模式设置
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
2
3
4
配置GPIO输出函数也已经在前两节详细介绍过了,这里与前面的配置一样,修改引脚即可直接复用。
速度此处设置为10Mhz,也可以设置为50Mhz,并不影响,串口实际发送速度受波特率影响,这里只是决定波特率上限,一般情况下波特率不建议设置超过921600,。
//GPIO输出配置
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
2
3
和之前库函数点灯不同的是,使用串口还需要进行串口的配置。前面只是对GPIO的引脚做了配置,然后就可以操作GPIO进行输入输出,但是要使用串口,还需要设置串口的一些参数,后面使用其它的外设资源也是如此。
在使用一个外设之前,可以先复位一下,防止一些不必要的事情发生。在对应的外设库文件中一般都会有这个函数。在gd32e23x_uart.h头文件中有这个函数的声明,也可以通过《GD32库函数指南》查看详细说明。
//串口复位函数,参数是对应的串口接口
void usart_deinit(uint32_t usart_periph);
2
根据上述串口通信参数介绍,依次来对参数进行配置。以下配置函数均可在附件资料中的《GD32固件库使用指南》中找到对应的函数说明。
除了对串口通信参数进行配置,如果需要串口发送数据,则需要使能发送功能,这里我们不做数据接收,接收使能就无需配置。
最后是串口的总开关,我们需要使能串口才可打开串口功能。简单理解串口的发送与接收是功能开关,使能哪个就是打开哪个功能,串口使能是总开关,只有总开关打开才能使用串口功能。
usart_word_length_set(USART0, USART_WL_8BIT); //8位数据位宽
usart_stop_bit_set(USART0, USART_STB_1BIT); //1位停止位
usart_parity_config(USART0, USART_PM_NONE); //无校验位
usart_baudrate_set(USART0, bound); //波特率
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); //发送使能
//使能串口0
usart_enable(USART0)
2
3
4
5
6
7
上述串口通信参数的配置与电脑的串口调试助手参数需保持一致,否则无法正常通信。该串口助手读者可自行前往微软应用商店进行下载。
至此,串口配置参数完成。
6.5.8 串口发送数据
配置好串口参数后,下一步操作就是要发送数据。
发送数据的流程分三步走:
第一步:调用串口发送函数;
第二步:等待串口发送完成;
第三步:清除串口发送标志位;
void USART0_SendData(char sendData)
{
//串口发送函数,发送一个字节数据
usart_data_transmit(USART0, (uint8_t)sendData);
//等待串口发送完成
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
//清除串口发送标志位
usart_flag_clear(USART0,USART_FLAG_TC);
}
2
3
4
5
6
7
8
9
打开电脑串口调试助手,当我们调用该发送函数时,
USART0_SendData('h');
USART0_SendData('e');
USART0_SendData('l');
USART0_SendData('l');
USART0_SendData('o');
2
3
4
5
将会在串口助手上打印"hello"
这样每发送一个字节就需调用依次函数,非常麻烦,这里对发送函数进行修改,使用C语音指针的功能,实现串口发送字符串函数。
void USART0_SendString(char *SendString)
{
//清除串口发送标志位
usart_flag_clear(USART0,USART_FLAG_TC);
//判断字符串是否到末尾
while(*SendString != '\0')
{
//串口发送函数,发送一个字节数据
usart_data_transmit(USART0, (uint8_t)*SendString);
//等待串口发送完成
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
//清除串口发送标志位
usart_flag_clear(USART0,USART_FLAG_TC);
//字符串向后移一位
SendString++;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
现在通过调用一个函数即可发送“hello world”;
USART0_SendString("hello world");
6.5.9 串口重定向
上面封装的发送字符串的方式打印信息看似方便,但如果我们想要打印数字,小数,该怎么打印呢?大家在学习C语言时已经习惯了使用了printf这个函数来进行打印,可以通过%d,%f打印整形和小数,这一小节就教大家怎么把串口重定向到printf函数中。
C语言中的printf函数默认输出设备是显示器,如果要在串口显示,必须重新定义标准库函数里调用的与输出设备相关的函数。需要注意的是,在keil中使用printf一定要勾选“MicroLIB”选项。
在C语言通用编辑器中,printf函数是通过调用fputc函数来向标准输出流输出数据,这里我们是需要printf函数来向串口输出数据,所以需要重写fputc函数,将其中的内容改为向串口发送数据。
重写函数需要与原函数的名称、参数、返回值保持一致,这里大家不可修改。重写好之后就可以通过printf函数输出信息了。
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t) ch);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
return ch;
}
2
3
4
5
6
6.5.10 注意事项
串口连接是在GD32E230小板上的TYPEC接口处,底板TYPE-C接口仅支持供电。想要使用串口功能,需将TYPE-C线连接到核心板的接口上;
每个人连接到电脑上的COM端口都不会完成一致,不需要完全按照图片的COM端口来。
6.6. 外部中断实验
6.6.1 什么是中断
中断即打断,当单片机在顺序执行当前程序时,由于系统出现了某种需要紧急处理的情况,单片机暂停正在执行的程序,转而去执行紧急事务,当处理结束后单片机自动返回到原先暂停的程序中继续执行,这种执行过程由于外界的原因被打断的情况叫做中断。
6.6.2 外部中断
GD32E230外部中断EXTI(中断/事件控制器)包括21个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。 EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI中的每一个边沿检测电路都可以独立配置和屏蔽。
6.6.3 中断触发源
GD32E230外部中断EXTI触发源包括来自I/O管脚的16根线以及来自内部模块的5根线。通过配置SYSCFG_EXTISSx寄存器,所有的GPIO管脚都可以被选作EXTI的触发源。
6.6.4 中断优先级
GD32E230是M23内核,没有中断优先级分组的概念,支持四个优先级设置(0~3),数字越小,等级越高。
6.6.5 项目分析
本次项目需要通过板载EC11旋转编码器来点亮&熄灭LED灯。正转一格点亮LED灯,反转一格熄灭LED灯。根据前面的原理介绍,可以通过外部中断下降沿触发来判断正转还是反转。
6.6.6 外部中断配置流程
一般使用GPIO的外部中断功能,都需要有以下几个步骤:
- 开启时钟(包括GPIO时钟和系统配置时钟)
- 配置GPIO的模式
- 使能NVIC中断并配置优先级
- 配置GPIO中断
- 使能中断和清除中断标志位
- 编写中断服务函数
GD32单片机的任意引脚都可以配置为外部中断触发,我们对应的去使能引脚的外部中断功能即可。
GD32的所有外设资源时钟默认都是关闭的,在配置外设之前需要先开启对应的时钟。这里我们需要开启按键引脚的GPIO端口时钟,同时还需要开启系统配置时钟,用于中断。
//使能时钟GPIOB,CMP
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_CFGCMP);
按键GPIO模式设置与前面的实验一致,将对应引脚设置为输入模式以及引脚上拉;
//设置引脚输入模式,上拉
gpio_mode_set(GPIOB,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_9);
2
3
4
5
6
想要使用中断,必须先使能它,同时,考虑到有多个不同的中断可能同时呼叫CPU,中断具有优先级,当有两个或两个以上中断同时产生,会优先响应等级高的中断,若中断优先级相同,则会根据中断向量表的位置来进行响应,位置靠前的先进行响应。
中断使能函数有两个参数,第一个参数是中断源类型,第二个是优先级,相关函数说明文档在《GD32库函数使用说明》中可以找到;
这里EC11旋转编码器KEYA接入的引脚是PB4,对应外部中断是4,所以中断源选择EXTI4_15_IRQn,
注意这里并不需要对KEYA与KEYB引脚都进行中断使能,通过前面的原理分析,我们可以通过在KEYA的下降沿读取KEYB电平是否与KEYA一致来判断是正转还是反转。
//中断使能,优先级为1
nvic_irq_enable(EXTI4_15_IRQn,1U);
2
使能中断源与中断优先级后,需要将中断线与GPIO引脚进行绑定,将对应GPIO引脚设置为中断引脚即可。
相关中断初始化函数在《GD32库函数使用说明指南》中有说明;
//配置中断线
syscfg_exti_line_config(EXTI_SOURCE_GPIOB,EXTI_SOURCE_PIN4);
2
现在该引脚已经可以作为中断引脚使用了,但是中断触发条件还未指明,需要对该中断线配置中断触发条件,当满足触发条件后,进入中断服务函数;
GD32E230总共有28条中断线,每一条中断线的对应的触发源在《GD32E230用户手册》第95页有说明;
相关中断初始化函数在《GD32库函数使用说明指南》中有说明;
//初始化中断线,设置为中断模式,下降沿触发
exti_init(EXTI_4,EXTI_INTERRUPT,EXTI_TRIG_FALLING);
2
配置好中断后,就可以开启中断了;前面配置中断优先级的使能是开启终端总开关,这里是中断线的开关。
//中断线使能
exti_interrupt_enable(EXTI_4);
2
同时,为了确保中断能够有效进入,在使用前先清一下中断标志位
//中断标志位清除
exti_interrupt_flag_clear(EXTI_4);
2
自此,中断配置完成,当指定中断线检测到下降沿到来时,进入中断服务函数。
中断服务函数不同于普通函数,它的函数名是固定的,也就是函数入口地址固定,在startup_gd32e23x.s启动文件中有定义。
中断服务函数并不只为某一个特殊的中断事件服务,所以,在进入中断服务函数后,需要先进行判断,检测对应中断标志位是否有被置位,确定是指定中断事件产生后再进行相应的处理。
处理完成后续将相应的中断标志位进行清除,以待下一次进入。
void EXTI4_15_IRQHandler(void)
{
if(RESET != exti_interrupt_flag_get(EXTI_4))
{
if(gpio_input_bit_get(GPIOB,GPIO_PIN_3) == RESET)
{
//反转
}
else
{
//正转
}
exti_interrupt_flag_clear(EXTI_4);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
6.6.7 实验现象
烧录代码后,正向旋转EC11,LED灯点亮,反向旋转EC11,LED灯熄灭。
文件下载
通过网盘分享的文件:Demo4-外部中断实验.7z 链接: https://pan.baidu.com/s/1fGC1yfJr7zCtvzt0wTpu4A?pwd=LCKF 提取码: LCKF
6.6.8 注意事项
中断服务函数内不做耗时较长或死循环操作
中断服务函数是响应中断事件的处理,处理内容应简明扼要,如果需要延时或复杂逻辑,最好是通过全局变量标志位将其放置在顺序逻辑内执行。
多个中断事件如何处理
当程序中存在多个中断事件时,应对事件紧急程度进行排序,最紧急的分配最高优先级。
EC11抖动问题如何解决
可以尝试在硬件引脚处加入100nf电容到GND进行硬件滤波;
软件滤波原理是;通过判断两次EC11-A相位变化时;B相位是否发生变化;若发生变化,则根据变化判断正转或反转;若A相位上升沿、下降压B相位都没有发生变化;则是抖动;
6.7. ADC检测实验
6.7.1 什么是ADC
ADC是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化。而数字信号则是离散的,仅能取有限的值。ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。
6.7.2 GD32的ADC介绍
GD32E230片上集成了12位逐次逼近式ADC,可以采样来自于10个外部通道、2个内部通道的模拟信号,最快采样速率2MSPS。
6.7.3 ADC转换使用说明
假设此时ADC分辨率设置为12位,采集一个0~5V的电压信号,这意味着我们的输入电压0~5V将被分成4096个离散模拟值,4096是12位ADC的分辨率(2^12=4096)。
这样,如果实际输入电压为0V,则单片机的ADC会读取到的值为0,如果是5V,单片机将读取4096,如果是2.5V,则MCU将读取2048。
另外一个知识点是ADC的参考电压,在ADC转换过程中,通过与参考电压进行比较,来获取未知电压,GD32E230ADC参考电压与供电电压保持一致,一般情况下为(3.3V)。
可以使用以下公式根据ADC的分辨率和ADC获取的值来转换为实际的电压值。
6.7.4 ADC引脚原理图
这里我们仅需知道ADC引脚是PA3即可,相关电路分析可前往本文的 二、电路原理分析
6.7.5 项目分析
本次项目是将获取到的ADC值转换为电压值通过串口发送到串口调试助手上进行显示。
6.7.6 ADC配置流程
一般使用ADC功能,都需要以下几个步骤:
- 配置时钟
- 配置引脚
- 配置ADC模式
- 配置数据对齐方式
- 配置分辨率
- 配置采集通道与通道长度
- 配置触发方式
- 使能ADC
- 开启校准
使用ADC功能,需要开启ADC的时钟,同时ADC引脚也是GPIO,我们同样需要使能GPIO时钟。
ADC的时钟来源有AHB(72MHz-MAX)、APB2(72MHz-MAX)和IRC28M(28MHz)总线,我们需要选择一个时钟源,并将其分频到28MHz以下。
本次实验选择AHB总线,使用3分频,这样输入时钟是72/3=24Mhz,接近最大速度了。
//使能引脚时钟
rcu_periph_clock_enable(RCU_GPIOA);
//使能ADC时钟
rcu_periph_clock_enable(RCU_ADC);
//配置ADC时钟,3分频
rcu_adc_clock_config(RCU_ADCCK_AHB_DIV3);
2
3
4
5
6
从前面的引脚原理图可知,ADC引脚是PA3;在配置引脚模式时需要注意,引脚必须设置为模拟浮空输入模式,若设置为上拉或下拉模式,则采集的电压是不准确的。
//配置引脚为模拟浮空输入模式
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3);
2
在代码中能够配置的ADC模式有以下三种情况:
ADC_SCAN_MODE:扫描模式
ADC_INSERTED_CHANNEL_AUTO:注入组自动转换模式
ADC_CONTINUOUS_MODE:连续模式
这里的注入组自动转换模式仅在使用注入组的情况下使用,这里不做说明,可自行参考《GD32E230用户手册》。
在连续模式下,一旦ADC被启动,它会不断对选定的通道进行采样和转换,直到函数命令其停止,连续模式下转换无需重新启动。
在扫描模式下,ADC按照预设的顺序自动扫描多个通道,并对每一个通道进行一次采样和转换,扫描模式下需要开启DMA;
注意这里的模式并不是多选一的结构,而是多选多,自由搭配,(例:连续扫描模式,可连续不间断扫描),当我们将连续模式与扫描模式都关闭,应该就是打开单次转换模式。
模式设置完成后,就是设置对应触发源与采样时长;
触发源可选择规则组通道与注入组通道(注入组类似于中断,当规则组正常运行时,若有例外情况参数,则可通过注入组打断规则组,进行采样);触发条件可选定时器、外部中断、软件触发三种;
//关闭扫描模式
adc_special_function_config(ADC_SCAN_MODE, DISABLE);
//关闭连续模式
adc_special_function_config(ADC_CONTINUOUS_MODE, DISABLE);
//设置为ADC规则组,软件触发模式。
adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
2
3
4
5
6
通过查找数据手册可知,PA3引脚对应ADC通道3,采样周期是单次ADC转换时间,时间越短转换越快,但误差较大。
该函数第一个参数为规则通道顺序表,在扫描模式下,是按照此顺序从小到大依次扫描转换的。 相关函数说明可参考《GD32固件库使用指南》
//常规通道配置。通道1 采样周期为55.5
adc_regular_channel_config(0U, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5);
2
GD32E230C8T6微控制器的ADC支持各种数据对齐方式以适应不同的应用场景。常见的数据对齐方式包括左对齐和右对齐。在右对齐模式下,ADC的数据在转换结束后被右对齐到最低位,不足的位数在高位填充0。右对齐模式允许实现在没有精度的损失下更好的动态范围。
在左对齐模式下,ADC的数据被左对齐到最高位,不足的位数在低位填充0。左对齐模式可以提高分辨率,但会导致动态范围降低。
这里选择右对齐方式,可以直接将转换完成的数据进行计算。
//数据右对齐
adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
2
GD32E230C8T6的分辨率可以配置为6、8、10、12 位。我们可以通过降低 ADC 的分辨率,获得较快的转换时间(tADC)。对于那些不需要高精度数据的应用,可以使用较低的分辨率来实现更快速地转换。
这里配置为12位分辨率
//ADC设置为12位分辨率
adc_resolution_config(ADC_RESOLUTION_12B);
2
此处配置的是规则组通道,且仅使用了通道3。若后续使用了多个规则组通道,需修改规则组通道的长度,以便扫描模式下循环遍历。
//ADC设置为规则组 一共使用 1 个通道
adc_channel_length_config(ADC_REGULAR_CHANNEL, 1);
2
触发方式有三种,定时器事件触发、外部中断触发、软件触发;
其中外部触发输入的上升沿、下降沿可以触发规则组或注入组的转换。具体见GD32E23用户手册的第150页。需要注意的是可以实时修改外部触发选择,在修改期间不会出现触发事件。
软件触发方式是由软件触发启动,通常用在单次采样转换中。在软件触发模式下,ADC转换工作被触发后,由软件控制以固定的时间间隔进行转换,因此通常用于采集时间不太关键,但要求精度较高的应用。
这里选择的是软件触发方式,软件触发方式的配置如下:
//ADC外部触发配置使能
adc_external_trigger_config(ADC_REGULAR_CHANNEL,ENABLE);
//使能软件触发
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
2
3
4
ADC配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。需要注意的是要先使能了ADC之后,才能开始进行自校准。
使能ADC后做一个简短的延迟,等待ADC使能完毕。
//ADC使能
adc_enable();
delay_1ms(1U);
2
3
ADC 带有一个前置校准功能。在校准期间,ADC 会计算一个出厂校准系数,这个系数是应用于 ADC 内部的,它直到 ADC 下次掉电才无效。在 A/D 转换前应执行校准操作。在校准期间,应用不能使用 ADC,它必须等到校准完成。通过软件设置 CLB=1 来对校准进行初始化,在校准期间CLB 位会一直保持 1,直到校准完成,该位由硬件清 0。可以直接使用库函数调用校准,完成操作。
开启校准的配置如下:
//开启ADC自校准
adc_calibration_enable();
2
6.7.7 采集转换
我们之前使用的是单次转换模式并使用的是软件触发方式,因此当我们要采集信号时,需要配置好采集通道并开启软件转换。开启转换后,等待EOC标志位置1。其中EOC为转换完成标志位,我们可以通过判断其是否置1,确定是否转换完成。转换完成后将数据从16位寄存器中取出。
/**********************************************************
* 函 数 名 称:Get_ADC_Value
* 函 数 功 能:读取ADC值
* 传 入 参 数:ADC_CHANNEL_x=要采集的通道
* 函 数 返 回:测量到的值
* 作 者:LC
* 备 注:默认采样周期为55.5个ADC采样时间
**********************************************************/
uint16_t Get_ADC_Value(uint8_t ADC_CHANNEL_x)
{
uint16_t adc_value = 0;
//设置采集通道
adc_regular_channel_config(0, ADC_CHANNEL_x, ADC_SAMPLETIME_55POINT5);
//开始软件转换
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
//等待 ADC 采样完成
while ( adc_flag_get(ADC_FLAG_EOC) == RESET )
{
}
//读取采样值
adc_value = adc_regular_data_read();
//返回采样值
return adc_value;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
6.7.8 实验现象
烧录代码后,在串口助手中会发现ADC值与电压值输出。
6.7.8 注意事项
无法打开串口
串口连接是在GD32E230小板上的TYPEC接口处,底板TYPE-C接口仅支持供电。想要使用串口功能,需将TYPE-C线连接到核心板的接口上。
每个人连接到电脑上的COM端口都不会完成一致,不需要完全按照图片的COM端口来。
6.8. 定时器中断实验
6.8.1 什么是定时器
定时器是单片机内部集成,可以通过编程控制。单片机的定时功能是通过计数来实现的,当单片机每一个机器周期产生一个脉冲时,计数器就加一。定时器的主要功能是用来计时,时间到达之后可以产生中断,提醒计时时间到,然后可以在中断函数中去执行功能。比如我们想让一个led灯1秒钟翻转一次,就可以使用定时器配置为1秒钟触发中断,然后在中断函数中执行led翻转的程序。
6.8.2 GD32定时器
GD32E230C8T6一共有7个定时器,可以分为六种类型:
- 高级定时器0
- 通用定时器(L0)2
- 通用定时器(L2)13
- 通用定时器(L3)14
- 通用定时器(L4)15/16
- 基本定时器5
不同类型的定时器所拥有的功能数量不同,一般高级定时器的功能最多,通用定时器次之,基本定时器功能最少。具体功能对照可以查看《GD32用户手册》的第206页。
6.8.3 定时器基本参数
预分频
预分频器可以将定时器的时钟(TIMER_CK)频率按1到65536(uint16_t)之间的任意值分频,分频后的时钟PSC_CLK驱动计数器计数。分频系数受预分频器TIMERx_PSC控制。这个控制寄存器带有缓冲器,它能够在运行时被改变。新的预分频器的参数在下一次更新事件到来时被采用。
分频器的分频公式为:PSC_CLK = TIMER_CK/ (TIMERx_PSC +1)
周期值
计数器溢出最大值,在初始化结构体配置时是uint32_t类型,在使用函数单独设置周期值时是uint16_t类型。
计数模式
GD32E230定时最多支持以下三种计数模式:
向上计数模式:计数器从0计数到自动加载值(周期值),然后重新从0开始计数并产生一个计数器溢出事件;
向下计数模式:计数器从自动加载值(周期值)开始向下计数到0,然后从自动装载值重新开始,并产生一个计数器向下溢出事件。
中央对齐模式(向上/向下计数):计数器从0开始计数到自动装载值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
6.8.4 项目分析
本次实验通过配置通用定时器15,使其每隔0.5s进入一次中断,让LED灯闪烁。
一般使用定时器功能,都需要以下几个步骤:
- 开启时钟(定时器时钟)
- 配置定时器参数
- 配置中断优先级
- 使能中断事件和定时器
- 编写中断服务函数
6.8.5 定时器配置流程
先来看一下定时器的时钟来源,在《GD32E230数据手册》的第18页;从图中可以看到所有定时器时钟来源都是CK_AHB(MAX=72Mhz)。
这里使用Timer15,就需要使能Timer15的时钟,从时钟树中可以看到,Timer15的时钟来源于APB2分频器/2,APB2时钟又需要外设使能,所以我们需要使能定时器外设时钟;
在时钟树下方还有一句话,如果APB1分频器设置为1,则定时器时钟是AHB时钟/1,否则是AHB时钟/2;
默认情况下APB1分频器是设置为1的,所以时钟的最大频率就是AHB时钟=72Mhz。
//使能时钟
rcu_periph_clock_enable(RCU_TIMER15);
2
开启时钟后,需要配置定时器的参数,比如预分频值、周期值、计数模式等等。
在配置参数前,建议先复位定时器外设与参数结构体,保证定时器处于初始状态,注意定时器参数结构体需要在函数最开始处进行声明:
//定时器初始化参数结构体
timer_parameter_struct timer_initpara;
//复位定时器15
timer_deinit(TIMER15);
// 初始化结构体
timer_struct_para_init(&timer_initpara);
2
3
4
5
6
定时器参数使用结构体类型进行整合,这里依次对结构体成员进行赋值完成配置:
这里介绍一下分频值参数与周期参数应该如何设置:
定时器时钟=72Mhz(72_000_000);
此时预分频值是7199,实际预分频值会+1,也就是7200;
此时频率为(72_000_000)/ 7200=10000Hz
若此时将周期设置为4999;计时时间=(5000)/10000=0.5s
在设置分频值与周期时需要注意最大值限制,prescaler(uint16_t)、period(uint32_t);
// 配置定时器参数
timer_initpara.prescaler = 7199; // 时钟预分频值 0-65535 psc_clk = CK_TIMER / pre
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timer_initpara.period = 4999; // 周期
//在输入捕获的时候使用 数字滤波器使用的采样频率之间的分频比例
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; // 分频因子
//只有高级定时器才有 配置为x,就重复x+1次进入中断
timer_initpara.repetitioncounter = 0; // 重复计数器 0-255
timer_init(TIMER15,&timer_initpara); // 初始化定时器
2
3
4
5
6
7
8
9
10
自此,定时器参数设置完成;
定时器参数配置完成后,需要配置对应的中断源与优先级,才可以在定时器计时时间到来后触发中断源中断执行对应的功能;
//中断优先级
nvic_irq_enable(TIMER15_IRQn,0U);
2
通过配置中断事件来使定时器按照指定情况触发中断,在使能更新中断前,建议先清除中断标志位,确保中断处于初始状态;
定时器中断事件有很多,此处设置为更新中断事件,当计时器计数完成更新时进入中断;
//定时器更新中断标志位清除
timer_interrupt_flag_clear(TIMER15,TIMER_INT_FLAG_UP);
//使能更新事件中断
timer_interrupt_enable(TIMER15,TIMER_INT_UP);
2
3
4
定时器配置完成,需打开总开关才可使定时器正常工作;
注意,当使能定时器后,定时器自动开始计数,时间到后会按照设置的中断事件进入中断;若不想此时打开定时器,可先不使能;
//使能定时器15
timer_enable(TIMER15);
2
使能中断、使能定时器后,如果中断事件到来,就会跳转到中断服务函数中执行。需要编写对应的中断服务函数,来实现指定的功能;
中断服务函数名是固定的,在startup_gd32e23x.s启动文件中有定义。
中断服务函数并不只为某一个特殊的中断事件服务,所以,在进入中断服务函数后,需要先进行判断,检测对应中断标志位是否有被置位,确定是指定中断事件产生后再进行相应的处理。
处理完成后续将相应的中断标志位进行清除,以待下一次进入。
void TIMER15_IRQHandler(void)
{
static uint8_t num=0;
if(timer_interrupt_flag_get(TIMER15,TIMER_INT_FLAG_UP) == SET)
{
if(num == 1){
Open_LED();
}
else{
num=0;
CLose_LED();
}
num++;
//定时器更新中断标志位清除
timer_interrupt_flag_clear(TIMER15,TIMER_INT_FLAG_UP);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
6.8.6 实验现象
烧录代码后,会观察到LED灯间隔进行1进行闪烁。
6.8.7 注意事项
程序运行死机
中断服务函数是响应中断事件的处理,处理内容应简明扼要,如果需要延时或复杂逻辑,最好是通过全局变量标志位其放置在顺序逻辑内执行。
6.9. PWM输出实验
6.9.1 什么是PWM
PWM(Pulse-width modulation 是脉冲宽度调制的缩写。脉冲宽度调制是一种模拟信号电平数字编码方法。脉冲宽度调制PWM是通过将有效的电信号分散成离散形式从而来降低电信号所传递的平均功率的一种方式。所以根据面积等效法则,可以通过对改变脉冲的时间宽度,来等效的获得所需要合成的相应幅值和频率的波形。
6.9.2 GD32-PWM介绍
前面我们介绍了GD32E230C8T6一共有7个定时器,可以分为六种类型,高级定时器0、通用定时器(L0)2、通用定时器(L2)13、通用定时器(L3)14、通用定时器(L4)15/16和基本定时器5。
而PWM功能就是在定时器的基础上实现的,但不是所有的定时器都支持PWM输出功能。
从《GD32E230用户手册》上可以了解到
- 高级定时器拥有4个PWM通道
- 通用定时器L0拥有4个PWM通道
- 通用定时器L2拥有1个PWM通道
- 通用定时器L3拥有2个PWM通道
- 通用定时器L4拥有1个PWM通道
基本定时器没有PWM通道。每一个PWM通道都对应单片机的一个管脚,这个引脚不是唯一固定的,可能有一个或者两个管脚都对应同一个通道。比如说TIMER14_CH0对应PA2和PB14,就是说PA2和PB14管脚都可以配置为定时器的通道0,我们在使用的时候可以任选其一进行配置。
6.9.3 PWM基本参数
PWM是脉冲宽度调制,具有两个非常重要的参数:频率和占空比。
- 频率:PWM的频率是整个周期的倒数。
- 占空比:占空比是指一个周期内高电平所占的比例。
控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等但宽度不一致的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使个脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,即可改变电路输出电压的大小,也可改变输出频率。
6.9.4 PWM应用原理图
底板使用2*2.54排针接口将PWM信号(PA2)进行引出,方便使用示波器进行测量。
6.9.5 项目分析
通过本次实验,实现引脚PA2输出占空比50%频率为1kHz的方波。
6.9.6 PWM配置流程
一般使用定时器PWM功能,都需要有以下几个步骤:
- 配置通道引脚GPIO
- 配置定时器
- 配置输出结构体
- 配置定时器输出通道
- 配置定时器输出通道占空比
- 定时器自动重载使能
从上方原理介绍已经了解到,PWM信号通过PA2进行输出,通过查找《GD32E230数据手册》可以看到PA2对应定时器14通道0;
这里选择PA2的复用功能AF0模式进行映射。
使用GPIO引脚功能以及定时器功能,需要打开其时钟,然后对GPIO引脚进行配置,使其映射到AF0模式下:
//使能时钟
rcu_periph_clock_enable(RCU_GPIOA);
//使能定时器14时钟
rcu_periph_clock_enable(RCU_TIMER14);
//GPIO复用模式设置--PA2-TIMER14_CH0
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
//输出类型设置
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
//复用模式0
gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_2);
2
3
4
5
6
7
8
9
10
11
PWM输出依赖定时器,所以需要对定时器进行配置,但是PWM输出并不需要使用到定时器中断功能,所以无需配置中断。
在配置定时器前,在函数最前方定义定时器参数结构体:
//定时器初始化参数结构体
timer_parameter_struct timer_initpara;
2
在配置参数前,先对定时器以及定时器参数结构体进行初始化,确保其在初始状态。
//复位定时器14
timer_deinit(TIMER14);
//初始化定时器结构体参数
timer_struct_para_init(&timer_initpara);
2
3
4
5
配置定时器参数,注意参数设置:
要求输出1KHz,系统时钟为72MHz(72_000_000);
预分频器参数为(71+1),定时器频率为(1_000_000);
周期设置为1000-1;
timer_initpara.prescaler = 71; //预分频器参数
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; //边沿对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; //向上计数
timer_initpara.period = 999; //周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; //时钟分频
timer_initpara.repetitioncounter = 0; //重装载值
timer_init(TIMER14, &timer_initpara);
2
3
4
5
6
7
自此,定时器参数设置完成。
要使用定时器的PWM功能就是用定时器的输出功能,PWM输出的相关参数通过定时器通道输出参数结构体进行配置。
outputstate: 通道输出状态(打开或关闭)
outputnstate: 通道互补输出状态(打开或关闭)互补是指定时器的两个通道在波形输出上完全相反,形成互补,常用与H桥驱动电路上,这里无需打开;
ocpolarity: 输出极性,也就是配置为低电平有效还是高电平有效,通常配合PWM模式使用
ocnpolarity: 互补输出极性
ocidlestate: 空闲状态通道输出ocnidlestate: 空闲状态互补通道输出
在配置输出状态前,先在函数最前方对输出参数结构体进行定义;
//定时器输出参数结构体
timer_oc_parameter_struct timer_ocinitpara;
2
在使用先进行初始化,确保其在初始状态;
//初始化定时器通道输出参数结构体
timer_channel_output_struct_para_init(&timer_ocinitpara);
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE; //输出状态,主输出通道开启
timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE; //互补输出状态关闭
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH; //输出极性为高
timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; //互补输出极性为高
timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; //空闲状态通道输出低
timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; //空闲状态互补通道输出低
2
3
4
5
6
7
8
9
配置好输出通道参数后,需要将该参数传入定时器对应通道中,
该函数有三个参数:参数1是定时器外设;
参数2是对应通道号,参数3是通道配置结构体。
//通道输出配置
timer_channel_output_config(TIMER14, TIMER_CH_0, &timer_ocinitpara);
2
这里对第三个参数比较值进行说明,
pulse--比较值参数,使用它来于计数器进行比较,加上周期最大值是1000,比较值设置为500,那么就是有一半的时间是低于比较值的,一半的时间是高于比较值的,形成两个区间,配合PWM模式也就变成了占空比设置。
//输出比较值
timer_channel_output_pulse_value_config(TIMER14, TIMER_CH_0, 500);
2
定时器通道输出模式设置,这里重点介绍第三个参数:
定时器通道作为PWM输出模式时,主要设置为PWM模式0或PWM模式1
PWM模式0:当计数器值小于比较值时,输出无效空闲电平,大于比较值时,输出有效电平;
PWM模式1:当计数器值小于比较值时,输出有效电平,大于比较值时,输出无效空闲电平;
//定时器通道输出模式设置
timer_channel_output_mode_config(TIMER14, TIMER_CH_0, TIMER_OC_MODE_PWM0);
2
在输出通道配置完成后,需要对未使用的模式进行关闭,并使能自动重装载寄存器,这样才可以使定时器在一次计数完成后自动开始下一次计数;
关于影子模式说明参考:STM32 影子寄存器
//影子模式输出关闭
timer_channel_output_shadow_config(TIMER14, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);
//使能自动重装载
timer_auto_reload_shadow_enable(TIMER14);
2
3
4
最后,对定时器通道以及定时器进行使能,打开总开关。
//配置定时器为主要输出函数,所有通道使能
timer_primary_output_config(TIMER14, ENABLE);
//使能定时器
timer_enable(TIMER14);
2
3
4
5
自此,定时器PWM输出全部配置完成。
6.9.7 实验代码
烧录实验代码后,使用示波器连接输出接口,会发现出现占空比为50%的1KHz方波。
6.9.8 注意事项
PWM输出无效
定时器通道输出默认是关闭的,若没打开通道仅使能定时器,会发现没有任何效果。
6.10. 输入捕获实验
6.10.1 输入捕获原理
通道输入捕获功能允许通道测量一个波形时序、频率、周期等, 输入级包括一个数字滤波器, 一个通道极性选择,边沿检测和一个通道预分频器。如果在输入引脚上出现被选择的边沿,定时器输入捕获寄存器会捕获计数器当前的值,同时捕获标志位置1,如果有使能捕获中断,则产生通道中断。
6.10.2 GD输入捕获介绍
GD32E230除基础定时器外,都有捕获比较通道,可自由选择配置。
6.10.3 捕获频率方法
下图是一个方波信号,一个信号从上升沿开始,到另一个上升沿结束,此时将输入捕获设置为上升沿捕获,当上升沿到来时,触发中断,记录当前时间,直到下一个上升沿到来时再次触发,记录两次时间的差值,即是整个信号的时间。最后通过定时器自身频率/时间=信号周期。
6.10.4 原理图
这里仅需要知道输入捕获测频引脚连接至PA6,电路说明请前往本文的 二、电路原理分析
阅读:
6.10.5 项目分析
通过本次实验,实现1KHz方波频率捕获测量,并将测量到的频率打印在串口上。
6.10.6 配置流程
一般使用定时器输入捕获功能,都需要有以下几个步骤:
- 配置通道引脚GPIO&时钟
- 配置定时器
- 配置定时器输入通道
- 配置定时器中断
- 使能定时器
- 编写中断服务函数
从上方原理介绍已经了解到,输入捕获通道引脚位于PA6,通过查找《GD32E230数据手册》可以看到PA6对应定时器2通道0;
这里选择PA6的复用功能AF1模式进行映射。
使用GPIO引脚功能以及定时器功能,需要打开其时钟,然后对GPIO引脚进行配置,使其映射到AF1模式下,同时捕获到指定跳变沿需要立刻响应,需要打开捕获中断功能:
//使能时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_TIMER2);
//设置引脚模式
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
//设置输出状态
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
//设置为复用功能
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);
//定时器中断使能
nvic_irq_enable(TIMER2_IRQn, 0U);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输入捕获依赖定时器,所以需要对定时器进行配置。
在配置定时器前,需要在函数最前方定义定时器参数结构体:
//定时器参数结构体
timer_parameter_struct timer_initpara;
2
在配置参数前,先对定时器以及定时器参数结构体进行初始化,确保其在初始状态。
//复位定时器
timer_deinit(TIMER2);
//定时器参数初始化
timer_struct_para_init(&timer_initpara);
2
3
4
5
配置定时器参数,注意参数设置:
要求捕获频率为1KHz方波,尽量保证自身定时器频率要大于被采样频率的10~100倍,否则加大计算难度。
相关频率计算参数在前两章已经介绍详细,这里就不在赘述了。
timer_initpara.prescaler = 710; //预分频器参数
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; //边沿对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; //向上计数
timer_initpara.period = 65535; //周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; //时钟分频
timer_init(TIMER2, &timer_initpara); //参数初始化
2
3
4
5
6
自此,定时器参数设置完成。
要使用定时器的输入捕获功能,需要对通道输入参数进行配置。
icpolarity:通道输入极性,指明捕获的跳边沿是上升沿还是下降沿
icselection: 通道输入模式选择,分为直接输入、间接输入、ITS输入,这个需要结合图像进行解答;一般情况选择直接输入即可。
icprescaler:输入捕获预分频值
icfilter:输入捕获滤波,简单了解就是当滤波器连续采样到N次个有效电平时,认为一次有效的输入电平。
在配置输入捕获前,先在函数最前方对输入参数结构体进行定义:
//定时器通道输入参数结构体
timer_ic_parameter_struct timer_icinitpara;
2
在使用前先进行初始化,确保其在初始状态:
//定时器通道输入参数初始化
timer_channel_input_struct_para_init(&timer_icinitpara);
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING; //通道输入上升沿
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; //通道输入直接模式
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1; //通道输入捕获1分频
timer_icinitpara.icfilter = 0x0; //通道输入捕获不滤波
timer_input_capture_config(TIMER2,TIMER_CH_0,&timer_icinitpara);
2
3
4
5
6
7
8
在输入通道配置完成后,需要使能自动重装载其,这样才可以使定时器在完成一次计数后自动开启下一次计数;
//使能自动重装载值
timer_auto_reload_shadow_enable(TIMER2);
2
在上升沿到来时,需要快速做出响应,所以需要使能定时器输入中断,并在使能前清除对应中断标志位,确保其在初始状态;
//清除中断标志位
timer_interrupt_flag_clear(TIMER2,TIMER_INT_FLAG_CH0);
//使能定时器通道中断
timer_interrupt_enable(TIMER2,TIMER_INT_CH0);
2
3
4
5
最后,打开总开关,使能定时器;
//定时器中断使能
timer_enable(TIMER2);
2
使能中断、使能定时器后,如果中断事件到来,就会跳转到中断服务函数中执行。需要编写对应的中断服务函数,来实现指定的功能;中断服务函数名是固定的,在startup_gd32e23x.s启动文件中有定义。
中断服务函数并不只为某一个特殊的中断事件服务,所以,在进入中断服务函数后,需要先进行判断,检测对应中断标志位是否有被置位,确定是指定中断事件产生后再进行相应的处理。
处理完成后续将相应的中断标志位进行清除,以待下一次进入。
在第二次捕获时,如果捕获值小于第一次的,则意味着一个周期已经过了,需要使用总周期减去第一次+第二次才是正确的。
注意不可使用局部变量,否则第一次捕获值会在第二次捕获到来是清空。
static uint16_t readvalue1 = 0, readvalue2 = 0;
static __IO uint16_t ccnumber = 0;
static __IO uint32_t count = 0;
static __IO float freq=0;
void TIMER2_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_CH0))
{
if(0 == ccnumber){
// 读第一次通道0捕获值
readvalue1 = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0);
ccnumber = 1;
}else if(1 == ccnumber){
// 读第2次通道0捕获值
readvalue2 = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0);
// 如果第二次捕获值大于第一次
if(readvalue2 > readvalue1){
count = (readvalue2 - readvalue1);
}else{
count = ((0xFFFFU - readvalue1) + readvalue2);
}
//计算频率
freq = (float)1000000U / count;
printf("freq:%.1f\r\n",freq);
readvalue1=0;
count=0;
freq=0;
readvalue2=0;
ccnumber = 0;
}
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
6.10.7 实验现象
烧录代码到单片机中,连接前方的输入探头到信号发生器,或结合上节课的输出PWM实验,测量一个1Khz的方波,会发现串口助手打印频率为1Khz。
6.11. 屏幕显示实验
6.11.1 屏幕显示原理
TFT屏主要的构成包括:背光源、导光板、扩散膜、棱镜膜、配向膜、液晶材料、薄膜晶体管等。
屏幕通电的时候,背光源发出白光,经过导光板的发射照亮整个屏幕,扩散膜让光跟家柔和,棱镜模确保光线能往正确的方向走,然后偏光片滤掉其他方向的光。只让垂直方向的偏振光通过。TFT根据需要来控制子像素电压的大小,影响液晶分子的扭曲程度,控制光的亮度。光穿过液晶,穿过彩色滤光膜,就有红光、绿光、蓝光。最后一层的偏光片只让水平偏振光通过,LCD就显示出了画面。
详细描述请前往:史上最详细最生动的TFT液晶显示屏原理介绍
6.11.2 TFT屏驱动方式
通用TFT屏幕厂商为了降低开发难度,一般会在屏幕中内置驱动芯片,同时引出引脚与外界进行通信,常见的驱动芯片有ST7735S、ILI9163、ST7789等等。
同时,TFT屏幕在引出引脚方面,也分为SPI串口引出(四线通信)、以及8080并口引出(8位)等方式。
SPI总线包括四条逻辑线,定义如下: ○MISO:Master Input slave output 主机输入,从机输出(数据来自从机);
○MOSI:Master output slave input 主机输出,从机输入(数据来自主机);
○SCLK:Serial Clock 串行时钟信号,由主机产生发送给从机;
○NSS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
屏幕厂商一般会根据自己所用驱动芯片,写好驱动案例,大家在购买屏幕时可以找厂商要对应的资料,根据板子引脚与时序进行修改即可。
6.11.3 GD-SPI特性
6.11.4 屏幕部分原理图
从简易示波器底板原理图中可以看出,该屏幕使用的是ST7735驱动芯片,引脚接口为四线SPI通信方式。
虽然看起来像IIC通信方式,但实则不是,SCL是SPI的时钟线,SDA是SPI的MOSI数据线,由于内部芯片无需向外部主控发送数据,所以使用MISO数据线就没有引出。
6.11.5 项目分析
通过本次实验,实现1.8寸TFT屏幕显示白色背景。
6.11.6 配置流程
一般使用TFT屏幕驱动,都需要有以下几个步骤:
- 配置TFT屏幕引脚GPIO&时钟
- 配置SPI驱动引脚&时钟
- 配置SPI通信参数
- 使能SPI
- 编写SPI发送数据函数
- 编写TFT屏幕发送数据函数
- 编写TFT屏幕发送命令函数
- 编写TFT屏幕设置起始地址函数
- 编写TFT屏幕初始化函数
- 移植厂商屏幕驱动函数
从上方的原理介绍中了解到,底板实验的是SPI驱动的屏幕,除了SPI通信引脚,还有复位、片选、背光、命令四个引脚。
这里需要对这四个引脚进行配置,配置方式与普通IO无异。
//使能时钟
rcu_periph_clock_enable(RCU_GPIOB);
//设置输出模式,不上下拉
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8);
//设置输出类型,推挽输出,50Mhz
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8);
//设置引脚高电平,默认拉高
gpio_bit_set(GPIOB,GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8);
2
3
4
5
6
7
8
9
10
11
使用GPIO引脚功能与SPI功能,需要打开其时钟,然后对GPIO引脚进行配置,使其映射到对应的SPI功能上。
相关引脚映射说明可查找《GD32E230数据手册》
//使能GPIOA时钟
rcu_periph_clock_enable(RCU_GPIOA);
//使能SPI0时钟
rcu_periph_clock_enable(RCU_SPI0);
//设置复用功能
gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_5 | GPIO_PIN_7);
//模式设置
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_7);
//输出设置
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
2
3
4
5
6
7
8
9
10
11
12
13
14
在配置SPI参数前,需要在函数最开始声明SPI参数结构体。
//spi结构体参数
spi_parameter_struct spi_init_struct;
2
在开始配置前,需要对SPI与参数进行复位初始化,确保其在初始状态。
//SPI0复位
spi_i2s_deinit(SPI0);
//SPI参数初始化
spi_struct_para_init(&spi_init_struct);
2
3
4
5
以下依次来对SPI参数进行讲解:
trans_mode:传输模式,SPI有四种工作模式,这里使用双线单向全双工模式,详细描述请前往
device_mode:设备模式,有主机模式与从机模式两种,其中从机模式不可产生时钟,并且不可主动向主机发送信息,这里单片机作为主机与芯片进行通信。
frame_size:帧长度,此处使用的是SPI0,根据GD32E230 SPI特性,支持8位、16为位宽选择;
clock_polarity_phase:时钟极性与相位设置,GD32将其综合为一个参数,该参数建议参考屏幕驱动芯片时序图进行设置,也可直接参考屏幕厂商提供的案例。详细描述请前往:
nss:片选信号控制,分为软件控制与硬件控制,软件控制需要在SPI发送、接收数据时通过代码将其拉低,硬件控制则是SPI控制器自行操作。
prescale:时钟分频,SPI0挂载在APB2总线上,最快频率为72Mhz,SPI时钟频率不仅与主机有关,也要考虑从机的处理能力,若发现数据发送出现问题,可尝试降低时钟频率。
endian:字节序,设置传输数据高位在前还是低位在前。
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;//双线单向全双工模式
spi_init_struct.device_mode = SPI_MASTER; //主机模式
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; //8位数据位宽
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; //极性高,第2个边缘采样
spi_init_struct.nss = SPI_NSS_SOFT; //NSS软件控制
spi_init_struct.prescale = SPI_PSC_2; //2分频
spi_init_struct.endian = SPI_ENDIAN_MSB; //高位在前
spi_init(SPI0, &spi_init_struct);
2
3
4
5
6
7
8
自此,SPI参数配置完成.
在参数配置完成后,需要打开总开关,才能使用SPI功能。
//使能SPI0
spi_enable(SPI0);
2
SPI发送数据非常简单,将待发送的数据放置在发送缓冲区即可,注意在发送前先等待缓冲区空闲。
/*
* 函数内容:SPI0发送数据
* 函数参数:无
* 返回值:无
*/
static void SPI0_Write(uint8_t data)
{
//等待发送缓冲区空闲
while(spi_i2s_flag_get(SPI0,SPI_FLAG_TBE)==RESET);
//发送数据
spi_i2s_data_transmit(SPI0, data);
}
2
3
4
5
6
7
8
9
10
11
12
13
在SPI配置时,使用的是软件控制片选,所以在发送数据前,需要先拉低片选信号告知从机,否则发送数据无法被从机接收。
/*
* 函数内容:TFT发送单个字节数据
* 函数参数:无
* 返回值:无
*/
void TFT_WR_DATA8(uint8_t data)
{
gpio_bit_reset(GPIOB,GPIO_PIN_7); //拉低片选信号
SPI0_Write(data);
gpio_bit_set(GPIOB,GPIO_PIN_7); //拉高片选信号
}
/*
* 函数内容:TFT发送2个字节数据
* 函数参数:无
* 返回值:无
*/
void TFT_WR_DATA(uint16_t data)
{
gpio_bit_reset(GPIOB,GPIO_PIN_7); //拉低片选信号
SPI0_Write(data>>8);
SPI0_Write(data);
gpio_bit_set(GPIOB,GPIO_PIN_7); //拉高片选信号
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在发送命令函数时,除了需要拉低片选信号,还需要拉低命令信号,该驱动芯片若命令信号为高时,认为发送的是数据,若命令信号为低则认为发送的是命令。
/*
* 函数内容:TFT发送命令数据
* 函数参数:无
* 返回值:无
*/
void TFT_WR_REG(uint8_t reg)
{
gpio_bit_reset(GPIOB,GPIO_PIN_6); //拉低命令信号
gpio_bit_reset(GPIOB,GPIO_PIN_7); //拉低片选信号
SPI0_Write(reg);
gpio_bit_set(GPIOB,GPIO_PIN_6); //拉高命令信号
gpio_bit_set(GPIOB,GPIO_PIN_7); //拉高片选信号
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TFT屏幕可以通过修改起始行列地址来实现不同显示方向的效果。
void TFT_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
if(USE_HORIZONTAL==0)
{
TFT_WR_REG(0x2a);//列地址设置
TFT_WR_DATA(x1);
TFT_WR_DATA(x2);
TFT_WR_REG(0x2b);//行地址设置
TFT_WR_DATA(y1);
TFT_WR_DATA(y2);
TFT_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==1)
{
TFT_WR_REG(0x2a);//列地址设置
TFT_WR_DATA(x1);
TFT_WR_DATA(x2);
TFT_WR_REG(0x2b);//行地址设置
TFT_WR_DATA(y1);
TFT_WR_DATA(y2);
TFT_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==2)
{
TFT_WR_REG(0x2a);//列地址设置
TFT_WR_DATA(x1);
TFT_WR_DATA(x2);
TFT_WR_REG(0x2b);//行地址设置
TFT_WR_DATA(y1);
TFT_WR_DATA(y2);
TFT_WR_REG(0x2c);//储存器写
}
else
{
TFT_WR_REG(0x2a);//列地址设置
TFT_WR_DATA(x1);
TFT_WR_DATA(x2);
TFT_WR_REG(0x2b);//行地址设置
TFT_WR_DATA(y1);
TFT_WR_DATA(y2);
TFT_WR_REG(0x2c);//储存器写
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
在TFT屏幕初始化函数中,对TFT引脚与SPI引脚进行初始化,同时在其他操作前需要执行一次复位操作,确保屏幕处于初始状态,随后打开背光,确保屏幕显示数据能被正常观看。
最后设置一系列屏幕参数,即可完成屏幕初始化,屏幕参数设置可参考屏幕厂商的案例代码进行移植,若想对屏幕进行调整,也可自行查找ST7735S的驱动手册进行查看修改。
初始化代码过长,可下载项目源码进行查看,以下仅做演示。
void TFT_Init(void)
{
//初始化TFT屏幕引脚
TFT_GPIO_Init();
//初始化SPI0引脚
Init_SPI0_GPIO();
gpio_bit_reset(GPIOB,GPIO_PIN_5); //复位
delay_1ms(100);
gpio_bit_set(GPIOB,GPIO_PIN_5); //复位完成
delay_1ms(100);
gpio_bit_set(GPIOB,GPIO_PIN_8); //打开背光
delay_1ms(100);
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TFT屏幕显示本质是通过在屏幕的每一个点设置RGB三种颜色来实现不同的显示效果,RGB颜色代码屏幕厂商都有提供,也可直接百度对应颜色十六进制代码。
屏幕厂商一般会提供填充颜色、显示字符、显示汉字、显示图片等函数,大家可自行移植。
代码过长,这里仅显示填充颜色函数做参考。
/*
* 函数内容: 在指定区域填充颜色
* 函数参数: xsta,ysta---起始坐标
* xend,yend---终止坐标
* color--------要填充的颜色
* 返回值: 无
*/
void TFT_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color)
{
uint16_t i=0,j=0;
TFT_Address_Set(xsta,ysta,xend-1,yend-1); //设置显示范围
for(i=ysta;i<yend;i++)
{
for(j=xsta;j<xend;j++)
{
TFT_WR_DATA(color);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
10.3.2 实验效果
烧录代码到单片机,会发现屏幕显示白色背景。
6.12. 波形显示实验
6.12.1 波形显示原理
波形是连续的,仔细观察波形发现,它也是一个个的点,假设手里此时有十个点,想要形成波形。就需要对相邻两点画直线,依次往后推,最后十个点会连接成线,这就是最简单的波形。
6.12.2 波形显示驱动
现在屏幕是128*160的大小,也就是有128*160=20480个像素点,如果我们希望以刷图片的方式,一帧一帧显示,就需要定义20480*2个字节,因为颜色显示是uint16_t
;单片机仅有8KRAM,显然不现实。
这里提供两个思路:
第一个:以时间为X轴,电压为Y轴建立平面直角坐标系,将电压转换为平面直角坐标系上的点,画一个点,使其与前面的点连接成线,然后将时间轴往后推一格,清除后一列的屏幕,这样循环往复。
第二个:还是以刷图片的方式,不过对图片进行缩放,不占据整个屏幕,就像手机小窗显示一样,这样定义一个大数组,同样是平面直角坐标系,对电压数据进行转换,将对应的点填充成波形颜色,其余点填充为背景色,实现一帧一帧刷屏。
6.12.3 显示一个方波
通过本次实验,实现1.8寸TFT屏幕显示方波信号,这里采用第一个思路进行显示。
6.12.4 配置流程
上一节课已经将屏幕显示的相关驱动程序都移植好了,但其中并没有绘制波形的函数,这节课需要将绘制波形的函数实现即可。
波形图类似于曲线图,曲线图又可以由非常多个折线图形成。这里介绍一下实现绘制折线函数的思路:
详情请参考:
采用横屏显示的方式,屏幕大小是128*160,有128个像素点可以用来表示输入的数值,这里为了提升速度,适当对其进行缩放,变成90个像素点(yoffset)可以用来表示输入的数值。
rawValue是对应转换的电压值,对电压值进行放大,放大越多,波形越高,但清除一列的数据量越大,这个需要依据实际情况调整。
x是时间轴,输入一个点,时间轴往后推,然后使其与前面一个点连接成线。随后清除后一点的列数据,确保数据的实时更新,这里是确保数据不会放大超过50,所以可以从40后开始刷新,提示速度。
static uint16_t lastX=0,lastY=0;
static uint8_t firstPoint = 1;
/*
* 函数内容:画折线
* 函数参数:short int rawValue--Y轴参数值
* 返回值: 无
*/
void drawCurve(uint8_t yOffset,short int rawValue)
{
uint16_t i=0,j=0;
uint16_t x=0,y=0;
y = yOffset - rawValue; //data processing code
if(firstPoint)//如果是第一次画点,则无需连线,直接描点即可
{
TFT_DrawPoint(0,y,GREEN);
lastX=0;
lastY=y;
firstPoint=0;
}
else
{
x=lastX+1;
if(x<160) //不超过屏幕宽度
{
TFT_DrawLine(lastX,lastY,x,y,GREEN);
for(i=40;i<90;i++)
{
TFT_DrawPoint(x+1,i,BLACK);//画点
}
lastX=x;
lastY=y;
}
else //超出屏幕宽度,清屏,从第一个点开始绘制,实现动态更新效果
{
TFT_DrawPoint(0,y,GREEN);
lastX=0;
lastY=y;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
自此,折线图函数编写完成
想要实现显示方波,除了折线图,还需要对应的电压数据,这里定义一个120个字节的数组,用于存放波形显示数据。随后依次输入折线函数中,让其根据前后两点绘制折线,逐渐形成波形。
注意输入参数的值需要在清除范围内(80-33=47,在范围内),否则波形清除不了导致显示有误。
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "led.h"
#include "main.h"
#include "tft_init.h"
#include "tft.h"
int main(void)
{
uint8_t i=0;
uint8_t data[120]={0};
// 初始化滴答定时器
systick_config();
//初始化LED引脚
Init_LED_GPIO();
//初始化TFT屏幕引脚及默认配置
TFT_Init();
//TFT屏幕显示UI
TFT_ShowUI();
while(1)
{
//模拟ADC采集数据
for(i=0;i<30;i++)
{
data[i]=0;
}
for(i=30;i<60;i++)
{
data[i]=33;
}
for(i=60;i<90;i++)
{
data[i]=0;
}
for(i=90;i<120;i++)
{
data[i]=33;
}
//显示波形
for(i=0;i<120;i++)
{
drawCurve(80,data[i]);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
6.12.5 实验现象
烧录代码进入单片机,会发现方波循环在屏幕显示。
6.13. 综合实验
6.13.1 实验所需知识
通过前面11个小节的学习,大家对每一个功能模块应该都有了了解,综合实验就是将前面11小节内容总结起来,分工合作,最终实现简易示波器效果。
若大家对某一个基础实验掌握不够,建议先会对应章节阅读掌握。否则可能对综合实验无从下手。
6.13.2 项目介绍
通过本次实验,实现以下内容:
- 在1.8寸TFT屏幕上显示波形;
- 支持通过旋钮对波形进行放大与缩小查看;
- 持暂停波形显示;
- 支持输出1K、2K、4K方波信号,能够被自身输入检测到并显示在示波器上;
- 支持通过按键调整占空比
- 支持频率测量; 12.2.2 配置流程
6.13.3 在1.8寸TFT屏幕上显示波形
- 需要参考屏幕显示实验以及波形显示实验,通过这两个实验,已经可以掌握基础的屏幕显示与波形绘制。
- 如果需要显示其余文字或字库,需要自行导入字库,详细请参考《附件资料》中对应1.8寸TFT屏幕资料,不同厂商写的字符显示函数与汉字显示函数会有差异,需要根据厂商的内容进行对应取字模如何转换到font.c中去。
- 在屏幕上显示波形,如果采样率和频率不整除,采样完后会有非整数倍的波形,当年循环执行采样,多出来的波形会和原来的不重合就会导致波形的滚动;
解决办法: 可通过固定起始显示波形的值,例如,一次采样300个点,然后需要将这三百个点的电压值转换为波形,如果直接转换,就会出现上述问题,此时在软件中认为加入一个阈值电压,只有当达到改阈值电压后,才开始后续波形的转换,并显示,显示数据多少根据屏幕大小决定。
这样,不管波形会不会被整除,起始波形只会是在该阈值电压上。
6.13.4 支持通过旋钮对波形进行放大与缩小查看
波形数据获取前面已经知道是通过ADC进行读取的,如何将对应ADC数据转换为波形图进行显示。这里假设是1KHz的方波,如果此时ADC时钟很快,采样周期也很短,那它可以迅速就将300个数据读完,但是波形可能还没开始变化。
所以,可以通过对ADC时钟的设置、采样周期的设置,来设置它采样的速度,从而实现波形的放大与缩小。当外接1KHz时,可以尝试慢一点采样,这样在300个点中,可能就能读取到几次波形变换的值。
当外接2K、4K甚至10KHz时,就需要增大采样速度,否则波形变换太快,采样速度跟不上波形变化的速度,就会导致波形失真。
6.13.5 支持暂停波形显示
因为目前波形显示是通过画点与画线函数逐个打上去进行显示的,所以暂停波形显示是非常简单的,当检测到按键按下,停止电压值转化为波形数据即可。
6.13.6 支持输出1K、2K、4K方波信号
通过PWM输出实验,已经掌握了输出1KHz方波信号,其余频率的方波信号,可以通过修改周期来达到目的,
注意周期修改了,预分频系数不变,也就意味着频率修改了,但是改变频率,占空比不能动,所以周期修改后,占空比的比较值需要同步进行调整。(否则原本周期为1000,比较值为500,占空比50%。现在周期10000,比较值500,占空比0.5%)。
6.13.7 支持通过按键调整占空比
占空比是周期/比较值,这里修改占空比,无需调整周期,别把事情弄复杂了,调整定时器比较值即可。
6.13.8 支持频率测量
频率测量是通过输入捕获实验进行的,前面实验中是将其输入到串口上,这里仅是改变输出到屏幕进行显示,没有较大改动,需要注意一点,假设原本有波形输入,频率测量也支持,但是突然波形断开,可能你频率仅采集到一个触发沿就没有下文了,这是频率应该是要为0,简单处理就是检测到输入电压为0时频率也为0。
6.13.9 实验现象
烧入代码到单片机中,屏幕显示对应的波形以及状态,当输入波形到来时,波形显示到屏幕上、能够准确测出频率。能够通过旋钮调整波形,能够通过独立按键打开或关闭输出PWM信号以及调整频率与占空比。