
#训练营#基于STM32F1的简易示波器项目-425272I
简介
创作于简易示波器训练营活动。 基于32位单片机裸机开发测量/仪器类系统级产品,用于训练和建立产品的基础研发流程:系统原理设计与器件选型、使用嘉立创EDA(专业版)绘制、MDK程序工程编程等。
简介:创作于简易示波器训练营活动。 基于32位单片机裸机开发测量/仪器类系统级产品,用于训练和建立产品的基础研发流程:系统原理设计与器件选型、使用嘉立创EDA(专业版)绘制、MDK程序工程编程等。开源协议
:CC BY-NC-SA 4.0
(未经作者授权,禁止转载)描述
一、项目背景
参与嘉立创/立创开源硬件平台/嘉立创EDA活动:简易示波器训练营活动,基于OSHWHub的简易示波器进行复刻。
硬件参数:
(1)交直流输入;
(2)低压档位测量范围:-1.6V~5V,高压档位测量范围:-80V~250V;
(3)单通道;
(4)输入信号VPP测量与Fre测量;
(5)PWM输出通道,可调节占空比;
(6)人机交互(含TFT、LED*n、KEY*n、EC11等)。
二、复刻拓展指标
硬件参数:
(1)交直流输入;
(2)低压档位测量范围:-1.6V~5V,高压档位测量范围:-80V~250V;略有变化
(3)双通道;
(4)输入信号VPP测量与Fre测量;
(5)PWM输出通道,可调节占空比;增加MOS开关使可接入电流敏感的负载使用PWM调压;
(6)人机交互(含TFT、LED*n、KEY*n、EC11等);
(7)接入MDAQ模块拓展数据采集功能;
(8)运放负压产生由TPS65135产生,可通过反馈电阻调节;
(9)LED改为RGB;
(10)增加SDCard;
(11)增加板载CH340,启用UART-USB-DEBUG;
(12)挡位调节由模拟开关替代拨动开关。
三、硬件设计
本节主要阐述底板的设计,相关板载模块会省略部分以免脱离主题。
为了应复刻的目的,是便于初入硬件产品设计领域,训练产品设计完整流程,使用模块化设计思想,设计基于底板+拓展模块板的总体架构,部分性能提升由贴片器件完成。
实际训练时,需要哪部分则直接焊接/接入部分,不需要的部分则可以保留备用,不做处理。
成品设计如图所布局:
(1)所有交互的部分,布局在顶面;其中按钮等需要操作的元器件放置在靠下位置、TFT显示屏放置在中间靠上位置、接入信号的接口放置在靠上的边缘;
(2)主板电源采用贴片放置在底面;
(3)运放等模拟部分藏于TFT屏幕下方;
(4)USB-TYPE-C除了供电外,还带UART,接入核心板的PA9/PA10,对应外设USART1;
(5)进行合理的丝印说明。
相关的硬件设计,官方给出了比复刻者(我)更专业的说明,参考:
https://www.yuque.com/wldz/jlceda/dso
如下仅对不同的部分进行说明,而相同的部分,请参考《202:简易数字示波器项目文档》
(1)SCH_P2_POWER
1> CC1/CC2下拉,满足UDF,可从PC或其他上源取电;
2> TYPE-C接口选用16P,含有正反接、USB2.0、VBUS功能;
3> 增加ESD保护电路,保护电源和信号线;
4> 增加保险丝,限流0.5A,防止特殊意外情况烧毁PC USB口;(本系统实测正常使用时电流100mA,满亮度TFT时150mA)
5> 增加电源前置滤波和接口地隔离。(请注意,这里电容容易拉启动时间,如果对此参数有特殊需求,请选焊小容量电容)
运放的正负电源,使用了基于TPS65135的双电源轨方案(因为笔者用的运放对电源需要限制,实际输出调整到了±3.5V),
本模块是严格按照芯片推荐应用典例设计的,如您需要复刻本工程,或需要此模块时,可以参考TPS65135的数据手册或联系笔者获取相关资料。
另外这里使用底板进行APWR_EN上拉,同时可以通过该引脚来检测模块是否接入。
(2)SCH_P3_AFE
信号调理部分电路使用了CV,如读者所看到的那样,CHA和CHB的硬件设计是完全相同的,
1> 挡位调节都使用了模拟开关来替代,其型号是RS2103,是常用的单刀双掷(SPDT)开关:
在其数据手册中,通过真值表,可以知道,当用户输入不同的逻辑电平时,分别闭合NO或者NC;
2> 比原电路多了一个电容(C16/C47),这是特殊封装了,希望通过此来美化电路或美化信号:
3> 对了从图中您应该看出我把测试点都换成了贴片的了,主要考量是 节约空间;
4>决定拓展双通道的另一个原因是,LM393比较器内部恰好有两个通道,这样完全用上了
另外笔者在这里增加了电位器(R86/R87),是用于微调滞回比较器门限的,如果不需要微调,可以保留不焊接处理;
(3)SCH_P1_CORE
至此,核心板几乎所有的引脚都使用上了,其中不同的颜色重点区分了不同的块;
1> TF卡模块
TF卡另一种称呼又叫SD卡,常用的通信方式有(SPI和SDIO),笔者使用的是小卡,使用SPI的通信方式进行数据读写;
2> TFT模块(1.8inch显示屏)
因为笔者经费紧张,只能有什么用什么了,因此这块TFT的引脚与官方参考工程所用TFT不一致,请特特特别注意!!!!
3> 按键部分
按键部分增加了硬件滤波部分,减少了代码的压力;
4> MDAQ模块
这是笔者为了参加首期彩色PCB做的一款小型的采集模块,使用MCU内部ADC,精度12bit,8通道,板载串口通信芯片,
这里通过RX/TX引脚与核心板的TX/RX连接,实际上连接到核心板的PA2/PA3(即USART 2)
5> PWM无极调压
这是很常用的PWM调压简单电路,对电流敏感的负载,如加热丝、恒压灯珠等;
6> 基于南京沁恒CH340的UART转USB电路
由于要频繁调试代码,没有串口打印log实在是不方便,因此增加了这个芯片及其周边元器件
四、软件设计
提示:本节仅对软件的部分代码进行深度说明,常规的操作这里将被略去,如遇到问题,可联系笔者讨论。
(1)硬件驱动调试
在总体程序设计之前,还需要对各硬件部分进行驱动调试,以确保各个硬件部分如设计预期那样正常工作,
笔者将从简单的部分开始,逐步介绍调试驱动的过程:
1> GPIO
GPIO的调试分为RGB和RS2103,因为它们实际上都是GPIO功能的OUTPUT功能:
在原理图中,我肯可以看到RGB的驱动情况是:
0 | 点亮 | 逻辑低 |
1 | 熄灭 | 逻辑高 |
先做简单的define映射,
再编写一些简单的函数用于初始化和集成测试,
如下仅介绍初始化函数:
对于RS2103的驱动部分,同样的步骤,在这个步骤之前,先进行真值表勘误:
define映射,
初始化函数
2> EXTI(KEY)
这里主要是按键部分的驱动,代码阐述之前,对硬件的配置进行说明:
按键名/芯片引脚 | GPIO MODE | 上/下拉 | 中断线(NVIC) | 优先级(您随意) |
KEY1/PB3 | EXTI---Falling Edge | LINE3 | ||
KEY2/PB4 | EXTI---Falling Edge | LINE4 | ||
KEY3/PB5 | EXTI---Falling Edge | LINE[9: 5] | ||
KEYA/PB6 | EXTI---Falling Edge | Pull-up | LINE[9: 5] | |
KEYB/PB7 | Input mode | Pull-up | ||
KEYD/PB8 | EXTI---Falling Edge | LINE[9: 5] |
这里笔者设计了按键事件伪线程用来记录按键动作并且在主线程中进行响应触发
其中KEY_EVENT的各个位作用:
BIT 8 | BIT 7 | BIT 6 | BIT 5 | BIT 4 | BIT 3 | BIT 2 | BIT 1 |
X | X | 反转 | 正转 | KEYD | KEY3 | KEY2 | KEY1 |
在main.c中重写void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
指向的void EXTI_Callback_2_EVENT(uint16_t GPIO_Pin)是用来映射到对应EVENT的:
正反转涉及到编码器,需要单独处理,指向void EC11_Encoder(void)
其中EC11_A和EC11_B是define映射过的宏:
3> TFT(SPI1)
对显示屏的控制,笔者参考了典例的大部分代码,但由于部分不同,做了细微的局部修改,以适配所使用的硬件:
芯片引脚 | 用途/复用功能 | 具体说明 |
PA00 | RES/RST | 复位控制 |
PA01 | BLK | 背光控制 |
PA04 | CS/NSS | SPI 硬件使能 |
PA05 | CLK | SPI 时钟线 |
PA06 | DC | 命令/数据指令切换 |
PA07 | MOSI/SDA | SPI 数据线 |
根据硬件连接,编写对应的define宏,方便移植TFT的相关函数:
需要特别注意的是,笔者启用了硬件SPI1 的NSS,OUTPUT,来自动根据HAL的底层启用CS使能,因此在很多代码中,笔者缺省了对CS引脚的处理。
典例和厂商提供的ST7735S驱动中,不少都几乎兼容软件SPI和硬件SPI,且对数据位的控制是严苛的,为了简洁代码也为了方便读者学习SPI-TFT控制,笔者在代码上做了相关优化,
对不同的HAL库API,可能SPI的控制大相径庭,因此首先对SPI的读写函数进行简单封装:
(这里不得不严正说明的一点:HAL_SPI_TransmitReceive()是优于单次写8bit数据的,如需写入16bit数据帧,可以参考笔者的后续做法,是简单的,而不必重写一个专门发16bit数据帧的函数)
接下来改写TFT操作最底层的两个函数(TFT_WR_REG)、(TFT_WR_DATA),他们将贯穿几乎整个TFT的各种函数
在初始化的时候,由于笔者的ST7735S似乎有些不听初始化序列,往往初始化序列写入后容易花屏,为了解决这个显示的不良信息,
笔者也没有找到彻底解决的方案,于是在初始化完成前,把背光给关了,刷一次黑屏,再开背光:
其他的函数,我想想有什么需要CV工程师们特别注意的地方,嗯,是这里:
如这个指定区域填充函数,典例给的是发16bit数据帧的,那笔者已经在初始化SPI1的时候选了8bit frame,如何办呢?
没错,如(r322/r323),那就发两次,发两次解君愁,还能省一个函数,降低了程序的耦合性,方便阅读理解代码!
为了其中一个功能,笔者需要绘制实心圆,这是相关的代码,用了标准圆方程的公式 x^2 +y^2 = r^2
基于这个公式,笔者在函数中判断点是否在圆内,若是,则画点,达到实心圆的绘制效果。
为了兼容CHA和CHB双通道绘制,笔者改写了典例中的画折现函数(主要修改通道数,和绘制位置,并填了个BUG,让刷新更加连贯)
需要注意这些参数,因为双通道绘制需要两个永久的缓冲区来保存上一个点的信息,这是与单通道差异最大的地方
双通道同屏显示,还需要对显示的位置做修正处理:
其次原本的单次绘制,改为双重绘制即可:
为了更好的显示效果,在新一轮的显示时,原典例代码上轮会因为显示问题导致残余留影,这里做双清处理:
其他就没有什么要注意的地方了,双通道绘制到此处完成。最后必须要说的一点是,这个老款的ST7735S实在是偏得离谱,大家还是尽可能购买全新的款
关于偏移,可以参考笔者,进行微小的修正,这在全屏清屏函数中,可以知晓,笔者如此处理偏移问题。当然也可以采用加大描绘区的方式,来弥补偏移的区域
4> SDCard(SPI2)
芯片引脚 | 用途/复用功能 | 具体说明 |
PB12 | CS/NSS | SPI 硬件使能 |
PB13 | CLK | 时钟线 |
PB14 | MISO | 数据线SDI |
PB15 | MOSI | 数据线SDO |
这个驱动,稍显复杂。实际上如果不是STM32F103C8T6容量限制,笔者更推荐使用Middleware 的FATFS方式,因为HAL对其做了内置兼容,就是加上其他模块的话,这个Middleware太大了,RAM和ROM不足以完成全编译;
笔者退而求求求....也不算次,就是按官方的说明,移植了一次:
从上面的官网中,获取FATFS源代码,最新版本是R0.15,但似乎变大了,笔者还是老老实实降下到R0.09,这是比较稳定和小型的
将其解压放置到工程路径下,并在src下重构diskio.c,实现几个函数
DSTATUS disk_initialize (BYTE); //SDCard Init
DSTATUS disk_status (BYTE); //get SDCard status
DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);//read SDCard
DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);//write SDCard
DRESULT disk_ioctl (BYTE, BYTE, void*);//get SDCard info
实现过程感谢各大论坛,各大开源网站,各大大佬,cv过程就不介绍了,笔者当然也是推荐大家手敲的--如果时间上允许的前提下
除了独立于HARDWARE文件夹中的.c文件,还需要对其他使用到的外设进行配置:
5> DEBUG USB2.0 (USART1)
重定向三步曲:
然后可以愉快printf();
6> MDAQ (USART2)
这里采用了中断接收的方式,空闲不定长中断接收,通过和MDAQ握手校验的方式验证数据完整性:
由于笔者还没有健全NVIC的优先次序,因此如果不在MDAQ显示界面,笔者通过MDAQ_Receive_IT_En位来控制USART 2的中断使能,
简单说就是,如果不在MDAQ界面,不允许USART中断,以此来避免程序其他中断受到干扰,从而预防裸机程序的相关进程阻塞。
由于MDAQ的数据协议是自己定的,笔者在末位增加了校验残缺和的校验位的机制,用来有效校验传过来的数据帧是否完整,防止因为传输丢帧导致的数据错乱
7> 示波器通道A/B(ADC+DMA)
CHA/CHB的采集对应了硬件的ADC1-IN8和ADC1-IN9
芯片引脚 | 用途/复用功能 | 具体说明 |
PB00 | ADC1-IN8 | 采样通道CHA |
PB01 | ADC1-IN9 | 采样通道CHB |
模式采用规则组,ADC_Regular_ConversionMode
为了采样的连续性,使用ADC+DMA的方式,同时干脆对内部的传感器和Vrefint 一起轮询了,用于显示CPU温度和校验采样电压
另外要注意的是,Vrefint通道是用来校验采样的,特别重要,采样周期稍微长一点,让其稳定,这里笔者所有的通道都用了最长的采样周期使得采样更加精准:
在DMA中,系统搬运采样值到指定的缓存内,但是保存的是(12bit)4096的对应值,因此还需要换算为电压,这里封装函数,对采样到的4096值进行电压或者温度值的具体映射:
同时,由于AFE电路存在信号放大、衰减、偏移等调理,ADC的采样值(Vadc)和实际探测点的值(Vin)是有偏差的,因此还需要根据硬件电路反算Vin:
笔者的硬件电路上有三处涉及到反算:
如果选择/1增益,则 Vo = Vi
如果选择/50增益,则 Vo = Vi * 20 / (510+470+20)
如果选择滤波挡,则 Vo = (1+1M/510k)*Vi
如果选择直通挡,则 Vo = Vi
这个是最重要的,标本的配置,固定启用的,因此必须要做反算映射:
如果按照图上的配置,则Vo = (5-Vi) *2
但是笔者实际没有+5V的输入,而是+2.97,因此调整了两个硬件参数:
器件编号 | 原配置 | 笔者的配置 |
R33 | 20kΩ | 50kΩ |
R35 | 20kΩ | 15kΩ |
因此,Vo = 1.44 - 0.2*Vi 理论计算公式哦
可能又有人会问,不是Vo = 1.44 - 0.2*Vi,算的Vi = (1.44 - Vadc) / 0.2,怎么实际编程又变成图中的呢??!!
1.42f 是实际测量的 V+ 端抬升电压;
0.59f / 3.3f 是标准电压输入,得到的α值(增益),
输入Vi = 3.3V
测量Vadc = 0.83V
可以按照理论公式计算α:0.83 = (1.42 - α*3.3)
算得 α = 0.59 / 3.3 = 0.17878787878...
因为是无限小数,直接将 α = 0.59 / 3.3 代入原本理论公式
最终,修正的校准过的反算公式出炉:Vi = (1.42 - Vadc) / ( 0.59 / 3.3 )
为了使得编译不进行优化,增加f来强制说明数据是float类型,避免优化计算,保证输出按预定反算。
8> 频率测量通道A/B(TIMER--输入捕获模式)
测频率,用到AFE图纸中的滞回比较器电路,具体原理官方典例中的说明,比笔者理解得透彻,因此就不过多赘述,
在不同的差异中,如R86是额外增加的,是用来微调滞回比较器的阈值的,当然可以选择保留不做处理:
【这个图来自https://www.yuque.com/wldz/jlceda/ga83zq8s4b55hpqm】
在硬件配置上,
芯片引脚 | 复用功能 | 对应通道 | 模式 |
PB10 | TIM2_CH3 | CHA-FRE | Input Capture direct mode |
PB11 | TIM2_CH4 | CHB-FRE | Input Capture direct mode |
【先说一下笔者算法的缺点,缺点是无法计算占空比,因为笔者只是粗暴的都设置为 上升沿触发捕获中断】
并且笔者算法中,没有去切这个,如果需要计算占空比,可以通过切换触发沿+二次捕获共同运算出duty
具体算法:懂我意思吧,大概就是这样捕获两次,触发沿不变,连续捕获两次,计算差值,这个差值就是所捕获的周期
我们知道 Fre = 1/period
前提是Fre的单位是 Hz, period的单位是 s
具体的周期单位并不是以秒作为单位的,而是 1/( 主频/预分频 +1)
这里使用HAL_RCC_GetHCLKFreq() 获得cpu主频( 72M ),预分频是( 72-1 ) ,那么定时器计数的周期是 1/ (72M / 72) s
最终可以获得计算公式:Fre = HAL_RCC_GetHCLKFreq()/72/(capture2 - capture1)
同理CHB也是这么计算的,但需要注意的是,需要static 定义下新的缓冲区:capture3、capture4、frequency2
9> PWM输出通道(TIMER--PWM模式)
PWM使用硬件PWM输出,stm32f103c8t6内部有PWM外设,对应PB9------选用PWM Generation CH4
采用PWM mode 1
至于PWM的原理,这个图特别好:(特别的,图中小于Vth,就是OCPolarity,笔者设置为LOW,先低极性)
为了在程序中可以调节PWM的输出频率和占空比,还需要编写两个函数:
10> 系统运行时间(TIMER--ClockInterrupt)
这个是为了测试的时候用得通用定时器,设置在TIM1,1ms,中断一次。过于简单常见,各位CV吧,这里就不做说明了。
(2)应用程式编程
1> APP(静态界面)
静态界面是选择不同模式下,首次且只需要操作一次的函数,作用是对显示界面的背景进行写入
***加载界面***
**key word:模块检测、进度条**
>>>S1 清屏,保证屏幕干净
>>>S2 绘制标题
>>>S3 绘制简易进度条
>>>S4 模块检测(APWR、MDAQ、SD_Card)
>>>S5 强制跳入示波器界面
片段展示:
实际界面:
***示波器界面***
**key word:初始化示波器界面**
>>>S1 清屏,保证屏幕干净
>>>S2 绘制标题
>>>S3 绘制坐标系、网格线
>>>S4 绘制初始参数位置、初始参数赋值
片段展示:
实际界面:
***采集卡界面***
**key word:初始化采集卡界面**
>>>S1 清屏,保证屏幕干净
>>>S2 绘制标题
>>>S3 绘制坐标系、网格线
>>>S4 绘制初始参数位置、初始参数赋值、通道提示按钮
片段展示:
实际界面:
***后台界面***
**key word:初始化后台界面**
>>>S1 清屏,保证屏幕干净
>>>S2 绘制PWM相关初始参数位置、初始参数赋值、方波LOGO等
>>>S3 绘制温度、温度LOGO、温度单位等
>>>S4 绘制内部参考电压、SD卡初始参数、运行时间初始参数
片段展示:
实际界面:
2> APP(运行界面)
运行界面是静态界面之后,持续运行的函数
***示波器界面***
**key word:示波器电压、频率持续检测、占空比实时可调可开关**
>>>S1 初始值赋值(含VPP/MAX/MIN)
>>>S2 获取显示长度个CHA和CHB
>>>S3 计算VPP/MAX/MIN
>>>S4 绘制参数VPP/FRE
>>>S5 绘制曲线
片段展示:
实际界面:
***采集卡界面***【半完成】笔者只对CH1进行了绘制
**key word:采集卡曲线绘制**
>>>S1 初始值赋值(含VPP/MAX/MIN)
>>>S2 获取显示长度个CHA和CHB
>>>S3 计算VPP/MAX/MIN
>>>S4 绘制参数VPP/FRE
>>>S5 绘制曲线
片段展示:
实际界面:
***后台界面***
**key word:CPU温度、内部参考电压、SD卡属性、系统运行时间、PWM相关参数**
>>>S1 计算并赋值内部CPU温度
>>>S2 计算并赋值内部参考电压
>>>S3 计算并赋值系统运行时间
>>>S4 计算并赋值SD卡属性
>>>S5 计算并赋值PWM相关参数
片段展示:
实际界面:
3> APP(被动交互)
主要指绘图或者数据刷新
当用户没有任何主动操作时,那么系统应稳定运行在对应的模式下。而笔者上述的函数都仅仅是进行一次整屏幕的刷新,因此需要一个调度这些SHOW函数的顶层函数:
在main函数中,只需要调用UI_Run_Switch()函数即可:
若不需要频繁刷新,可以少许增加延时。
4> APP(主动交互)
主要指按键操作和事件响应
特别注意呢,这里是有优先级的,从上到下,优先级从高到低,这个好理解吧,就是运行决策的先后关系
笔者还有很多预设的功能,没来得及在releaseV1.89版本上线
如 EC11旋钮--->切换选中各个块;
如 EC11按键--->确认进入功能;
如 PWM Fre可改;
如 网格显示与否;
... ...
这些相关的测试笔者留在了部分在
五、主线程架构回顾
总体设计方案
在主函数进程中,需要对相关启用的外设进行复用初始化:
还需要进行各种外设的使能,模块的初始化,以及加载界面:
之后进入主线程的while(1)循环中,进行上述的主动/被动交互:
六、实物展示
完整正面、背面、AFE局部面
正面装焊参考
背面装焊参考
AFE局部选焊参考
七、演示视频
提示:演示视频,测试上述的各种功能,并在附件中上传源代码。太大了,看链接吧。
【【立创训练营】简易示波器】 https://www.bilibili.com/video/BV1zm41167ri/?share_source=copy_web&vd_source=5d12763b5ade8bc59d748a8f28a5d25e
八、注意事项
提示:代码仅供参考,注意开源协议,需要合作请联系作者
九、附件内容
提示:releaseV1.89是笔者测试演示视频的版本
附件固件:releaseV1.89_.hex
附件工程:releaseV1.89_.zip
设计图

BOM


评论