MM32

01、开发环境描述

Cortex-M0型号:MM32F0133C7P
下载器与调试接口:MM32 DAP-Link + SWD
操作系统:Ubuntu20.0.4
集成开发环境平台:eclipse IDE for C/C++ developers
交叉编译链:arm-none-eabi-gcc
调试服务器:JLink GDB Server

02、安装eclipse IDE for C/C++ developers

2.1、准备工作

需要下载两个软件包:

JDK:
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

JDK是Eclipse的必要运行环境,本次实验使用的是jdk-8u231-linux-x64.tar.gz。

Eclipse:

https://www.eclipse.org/downloads/packages/

Eclipses根据开发语言选择合适的安装包,我们主要用C/C++开发,使用的是
Eclipse IDE for C/C++ Developers linux 64-bit。

注:安装包版本可自行选择,但要注意一点,新版本的eclipse可能会与低版本的JDK不兼容。

2.2、JAVA环境

在/opt文件夹下新建一个jvm目录,将解压后得到的jdk1.8.0_231(取决于JDK版本)移动到新建的jvm目录下,添加java路径后,在终端中键入命令。

sudo mkdir /opt/jvm
sudo gedit .profile

在文件最后添加如下内容。(JDK版本号可能有所不同,文件夹的名称由实际的文件夹名称为准)

# java path
export JAVA_HOME=/opt/jvm/jdk1.8.0_231
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

最后键入命令使得路径生效。

source .profile

此时,在终端中键入java版本查看命令。

java -version

如果出现如下信息,,则说明JAVA环境配置成功。

“”

2.3、安装Eclipse

解压eclipse的软件包会得到一个eclipse的文件夹,将其移动到/opt目录下,建立jre软连接。

sudo mkdir /opt/eclipse/jre
sudo ln -s /opt/jvm/jdk1.8.0_231/bin /opt/eclipse/jre/

添加eclipse的桌面图标。

sudo gedit /usr/share/applications/eclipse.desktop

在打开的文件中键入。(注意Exe和Icon路径是否正确)

[Desktop Entry]
Encoding=UTF-8
Name=Eclipse
Comment=Eclipse
Exec=/opt/eclipse/eclipse 
Icon=/opt/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;

接着ctrl+s保存文件,然后赋予可执行权限。

sudo chmod u+x /usr/share/applications/eclipse.desktop

最后将eclipse.desktop复制到桌面或者固定在dock上,后面就可以通过图标启动eclipse。

2.4、安装交叉编译链arm-none-eabi-gcc

我们从ARM官方选择合适的版本下载(此处选择了Linux64):

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

“”

下载的为tar.ba2格式压缩包,使用命令tar -jxf <要解压的文件>解压到我们要安装的目录:

“”

为了以后使用方便,将文件夹重命名:

“”

它下面的bin目录就是我们要使用的编译工具链:

“”

接下来我们要将bin目录添加到环境变量,这样可以直接在命令行输入要使用的工具名,然后系统就可以找到该工具,在此我们仅为当前用户添加环境变量,使用gedit ~/.bashrc编辑当前用户配置文件,在文件最后添加:

export PATH=$PATH:/home/neomissing/gcc-arm-none-eabi/bin

“”

然后使用命令source ~/.bashrc更新系统路径,使添加的环境变量立即生效:

“”

然后输入命令arm-none,然后按三下Tab(一定不要输入全部),检查系统是否可以自动补全。如果系统可以提示,说明环境变量配置成功,可以放心使用arm-none-eabi工具链。

2.5、安装GNU ARM Eclipse插件包

The recommended way to install these plug-ins is to use the Eclipse standard install/update mechanism: In the Eclipse menu: Help → Install New Software…
in the Install window, click the Add… button (on future updates, just select the URL in theWork with: combo)
fill in Name: with GNU ARM Eclipse Plug-ins
fill in Location:
with http://gnuarmeclipse.sourceforge.net/updates

“”

click the Add button
normally the main window should list a group named CDT GNU Cross Development Tools; expand it select all the plug-ins (the one marked End of life is needed only for compatibility with previous version, normally can be safely skipped)
click the Next button and follow the usual installation procedure

“”

Once you define the update site URL, further updates are greatly simplified (Help → Check For Updates)。

2.6、安装OpenOCD

安装openocd 打开Ubuntu终端(alt+ctrl+t)输入命令:

sudo apt install openocd

完成之后 输入openocd查看版本信息。

“”

目前默认安装的openocd芯片支持不全,找到openocd安装路径删除,复制替换MindMotion提供的openocd即可。

检查MM32 DAP-Link连接正常。

打开Ubuntu终端(alt+ctrl+t)输入lsusb,查看连接如下:

“”

03、创建MM32工程并配置、编译、调试

3.1、工程创建

打开eclipse,创建一个c工程,点击file,新建New Project,选择C Project。

“”

点击next,输入工程名字test,如下配置:

“”

连续点击next。

“”

选择选择工具链路径,这一路径就是我们第二步中arm-none-eabi-gdb的路径,需要匹配,点击finish。

“”

添加自己的工程文件,然后指定头文件路径以及配置路径,在工程浏览器中选中工程,右键单击选择Properties,再选择C/C++ Build-->Settings,跳出如下界面:

“”

配置汇编器Cross ARM GNU Assembler,主要是添加预处理宏:

“”

配置编译器Cross ARM C Compiler,添加预处理宏。

“”

添加头文件搜索目录。

“”

配置连接器 Cross ARM C Linker,主要是选择连接脚本文件。

“”

3.2、编译工程

选择Project-> Build Project选项来编译整个工程。

“”

3.3、连接目标板

在eclipse中配置openocd,连接目标板。点击External Tools Configurations,双击Program选项。

“”

然后打开终端输入如下命令 which openocd,找可执行文件openocd的路径,然后把路径复制到Location一栏,在Arguments一栏中输入如下配置信息:

-f /usr/share/openocd/scripts/interface/cmsis-dap.cfg
-f /usr/share/openocd/scripts/target/mm32f013x.cfg

其中的mm32f013x.cfg需要根据你的目标板上的MCU不同而会改变。

此配置文件路径是安装openocd通过命令sudo apt install openocd安装的,是系统默认路径。如果用户在此路径中找不到相关文件,那么请自行查找与修改路径。

“”

“”

然后点击Apply,Run后会在窗口出现信息,表示连接成功。

3.4、Debug调试

配置debug环境:点击Debug Configurations选项,双击GDB OpenOCD Debugging在窗口点击Debugger一栏。

“”

分别修改如下三个窗口的内容,第一个为可执行openocd命令所在路径,第二个为openocd连接DAP-Link与目标板的命令,第三个为工具链arm-none-eabi-gdb等所在的路径。

“”

点击Apply,Debug就可以开始调试。

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

围观 44

在上周的《基于Embedded Studio搭建MM32开发环境》的章节中,我们使用了Ozone这款工具进行调试,今天我们来向大家详细地介绍如何使用Ozone调试MM32 MCU,以及Ozone能给大家带来哪些便利性,体验这款工具的强大之处。

01、Ozone简介

Ozone 是 SEGGER公司开发的一个调试工具,用于J-Link和J-Trace的跨平台调试器和性能分析器,具有所有常见的视图(源代码、内存、控制台/终端、寄存器、反汇编、调用堆栈等等)。

主要特点

- 独立图形调试器
- 调试任何工具链和IDE的输出
- C / C ++源代码级调试和汇编指令调试
- 用于任何目的的调试信息窗口:反汇编,内存,全局和本地,(实时)监视,CPU和外围设备寄存器
- 源代码编辑器可立即修复错误
- 将应用程序高速编程到目标中
- 直接使用J-Link内置功能(无限的Flash断点,Flash下载,实时终端,指令跟踪)
- 可编写脚本的项目文件可自动设置所有内容
- 新项目向导可简化新项目的基本配置
Ozone已支持的编译器:Embedded Studio,GCC,Clang,MDK,IAR。

02、Ozone环境搭建

2.1、软件下载

在SEGGER的官网(https://www.segger.com/)下载最新的Ozone软件。

“”

“”

2.2、软件安装

双击Ozone软件安装包进行安装,完成安装如下图所示,同时会在桌面生成快捷方式。

“”

03、工程创建及调试

Ozone调试的方式有两种:

1、 在集成IDE环境中直接调用Ozone来进行调试。

2、 通过创建Ozone工程来进行调试。

第一种方式可以参照《基于Embedded Studio搭建MM32开发环境》,在这里就不过多的讲解说明,下面对通过创建Ozone工程来进行MCU调试进行讲解说明。

3.1、Ozone工程创建

打开Ozone软件,如下图所示:

“”

点击菜单栏File->New->New Project Wizard..来新建工程。

“”

然后在弹出的New Project Wizard对话框中,Device选项根据芯片的具体型号来选择实际的内核版本,例如此次使用的芯片为MM2F013x,为Cortex-M0内核,因此在Device选项中选择M0,Register Set对话框中的型号在Device对话框选择完成以后会自动选择,在Peripheral对话框中选择芯片的svd文件(目前为止Ozone安装包中包含的svd很不全面,如果我们使用的芯片的svd文件在Ozone的Periphaeral文件夹中没有包含,我们可以到MDK-Keil的安装路径下面找到PACK文件夹,并在此文件夹中找到相应芯片的svd文件,并拷贝到Ozone安装路径下面的Peripheral文件夹中即可,然后并选择芯片的svd文件)。

“”

然后点击Next选项进入到Connection Settings选项卡来选择调试接口以及通讯速率。MM32F013x支持SWD接口,因此我们在Target Interface按钮中选择SWD接口,并在Target Interface Speed选项中选择通讯速率为4MHz。

“”

点击Next选项,然后进入到Program File选项卡来选择需要Debug的.elf文件。

“”

后面的配置选择默认即可。

3.2、下载并进行调试

选择Debug选项卡的Download & Reset Program选项来下载程序到MCU并进入到调试界面,用户可以在View选项卡中选择各种窗口来帮助调试。

“”

3.2.1 Memory窗口

Ozone可以直接通过在寄存器或者变量窗口直接右键Show Data就可以看到变量甚至寄存器的Memory状态。

“”

“”

在Memory中用户可以选择保存指定地址区间的数据。

“”

“”

3.2.2 Watch窗口

用户可以在View选项卡中选择Watch Data选项中的New Watch Data Window来调出Watch窗口显示变量的数值。

“”

“”

当用户需要查看某一个变量的数值的时候,直接选中此变量然后右击弹出相关的选项卡,然后选择Watch选项来将变量添加到Watch Data窗口。

“”

3.2.3 Disassembly窗口

用户可以在View选项卡中选择Disassembly选项来显示汇编窗口,熟悉汇编的用户可以在此窗口中查看汇编指令,指令跟踪显示已执行的指令,并且与源代码视图同步。

“”

3.2.4 Registers窗口

用户可以在View选项卡中选择Registers选项来查看寄存的数据,其中CPU为内核相关的寄存器,Peripheral为外设相关的寄存器。

“”

如果用户在创建工程的时候没有选择svd文件,那么在进行调试的时候将无法查看外设寄存的数据。

3.2.5 Source File窗口

用户可以在View选项卡中选择Source File选项来查看相关的源文件,包含被编译的c文件、头文件,其中有程序大小,指令数,位置和状态(编译、包含、外部调用)。

“”

3.2.6 Data Sampling窗口

选择Data Sampling选项来定时查看某一个变量的数据变化情况。如下图所示,Index为打印的序号,Times为打印的时间戳,sTimingDelay为变量名称。

“”

使用Timeline来查看变量的变化曲线:

“”

3.2.7 断点

用户可以在IDE中间源文件的左侧添加断点,并同步到Break & Tracepoints窗口,在Break & Tracepoints窗口展示了断点的数量,断点所在的文件以及所在的行等基本信息。

“”

视图可以移动和放在彼此以具有“标签”视图,可以将视图移出主窗口,例如放置在单独的监视器显示器上。

3.2.8 调用窗口

可以直观的看到编译后工程之间的函数调用关系,和调用深度等相关内容。这是一个静态的程序图框,用于描述函数、子函数之间的相互引用关系以及所占用的堆栈量、代码总量、调用深度等有点类似于keil中的htm(Obj过程文件中)文件的描述。

“”

3.2.9 脚本

Ozone有一个很好的脚本引擎,几乎一切都是可脚本的:

“”

实际上Ozone项目文件是用脚本语言编写的C文件,这样我可以很容易地更改调试环境,在脚本中我们只需要按照Ozone的Console的命令格式来编写脚本命令即可,比如我们需要打开Global Data窗口,在Console窗口输入命令Window.Show ("Global Data");即可调出Global Data窗口,如果关闭此窗口输入Window.Close ("Global Data");即可。

“”

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

围观 81

mbedded Studio是用于嵌入式系统的多合一集成开发环境(IDE),用于管理,构建,测试和部署嵌入式应用程序。Embedded Studio还提供了功能强大的项目管理器和源代码编辑器,以及随附的C / C ++编译器和具有高级调试信息窗口的集成调试器,还提供用于自动部署应用程序的直接J-Link集成和版本控制功能,项目生成器支持常见的微控制器。

主要特点

  • 免费用于非商业用途,例如教育和评估目的,没有任何限制
  • 跨平台:可在Windows,macOS和Linux上运行
  • 多线程构建可最大程度地减少构建时间
  • 高度优化的运行时库,可实现最佳性能和最小代码量
  • 具有无缝J-Link集成的功能丰富的调试器
  • 强大的项目管理,能够处理非常大的项目
  • 基于软件包的项目生成器,适用于所有常见的微控制器
  • IAR,MDK(AC5),MDK(AC6)和Eclipse创建的工程都可以转换成到 Embedded Studio平台使用
  • 适用于嵌入式C / C ++编程的专业IDE解决方案,包括Clang / LLVM&GCC&SEGGER C / C ++工具链

MM32系列MCU也是早期就得到SEGGER官方支持的MCU厂商之一,因此MM32系列MCU也可以完美在Embedded Studio平台进行开发、调试,今天我们将介绍在Embedded Studio平台开发、调试MM32 MCU。

环境搭建

01、 软件下载

登录SEGGER的官网下载最新的Embedded Studio软件(https://www.segger.com/)。

根据电脑的不同版本选择不同版本的软件进行下载,由于本次教程使用的电脑是win10、64位的,因此选择64位win10版本的软件进行下载。

02、软件安装

软件安装可以一直Next,基本上选择默认的配置即可,操作比较简单。

“”

完成以后,双击打开Embedded Studio IDE,会弹出一个预警对话框,我们直接点击CONTINUE按钮跳过即可,然后会进入到系统默认的工程页面,则说明我们的环境配置成功。

“”

03、安装PACK

打开Embedded Studio上位机软件,打开选项卡Tools->Package Manager进入pack管理选项卡。

“”

在SearchPackages搜索框中输入MM32查找pack包进行安装,pack根据自己的工程需求来进行安装即可(MM32最新系列的MCU的pack正在得到SEGGER支持过程中,用户也可以选择相同的型号pack)。

点击我们选中的pack包我们就会看到IDE弹出Next按钮。

“”

点击Next按钮进入下载安装选项卡。

04、查看安装完成的pack包

点击Display Installed选项卡就会弹出已经安装完成的pack包,并可以查看已经安装完成pack包的相关信息。

“”

IAR,MDK(AC5),MDK(AC6)和Eclipse创建的工程都可以转换成到Embedded Studio平台使用,也可以基于Embedded Studio平台创建新的工程,本章将实验两种方式创建MM32F013X工程环境流程。

Embedded Studio创建MM32工程

具体的操作如下:

01、新建工程

选择File->New Project选项卡。

“”

选择MM32的芯片型号。

“”

并配置工程名,将默认的工程名修改为MM32。

“”

选择相关的工程配置,点击Target Processor来选择芯片的具体型号。

“”

02、加载文件

“”

移植MM32F013x的库到我们的工程,首先我们从MM32官网下载最新的MM32F013x的SDK包到我们的电脑并解压缩。

“”

并将Device下面的HAL_lib复制到我们的工程下面,并在工程中新建一个文件夹并将HAL_lib中的文件添加到工程。具体的操作如下:

“”

“”

并将MM32F013x工程中的IOtoggle的main.c替换工程中的main.c文件。

“”

添加led的驱动文件到工程中:在工程中新建BSP文件夹并将IOtoggle文件夹下面的HARDWARE文件夹下面的LED.c复制到BSP文件夹下面,并添加到工程中。

“”

工程中添加SYSTEM文件夹并添加文件,具体的操作就是将IOtoggle文件夹下面的SYSTEM文件夹复制到我们的工程中,并添加到工程项目中,具体的操作如下:

“”

在SYSTEM文件夹下面新建一个inc文件夹,并将从官方库SYSTEM移植过来的.h文件放在此文件夹下面。

“”

“”

添加.C文件到工程中。

“”

添加MM32F013x的库的头文件,具体的操作如下:

将Device文件夹下面CMSIS文件夹中的文件复制到工程文件夹下面的CMSIS_5->CMSIS文件夹下面的Include文件夹下面。

“”

03、添加路径

右击Project->Options。

“”

在Code目录下的Preprocessor中点击User Include Directories选中添加路径即可。

“”

04、 编译

“”

我们会发现很多的错误,在delay.c \ uart.c中都需要添加#include "HAL_conf.h"头文件,并在uart中屏蔽掉FILE __stdout这行代码。

出现Build complete则说明我们的文件编译成功了,接下来进行验证,我们将代码下载进我们的板子测试OK,说明我们工程搭建成功。

在debug的时候我们既可以选择软件自带的调试方式也可以选择Ozone进行调试。

将KEIL工程导入Embedded Studio编译器

01、导入MDK工程

将KEIL工程导入到SEGGER Embedded Studio编译器去编译文件具体的操作如下:

选择File->Import Project选项卡来添加MDK工程,并选择导入mdk工程类型,目前支持的类型有MDK、MDK-ARM6、IAR、GCC。

“”

选择MDK工程文件。

“”

选择内核型号:

“”

选择编译配置,则选择外部编译工具。

“”

“”

到此我们就可以看到我们文件导入成功了。

02、编译

“”

出现Build complete则说明我们的文件编译成功了。

03、下载调试

选择Debug->Debug with Ozone选项卡来进入Debug模式。

“”

下载并开始调试:

“”

今天主要讲解Embedded Studio的环境搭建及新建MM32F013x工程文件,在后续的教程中将继续讲解基于MM32F013x使用SEGGER相关工具的方法。

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

围观 23

前面一章节介绍了在Windows下搭建Eclipse开发环境,本章节将介绍在Windows环境下基于Eclipse开发、调试MM32。

01、GCC创建工程

打开File->New->Project,选择C Project,然后点击NEXT,在Project name选项框中设置GCC工程的名字,在Project type选项框中选择Empty Project,在toolchains中选择Cross ARM GCC。

“图1"
图1

“图2"
图2

“图3"
图3

“图4"
图4

“图5"
图5

在Cross GNU ARM Toolchain界面用户需要选择GNU Tools for ARM Embedded Processors (arm-none-eabi-gcc)作为A工程的编译工具,并选择编译工具的路径。后面点击Finish按钮,至此工程模板的创建已经完成。

02、添加MM32相关库函数

在上面的模板中添加与MM32F013x相关的文件:Source文件夹中存放的为F013x的相关代码,在Source文件夹中Device文件夹中存放的是MM32F013x的Library以及启动文件。BSP文件夹中存放的为外设相关的驱动文件, SYS文件夹下面存放的为芯片UART,Delay的配置文件。APP中存放main.c文件。

“图6"
图6

“”

“
图7

在添加所要操作的文件以后,为了防止文件没有加载出来,需要刷新一下(直接按快捷键F5刷新工程)。

“图8"
图8

03、工程配置

在GCC中添加以上文件的头文件所在的路径,点击在工程浏览器中选中该工程,然后点击project->properties 选择项。

“图9"
图9

“图10"
图10

设置需要打开的宏定义:

“图11"
图11

给工程添加flash.ld文件。

“图12"
图12

如果在我们的工程中有lib文件,比如MM32的W系列使用eclipse编程的时候据需要向工程中添加lib库文件,在进行lib文件添加的时候要注意lib文件的格式,例如:蓝牙的lib文件为libmg_BLE-gcc.a,lib为lib文件前缀.a为文件的类型,添加名字的时候直接添加为mg_BLE-gcc即可。

“图13"
图13

04、编译工程

选择CDT编译。

“图14"
图14

然后选择Build Project进行工程的编译,看到有hex文件生成并且无错误、无警告,则说明我们的工程编译成功。

“图15"
图15

05、DEBUG配置

编译完成后, 我们就要进行下载和调试了,首先进行调试的配置。

“图16"
图16

在GDB SEGGER J-LINK Debugging上面双击创建debug工程,新建了一个选项卡,设置 debug 的名称,调试的工程和源文件。

“图17"
图17

选择相关的工程以及对应的elf文件。

“图18"
图18

由于debug依靠的GDB框架,所以选择JLinkGDBServerCL.exe来进行调试。

“图19"
图19

“图20"
图20

到此我们的GCC的debug已经配置完成了,打开Debug->MM32TEST DEBUG进入debug界面。点击 Debug 开始调试,我们就可以看到我们的软件进入了调试模式,并且停留在了main 函数的第一个有效行上面,点击RUN。

“图21"
图21

调试界面基本都是一样的,watch窗口也能调出来设置断点、全速运行等功能。

如果想结束调试,只需要点击上方的红色方块即可,此时 Jlink 的 GDB 会自动关闭,然后点击右侧的 C/C++选项卡即可回到工程的编辑视图了,到此debug配置验证完成。

本次实验参考代码:

https://github.com/Samplecode-MM32/MM32MCU_Code

来源:灵动微电子

围观 35

2020年的世界格局使得半导体电子行业受到疫情、中美贸易等外部环境等的影响深远、意义重大。虽然中国整体市场回暖,但处于产业核心竞争力的汽车电子主控芯片,仍然受到国外半导体公司的极大制衡,并随着近期的美国极端天气、日本地震及工厂失火等天灾人祸的影响,车规主控芯片的供应日益趋紧。

从下图可以看到,汽车电子市场在中国的发展迅猛,每年以17%的速度增长。但是汽车芯片的研发难度高、周期长,国产汽车芯片在短期内快速补足国外厂商的可能性并不大。随着新能源汽车的推广和全球“缺芯”形势的蔓延,此番缺“芯”让汽车全产业链意识到国产汽车芯片的重要性,国产芯片加速获得了验证和进入的机会。

“”

作为国内领先的 32位 Arm Cortex-M 的微控制器芯片和解决方案提供商,灵动微电子的MM32产品系列在经历了多年的发展和升级后,已被广泛使用在车体的次系统上,作为原先8位MCU的替代升级之用。

在过去几年,MM32系列MCU在这些应用的累计出货已经接近3000万颗,其中在OBD应用超过1000万颗,在两轮电动车电机应用超过1000万颗,在系统控制协处理器应用超过500万颗。

这些产品系列包括:

基于Arm Cortex-M0 的MM32F0010和MM32F003超值型MCU,主要用于车窗控制器、倒车雷达模块、转向灯和尾灯控制

内置CAN 控制器的MM32F0130、MM32L07x以及内置BLE功能的MM32W07x系列,用于汽车诊断设备 OBD或无线OBD

面向电机应用的MM32SPIN系列,如MM32SPIN25、MM32SPIN360等,用于汽车风机与水泵控制,以及电动车电机控制

基于高性能Arm Cortex-M3 内核的MM32F3270、MM32F103系列,用于彩显仪表盘,智能汽车充电桩系统主控,以及电动车控制系统的协处理器

“”

汽车电子MCU的应用场景

在汽车应用中,微控制器(MCU)提供着至关重要的性能。随着价格的降低及整体控制系统和计算系统在汽车电子中占比的增加,MCU也逐渐走向多样化。微控制器被广泛使用在从电机控制,车身控制到信息娱乐系统等越来越多的汽车子应用中,对于选择的不同MCU来说,仍存在很大的差异。因此,如何选择合适的MCU以降低成本而不影响所需的性能,甚至提高性价比变得尤为重要。车载MCU的市场主要集中在8、16和32位的微控器,可按汽车电子产品的不同需求用于不同性能的场景:

32位MCU主要应用包括仪表板控制、车身控制、多媒体信息系统、引擎控制,以及新兴的智能性和实时性的安全系统及动力系统,如ADAS、驾驶辅助系统、电子稳定程序等安全功能,以及复杂的传动功能或域控制。

16位MCU主要应用为动力传动系统,如引擎控制、齿轮与离合器控制,以及电子式涡轮系统等,也适合用于底盘机构上,如悬吊系统、电子式动力方向盘、扭力分散控制,电子刹车等。

8位MCU主要应用于车体的各个次系统,包括风扇控制、空调控制、雨刷、天窗、车窗升降、低阶仪表板、集线盒、座椅控制、门控模块等较低阶的控制功能,近年来不断被32位MCU替代。

随着新型域控制器架构在汽车电子行业的逐渐使用,灵动微电子也致力于在全新MM32 MCU平台上打造高性能产品。在优化处理性能、提高品质、降低成本等方面的基础上,与国内更多传感器和模拟器件合作伙伴一起,群策群力,通过全产业链的协作,为化解汽车芯片产业面临的问题做出努力。

“”

关于灵动

灵动成立于2011年,是中国本土领先的通用32位MCU产品及解决方案供应商。公司基于Arm Cortex-M系列内核开发的MM32 MCU产品拥有F/L/SPIN/W四大系列,200多个型号,累计交付超2亿颗,在本土通用32位MCU公司中位居前列。MM32 MCU被广泛应用于智能工业、汽车电子、通信基建、医疗健康、智慧家电、物联网、个人设备、手机与电脑等领域,每年都有数千万件配备了灵动MM32 MCU的优秀产品交付到客户手中。

“”

迄今为止,灵动是同时获得了 Arm-KEIL、IAR、SEGGER 等开发工具官方支持的本土 MCU 公司,是为数不多的建立了独立、完善的生态体系的通用MCU公司,致力于为客户提供从芯片硬件到软件算法、从参考方案到系统设计的全方位支持,真正为中国电子信息产业提供底层技术驱动和支持。

围观 58

作为中国电子信息产业风向标的“第九届中国电子信息博览会(2021CITE)”于2021年4月9日在深圳福田区会展中心拉开帷幕,本届博览会集中展示包括智慧家庭、智能终端、人工智能、智能制造、高端芯片、新型显示、虚拟现实及增强现实、智能网联汽车、5G和物联网等代表电子信息产业未来发展的核心内容。通过CITE主题馆、新型显示馆、智能制造与3D打印馆、机器人与智能系统馆、人工智能馆、5G和物联网馆、汽车电子、智能驾驶与锂电新能源馆及电子元器件九个展馆等25个专业展区,为业界充分展示了智能时代电子信息产业最新发展成果与趋势。

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

“第九届中国电子信息博览会(2021CITE)”也是灵动2021年参加的第一场展会,此次参展灵动给大家带来了全新的MM32 MCU产品和当今市场上最热门的应用方案展示,现场还有基于灵动MM32 MCU应用到各领域的Demo演示。

全新MM32系列MCU

全新MM32系列是灵动推出的新一代通用MCU平台,旨在为客户提供更高性能、更低功耗、更高可靠性、稳定性和健壮性的微控制器。全新MM32硬件上与经典MM32全兼容,保留了2.0-5.5V 宽压供电设计。

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

全新的MM32F系列MCU

MM32F 系列是灵动微电子新一代 MM32 系列中率先升级推出的通用高性能MCU平台。

全新MM32F系列和经典MM32F引脚兼容,并在系统性能、功能扩展、可靠性、稳定性上获得了大幅度提升,ESD(HBM)高达±8KV。

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

在超值型产品中,增加了可用的GPIO数量,并提高了72MHz M0产品的存储容量比,以便支持更强的计算处理能力。

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

在主流型产品中,增加了USB FS OTG、SDIO、Ethernet、I2S和FSMC外扩总线接口,提高系统可连接性,并提升了模拟性能,内置更多ADC通道和DAC。

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

全新MM32F扩展了工作温度范围,提供-40~85℃工业级和 -40~105℃ 扩展工业级的不同选择。

全新的MM32SPIN系列MCU

“CITE2021|灵动全新MM32系列MCU惊艳亮相"

MM32SPIN是电机与电源相关应用设计的专用产品家族,使用高性能Arm Cortex-M0与Arm Cortex-M3 内核,依据功能区分成专用MCU与驱动MCU两种。MM32SPIN系列最高提供了128KB Flash,内置了多路UART、I2C、SPI、CAN 以及多种高精度模拟外设,包括:比较器、12位3Msps ADC与运算放大器。驱动MCU提供了集成电源的功能, 预驱、LDO以及MOSFET等丰富的外设,规划的电压范围有20V、60V、200V以及600V,产品丰富且应用涵盖广泛。

基于MM32 MCU的部分热门应用

“空调室外机,室内机"

空调室外机,室内机

MCU型号:

MM32SPIN3270,MM32SPIN06

MCU特性:

MM32SPIN3270:多达512KB Flash,三个独立ADC以及高达8路串口资源。

MM32SPIN06:高达16KB RAM以及64K Flash资源,并且支持电机计算的外设资源。

“电子差速器"

电子差速器

MCU型号:

MM32F031/MM32F0130

MCU特性:

高性能的Arm Cortex-M0为内核,最高工作频率可达72MHz,搭配内置高速存储器,丰富的增强型I/O端口和外设连接到外部总线。工作电压为2.0V -5.5V,工作温度范围包含-40℃ +85℃ 工业级和-40℃ +105℃扩展工业级。

产品特性:

● 专为后内变速花毂设计的变速器,大扭力反应速度快

● 可配合ChargeON CleverDrive实现自手排变速功能

● 体积小,可直接附挂于各式车架

“HDMI转换器"

HDMI转换器

MCU型号:

MM32F031/MM32F0130

MCU特性:

高性能的Arm Cortex-M0为内核,最高工作频率可达72MHz,搭配内置高速存储器,丰富的增强型I/O端口和外设连接到外部总线。工作电压为2.0V -5.5V,工作温度范围包含-40℃ +85℃ 工业级和-40℃ +105℃扩展工业级。

产品特性:

● 支持输出最高分辨率为1080p

● 使用原有的线缆可快速安装额外的频道

● 用拨码开关设置频道,最多支持60个频道

“胎心仪"

胎心仪

MCU型号:

MM32SPIN25PF

MCU特性:

高性能的Arm Cortex-M0为内核,最高工作频率可达96MHz,搭配内置高速存储器,丰富的增强型I/O端口和外设连接到外部总线。工作电压为2.0V -5.5V,工作温度范围包含-40℃ +85℃ 工业级和-40℃ +105℃扩展工业级。

产品特性:

智能降噪;精度计算,高灵敏探头;医用品质,大屏显示,胎音清晰

“高速吹风筒"

高速吹风筒

MCU型号:

MM32SPIN06

MCU特性:

● 提供超高速FOC Sensorless驱动算法

● MCU支持单电阻采样所设计的PWM相位移功能

● 可以快速实现高速吹风机应用

灵动始终以“专业、可靠、便捷、高效”为服务理念,聚焦半导体产业升级和国产化趋势,紧抓用户需求,积极推出不同定位的新产品,并以提供“保姆式”的技术支持与方案设计,和完善自主的软件生态,让灵动MCU产品得以广泛应用于各类产品终端,接受市场检验。

围观 37

RTC可用于周期性从低功耗模式下唤醒MCU,RTC可选三个时钟源:

  • 低功耗 32.768kHz 外部低速晶振 (LSE):该时钟源提供了一个低功耗且精确的时间基准。
  • 低功耗内部低速振荡器 (LSI):使用该时钟源,节省了一个 32.768kHz 晶振的成本,但是精度没有外部晶振的精度高。
  • 外部高速晶振(HSE)128分频:在某些低功耗模式中HSE被关闭会导致RTC无时钟源。

为了用 RTC 闹钟事件将系统从停机模式下唤醒,必须进行如下操作:

  • 配置外部中断线 17 为上升沿触发。
  • 配置 RTC 使其可产生 RTC 闹钟事件。

如果要从待机模式中唤醒, 不必配置外部中断线 17。

MM32F013x有三种低功耗模式:睡眠模式(Sleep Mode)、停机模式(Stop Mode)和待机模式(Standby Mode),三种低功耗模式的对比如下表所示:

MM32F013x的三种低功耗模式

从上表中可以看出,当MCU工作在停机模式时,可以通过任一外部中断事件来唤醒。MM32F013x低功耗模式下的功耗列表如下图所示:

MM32F013x低功耗模式下的功耗列表

MM32F013x有22个外部中断源,其中EXTI 17对应的是RTC闹钟事件,所以结合RTC闹钟的功能,本文将重点介绍如何在MM32F013x上通过内部RTC模块的闹钟事件来唤醒处于停机模式下的MCU。

01、实现功能

通过内部RTC模块的闹钟事件(对应的是外部中断EXTI 17)来唤醒处于停机模式下的MCU。系统在进入停机模式时拉高GPIO端口,在恢复到正常运行状态时拉低GPIO端口。

02、配置顺序

1)使能PWR和BKP时钟:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

2)使能后备寄存器访问:

PWR_BackupAccessCmd(ENABLE);

3)配置RTC时钟源,使能RTC时钟,如果使用LSE,要打开LSE:

RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);

4)设置RTC预分频系数:

RTC_SetPrescaler();

5)开启相关中断:

RTC_ITConfig(RTC_IT_ALR, ENABLE);

6)配置外部中断线:

EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line    = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

7)配置中断服务函数:

NVIC_InitStructure.NVIC_IRQChannel  = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

8)部分操作要等待写操作完成和同步。

03、参考代码

3.1 RTC初始化配置:使用外部32.768kHz的晶振源

void RTC_Configure(void)
{
    uint16_t BKP_Value = 0x5A5A;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* Enable PWR Clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    /* Enable Access To The RTC & Backup Registers */
    PWR_BackupAccessCmd(ENABLE);

    if(BKP_ReadBackupRegister(BKP_DR1) != BKP_Value)
    {
        BKP_DeInit();

        /* Enable LSE Clock Source */
        RCC_LSEConfig(RCC_LSE_ON);

        /* Wait LSI Clock Source Ready */
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

        /* Config RTC Clock Source : LSE */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

        /* Enable RTC Clock */
        RCC_RTCCLKCmd(ENABLE);

        /* Wait For Synchronization */
        RTC_WaitForSynchro();
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Set The RTC Prescaler Value */
        RTC_SetPrescaler(32767);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
        /* Enable RTC Alarm Interrupt */
        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
        /* Exit From The RTC Configuration Mode */
        RTC_ExitConfigMode();
        BKP_WriteBackupRegister(BKP_DR1, BKP_Value);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
    else
    {
        /* Wait For Synchronization */
        RTC_WaitForSynchro();
        /* Enable RTC Alarm Interrupt */
        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
    /* Clear EXTI Line17 Flag */
    EXTI_ClearITPendingBit(EXTI_Line17);

   /* Configure EXTI Line17(RTC Alarm) To Generate An Interrupt On Rising Edge */
    EXTI_StructInit(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line    = EXTI_Line17;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* Config RTC NVIC */
    NVIC_InitStructure.NVIC_IRQChannel  = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

3.2 RTC闹钟中断函数

void RTC_BKP_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {
        GPIO_WriteBit(LED_GPIO, LED_PIN, Bit_RESET);

        /* Clear EXTI Line17 Flag */
        EXTI_ClearITPendingBit(EXTI_Line17);

        /* Check If The Wake-Up Flag Is Set */
        if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET)
        {
            /* Clear Wake Up Flag */
            PWR_ClearFlag(PWR_FLAG_WU);
        }
        /* Clear Alarm Flag */
        RTC_ClearITPendingBit(RTC_IT_ALR);

        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
}

3.3 设置RTC闹钟时间,系统进入STOP模式

void RTC_EntryStopMode(void)
{
    /* Wait till RTC Second event occurs */
    RTC_ClearFlag(RTC_FLAG_SEC);
    while(RTC_GetFlagStatus(RTC_FLAG_SEC) == RESET);

    /* Set The RTC Alarm Value */
    RTC_SetAlarm(RTC_GetCounter() + 3);

    /* Wait Until Last Write Operation On RTC REG Has Finished */
    RTC_WaitForLastTask();

    GPIO_WriteBit(LED_GPIO, LED_PIN, Bit_SET);

    /* Enter Stop Mode */
    PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
}

04、运行结果

编译软件工程无误后下载代码,调用RTC_EntryStopMode()函数使系统进入到停机模式,等待RTC闹钟事件3S后唤醒停机模式下的MCU,唤醒后继续执行程序,通过观察GPIO的电平状态来查看运行结果:


用户可以结合上一篇万年历功能设置在某年某月某时某刻定时唤醒MCU功能,万年历的具体实方式可以参考上一篇《MM32F013x——万年历》

转自:灵动微电子

围观 100

MM32F013x内部的RTC是一个独立的定时器单元,它拥有一组连续计数的计数器,配置相应的寄存器参数,可以实现闹钟、秒中断、毫秒中断、MCU定时唤醒、万年历等功能。

主要特征

① 可编程的预分频系数:分频系数最高为 220

② 32 位的可编程计数器,用于较长时间段的测量

③ 2 个分离的时钟:用于 APB1 接口的 PCLK1 和 RTC 时钟 (RTC 时钟的频率必须小于PCLK1 时钟频率的四分之一以上)

④ 可以选择以下三种 RTC 的时钟源
– HSE 时钟除以 128
– LSE 振荡器时钟
– LSI 振荡器时钟

⑤ 2 个独立的复位类型
– APB1 接口由系统复位
– RTC 核心 (预分频器、闹钟、计数器和分频器) 只能由后备域复位

⑥ 3 个专门的屏蔽中断
– 闹钟中断,用来产生一个软件可编程的闹钟中断
– 秒 / 毫秒中断,用来产生一个可编程的周期性中断信号 (最长可达 1 秒)
– 溢出中断,指示内部可编程计数器溢出并返回为 0 的状态

本文将重点介绍如何在MM32F013x上通过内部RTC模块实现万年历的功能。

实现功能

通过修改RTC计数器的初始值来设置系统当前的时间和日期,使能RTC秒中断功能;在RTC产生秒中断后,通过获取当前RTC的计数值,将其转换为对应的年月日信息,再通过蔡勒公式计算出星期,将最终的结果通过串口的形式输出显示。

RTC模块的电源域处在VDD数字电源域,只要MCU供电就可以使用RTC,没有独立的VBAT供电引脚,所以无法使用纽扣电池类的应用。

参考代码

01、结构体定义及全局变量

typedef struct
{
    uint16_t year;
    uint8_t  month;
    uint8_t  day;
    uint8_t  week;
    uint8_t  hour;
    uint8_t  minute;
    uint8_t  second;
} CALENDAR_t;
const uint8_t RTC_DayOfMonth[12] =
{
    31,28,31,30,31,30,31,31,30,31,30,31
};
CALENDAR_t    RTC_Calendar;

02、RTC初始化配置:使用外部32.768kHz的晶振源

void RTC_Configure(void)
{
    uint16_t BKP_Value = 0x5A5A;
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable PWR Clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    /* Enable WKUP pin */
    PWR_WakeUpPinCmd(ENABLE);
    /* Enable Access To The RTC & Backup Registers */
    PWR_BackupAccessCmd(ENABLE);
    if(BKP_ReadBackupRegister(BKP_DR1) != BKP_Value)
    {
        BKP_DeInit();
        /* Enable LSE Clock Source */
        RCC_LSEConfig(RCC_LSE_ON);

        /* Wait LSI Clock Source Ready */
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

        /* Config RTC Clock Source : LSE */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

        /* Enable RTC Clock */
        RCC_RTCCLKCmd(ENABLE);

        /* Wait For Synchronization */
        RTC_WaitForSynchro();
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Set The RTC Prescaler Value */
        RTC_SetPrescaler(32767);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Enable RTC Second Interrupt */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Exit From The RTC Configuration Mode */
        RTC_ExitConfigMode();

        BKP_WriteBackupRegister(BKP_DR1, BKP_Value);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Set initial time */
        RTC_SetDateTime(2021, 1, 12, 14, 48, 0);
    }
    else
    {
        /* Wait For Synchronization */
        RTC_WaitForSynchro();

        /* Enable RTC Second Interrupt */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }

    /* Config RTC NVIC */
    NVIC_InitStructure.NVIC_IRQChannel  = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

03、RTC秒中断函数

void RTC_BKP_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_SEC) != RESET)
    {
        /* Update Date and Time */
        RTC_UpdateCalendar();

        /* Print current Date and Time */
        RTC_PrintDateTime();

        /* Clear Alarm Flag */
        RTC_ClearITPendingBit(RTC_IT_SEC);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
}

04、将RTC计数值转换为日期信息

void RTC_UpdateCalendar(void)
{
    static uint32_t PreTotalDay = 0;

    uint32_t TotalSecond = 0;
    uint32_t TotalDay    = 0;

    uint16_t Year  = 1970;
    uint8_t  Month = 0;

    /* Get The RTC Counter Value */
    TotalSecond = RTC_GetCounter();
    TotalDay    = TotalSecond / 86400;

    if(PreTotalDay != TotalDay)
    {
        PreTotalDay = TotalDay;

        while(TotalDay >= 365)
        {
            if(RTC_LeapYear(Year) == 1)
            {
                if(TotalDay >= 366)
                {
                    TotalDay -= 366;
                }
                else
                {
                    break;
                }
            }
            else
            {
                TotalDay -= 365;
            }

            Year++;
        }
        RTC_Calendar.year = Year;
        while(TotalDay >= 28)
        {
            if((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1))
            {
                if(TotalDay >= 29)
                {
                    TotalDay -= 29;
                }
                else
                {
                    break;
                }
            }
            else
            {
                if(TotalDay >= RTC_DayOfMonth[Month])
                {
                    TotalDay -= RTC_DayOfMonth[Month];
                }
                else
                {
                    break;
                }
            }

            Month++;
        }

        RTC_Calendar.month = Month    + 1;
        RTC_Calendar.day   = TotalDay + 1;

        RTC_Calendar.week  = RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
    }

    RTC_Calendar.hour   =  (TotalSecond % 86400) / 3600;
    RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60;
    RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60;
}

05、将日期信息转换为RTC计数值

void RTC_SetDateTime(uint16_t Year, uint8_t Month, uint8_t Day,
                     uint8_t  Hour, uint8_t Min,   uint8_t Sec)
{
    uint32_t TotalSecond = 0;
    uint16_t y = 0;
    uint8_t  m = 0;

    if((Year >= 1970) && (Year <= 2099))
    {
        for(y = 1970;  y < Year; y++)
        {
            if(RTC_LeapYear(y) == 1)
            {
                TotalSecond += 31622400;    /* Total Seconds Of Leap   Year */
            }
            else
            {
                TotalSecond += 31536000;    /* Total Seconds Of Normal Year */
            }
        }

        for(m = 0; m < (Month - 1); m++)
        {
            TotalSecond += RTC_DayOfMonth[m] * 86400; /*Total Seconds Of Month */
            if((RTC_LeapYear(Year) == 1) && (m == 1))
            {
                TotalSecond += 86400;
            }
        }
        TotalSecond += (uint32_t)(Day - 1) * 86400; /* Total Seconds Of Day    */
        TotalSecond += (uint32_t)Hour      * 3600;  /* Total Seconds Of Hour   */
        TotalSecond += (uint32_t)Min       * 60;    /* Total Seconds Of Minute */
        TotalSecond += Sec;

        /* Enable PWR Clock */
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

        /* Enable Access To The RTC & Backup Registers */
        PWR_BackupAccessCmd(ENABLE);

        /* Set The RTC Counter Value */
        RTC_SetCounter(TotalSecond);

        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        RTC_UpdateCalendar();
    }
    else
    {
        printf("\r\nError Date & Time!!!\r\n");
    }
}

06、RTC信息打印

void RTC_PrintDateTime(void)
{
    printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day);

    switch(RTC_Calendar.week)
    {
     case 0 :
        printf(" SUN ");
        break;

     case 1 :
        printf(" MON ");
        break;

     case 2 :
        printf(" TUE ");
        break;

     case 3 :
        printf(" WED ");
        break;

     case 4 :
        printf(" THU ");
        break;

     case 5 :
        printf(" FRI ");
        break;

     case 6 :
        printf(" SAT ");
        break;

     default:
        break;
    }

    printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
}

07、RTC功能函数:判断闰年、蔡勒公式计算星期

uint8_t RTC_LeapYear(uint16_t Year)
{
    if(
        (((Year % 400) == 0)                     ) ||   /* Century Leap Year */
        (((Year % 100) != 0) && ((Year % 4) == 0))      /* Normal  Leay Year */
    )
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

uint8_t RTC_GetWeek(uint16_t Year, uint8_t Month, uint8_t Day)
{
    int w, c, y;
    /* Month 1 Or 2 of This Year Must Be As Last Month 13 Or 14 */
    if((Month == 1) || (Month == 2))
    {
        Month += 12;
        Year  -= 1;
    }

    w = 0;          /* Weekday */
    c = Year / 100; /* Century */
    y = Year % 100; /* Year    */

    w = y + (y / 4) + (c / 4) - (2 * c) + (26 * (Month + 1) / 10) + Day - 1;

    while(w < 0) w += 7;

    w %= 7;

    return w;
}

运行结果

编译软件工程无误后下载代码,在串口终端工具中我们可以看到每间隔1秒钟,RTC产生一次中断,在中断中我们将当前的日期信息通过串口打印在显示终端软件上:

转自:灵动微电子

围观 59

相信很多做变频设计或者电机控制领域的朋友们都熟悉一项重要技术——使用单电阻电流重构技术采样相电流,实现FOC控制。那采样相电流时就涉及到低调制区域,采样时间大于PWM分段矢量作用时间,此时就需要使用移相技术,就是在中央对齐互补模式下实现非对称PWM输出。

电机控制单电阻采样机制是在一个 PWM 波形内采集两相电流 ADC 数据,但某些扇区边界条件下只能获得一路电流 ADC 数据, 需要对 PWM 波形进行变形用于构造电流采样区域。


本文将重点介绍如何在MM32F013x上实现TIM1的硬件移相功能。

实现方式

目前MM32 MCU实现PWM 移相功能有多种实现方式,其中一种实现方式:设置TIM1和TIMx主从模式,开启TIMx的CCx中断,且开启对应的DMA请求通道去完成TIM1 CCRx值的改变,此外只需要在TIMx_CCx中断服务函数中改变TIM1 对应通道的CCR值即可在软件中实现动态修改移相角度功能,同样也就实现了PWM占空比在每半个周期内交替变化。

MM32F013x系列MCU中新增 TIM1的硬件移相功能,新增了PDER(通道x输出 PWM 移相使能位) 和 CCRxFALL(通道x在 PWM 中央对齐模式向下计数时的捕获/比较值)寄存器,允许 TIM1的5 个通道在硬件上完成输出 PWM 移相操作。开启 PDER 寄存器的 PWM移相使能,根据需要移动相位,配置 CCRxFALL 以及 CCRx,即可实现 PWM 输出可编程的移相波形,可左移或是右移。

相关寄存器

除了之前熟悉的TIM1 PWM输出相关的寄存器外,主要还需要关注以下新增寄存器。


定时器1的中央对齐模式

脉冲宽度调制模式可以产生一个由 TIMx_ARR 寄存器确定频率、由 TIMx_CCRx 寄存器确定占空比的信号。

当 TIMx_CR1 寄存器中的 CMS 位不为‘00’时为中央对齐模式 (所有其它的配置对 OCxREF/OCx 信号都有相同的作用)。根据不同的 CMS 位的设置,比较标志可以在计数器向上计数时被置 1、在计数器向下计数时被置 1、或在计数器向上和向下计数时被置‘1’。TIMx_CR1寄存器中的计数方向位 (DIR) 由硬件更新,不要用软件修改它。

中央对齐模式的图例可用下图来表示:


中央对齐有3种不同模式,可以通过软件设置TIM1_CR寄存器的CMS位来选择对应模式。3种模式中计数器的计数方式都是一样的,计数器从 0 开始计数到自动加载的值 (TIM1_ARR - 1),产生一个计数器溢出事件,然后向下计数到 1 并且产生一个计数器下溢事件,之后再循环从 0 开始重新计数。3种模式的不同之处在于,输出比较中断标志位被设置的时刻点均不一致,根据实际需求来进行选择。

使用中央对齐模式还有以下几点需要注意:

进入中央对齐模式时,使用当前的上/下计数配置;这就意味着计数器向上还是向下计数取决于 TIMx_CR1 寄存器中 DIR 位的当前值。此外,软件不能同时修改 DIR 和 CMS位。

不推荐当运行在中央对齐模式时改写计数器,因为会产生不可预知的结果。特别地:
– 如果写入计数器的值大于自动重加载的值 (TIMx_CNT > TIMx_ARR),则方向不会被更新
– 例如,如果计数器正在向上计数,它就会继续向上计数
– 如果将 0 或者 TIMx_ARR 的值写入计数器,方向被更新,但不产生更新事件UEV

使用中央对齐模式最保险的方法,就是在启动计数器之前产生一个软件更新 (设置 TIMx_EGR位中的 UG 位)。

软件实现步骤

了解完应用场景、实现原理以及涉及到的寄存器,下面介绍配置代码。

01、主程序初始化

以上为整个软件工程的入口主函数,默认主频为内部时钟HSI倍频到72M。初始化好了TIM1 前3个PWM输出通道所用引脚以及TIM1,并且开启TIM1一直输出PWM波形。

extern u32 SystemCoreClock;

s32 main(void)
{
    /* TIM1 PWM GPIO AF initial */
    TIM1_GPIO_Init();

    TIM1_PWM_Init(500 - 1, SystemCoreClock / 1000000 - 1);
    DELAY_Ms(100);
    TIM1_PWM_Shift_Test() ;
}

02、TIM1初始化

void TIM1_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_2);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_2);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_2);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_7) ;
}

以上代码完成了TIM1前3个PWM输出通道所用引脚的复用功能配置,对应的复用功能号需要根据芯片数据手册中的引脚功能定义表来获取到。

void TIM1_PWM_Shift_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);  

    TIM_DeInit(TIM1) ;   
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Period = arr;
    TIM_TimeBaseStructure.TIM_Prescaler = psc;   
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 250 ;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    TIM_OCInitStructure.TIM_Pulse = 250 ;
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);

    TIM_OCInitStructure.TIM_Pulse = 250 ;
    TIM_OC3Init(TIM1, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_Update);

    TIM_Cmd(TIM1, ENABLE);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);    
}

以上配置接口程序为时基相关参数,调用时候只需要根据实际应用情况传入不同的参数即可改变PWM输出的频率。

计数模式设置为中央对齐模式1,输出比较模式设置为PWM1输出模式,输出有效极性设置为高电平。当计时器值小于比较器设定值时则对应通道输出有效高电平,当计时器值大于或等于比较器设定值时则对应通道输出无效低电平。

void TIM1_PWM_Shift_Test(void)
{    
    GPIO_ResetBits(GPIOA,GPIO_Pin_7) ;

    TIM_SetCCR2FALL(TIM1, 200);
    TIM_SetCCR3FALL(TIM1, 300);

    TIM_PWMShiftConfig(TIM1, TIM_PDER_CCR2SHIFTEN|TIM_PDER_CCR3SHIFTEN, ENABLE);
}

TIM_PWMShiftConfig库函数实现开启或关闭对应通道输出PWM移相功能。TIM_SetCCRxFALL库函数实现设置对应通道在PWM中央对齐模式向下计数时的捕获/比较值。开启 PDER 寄存器的PWM移相使能后,根据需要移动相位,配置 CCRxFALL 以及 CCRx,即可实现PWM 输出可编程的移相波形,即可以左移亦或是右移。

测试验证

本次微课堂实验是基于eMiniboard MB-025硬件资源完成的,前述代码中,通过设置TIM1的时基为1KHz,且不开启OC1通道的移相功能,只使能OC2和OC3通道的允许输出PWM移相使能位,通过逻辑分析仪抓取TIM1 前3个PWM输出通道的波形,稳定的波形结果如下图所示:


粉红色表示通道1波形,深蓝色表示通道2波形,浅绿色表示通道3波形,黄色表示开始移相时刻点。图中看出,通道2较通道1往右移了18度的相位(最大只能移动180度,这里占用时长为500us,右移了50us为18度),通道2较通道1往左移了18度的相位(左移了50us为18度)。根据上述波形结果展示,TIM1 的硬件PWM移相功能可以配置相关寄存器的配置即可实现,操作简单。

转自:灵动微电子

围观 56

页面

订阅 RSS - MM32