STM32

STM32是STMicroelectronics(意法半导体)推出的一系列基于ARM Cortex-M内核的32位微控制器(MCU)产品。这些微控制器提供了广泛的产品系列,覆盖了多种不同的性能和功能需求,适用于各种应用领域,包括工业控制、汽车电子、消费类电子、医疗设备等。

STM32系列微控制器以其高性能、低功耗、丰富的外设接口和灵活的开发工具而闻名。它们通常具有丰富的存储器、多种通信接口(如UART、SPI、I2C、CAN等)、模拟数字转换器(ADC)、定时器、PWM输出等功能,以满足不同应用场景下的需求。

STM32微控制器通常使用标准的ARM Cortex-M内核,包括Cortex-M0、M0+、M3、M4和M7等,这些内核具有不同的性能和功耗特性,可根据具体应用的需求进行选择。此外,STM32系列还提供了多种封装和引脚配置,以满足不同尺寸和集成度的要求。

STMicroelectronics为STM32系列提供了丰富的开发工具和支持资源,包括基于ARM开发环境的集成开发环境(IDE)、调试器、评估板和参考设计等。这些工具和资源有助于开发人员快速开发和部署他们的应用,并提供了全面的技术支持和文档资料,帮助用户充分发挥STM32微控制器的性能和功能优势。

燃气爆炸到底有多可怕?!
2020年,中国共计发生燃气安全事故539起,共造成88人死亡,496人受伤。
2021年上半年,中国共计发生燃气安全事故544起,共造成71人死亡,412人受伤。
2021年6月13日,湖北十堰市发生严重燃气爆炸事故,造成26人死,138人伤,损失5395万!
*数据来源:《全国燃气事故分析》,图源:中国消防

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"

随着城市燃气的广泛应用,因燃气泄漏引发的爆炸、中毒和火灾等事故时有发生。在密闭空间内,燃气与空气混合后易燃易爆,当空气中的燃气浓度达到5%~15%时,遇到明火就会爆炸,给生命财产带来重大威胁。

在中国,家用燃气泄露报警器的安装比例相比发达国家仍处在一个很低的水平,部分大中城市地区的燃气报警设备安装比例不足10%。早在1996年,中国就出台了有关家用燃气报警器的相关标准(《CJ3057-1996家用燃气泄漏报警器》),并于2010年修订。

1、燃气安全市场迎来政策风口

从2021年起,国家应急管理部就联合住建部等部门推动各地区全面摸排,切实解决影响燃气安全突出问题,要求紧急部署燃气安全装置,开展燃气行业专项整治。相关部门单位也多次印发关于加强燃气安全防范力度要求,这也意味着各行各业加强燃气安全监管已成必然趋势,城市燃气行业将迎来新的发展机遇。

一些强制性国家法规纷纷出台:

我国《安全生产法》在2021年6月10日在人大会议上进行了第三次修正,于当年9月1日开始施行,规定餐饮等行业的生产经营单位必须安装防止燃气泄漏的可燃气体报警装置,不按规定安装的,最高可以处以二十万元罚款。

《燃气工程规则》于2022年1月1日开始施行,规定建筑高度大于100米的高层家庭用气场所以及商业用户使用的商业燃具、用气设备等,需设置燃气泄漏报警和切断等安全装置。

2022年2月14日,国务院印发《“十四五”国家应急体系规划》,对“十四五”时期安全生产、防灾减灾等工作进行全面部署,要求重点深化城镇燃气及燃气设施安全管理等安全生产治本攻坚。

国家新政的出台将加速释出工业安全类及居民类仪表的市场需求。中国家用燃气安全报警器的渗透率大约在 7%左右,一线城市的餐饮业渗透率约还不到 30%。餐饮应用场景(不含居民家庭)可能会带来百亿左右的市场规模,而居民家装需求将是餐饮业十倍以上,市场达千亿,未来渗透空间广阔。

2016年中国燃气报警器行业的市场规模达到12.5亿元,2017年市场规模为17.7亿元,同比增长了41.6%,从这两年的市场规模发展来看,燃气报警器行业正处于快速发展之中。预计到2025年燃气报警器行业的市场规模将达到57.1亿元。

目前新建住宅项目燃气报警器已经成为标配,各级政府及职能机构也正在逐步投入资金对老旧住户进行升级改造,再加上新《安全生产法》刚实施不久,燃气报警器市场天花板正在逐步打开,未来市场空间巨大。

2、燃气报警器的工作方式

当燃气发生泄漏时,燃气报警器会检测到并发出警报,同时将信号传输至紧急自动切断装置,自动快速关闭主供气体阀门,切断燃气供给,以降低燃气事故发生率,保障用户的人身和财产安全。

“▲
▲ 图1 有线连接方式的燃气报警器 图片来自网络

3、燃气报警器分类及应用场景

✦ 燃气报警器分类

按照安装方式,燃气报警器分为前装和后装报警器。

前装报警器,在新建建筑项目时即完成安装和走线。此类报警器具有常供电的优势,对于能耗要求较低,同时预留与燃气表、切断装置之间的有线连接通路。

后装报警器,即后期加装的报警器,在业主已经完成装修后才进行安装。这种报警器如果还使用前装报警器的走线方式,会增加部署成本以及有可能会破坏业主装修,导致无法顺利推广。因此,后装报警器需要电池供电,对低功耗特性有较为严格的要求,且需要具备无线通信能力。

✦ 燃气报警器应用场景

在餐饮等商业场景中,因为场地较大,环境复杂,所以需要更多的探头(传感器)来进行检测,绝大部分采用后装方式,如果采用有线连接,则部署成本增加,而且实施也不方便。如果采用电池+无线方式,则对低功耗和多连接的特性要求更高。

商用报警器有两种架构方式,不管采用哪种方式,都会存在多设备连接的情况。

  • 多个报警器+集中器(可选)+阀门
  • 多个气体探头+1个报警器(作为集中器)+阀门

在家用场景,前装/后装燃气报警器都有采用。家用前装燃气报警器需要满足长期使用的目的,优先考虑元器件的使用寿命,对MCU的稳定性要求更高。家用后装燃气报警器和餐饮场景要求类似,对低功耗和多连接能力更加重视。

“▲图2
▲图2 多探头报警器燃气安全系统示意图

4、STM32如何防患于未“燃”

不论前装还是后装市场,有线或无线连接方式,STM32都通过丰富的MCU产品线满足各种应用场景的不同需求。

对于前装报警器,STM32 MCU通过长期供货保证能力以及出色的产品质量为用户提供可靠保障。

“▲图3
▲图3 STMCU产品系列

STM32G0与STM32U5是针对前装燃气报警器市场的MCU产品的杰出代表。前装燃气报警器不需要高性能的MCU,更看重MCU的低成本、低功耗、灵活性、可靠性,STM32G0系列可充分满足该市场的要求。

“▲图4
▲图4 STM32G0产品特性

✦ STM32G0系列关键特色:

小身材大容量

  • Flash高达512KB, RAM高达144KB,大于4:1比例
  • 64pin封装仅有一对电源,相同封装可获得更多的I/O资源
  • 最小封装仅SON8,节省PCB尺寸

无需外部时钟

  • 0~90℃ 内部 ±1%精度高速时钟,全温度范围±2%

更多的创新外设

  • 新一代FD-CAN总线,速度更快,效率更高。且向下兼容CAN2.0
  • 集成全速USB HOST/DEVICE, 支持USB Type-C PD协议处理
  • 主频高达128MHz的高级定时器,< 8ns PWM输出
  • 2.5MSPS 12-bit ADC,比较器及DAC
  • 32Mb/s SPI,多达8路U(S)ART

更稳定更安全

  • 全新Robust技术,低EMI,强EMC。增强产品抗干扰能力
  • Flash自带错误检查和纠正(ECC)
  • 硬件AES256加密及新增可信任存储区

如果前装燃气报警器系统中需要类似集中器的角色,STM32U5是一个不错的选择。STM32U5是STM32低功耗MCU家族的新一代旗舰产品,在性能、功耗以及信息安全等方面都有出色表现。

“▲图5
▲图5 STM32U5是STM32低功耗MCU家族的旗舰产品

✦ STM32U5系列新特性:

更低的系统功耗特性

  • 升级的功耗管理模式
  • LPBAM, DMA 及可在低功耗模式下工作的 IP
  • Stop模式下的Timer可工作在输入捕获模式

更高的系统信息安全特性

  • AES 和 PKA硬件加密引擎, 可抵抗边界攻击
  • HUK(Hardware Unique Key)
  • 读保护: 可通过密码来回退RDP

更好的功能安全特性

  • 内部存储支持ECC校验

增强的数据存储能力

  • Flash存储器有512KB支持100K次擦写

更高的ADC精度

  • 新的ADC支持SAR型14-bit精度

更新IP

  • MDF (Multi-function Digital Filter)
  • ADF (Audio Digital Filter): DFSDM的升级版本

针对后装燃气报警器市场,STM32WB是理想之选。STM32WB在后装燃气报警器行业中的优势包括:

  • 支持多种射频协议以及多种连接方式

STM32WB 支持包括 BLE,Zigbee, OpenThread 和基于 IEEE 802.15.4 和 BLE LLD 的私有协议,也包括 BLE Mesh,Zigbee Mesh 网络和支持多达8个点对点连接的星型拓扑等。

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"

“▲图6
▲图6 STM32WB支持多种协议

“▲图7
▲图7 BLE MESH网络

“▲图8
▲图8 Zigbee MESH

“▲图9
▲图9 BLE星型拓扑网络

此外,STM32WB55还支持动态多协议并发功能,可以做为中继节点同时运行BLE+Zigbee协议,在通过BLE与手机建立通信的同时,保持Zigbee与节点或网路之间的通信,解决需要使用两颗射频芯片所带来的成本、PCB面积以及软件复杂度等方面的挑战。

“▲图10
▲图10 STM32WB55动态多协议并发功能

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"
  • 低功耗特性

STM32WB的双核架构,内置SMPS,支持多种低功耗模式,可以在低功耗模式下保持射频收发能力。

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"
  • 产品组合丰富

STM32WB既有可以满足报警器和切断装置的STM32WB15系列,也有可以作为燃气表主控MCU的STM32WB55系列。

  • STM32WB15系列价格低,封装小,低功耗;

  • STM32WB55系列支持段码屏,低功耗,安全性高。

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"
  • 专用集成滤波器

滤波器集成了:

  • 阻抗匹配网络 – 转换为50Ω阻抗
  • 谐波滤波器 – 减少带外TX谐波发射,提高RX灵敏度

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"

集成滤波器可以避免用户调试复杂的射频电路,同时还能有效保证量产产品的射频一致性,最大程度优化了多协议射频性能,且PCB面积比分离方案节省80%以上。

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"
  • 安全性

STM32WB的双核架构以及内置的安全功能可以有效地保护数据安全和通信安全。

“燃气安全市场迎来风口,看STM32如何防患于未“燃”"

结语:随着燃气安全市场的逐步规范化,作为安全用气的最后一道防线,燃气报警器市场规模将迅速扩大,STM32G0、STM32U5及STM32WB将为燃气报警器应用提供不同的解决方案。

来源:STM32
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 131
围观 193

在当今的万物互联时代,云连接已经是诸多应用开发的基石,包括消费电子,家居和楼宇自动化,智能家电,手持和可穿戴设备,玩具和娱乐设备等。无论设备处于何地,开发者都需要保证远端设备的连接性和安全性,这为物联网应用的开发带来了挑战。虽然各类应用面向的需求场景不同,但在底层,都需要通过某种联接方式,将远端设备安全的连接到某个后台。有鉴于此,ST和微软云Azure合作,通过端云协同,提供多种连接方式,赋能企业和开发者在云上便捷的构建物联网应用,从而更好的结合自身业务场景,创造产业价值。

“基于微软云Azure的STM32物联网联接"

作为微软云Azure的全球合作伙伴,ST在数年前就依托STM32 Cube生态,开发了用于连接Azure IoT Hub服务的扩展包,即X-CUBE-AZURE,其中包含连云所需的IoT SDK,可以帮助开发者将STM32L4、F4、F7等型号的开发板,快捷的联接到Azure。目前支持的联接方式包括以太网,WiFi和蜂窝,其中蜂窝联接需要配合X-CUBE-CELLULAR扩展包一起使用。如果开发者还需要安全的固件升级功能(secure firmware update),则可以通过X-CUBE-SBSFU扩展包来实现。上述扩展包都可以在ST官网(www.st.com)进行下载。

除了上述这种开发者较为熟悉的方式,近期ST还和微软Azure RTOS团队一起,定制了一些涵盖所有层级的联接示范开发包,可以做到开箱即用,其大致架构如下:

“基于微软云Azure的STM32物联网联接"

这些开发包中,包括联接所需的各类接口、驱动、中间件、联云模块等。依托Azure RTOS的丰富组件,方便开发者自由搭配,更快速地开发物联网应用。目前已有的联接方式和开发包请参考下表,后续ST也将和Azure RTOS团队保持合作,推出更多的联接示范开发包。

“基于微软云Azure的STM32物联网联接"
▲ * 待近期发布

参考文档

相关阅读:

STM32WL私有LoRa网络设计原理以及演示介绍
利用Opus在STM32WB上实现BLE全双工语音流

来源:意法半导体中国
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 57

海德格尔在上世纪30年代曾说过:我们正在进入一个 “ 世界图像时代。”

从工作电脑到个人手机,从商场橱窗到楼宇电视,生活正在被屏幕所包围。在视觉文化时代,“看”与“被看”成了我们的存在状态。而这一趋势,也迅速主宰了嵌入式系统的世界。MCU是嵌入式产品的核心,它的图形处理性能日益成为关注焦点。

意法半导体不断提升微控制器产品的图形性能,推出NeoChrom GPU,一款内置于特定STM32U5产品中的新型图形处理单元。它优化了翻转、旋转、缩放等动画性能,通过新硬件IP在整体上加快了纹理映射和alpha混合的速度。计算任务被分流到NeoChrom GPU上,大大提高了每秒帧数。因此,类似STM32U5这样的微控制器可以运行之前无法实现的复杂界面。许多客户也在此基础上成功完成了产品开发。此外,TouchGFX从TouchGFX 4.19.1开始,即为NeoChrom GPU提供支持。

从Chrom-ART到NeoChrom GPU的进化

什么是Chrom-ART和NeoChrom GPU?

Chrom-ART是多款STM32 MCU中内嵌的2D加速器(DMA2D),可优化内存访问,以便更快速地操作图像。许多来自意法半导体合作伙伴的工具和产品都使用了Chrom-ART加速器,例如Crank的Storyboard、Twilio的Microvisor,或者来自TouchGFX实现者PsiControl的interface。

NeoChrom GPU正是Chrom-ART的升级版本,在借鉴相同理念的同时,实现了更大优化。新技术基于双线性和点取样特性的纹理映射功能,提供了新的绘图加速并支持动画。因此,过去需要桌面PC机GPU卡才能实现的应用,现在可以在微控制器上运行。

“▲
▲ Chrom-ART和NeoChrom之间的参数对比

NeoChrom GPU如何集成到TouchGFX生态系统?

Chrom-ART代表意法半导体对图形优化的第一次尝试。此后,我们收购了TouchGFX,并通过TouchGFX Designer、Simulator和Generator构建了一个完整的生态系统。因此,意法半导体从一开始就将NeoChrom GPU集成到这些工具中。TouchGFX Designer包含的示例代码展示了在圆形显示器上以60帧/秒显示平滑动画。更重要的是,它展示了NeoChrom GPU关闭时对帧速率的影响,从而让决策者认识到它的重要性。同样,纹理映射控件将自动使用新的GPU(如果MCU支持)。因此,新IP在TouchGFX内的即时集成充分印证了意法半导体的生态系统凝聚力。

NeoChrom GPU 如何帮助 STM32 解决图形挑战?

图形性能面临两个主要挑战:每时钟周期指令数限制和内存问题。第一种通常与架构创新有关。微控制器因尺寸和低功耗因素,存在ALU和FPU限制,但改进后的制造工艺可容纳更多晶体管,以处理更多任务。NeoChrom GPU也遵循这样的趋势,硬件IP能够加速更多类型的图形计算。第二个挑战更为棘手,也更加关键,因为内存始终是一种稀缺资源。虽然STM32U599将采用2.5 MB内部RAM(能够存储两个帧缓冲区),但许多嵌入式系统需要外部RAM,这会带来显著的性能损失。

“▲STM32U5"
▲STM32U5

为了应对第二个挑战,NeoChrom GPU针对STM32 MCU进行了硬件优化。例如,STM32U5依靠一个8位8线串行外设接口(OSPI)或一个200 MHz频率的16位高速接口(HSPI)与外部存储模块对接,以加快到L2缓存的读取速度。同样,L1缓存可以直接访问内部RAM中的两个832 KB帧缓冲区。因此,由于能够快速访问实时渲染图像所需的所有数据,细分曲面速度显著加快。简而言之,减少访问纹理和其他素材所需的时间帮助工程师优化了性能,最终使渲染速度提高到以前的六倍。

NeoChrom GPU的未来

目前,行业对NeoChrom GPU的反馈非常积极。虽然许多MCU制造商也通过类似的IP来加速图形计算,但特定的STM32优化和TouchGFX生态系统使许多早期alpha测试者都采用NeoChrom GPU,而没有选择其他解决方案。

这一新技术已经应用于TouchGFX 4.19,一些alpha客户已使用即将面市的STM32U599开发套件,开发即时可用的UI。意法半导体还将在其他即将面市的微控制器中应用NeoChrom GPU。

来源:STM32
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 304

功能安全认证

基于内置强大功能安全特性的 STM32 MCUSTM8 MCU,和 MPU产品,ST提供免费的功能安全设计包,包括:经认证的自检库和完整的安全文档。旨在为客户实现功能安全认证或满足功能安全标准极大减少开发工作量,时间及成本。在现有工业 4.0、医疗、家用电器及新兴的电池管理系统(BMS)、无人机等功能安全相关应用系统中,功能安全机制已经嵌入在软件系统中,自动识别系统故障并触发安全处理机制。为了保证产品的使用安全,防止由于硬件随机故障以及系统故障带来的严重风险,越来越多的行业也规定产品必须取得相应的功能安全认证才能生产上市。

ST提供了两大类的功能安全设计包

“STM32

针对工业应用 IEC 61508 (STM32) 

针对家电应用IEC 60335-1 / 60730-1 (STM32 & STM8)

功能安全属性

下表中列出了STM32MCU内置的一些主要硬件安全属性。新产品中具有更丰富的 硬件安全属性 。

“STM32

功能安全设计包

SIL 和 ClassB 功能安全设计包既包括认证过的自检库,也包括各种用户基于该自检库做开发和认证需要的文档。具体每个功能安全设计包所提供的内容会有差异,请查看相应的功能安全设计包了解详情。用户通过这些设计包,可以降低产品开发的成本和缩短开发时间。

“STM32

总得来说,对于功能安全,ST MCU 从芯片内置的硬件安全属性,经过认证的软件自检库和完备的安全文档三个层面来支持 STM32 用户在系统级进行开发,达到要求的功能安全等级。

成功案例

STM32 功能安全曾应用于以下成功案例,可简化、加快客户获得功能安全认证。

“STM32

“STM32

“STM32

设计资源

“功能安全通用设计资源"
功能安全通用设计资源

“SIL设计包设计资源"
SIL设计包设计资源

“Class
Class B 设计包设计资源

相关产品

可支持

SIL功能安全设计包

STM32F0/F1/F3/F4/F7

STM32L0/L4/L4+/L5

STM32G0/G4/H7

STM32H7(Dual core)

STM32MP1(CM4)

Class B 功能安全设计包

STM32F0/F1/F2/F3/F4/F7

STM32L0/L1/L4/L4+/L5

STM32G0/G4/H7

STM32WB

STM32H7(Dual core)

STM8AF/AL/L/S

合作伙伴

“”

来源:STM32
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 82

STM32图像处理库介绍

STM32图像处理库(STM32IPL)是一个用C语言编写的开源软件库,它提供图像处理和计算机视觉功能,以便在STM32微控制器上更快地开发视觉分析应用程序。最近,STM32IPL作为STM32Cube的一个中间件组件, 在FP-AI-VISION1功能包的最新版本(v3.1.0)中一起发布。

众所周知,计算机视觉深度学习模型的输入通常与相机捕获的图像帧不同。深度学习模型的输入可能有不同的维度和不同的图像格式,因此通常需要对原始图像进行一些基本的预处理,例如:调整大小、图像格式转换、归一化等。STM32IPL通过提供一些功能强大且随时可用的软件组件,简化并加速此类图像预处理功能的开发。

主要特性

STM32IPL的关键特性:

  • 为开发人员提供功能强大且随时可用的软件组件,缩短STM32平台上图像处理和计算机视觉应用程序的开发时间;
  • 满足嵌入式系统在图像处理和计算机视觉功能方面最常见的需求;
  • 通过尽可能封装和隐藏典型图像处理和计算机视觉操作的复杂性,简化并加快应用程序的开发。

STM32IPL在功能上被划分为各种模块,如下图所示:

“STM32图像处理库介绍"

这些模块可以虚拟分组在以下宏组中:

  • 库初始化和反初始化组(深绿色模块);

  • 包括图像创建、初始化、释放等功能的组(浅绿色模块);

  • 包括图像变换功能的组,如:过滤、颜色转换、缩放、形态算子、扭曲等(深黄色模块);

  • 包含特征和对象提取功能的组,如:边缘和斑点检测器、霍夫变换等(浅黄色模块);

  • 包括对直线、矩形、椭圆等进行操作的功能组(深蓝色模块);

  • 包括图像读写功能的组(浅蓝色模块);

  • 包括在图像中绘制图形元素的功能组(深灰色模块);

  • 最后一组,包括用于计算积分图像和图像统计的剩余模块(浅灰色模块)。

下图展示了STM32IPL中提供的图像处理功能示例:

原图

“STM32图像处理库介绍"

高斯滤波后

“STM32图像处理库介绍"

经过Canny边缘检测算法后

“STM32图像处理库介绍"

镜像翻转后

“STM32图像处理库介绍"

旋转180°后

“STM32图像处理库介绍"

软件架构

一个典型的使用STM32IPL的STM32应用程序的软件架构如下所示:

“STM32图像处理库介绍"

STM32IPL位于BSP和HAL之上的中间件内。基本上,几乎所有的STM32IPL函数都与平台无关,除了以下两种函数,它们利用了一些STM32 MCU提供的硬件功能,而这些硬件功能只在部分而不是全部STM32 MCU里提供:

✦ 对文件执行读写操作的I/O函数。特别是用于处理所支持的图像文件格式(如 BMP、PPM、PGM 和 JPEG)的两个读/写函数。这些函数依赖于以下作为STM32Cube中间件组件的第三方开源库:

  • FatFs,在FatFs文件系统上提供读/写操作。例如,在microSD卡上读写图像。

  • LibJPEG,提供JPEG编码和解码功能。

✦ 通过利用STM32 DMA2D(用于图形操作的硬件加速器)在屏幕上快速绘制图像的函数。

高级功能示例

本节将以两个高级功能为例来说明其工作原理以及其应用在图像上的效果:霍夫变换和目标检测。

霍夫变换

霍夫变换是一种特征提取方法,用于检测图像中的线条和圆形等简单形状。这里的“简单”形状是指只需要几个参数就能表示的形状。例如,一条线可以用两个参数(斜率、截距)来表示,而一个圆有三个参数——圆心坐标和半径。霍夫变换在寻找图像中这种形状方面有出色的表现。

STM32IPL支持两种霍夫变换函数:

  • STM32Ipl_FindLines(),通过霍夫变换找到图像中的所有无限线。

  • STM32Ipl_FindCircles(),通过霍夫变换在图像中查找圆。

下面两张照片显示了使用霍夫变换进行线条检测的结果。

原图

“STM32图像处理库介绍"

通过霍夫变换进行线条检测后

“STM32图像处理库介绍"

请注意,检测到的线的质量很大程度上取决于边缘图的质量。因此,在现实世界中,霍夫变换通常在当您可以控制环境并因此获得一致的边缘图,或者当您可以针对您正在寻找的特定类型的边缘来训练边缘检测器时使用。

下面的另外两张照片显示了使用霍夫变换进行圆检测的结果。同样,结果的质量很大程度上取决于您可以找到的边缘的质量,以及您对要检测的圆的大小有多少先验知识。

原图

“STM32图像处理库介绍"

通过霍夫变换进行圆检测后

“STM32图像处理库介绍"

目标检测

目标检测是一种计算机视觉技术,可以识别和定位图像或视频中的对象。常用于自动驾驶汽车、机器人、面部识别等许多应用场景。

STM32IPL提供三种函数用于目标检测:

  • STM32Ipl_LoadFaceCascade(),加载面部级联。

  • STM32Ipl_LoadEyeCascade(),加载眼部级联。

  • STM32Ipl_DetectObject(),检测特定级联描述的对象。

以下照片显示了面部检测和眼部检测功能的结果:

原图

“STM32图像处理库介绍"

眼部及面部检测后

“STM32图像处理库介绍"

最后,要获得STM32IPL库,用户需下载最新的FP-AI-VISION1功能包,然后该库便可以在以下路径找到:

FP-AI-VISION1_V3.1.0\Middlewares\ST\STM32_ImageProcessing_Library

点击这里,下载FP-AI-VISION1功能包

来源:意法半导体中国
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 197

“MCU国产替代选型合集来了,干货不容错过!"

GD32F103是GD早期的产品,GD32E103和GD32F303是对GD32F103的升级和优化,所以4者是兼容的,虽然内核不同,但是通用外设几乎很少涉及到内核部分,在时间急迫的情况下可以使用ST的库开发。

一、相同点

1)外围引脚PIN TO PIN兼容,每个引脚上的复用功能也完全相同。

2)芯片内部寄存器、外部IP寄存器地址和逻辑地址完全相同,但是有些寄存器默认值不同,有些外设模块的设计时序上和STM32有差异,这点差异主要体现在软件上修改,详情见下文。

3)编译工具:完全相同例如:KEIL 、IAR

4)型号命名方式完全相同,所以替代只需找尾缀相同的型号即可,例如:STM32F103C8T6 与 GD32E103C8T6。

5)仿真工具:JLINK GDLINK

二、外围硬件区别

“MCU国产替代选型合集来了,干货不容错过!"

三、硬件替换需要注意的地方

从上面的介绍中,我们可以看出,GD32F30/E103系列和STM32F103系列是兼容的,但也需要一些注意的地方。

1)BOOT0必须接10K下拉或接GND,ST可悬空,这点很重要。

2)RC复位电路必须要有,否则MCU可能不能正常工作,ST的有时候可以不要。

3)有时候发现用仿真器连接不上。因为GD的swd接口驱动能力比ST弱,可以有如下几种方式解决:

a、线尽可能短一些;

b、降低SWD通讯速率;

c、SWDIO接10k上拉,SWCLK接10k下拉。

4)使用电池供电等,注意GD的工作电压,例如跌落到2.0V~2.6V区间,ST还能工作,GD可能无法启动或工作异常。

四、使用ST标准库开发需要修改的地方

1)GD对时序要求严格,配置外设需要先打开时钟,在进行外设配置,否则可能导致外设无法配置成功;ST的可以先配置在开时钟。

2)修改外部晶振起振超时时间,不用外部晶振可跳过这步。

原因:GD与ST的启动时间存在差异,为了让GD MCU更准确复位。

修改:

将宏定义:

#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)

修改为:

#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)

3)GD32F10X flash取值零等待,而ST需要2个等待周期,因此,一些精确延时或者模拟IIC或SPI的代码可能需要修改。

原因:GD32采用专利技术提高了相同工作频率下的代码执行速度。

修改:如果使用for或while循环做精确定时的,定时会由于代码执行速度加快而使循环的时间变短,因此需要仿真重新计算设计延时。使用Timer定时器无影响。

4)在代码中设置读保护,如果使用外部工具读保护比如JFLASH或脱机烧录器设置,可跳过此步骤。
在写完KEY序列后,需要读该位确认key已生效,修改如下:

“MCU国产替代选型合集来了,干货不容错过!"

总共需要修改如下四个函数:

FLASH_Status FLASH_EraseOptionBytes(void);
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
uint32_t FLASH_GetWriteProtectionOptionByte(void);
FlagStatus FLASH_GetReadOutProtectionStatus(void);

5)GD与ST在flash的Erase和Program时间上有差异,修改如下:

“MCU国产替代选型合集来了,干货不容错过!"

6)需求flash大于256K注意,小于256K可以忽略这项。

与ST不同,GD的flash存在分区的概念,前256K,CPU执行指令零等待,称code区,此范围外称为dataZ区。两者在擦写操作上没有区别,但在读操作时间上存在较大差别,code区代码取值零等待,data区执行代码有较大延迟,代码执行效率比code区慢一个数量级,因此data区通常不建议运行对实时性要求高的代码,为解决这个问题,可以使用分散加载的方法,比如把初始化代码,图片代码等放到data区。

总结:至此,经过以上修改,在不使用USB和网络能复杂协议的代码,就可以使用ST的代码操作了。

来源:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 301

1、前言

直接存储器访问(Direct Memory Access),简称DMA。DMA是CPU一个用于数据从一个地址空间到另一地址空间“搬运”(拷贝)的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。因此,大量数据拷贝时,使用DMA可以释放CPU资源。DMA数据拷贝过程,典型的有:

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程

2、串口有必要使用DMA吗

串口(uart)是一种低速的串行异步通信,适用于低速通信场景,通常使用的波特率小于或等于115200bps。对于小于或者等于115200bps波特率的,而且数据量不大的通信场景,一般没必要使用DMA,或者说使用DMA并未能充分发挥出DMA的作用。

对于数量大,或者波特率提高时,必须使用DMA以释放CPU资源,因为高波特率可能带来这样的问题:

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源

因此,高波特率场景下,串口非常有必要使用DMA。

3、实现方式

“整体设计图"
整体设计图

4、STM32串口使用DMA

关于STM32串口使用DMA,不乏一些开发板例程及网络上一些博主的使用教程。使用步骤、流程、配置基本大同小异,正确性也没什么毛病,但都是一些基本的Demo例子,作为学习过程没问题;实际项目使用缺乏严谨性,数据量大时可能导致数据异常。

测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)

“一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制"

5、串口DMA接收

5.1 基本流程

“串口接收流程图"
串口接收流程图

5.2 相关配置

关键步骤

【1】初始化串口

【2】使能串口DMA接收模式,使能串口空闲中断

【3】配置DMA参数,使能DMA通道buf半满(传输一半数据)中断、buf溢满(传输数据完成)中断

【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
【4】执行完第三步,DMA返回执行第一步,一直循环

“双缓存DMA数据搬运过程"
双缓存DMA数据搬运过程

STM32F0系列DMA不支持双缓存(以具体型号为准)机制,但提供了一个buf"半满中断",即是数据搬运到buf大小的一半时,可以产生一个中断信号。基于这个机制,我们可以实现双缓存功能,只需将buf空间开辟大一点即可。

【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据
【4】执行完第三步,DMA返回执行第一步,一直循环

“使用半满中断DMA数据搬运过程"
使用半满中断DMA数据搬运过程

UART2 DMA模式接收配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel5); 
	DMA_Cmd(DMA1_Channel5, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; /* 接收buf */
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC; 	/* 传输方向:外设->内存 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; /* 接收buf大小 */
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Circular; /* 连续模式 */
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_VeryHigh; 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel5, &DMA_InitStructure); 
	DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
	DMA_ClearFlag(DMA1_IT_TC5);	/* 清除相关状态标识 */
	DMA_ClearFlag(DMA1_IT_HT5);
	DMA_Cmd(DMA1_Channel5, ENABLE); 
}

DMA 错误中断“DMA_IT_TE”,一般用于前期调试使用,用于检查DMA出现错误的次数,发布软件可以不使能该中断。

5.3 接收处理

基于上述描述机制,DMA方式接收串口数据,有三种中断场景需要CPU去将buf数据拷贝到fifo中,分别是:

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景

前两者场景,前面文章已经描述。串口空闲中断指的是,数据传输完成后,串口监测到一段时间内没有数据进来,则触发产生的中断信号。

5.3 .1 接收数据大小

数据传输过程是随机的,数据大小也是不定的,存在几类情况:

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断

因此,我们需根据“DMA通道buf大小”、“DMA通道buf剩余空间大小”、“上一次接收的总数据大小”来计算当前接收的数据大小。

/* 获取DMA通道接收buf剩余空间大小 */
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

DMA通道buf溢满场景计算

接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小

DMA通道buf溢满中断处理函数:

void uart_dmarx_done_isr(uint8_t uart_id)
{
  	uint16_t recv_size;
	
	recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;

	fifo_write(&s_uart_dev[uart_id].rx_fifo, 
				   (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);

	s_uart_dev[uart_id].last_dmarx_size = 0;
}

DMA通道buf半满场景计算

接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小
DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小

DMA通道buf半满中断处理函数:

void uart_dmarx_half_done_isr(uint8_t uart_id)
{
  	uint16_t recv_total_size;
  	uint16_t recv_size;
	
	if(uart_id == 0)
	{
	  	recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
	}
	else if (uart_id == 1)
	{
		recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
	}
	recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
	
	fifo_write(&s_uart_dev[uart_id].rx_fifo, 
				   (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
	s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
}

串口空闲中断场景计算

串口空闲中断场景的接收数据计算与“DMA通道buf半满场景”计算方式是一样的。

串口空闲中断处理函数:

void uart_dmarx_idle_isr(uint8_t uart_id)
{
  	uint16_t recv_total_size;
  	uint16_t recv_size;
	
	if(uart_id == 0)
	{
	  	recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
	}
	else if (uart_id == 1)
	{
		recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
	}
	recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
	s_UartTxRxCount[uart_id*2+1] += recv_size;
	fifo_write(&s_uart_dev[uart_id].rx_fifo, 
				   (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
	s_uart_dev[uart_id].last_dmarx_size = recv_total_size;
}

注:
串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。

5.3.2 接收数据偏移地址

将有效数据拷贝到fifo中,除了需知道有效数据大小外,还需知道数据存储于DMA 接收buf的偏移地址。有效数据偏移地址只需记录上一次接收的总大小即,可,在DMA通道buf全满中断处理函数将该值清零,因为下一次数据将从buf的开头存储。

在DMA通道buf溢满中断处理函数中将数据偏移地址清零:

void uart_dmarx_done_isr(uint8_t uart_id)
{
 	/* todo */
	s_uart_dev[uart_id].last_dmarx_size = 0;
}

5.4 应用读取串口数据方法

经过前面的处理步骤,已将串口数据拷贝至接收fifo,应用程序任务只需从fifo获取数据进行处理。前提是,处理效率必须大于DAM接收搬运数据的效率,否则导致数据丢失或者被覆盖处理。

6、串口DMA发送

6.1 基本流程

“串口发送流程图"
串口发送流程图

5.2 相关配置

关键步骤

【1】初始化串口

【2】使能串口DMA发送模式

【3】配置DMA发送通道,这一步无需在初始化设置,有数据需要发送时才配置使能DMA发送通道

UART2 DMA模式发送配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:

  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据
void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel4);
	DMA_Cmd(DMA1_Channel4, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART2->TDR);/* UART2发送数据地址 */
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; 	/* 发送数据buf */
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST; 	/* 传输方向:内存->外设 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; 			/* 发送数据buf大小 */
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal; 		/* 单次模式 */
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_High;	 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
	DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); /* 使能传输完成中断、错误中断 */
	DMA_ClearFlag(DMA1_IT_TC4);	/* 清除发送完成标识 */
	DMA_Cmd(DMA1_Channel4, ENABLE); /* 启动DMA发送 */
}

5.3 发送处理

串口待发送数据存于发送fifo中,发送处理函数需要做的的任务就是循环查询发送fifo是否存在数据,如存在则将该数据拷贝到DMA发送buf中,然后启动DMA传输。前提是需要等待上一次DMA传输完毕,即是DMA接收到DMA传输完成中断信号"DMA_IT_TC"。

串口发送处理函数:

void uart_poll_dma_tx(uint8_t uart_id)
{
  	uint16_t size = 0;
	
	if (0x01 == s_uart_dev[uart_id].status)
    {
        return;
    }
	size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,
					 s_uart_dev[uart_id].dmatx_buf_size);
	if (size != 0)
	{
        s_UartTxRxCount[uart_id*2+0] += size;
	  	if (uart_id == 0)
		{
            s_uart_dev[uart_id].status = 0x01;	/* DMA发送状态 */
		  	bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
		}
		else if (uart_id == 1)
		{
            s_uart_dev[uart_id].status = 0x01;	/* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
			bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
		}
	}
}

注意发送状态标识,必须先置为“发送状态”,然后启动DMA 传输。如果步骤反过来,在传输数据量少时,DMA传输时间短,“DMA_IT_TC”中断可能比“发送状态标识置位”先执行,导致程序误判DMA一直处理发送状态(发送标识无法被清除)。

注:
关于DMA发送数据启动函数,有些博客文章描述只需改变DMA发送buf的大小即可;经过测试发现,该方法在发送数据量较小时可行,数据量大后,导致发送失败,而且不会触发DMA发送完成中断。因此,可靠办法是:每次启动DMA发送,重新配置DMA通道所有参数。该步骤只是配置寄存器过程,实质上不会占用很多CPU执行时间。

DMA传输完成中断处理函数:

void uart_dmatx_done_isr(uint8_t uart_id)
{
 	s_uart_dev[uart_id].status = 0;	/* 清空DMA发送状态标识 */
}

上述串口发送处理函数可以在几种情况调用:

主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出

void thread(void)
{
    uart_poll_dma_tx(DEV_UART1);
    uart_poll_dma_tx(DEV_UART2);
}

定时器中断中调用

void TIMx_IRQHandler(void)
{
    uart_poll_dma_tx(DEV_UART1);
    uart_poll_dma_tx(DEV_UART2);
}

DMA通道传输完成中断中调用

void DMA1_Channel4_5_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC4))
	{
		UartDmaSendDoneIsr(UART_2);
		DMA_ClearFlag(DMA1_FLAG_TC4);
		uart_poll_dma_tx(DEV_UART2);
	}
}

每次拷贝多少数据量到DMA发送buf:

关于这个问题,与具体应用场景有关,遵循的原则就是:只要发送fifo的数据量大于等于DMA发送buf的大小,就应该填满DMA发送buf,然后启动DMA传输,这样才能充分发挥会DMA性能。因此,需兼顾每次DMA传输的效率和串口数据流实时性,参考着几类实现:

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流

6、串口设备

6.1 数据结构

/* 串口设备数据结构 */
typedef struct
{
	uint8_t status;			/* 发送状态 */
	_fifo_t tx_fifo;		/* 发送fifo */
	_fifo_t rx_fifo;		/* 接收fifo */
	uint8_t *dmarx_buf;		/* dma接收缓存 */
	uint16_t dmarx_buf_size;/* dma接收缓存大小*/
	uint8_t *dmatx_buf;		/* dma发送缓存 */
	uint16_t dmatx_buf_size;/* dma发送缓存大小 */
	uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
}uart_device_t;

6.2 对外接口

/* 串口注册初始化函数 */
void uart_device_init(uint8_t uart_id)
{
  	if (uart_id == 1)
	{
		/* 配置串口2收发fifo */
		fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0], 
                      sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);
		fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0], 
                      sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);
		
		/* 配置串口2 DMA收发buf */
		s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
		s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
		s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];
		s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
		bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf, 
							   sizeof(s_uart2_dmarx_buf));
		s_uart_dev[uart_id].status  = 0;
	}
}

/* 串口发送函数 */
uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
{
	return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
}

/* 串口读取函数 */
uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
{
	return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);
}

7、相关文章

依赖的fifo参考该文章:

【1】通用环形缓冲区模块

8、完整源码

代码仓库:https://github.com/Prry/stm32f0-uart-dma

串口&DMA底层配置:

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "stm32f0xx.h"
#include "bsp_uart.h"

/**
 * @brief  
 * @param  
 * @retval 
 */
static void bsp_uart1_gpio_init(void)
{
    GPIO_InitTypeDef    GPIO_InitStructure;
#if 0
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
	
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0); 
	
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed  	= GPIO_Speed_Level_3;
    GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
#else
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1); 
	
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed  	= GPIO_Speed_Level_3;
    GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
#endif
}

/**
 * @brief  
 * @param  
 * @retval 
 */
static void bsp_uart2_gpio_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
	
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/**
 * @brief  
 * @param  
 * @retval 
 */
void bsp_uart1_init(void)
{
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	bsp_uart1_gpio_init();
	
	/* 使能串口和DMA时钟 */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	USART_InitStructure.USART_BaudRate            = 57600;
	USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits            = USART_StopBits_1;
	USART_InitStructure.USART_Parity              = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);	/* 使能空闲中断 */
	USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);
	
	USART_Cmd(USART1, ENABLE);
	USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */

	/* 串口中断 */
	NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	/* DMA中断 */
  	NVIC_InitStructure.NVIC_IRQChannel 		   = DMA1_Channel2_3_IRQn;       
  	NVIC_InitStructure.NVIC_IRQChannelPriority = 0; 
	NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);
}

/**
 * @brief  
 * @param  
 * @retval 
 */
void bsp_uart2_init(void)
{
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	bsp_uart2_gpio_init();
	
	/* 使能串口和DMA时钟 */
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

	USART_InitStructure.USART_BaudRate            = 57600;
	USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits            = USART_StopBits_1;
	USART_InitStructure.USART_Parity              = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART2, &USART_InitStructure);
	
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);	/* 使能空闲中断 */
	USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);
	
	USART_Cmd(USART2, ENABLE);
	USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); 	/* 使能DMA收发 */

	/* 串口中断 */
	NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	/* DMA中断 */
	NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;       
  	NVIC_InitStructure.NVIC_IRQChannelPriority = 0; 
	NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  	NVIC_Init(&NVIC_InitStructure);
}

void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel2);
	DMA_Cmd(DMA1_Channel2, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART1->TDR);
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; 
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST; 	/* 传输方向:内存->外设 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; 
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal; 
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_High; 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel2, &DMA_InitStructure);  
	DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE); 
	DMA_ClearFlag(DMA1_IT_TC2);	/* 清除发送完成标识 */
	DMA_Cmd(DMA1_Channel2, ENABLE); 
}

void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel3); 
	DMA_Cmd(DMA1_Channel3, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART1->RDR);
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; 
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC; 	/* 传输方向:外设->内存 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; 
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Circular; 
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_VeryHigh; 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel3, &DMA_InitStructure); 
	DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
	DMA_ClearFlag(DMA1_IT_TC3);
	DMA_ClearFlag(DMA1_IT_HT3);
	DMA_Cmd(DMA1_Channel3, ENABLE); 
}

uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
{
	return DMA_GetCurrDataCounter(DMA1_Channel3);	/* 获取DMA接收buf剩余空间 */
}

void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel4);
	DMA_Cmd(DMA1_Channel4, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART2->TDR);
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; 
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST; 	/* 传输方向:内存->外设 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; 
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal; 
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_High; 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
	DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE); 
	DMA_ClearFlag(DMA1_IT_TC4);	/* 清除发送完成标识 */
	DMA_Cmd(DMA1_Channel4, ENABLE); 
}

void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
{
  	DMA_InitTypeDef DMA_InitStructure;
	
	DMA_DeInit(DMA1_Channel5); 
	DMA_Cmd(DMA1_Channel5, DISABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(USART2->RDR);
	DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)mem_addr; 
	DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC; 	/* 传输方向:外设->内存 */
	DMA_InitStructure.DMA_BufferSize 			= mem_size; 
	DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable; 
	DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable; 
	DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode 					= DMA_Mode_Circular; 
	DMA_InitStructure.DMA_Priority 				= DMA_Priority_VeryHigh; 
	DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable; 
	DMA_Init(DMA1_Channel5, &DMA_InitStructure); 
	DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
	DMA_ClearFlag(DMA1_IT_TC5);
	DMA_ClearFlag(DMA1_IT_HT5);
	DMA_Cmd(DMA1_Channel5, ENABLE); 
}

uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)
{
	return DMA_GetCurrDataCounter(DMA1_Channel5);	/* 获取DMA接收buf剩余空间 */
}

压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接收数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。

“1.5Mbps串口回环压力测试"
1.5Mbps串口回环压力测试

————————————————

版权声明:本文为CSDN博主「Acuity.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_20553613/article/details/108367512
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 864

阅读本文时,请先阅读:

臻于至善,履践致远|STM32CubeProgrammer 和 STM32CubeMonitor上新,提高STM32开发效率-上

ST 近期推出新版本的 STM32CubeProgrammer、STM32CubeMonitor、STM32CubeMonitor-RF 和 STM32CubeMonitor-UCPD。许多 STM32 开发人员通过使用它们更快地将产品推向市场。所有嵌入式系统工程师都需要面对这样的挑战,为选用的微控制器或微处理器寻找功能全面的开发平台。一个设备可能有很多特性需求,设计人员如何有效地实现这些性能非常关键。因此,泛生态软件工具在推动基于 STM32 的嵌入式系统开发至关重要。让我们一起探索STM32CubeProgrammer 和 STM32CubeMonitor的新功能。

“STM32CubeProgrammer

STM32CubeProgrammer关键词,请参见“臻于至善,履践致远|STM32CubeProgrammer 和 STM32CubeMonitor上新,提高STM32开发效率-上

STM32 Cube Monitor关键词

MCU 的 “奈飞”

STM32CubeMonitor 是一个实时变量监控和可视化工具,提供支持远程连接的 Web 界面和可以创建自定义仪表板的图形界面。它确保开发人员可以通过可视化图形界面有效地监控他们的应用程序。这种基于流的编程工具使用户无需编码即可创建复杂的数据图表,使他们能够在不破坏程序代码的情况下轻松调试软件和分析行为。此外,用户可以在 Node-RED 和 ST 社区上共享他们的仪表板,以相互构建。

为了让 STM32CubeMonitor 的首次体验更加直观,ST Wiki 详细解释了开发人员如何通过两个简单的步骤监控应用程序中的变量。用户选择他们在内存中跟踪的数据起始地址及其类型。为了协助完成这项任务,我们提供了一份指南,展示了如何从 ELF 文件中获取地址,以及CubeMonitor 如何通过 STLINK 与 STM32 连接。

基于Node-RED的实时变量监控工具

跟踪寄存器、内存中的变量以及在任何给定时刻发生的无数事件难度非常大,手动监控它们的要求非常高,以至于团队通常没有资源来进行这项工作。STM32CubeMonitor 为这个问题提供了一个解决方案,并尽可能使其简单易用。用户可将可编辑的图形化节点拖放到画布上来创建流程,即一系列事件。例如,通过设置条件可以触发通过电子邮件发送警报或使用 MQTT 将数据推送到云平台的模块。

“▲
▲ STM32CubeMonitor

用户一行代码都不用输入,就可以创建图形、图表或生成仪表板,帮助他们计数器中的数据、传感器的数据以及应用程序的许多其他方面进行可视化。此外,网络服务器的存在意味着可以在任何 PC 或移动浏览器上使用这些可视化,无论是在本地网络上还是远程。此外,得益于 Node-RED 和 ST 社区,用户可以从简单地查看其他用户的仪表板开始,并通过学习其他人的示例来系统地学习。

贯穿产品生命周期的支持工具

在原型设计阶段,工程师可能会使用 STLINK ,例如目前可用的 STLINK-V3 模块之一。它将 MCU 板连接到 PC,这有助于设置 STM32CubeMonitor 仪表板并充当 Web 界面的网关。当设计人员准备好交付他们的最终产品时,他们还可以创建一段程序,该程序将通过 UART 将数据发送到 USB 端口。因此,开发人员仍然可以通过运行在PC上的STM32CubeMonitor连接到该 USB 端口来安全地监控他们的应用程序。因此,该工具提供了有助于计划升级或未来新功能的长期分析。

STM32CubeMonitor 的新功能

支持Node-RED 1.3.7

STM32CubeMonitor 1.3.0使用NodeRED 1.3.7框架,目前最新版本是1.3版本。1.3 版本带来了新的插件框架和自动执行功能节点的能力。Change/Switch 节点的更新也提高了它们的可读性。

OpenJS 基金会将在 2022 年推出新版本的 Node-RED,ST 将继续更新 STM32CubeMonitor。

新格式和符号更改通知

最新版本的 STM32CubeMonitor 能够以 CSV 格式导出数据,而不是仅仅使用专有格式。因此,用户将能够将信息导入 Excel、MATLAB 等,从而为更多的数据优化和操作打开大门。如果符号发生变化,新软件也会发出通知。简而言之,该软件通过在文件中定义变量并将它们与符号相关联来跟踪变量。但是,重新编译代码可能会使符号文件过时,从而与 Node-RED 仪表板产生差异。如果用户忘记更新符号文件,新的 STM32CubeMonitor 会提醒用户。

STM32CubeMonitor-RF关键词

STM32CubeMonitor-RF 是一款用于测试 STM32WB 微控制器的蓝牙和 802.15.4 无线性能的工具。图形用户界面有助于实现信号强度和数据包错误的可视化,而命令行界面为宏、批处理文件和其他类型的自动化打开了大门。简而言之,它借鉴了与传统 STM32CubeMonitor 相同的理念,但专注于无线性能。因此,开发人员可以快速测试他们的设计并发现问题。该实用程序还可以探测设备之间的 802.15.4 通信。试用方法非常简单,将 STM32WB 开发板通过其 USB 或 UART 接口连接到计算机,就可以开始使用该软件了。

STM32CubeMonitor-RF的新功能

STM32CubeMonitor-RF 2.8.0 是一项重大更新,因为使用了更大的数据包,无线性能提高了一倍以上。当用户在“OTA Updater”中选择“Optimize MTU size”选项时,软件工具将OTA传输从16 kbit/s增加到41 kbit/s。因此,对于开发人员来说,这是提高生活质量的必要条件。发送文件或更新设备固件是开发过程中的日常操作。更快的速度将确保开发人员更快、更高效地工作。

“▲
▲ The OTA Updater 和 Optimize MTU Size option

STM32 CubeMonitor-UCPD关键词

STM32CubeMonitor-UCPD 监控并帮助在运行 ST USB PD 堆栈的 STM32 微控制器上设置 USB-C 和供电系统。开发人员可以使用该工具监控 USB-C 接口上的交互,使用sink或source电源配置文件,并使用供应商定义的消息 (VDM)。该工具甚至具有预定义的设置,通过处理这些新技术固有的复杂性来加速开发。STM32CubeMonitor-UCPD 于2019年推出,是意法半导体 USB-C 供电生态系统不可或缺的一部分。一直以来,我们都在不断改进软件,以帮助开发人员更快地评估性能并获得认证。

STM32CubeMonitor-UCPD的新功能

STM32CubeMonitor-UCPD 1.2.0 最重要的新增功能之一是 Java 机器的集成。与本文中的其他工具一样,该软件具有安装程序所需的一切。用户在运行应用程序之前不再需要自己安装 Java。此外,用户现在可以追踪显示电压和电流、VDM、UCSI 等轨迹。新的 STM32CubeMonitor-UCPD 还可以监控电池的电气值。因此,开发人员可以跟踪更多进程并了解连接两个 USB-C 设备或使用 Power Delivery 时发生的情况。

更多资料

围观 276

页面

订阅 RSS - STM32