灵动微电子

经历了2021的全球缺芯潮,我们迎来了2022 年,今年,世卫组织认为新冠疫情会得到控制,随着疫情影响减弱,以及AIOT、智慧家居市场、老年健康市场走热,半导体产业会有哪些新的变化?电子创新网采访数十位半导体高管,并以"行业领袖看2022”系列问答形式向业界传达知名半导体眼中的2022 ,这是该系列第四篇报道---来自灵动微董事长兼总裁吴忠洁的答复。

“灵动微董事长兼总裁
灵动微董事长兼总裁 吴忠洁

1问、您认为新冠疫情在2022年会得到有效控制吗?为什么?

吴忠洁:从2022年伊始国内外情况来看,奥密克戎的威力虽有所降低,但其传播性更强。举例来讲,法国在1月中的每日新增突破了36万例,创了新高。因此,我们认为新冠疫情在短期内不会消失,但其死亡病例会持续降低。随之而来的影响是传统半导体IDM大厂、电子设备制造企业、供应链和物流等仍无法恢复到疫情前水平。

2问、回顾2021 ,贵司有哪些亮点表现?

吴忠洁:2021年对灵动而言是非常重要的一年。灵动迎来了成立十周年的里程牌,在大家电、智能工业、汽车电子行业都有新突破。在产品广度和深度方面,高性能产品线布局加强;低功耗产品逐步推出;电机拳头产品进一步提高,12寸产品也在下半年率先量产。CRM和ERP系统升级,为客户和销售人员提供更专业、更快速的响应支持。

3问、对于2022 ,您认为有哪些行业热点会激发对半导体的需求?

吴忠洁:从大市场来看,数据中心、5G、新能源、汽车电子、AI-IOT、健康医疗等行业的强大需求促进了各个细分半导体产业的发展。从一些行业分析报告来看,存储器、功率器件在2022年持续看好,与之呼应的计算和控制芯片,也仍会处于市场需求的高位。

4问、对这些新的点,贵司有什么产品布局和应对策略?

吴忠洁:针对上述提到的热点应用,灵动在2022年将 (1) 推出基于安谋科技Star-MC1内核的MCU,此芯片从系统架构来看,其性能处于Arm Cortex-M33与Arm Cortex-M7之间,具有面向AI-IOT的较高算力;(2) 推出基于超低功耗架构设计的内置段码LCD的MCU,面向绿色家电、智能家居和健康医疗等应用,实现了100nA的极低功耗模式;(3) 继续提升电机MCU的性能与集成度,应对碳中和、碳达峰的大环境要求;(4) 积极推进在汽车电子上的布局,会在适当的时间发布车规MCU。

5问、2022 ,缺芯潮会延续吗?为什么?如果缓解,会在什么时间缓解?

吴忠洁:产业界和供应链并未完全恢复,且有很多计划外的情况发生,如疫情反复、货运成本、自然灾害、国际争端等问题。当疫情控制常态化、工厂(特别是IDM企业)完全复工且国际间人员交流正常化后,缺芯潮会逐步缓解。

6问、2022 年,您认为客户对半导体产品会有哪些新的需求?

吴忠洁:从目前的情况来看,供货保障仍是客户对芯片的第一要求;其次是品质,高品质的国产芯片在前面提到的大行业大市场中应用广泛,特别是产业升级带来的对终端产品附加值的提升,其中重要一环就是对高品质高可靠性的芯片要求;再次是和各应用相关的分门别类的需求,包括诸如对微型电机所需的高集成度的要求,对智能家电,特别是冰空洗、厨电等5V宽压,强抗干扰能力的要求,对工业自动化产品的长生命周期供货保障的要求,抑或是对车规产品高低温、可靠性的要求,等等。

7问、贵司在2022年的产能情况如何?

吴忠洁:谨慎乐观,但对于市场的波动仍需时刻警惕。

8问:2022年的中国半导体市场会有哪些新的变化?为什么?

吴忠洁:随着在过去三五年内国内各类半导体传统企业和新兴企业的发展,2022年会有更多各类新产品问世。届时,对于市场和客户来说,会有更多选择。对于半导体企业来说,竞争更为激励,也更需要利用自身的核心能力,抓住市场机遇,实现突破、追赶和超越。

9问、预计2022年全球半导体市场将继续增长,营收将突破6000亿美元以上,在这样的背景下,您认为中国本土公司和国外公司该会如何竞合?

吴忠洁:产品、推广、生态等方面向先进公司学习与合作,在力所能及的市场上与之竞争。

10问、应对摩尔定律减缓,您认为2022年3D IC和chiplet会有怎样的发展?

吴忠洁:在高性能计算和高集成度的市场,如在5nm或7nm为主的系统中,如何将先进数字逻辑工艺与较先进模拟工艺结合,在花费较低投资成本的前提下,制造出能快速迭代和推向市场的产品,chiplet可能是一种可行的技术方式。(完)

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

围观 41

灵动微电子推出全新超值型 MM32F0020 系列 MCU。该系列是灵动继 MM32F0140 后又一款基于 12 寸晶圆打造的产品系列,其搭载 48MHz Arm® Cortex®-M0 内核,提供 32KB Flash 和 2KB SRAM。MM32F0020 系列 MCU 适用于各类汽车,工业和消费市场,其典型应用包括充电器、电池管理、散热风扇、烟感、玩具、电机以及 8/16 位 MCU 升级替换。

“灵动再发12寸新品!全新

MM32F0020 的主要特点:

  • Arm® Cortex®-M0 内核,运行频率高达48MHz
  • 32KB Flash和2KB SRAM
  • 2 组 UART、1 组 SPI、1 组 I2S、1 组 I2C
  • 1 组高级定时器,可输出 4 通道带互补端口的 PWM,支持死区和刹车
  • 2 组 16 位定时器
  • 1 组 12-bit SAR ADC,支持高达 1MSPS 采样率和 8 路外部通道
  • 多达 18 个 GPIO
  • 2.0 – 5.5V 宽压设计,适用于各种电源供电场合
  • 高可靠性:高达 ±6000V HBM ESD,105°C 高温 Latch-up 达到 ±300mA
  • 提供 -40~85°C 和 -40~105°C 环温选项
  • 引脚兼容 MM32F0010和 MM32F003 系列

“灵动再发12寸新品!全新

产品供货情况

MM32F0020现已开始提供样片,目前有QFN20 和 TSSOP20 两种可选封装形式,全系列配置 32KB Flash 和 2KB SRAM,提供 -40~85°C 和 -40~105°C 产品型号,具体选型信息请参考数据手册。

有关样片的申请事宜,请洽灵动的销售和官方代理商。

更多详细信息,请访问 www.mm32mcu.com

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

围观 47

“灵动微电子发布全新超值型MM32F0140系列MCU"

灵动微电子推出全新超值型 MM32F0140系列 MCU。该系列是灵动第一款基于 12 寸晶圆打造的产品系列,其搭载 72MHz Arm Cortex-M0 内核,提供最高 64KB Flash 和 8KB SRAM,并集成了性能升级的 FlexCAN 接口。MM32F0140 系列 MCU 适用于各类汽车,工业和消费市场,其典型应用包括电池管理、电梯外呼板、断路器、消防、车载诊断仪、照明等。

“灵动微电子发布全新超值型MM32F0140系列MCU"

MM32F0140 的主要特点如下:

- Arm Cortex-M0 内核,运行频率高达 72MHz

- 最高 64KBFlash和8KB SRAM

- 内置 5 通道 DMA

- 内置 32-bit 硬件除法单元

- 3 组 UART、2 组 SPI、2 组 I2S、1 组 I2C

- 1 组 FlexCAN,支持 CAN 2.0B 协议

- 1 组高级定时器,可输出 4 通道带互补端口的 PWM,支持死区和刹车

- 5 组 32/16-bit 通用定时器

- 1 组 12-bit SAR ADC,支持最高 1MSPS 采样率和 13 路外部通道

- 1 组高速模拟比较器

- 2.0 – 5.5V 宽压设计,适用于各种电源供电场合

- 高可靠性: 支持最高 ±6000V HBM ESD,高温 Latch-up 可耐受电流 ±300mA

- 提供 -40~85°C 和 -40~105°C 环温选项

- 引脚兼容 MM32F031 和 MM32F0130 系列

“灵动微电子发布全新超值型MM32F0140系列MCU"

产品供货情况和支持

MM32F0140 现提供 LQFP48、LQFP32、QFN32 和 TSSOP20 四种可选封装形式。全系列提供 -40~85°C 和 -40~105°C 产品型号。主流开发设计工具和编程器厂家也已实现对 MM32F0140 的支持。

MM32F0140 系列现已提供样片和开发套件,预计将于 12 月量产。有关芯片购买事宜,请洽灵动的销售、官方代理商和方案设计公司。

更多详细信息,请访问灵动官网 www.mm32mcu.com

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

围观 60

如何在Windows环境下基于Eclipse开发、调试MM32 MCU,经过尝试,现将环境搭建分享给大家。

使用 Eclipse 与 GCC 优点:两者均为开源软件,可以自由使用,并支持 Windows,Linux等多个平台,同时还可以通过各种插件拓展其功能,例如 EGit。

环境需要工具

搭建过程中用到了以下工具:

  • Eclipse for C/C++ IDE(需要JAVA开发环境)

  • CDT-8.3.0

  • GNUARM for Eclipse plugin

环境简介

Eclipse

一款开源的集成开发环境(Integrated Development Environment),配合众多插件,可以用于Java应用程序开发、Android应用程序开发等。最精简的Eclipse只是一个框架,开发不同应用程序时需要安装对应的插件才能进行,不像微软的VS已经集成了众多的编译工具。

CDT

开发ARM内核的应用程序主要使用的是C/C++,所以Eclipse需要安装CDT(C/C++ Development Tooling)插件。

GNUARM Eclipse

包含一套Eclipse插件和用于跨平台嵌入式ARM程序开发工具的开源项目。有过stm32库函数开发经历的朋友可能知道,开发前工程师一般都会找一套工程模板,包含了对各个库文件的引用,我们只需要关注核心的应用即可,换一个项目时我们就拷贝一份模板,重新进行开发。Eclipse装了GNU ARM Eclipse工具簇后,新建项目时我们只需要选择对应模板的项目,插件就会自动帮我们配置好工程。

Windowsbuild tools

程序由代码变为可执行文件需要进过编译和链接的过程。Windows下的IDE无论是VisualStudio还是KEIL,编译工具都集成到IDE中了,且有一套自己的管理项目文件的方式。

Eclipse创建的工程会自带makefile文件,该文件的解析需要make工具。Linux下自带make工具,Windows下需要使用Windows build tools作为make工具使用。

GNUARM Embedded Toolchain

ARM交叉编译链,被编译的程序运行于基于ARM架构的处理器上。

由于Eclipse是基于JAVA,在安装之前首先要确定机器是否有JAVA环境,需要在甲骨文的官网http://www.oracle.com/index.html下载。

环境搭建

01、Eclipse Download

安装完JAVA环境后就可以进行安装Eclipse,下载最新的Eclipse in C/C++版本。

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

“图1
图1 Eclipse下载地址

我们下载的Eclipse为免安装版本的,下载完成以后直接解压运行即可。

02、CDT Download

URL:https://www.eclipse.org/cdt/downloads.php

在此网址下面下载适合开发者的CDT版本,Eclipse的不同版本会有不同版本的CDT来做适配,由于上面我们安装的为Eclipse的2020-12版本,所以本次教程选择与2020-12 Eclipse版本适配的CDT 10.1.0版本来进行安装。

“图2
图2 CDT下载地址

03、安装CDT

首先我们打开已经下载完成的CDT安装包来安装CDT,具体的安装步骤如下:

“图3
图3 安装CDT

打开Eclipse->Help->Install New software,点击Add按钮来添加插件,在弹出来的对话框中输入插件的名字以及下载的位置或者网址,点击Add按钮,并勾选我们所要添加的插件的类型,按Next进行安装,最后等待即可。

“图4
图4 安装插件

“图5
图5 安装插件

04、安装Eclipse插件 – 编译工具

在Eclipse的Help->install new software里面添加如下信息:

Name: GNU ARM Eclipse Plug-ins
URL: http://gnuarmeclipse.sourceforge.net/updates

“图6
图6 安装编译工具

“图7
图7 安装插件

05、下载并安装GNU Arm Embedded Toolchain

安装方式与CDT插件安装方式相同,目前已经支持基于 GDB SEGGER JLINK 以及 GDB OpenOCD的调试。

URL: https://developer.arm.com/tools-and-software/open-source-software/develo...

“图8
图8 下载并安装GNU

在上面的链接中用户可以下载exe文件,也可以下载zip文件,下载exe文件的时候需要进行安装,安装生成的文件与zip压缩文件解压缩的文件是一样的,使用哪一种方式用户可以根据自己的喜好来选择。

查看安装结果

通过上面的安装步骤我们已经完成安装,下面通过查看我们是否成功安装插件。

“图9
图9 Eclipse Marketplace

“图10
图10 查看CDT安装结果

至此关于Eclipse编译的插件都已经安装完毕,在下一章节我们将讲解如何使用Eclipse新建MM32工程文件。

来源:灵动微电子

围观 174

在上篇文章中我们介绍了EasyFlash组件三大功能中的ENV功能及使用,本篇通过移植开源的EasyLogger组件结合EasyFlash,使用MM32F013x内置空闲的FLASH存储空间来实现LOG日志的存储记录,使用芯片自带的RTC功能使日志在存储的时候带有日期和时间信息。

EasyLogger介绍

EasyLogger是一款超轻量级、高性能的C/C++日志库,非常适合对资源敏感的软件项目。相比于log4c、zlog这些知名的C/C++日志库,EasyLogger的功能更加简单,提供给用户的接口更少,但上手会很快,更多实用功能支持以插件(Flash、File等)形式进行动态扩展。

EasyLogger主要特性

  • 支持用户自定义输出方式(例如:终端、文件、数据库、串口、RS-485、Flash等等)

  • 日志内容可包含级别、时间戳、线程信息、进程信息等

  • 日志输出被设计为线程安全的方式,并支持异步输出和缓冲输出模式

  • 支持多种操作系统(例如:RT-Thread、uCOS、Linux、Windows等等),也支持裸机平台

  • 日志支持RAW格式(未经过格式化的原始日志)、支持HEXDUMP

  • 支持按标签、级别、关键词进行动态过滤

  • 各级别日志支持不同颜色显示,用户也可以根据自己的喜好,在 elog_cfg.h 对各个级别日志的颜色及字体风格进行单独设置

  • 扩展性强,支持以插件的形式扩展新功能

EasyLogger资源占用

最低要求:ROM < 1.6KB,RAM < 0.3KB。

EasyLogger移植说明

下载最新的EasyFlash源代码:
https://github.com/armink/EasyLogger

01、添加EasyLogger源文件

将\easylogger\目录下的inc、src、port及plugins文件夹拷贝到项目中:

“”

02、添加工程文件

添加\ easylogger \src\、\ easylogger \port\、\ easylogger \plugins\flash文件夹下的源文件到项目工程目录中:

“”

03、添加路径

根据项目需求,选择性添加\ easylogger \src\中的其他源码文件,\easylogger\inc\和\ easylogger \plugins\flash文件夹到编译的头文件目录列表中;

“”

EasyLogger接口移植

01、初始化和配置

easyflash_init初始化EasyLogger移植所需的资源等等。在easyflash_init成功后,我们再来进行easylogger的初始化和配置操作。

/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
int main(void)
{
    InitSystem();

    if(easyflash_init() == EF_NO_ERR)
    {
        EasyFlash_ENV_Demo();

        if(elog_init() == EF_NO_ERR)
        {
            elog_set_fmt(ELOG_LVL_ASSERT,  ELOG_FMT_ALL & ~ELOG_FMT_P_INFO);
            elog_set_fmt(ELOG_LVL_ERROR,   ELOG_FMT_LVL |  (ELOG_FMT_TAG  | ELOG_FMT_TIME));
            elog_set_fmt(ELOG_LVL_WARN,    ELOG_FMT_LVL |  (ELOG_FMT_TAG  | ELOG_FMT_TIME));
            elog_set_fmt(ELOG_LVL_INFO,    ELOG_FMT_LVL |  (ELOG_FMT_TAG  | ELOG_FMT_TIME));
            elog_set_fmt(ELOG_LVL_DEBUG,   ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
            elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));

            /* set EasyLogger assert hook */
            elog_assert_set_hook(elog_user_assert_hook);

            /* initialize EasyLogger Flash plugin */
            elog_flash_init();

            /* start EasyLogger */
            elog_start();
        }
    }

    while(1)
    {
        TASK_Scheduling();
    }
}

02、elog接口输出

日志最终输出的末端接口,可以在里面增加输出到终端、输出到文件、输出到Flash等方法。

/**
 * output log port interface
 *
 * @param log output of log
 * @param size log size
 */
void elog_port_output(const char *log, size_t size)
{
    /* add your code here */
    printf("%.*s", size, log);
    elog_flash_write(log, size);
} 

03、elog获取时间信息

返回当前时间,将会显示在日志中。

/**
 * get current time interface
 *
 * @return current time
 */
const char *elog_port_get_time(void)
{
    /* add your code here */
    memset(elog_time, 0, sizeof(elog_time));
    sprintf(elog_time, "%d-%02d-%02d %02d:%02d:%02d", 
        RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day,
        RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
    return elog_time;
}

参数设置

配置时需要修改项目中的elog_cfg.h文件,开启、关闭、修改对应的宏即可。

可以配置内容参数有输出开关、输出级别、断言开关、每行日志缓冲大小、行号最大长度、过滤标签最大长度、过滤关键字最大长度、标签+级别过滤器的最大数目、换行符、颜色和异步输出模式等,具体的配置参数方式可以参考链接。

开启缓冲输出模式后,如果缓冲区不满,用户线程在进行日志输出时,无需等待日志彻底输出完成,即可直接返回。但当日志缓冲区满以后,将会占用用户线程,自动将缓冲区中的日志全部输出干净。同时用户也可以在非日志输出线程,通过定时等机制使用 void elog_flush(void) 将缓冲区中的日志输出干净。

操作方法:
开启、关闭ELOG_BUFF_OUTPUT_ENABLE宏即可

默认大小:
(ELOG_LINE_BUF_SIZE * 10) ,不定义此宏,将会自动按照默认值设置

操作方法:
修改ELOG_BUF_OUTPUT_BUF_SIZE宏对应值即可

/* EasyLogger flash log plugin's RAM buffer size */
#define ELOG_FLASH_BUF_SIZE        1024   /* @note you must define it for a value */

测试验证

我们使用了芯片内部的RTC功能,在日志存储的时候记录了当前系统的日期和时间;结合EasyFlash我们将EasyLogger的日志记录存储到片内FLASH空间,这样在芯片重启后仍能查询到之前日志信息。同时我们将需要测试的函数注册到Shell命令中,通过调用Shell命令可以便捷的进行调试过程。

01、编写elog测试记录

/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void EasyLogger_SHELL_elog_test(void)
{
    log_a("Hello EasyLogger!"); /* 断言Assert  */
    log_e("Hello EasyLogger!"); /* 错误Error   */
    log_w("Hello EasyLogger!"); /* 警告Warn    */
    log_i("Hello EasyLogger!"); /* 信息Info    */
    log_d("Hello EasyLogger!"); /* 调试Debug   */
    log_v("Hello EasyLogger!"); /* 详细Verbose */
}
SHELL_EXPORT_CMD(elog_test, EasyLogger_SHELL_elog_test, EasyLogger test);

02、编写elog操作Flash插件的函数

/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void EasyLogger_SHELL_elog_flash(char *argv)
{
    if(!strcmp(argv, "read"))
    {
        printf("\r\nelog_flash read \r\n"); elog_flash_output_all();
    }
    else if(!strcmp(argv, "clean"))
    {
        printf("\r\nelog_flash clean\r\n"); elog_flash_clean();
    }
    else if(!strcmp(argv, "flush"))
    {
        printf("\r\nelog_flash flush\r\n"); elog_flash_flush();
    }
    else
    {
        printf("\r\nelog_flash error\r\n");
    }
}
SHELL_EXPORT_CMD(elog_flash, EasyLogger_SHELL_elog_flash, EasyLogger read/clean/flush flash log);

运行测试

01、下载程序后

待EasyFlash成功初始化完成后,再进行EasyLogger的初始化和配置,并记录Info类型日志信息,如下图所示:

“”

02、elog功能测试

在命令行窗口中,我们输入TAB按键可以查看当前程序支持的SHELL命令;我们通过elog_test命令来进行log信息的更新和记录,通过elog_flash flush命令来将当前的log日志信息存储到用户自定义规划的MM32F013x芯片片内Flash空间,使用elog_flash clean命令来清除存储在Flash内的所有log日志信息,使用elog_flash read命令可以将存储在Flash内的所有log日志信息读取出来;测试过程如下图所示:

“”

本次实验参考代码:
https://github.com/Samplecode-MM32/MM32MCU_Code

来源:灵动微电子

围观 321

一. MM32F013x片内FLASH

MM32F013x芯片内嵌高达64KB的程序FLASH存储空间,由64页组成,每页大小为1KB;用户的可执行程序从FLASH的起始地址0x08000000开始存放,支持读、写操作,页擦除,整片擦除,可通过 16 位(半字)方式编程写入闪存,其擦写寿命可达 20000 次。闪存控制器在读取数据时,支持带预取缓冲器的数据接口,以支持 MCU 运行在更高的主频。FLASH的每页都可以独立的设置写保护功能,以防止芯片内部可执行程序被复制,增强产品的安全性。

如果用户的可执行程序没有占满FLASH的存储空间,那我们就可以利用剩余的FLASH空间来当作存储器使用,可以存储一些用户配置、运行日志等记录,同时对于存储数据量不大的情况,可以省去外置的存储芯片,节省BOM成本。

本篇通过移植开源的EasyFlash组件,使用MM32F013x内置的空闲的FLASH存储空间来实现用户数据存储记录的功能。

二. EasyFlash介绍

EasyFlash是一款开源的轻量级嵌入式FLASH存储器库,方便开发者更加轻松的实现基于FLASH存储器的常见应用开发,非常适合智能家居、可穿戴、工控、医疗、物联网等需要断电存储功能的产品,资源占用极低,支持各种MCU片上存储器。该库主要包括三大实用功能:

ENV

快速保存产品参数,支持写平衡(磨损平衡)及掉电保护功能

EasyFlash不仅能够实现对产品的设定参数或运行日志等信息的掉电保存功能,还封装了简洁的增加、删除、修改及查询方法,降低了开发者对产品参数的处理难度,也保证了产品在后期升级时拥有更好的扩展性。让Flash变为NoSQL(非关系型数据库)模型的小型键值(Key-Value)存储数据库。

IAP

在线升级再也不是难事儿

该程序库封装了IAP(In-Application Programming)功能常用的接口,支持CRC32校验,同时支持Bootloader及Application的升级。

Log

无需文件系统,日志可直接存储在FLASH上

非常适合应用在小型的不带文件系统的产品中,方便开发人员快速定位、查找系统发生崩溃或死机的原因。同时配合EasyLogger一起使用,轻松实现日志的FLASH存储功能。

本篇章我们需要通过MM32F013x来实现ENV环境变量的存取功能,也可以叫做KV数据库模式;目前ENV功能有两种主要模式,一种为V4.0版本带来的NG(Next Generation)模式,还有一种为延续V3.0版本的Legacy模式。

对于NG模式相比较于Legacy模式具有以下新特性:

  • 更小的资源占用,内存占用几乎为0;V4.0以前版本会使用额外的RAM空间进行缓存;
  • ENV的值类型支持任意类型、任意长度,相当于直接memcpy变量至Flash;V4.0 之前只支持存储字符串;
  • ENV操作效率比以前的模式高,充分利用剩余空闲区域,擦除次数及操作时间显著降低;
  • 原生支持磨损平衡、掉电保护功能;V4.0之前需要占用额外的Flash扇区;
  • ENV支持增量升级,固件升级后ENV也支持升级;

三. EasyFlash ENV模式对比


四. EasyFlash资源占用

最低要求:ROM:6KB,RAM:0.1KB。

五. EasyFlash移植说明

下载最新的EasyFlash源代码:
https://github.com/armink/EasyFlash

01、目录结构

先解压下载好的源码包,文件的目录结构大致如下:


02、拷贝port文件

将\easyflash\目录下的inc、src及port文件夹拷贝到项目中:


03、添加工程文件

添加\easyflash\src\及\easyflash\port\文件夹下的源文件到项目工程目录中:


04、添加路径

根据项目需求,选择性添加\easyflash\src\中的其他源码文件,\easyflash\inc\文件夹到编译的头文件目录列表中:


六. EasyFlash接口移植

01、移植初始化

EasyFlash移植初始化。可以传递默认环境变量,初始化EasyFlash移植所需的资源等等。

/**
 * Flash port for hardware initialize.
 *
 * @param default_env default ENV set for user
 * @param default_env_size default ENV size
 *
 * @return result
 */
EfErrCode ef_port_init(ef_env const **default_env, size_t *default_env_size)
{
    EfErrCode result = EF_NO_ERR;

    *default_env = default_env_set;
    *default_env_size = sizeof(default_env_set) / sizeof(default_env_set[0]);
    return result;
}

02、读取FLASH

/**
 * Read data from flash.
 * @note This operation's units is word.
 *
 * @param addr flash address
 * @param buf buffer to store read data
 * @param size read bytes size
 *
 * @return result
 */
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size)
{
    EfErrCode result = EF_NO_ERR;
    /* You can add your code under here. */
    uint8_t *Data = (uint8_t *)buf;
    for(size_t i = 0; i < size; i++, addr++, Data++)
    {
        *Data = *(uint8_t *)addr;
    }
    return result;
}

03、擦除FLASH

/**
 * Erase data on flash.
 * @note This operation is irreversible.
 * @note This operation's units is different which on many chips.
 *
 * @param addr flash address
 * @param size erase bytes size
 *
 * @return result
 */
EfErrCode ef_port_erase(uint32_t addr, size_t size)
{
    EfErrCode result = EF_NO_ERR;

    /* make sure the start address is a multiple of EF_ERASE_MIN_SIZE */
    EF_ASSERT(addr % EF_ERASE_MIN_SIZE == 0);
    /* You can add your code under here. */
    FLASH_Status Status;
    size_t Number;
    Number = size / 1024;
    if((size % 1024) != 0) Number++;
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    for(size_t i = 0; i < Number; i++)
    {
        Status = FLASH_ErasePage(addr + 1024 * i);
        FLASH_ClearFlag(FLASH_FLAG_EOP);
        if(Status != FLASH_COMPLETE)
        {
            printf("\r\nErase Error!!!");
            result = EF_ERASE_ERR; break;
        }
    }
    FLASH_Lock();
    return result;
}

04、写入FLASH

/**
 * Write data to flash.
 * @note This operation's units is word.
 * @note This operation must after erase. @see flash_erase.
 *
 * @param addr flash address
 * @param buf the write data buffer
 * @param size write bytes size
 *
 * @return result
 */
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size)
{
    EfErrCode result = EF_NO_ERR;
    EF_ASSERT(size % 4 == 0);
    /* You can add your code under here. */
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    for(size_t i = 0; i < size; i+=4, buf++, addr+=4)
    {
        FLASH_ProgramWord(addr,   *buf);
        FLASH_ClearFlag(FLASH_FLAG_EOP);
        uint32_t Data = *(uint32_t *)addr;
        if(Data != *buf)
        {
            printf("\r\nWrite Error!!!");
            result = EF_WRITE_ERR; break;
        }
    }
    FLASH_Lock();
    return result;
}

05、对环境变量缓冲区加锁

/**
 * lock the ENV ram cache
 */
void ef_port_env_lock(void)
{
    /* You can add your code under here. */
    __disable_irq();
}

06、对环境变量缓冲区解锁

/**
 * unlock the ENV ram cache
 */
void ef_port_env_unlock(void)
{
    /* You can add your code under here. */
    __enable_irq();
}

07、打印调试日志信息

static char log_buf[128];

/**
 * This function is print flash debug info.
 *
 * @param file the file which has call this function
 * @param line the line number which has call this function
 * @param format output format
 * @param ... args
 *
 */
void ef_log_debug(const char *file, const long line, const char *format, ...)
{
#ifdef PRINT_DEBUG
    va_list args;
    /* args point to the first variable parameter */
    va_start(args, format);
    /* You can add your code under here. */
    ef_print("\r\n[Debug](%s:%ld) ", file, line);
    vsprintf(log_buf, format, args);
    ef_print("%s", log_buf);
    printf("\r\n");
    va_end(args);
#endif
}

08、打印普通日志信息

/**
 * This function is print flash routine info.
 *
 * @param format output format
 * @param ... args
 */
void ef_log_info(const char *format, ...)
{
    va_list args;
    /* args point to the first variable parameter */
    va_start(args, format);
    /* You can add your code under here. */
    ef_print("\r\n[LogInfo]");
    /* must use vprintf to print */
    vsprintf(log_buf, format, args);
    ef_print("%s", log_buf);
    printf("\r\n");
    va_end(args);
}

09、无格式打印信息

/**
 * This function is print flash non-package info.
 *
 * @param format output format
 * @param ... args
 */
void ef_print(const char *format, ...)
{
    va_list args;
    /* args point to the first variable parameter */
    va_start(args, format);
    /* You can add your code under here. */
    vsprintf(log_buf, format, args);
    printf("%s", log_buf);
    va_end(args);
}

10、默认环境变量集合

/* default environment variables set for user */
static const ef_env default_env_set[] =
{
    {"startup_times", "0"},
    {"pressed_times", "0"},
};

七. 设置参数

配置时需要修改项目中的ef_cfg.h文件,开启、关闭、修改对应的宏即可。

01、FLASH最小擦除单元

操作方法:修改EF_ERASE_MIN_SIZE宏对应值即可,单位:byte

/* The minimum size of flash erasure. May be a flash sector size. */
#define EF_ERASE_MIN_SIZE      1024  /* @note you must define it for a value */

02、FLASH写入粒度

操作方法:修改EF_WRITE_GRAN宏对应值即可,单位:bit,仅支持:1/8/32

/* the flash write granularity, unit: bit. only support 1(nor flash)/ 8/ 32 */    
#define EF_WRITE_GRAN          32    /* @note you must define it for a value */

03、备份区起始地址

操作方法:修改EF_START_ADDR宏对应值即可

/* backup area start address */
#define EF_START_ADDR    (0x08000000 + 50 * 1024)  /* @note you must define it for a value */

04、环境变量区总容量

操作方法:修改ENV_AREA_SIZE宏对应值即可

/* ENV area size. It's at least one empty sector for GC. So it's definition must
more then or equal 2 flash sector size. */
#define ENV_AREA_SIZE    (2 * 1024)    /* @note you must define it for a value if you used ENV */

在配置时需要注意以下几点:

1、所有的区域必须按照EF_ERASE_MIN_SIZE对齐;

2、环境变量分区大少至少为两倍以上EF_ERASE_MIN_SIZE;

3、从V4.0开始ENV的模式命名为NG模式,V4.0之前的称之为LEGACY遗留模式;遗留模式已经被废弃,不再建议继续使用;如果需要继续使用遗留模式,请EasyFlash的V3.X版本。

八. 测试验证

每次使用前,务必先执行easyflash_init()方法对EasyFlash库及所使用的FLASH进行初始化,保证初始化没问题后,再使用各功能的API方法。如果出现错误或断言,需根据提示信息检查移植配置及接口。

01、编写系统启动次数记录

/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void EasyFlash_Demo(void)
{
    uint32_t startup_times = 0;
    char *old_startup_times, new_startup_times[30] = {0};
    old_startup_times = ef_get_env("startup_times");
    startup_times = atol(old_startup_times);
    startup_times++;
    sprintf(new_startup_times, "%d", startup_times);
    printf("\r\nThe system now startup %d times\r\n\r\n", startup_times);
    ef_set_env("startup_times", new_startup_times);
    ef_save_env();
}

02、编写按键次数记录

/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void EasyFlash_Demo(void)
{
    uint32_t startup_times = 0;
    char *old_startup_times, new_startup_times[30] = {0};
    old_startup_times = ef_get_env("startup_times");
    startup_times = atol(old_startup_times);
    startup_times++;
    sprintf(new_startup_times, "%d", startup_times);
    printf("\r\nThe system now startup %d times\r\n\r\n", startup_times);
    ef_set_env("startup_times", new_startup_times);
    ef_save_env();
}

九. 运行测试

01、下载程序后,第一次运行

检测到EasyFlash没有默认配置或者说存储的数据CRC校验错误时,EasyFlash都会将存储恢复到默认值;随后将当前系统启动的次数记录到片机FLASH中,按键2次按键后,对按键的次数进行存储记录,如下图所示:


02、开发板重新上电运行

此时片内FLASH已经存在记录数据,等EasyFlash初始化成功后,取出当前的启动次数记录,进行操作后,更新启动次数存储记录;接着按键用户按键,我们会发现按键的次数接在上次的记录数据后面继续累加了,如下图所示:


参考源码:
https://github.com/Samplecode-MM32/MM32MCU_Code

从 EasyFlash V4.1 后,基于 EasyFlash 全新设计开发的 FlashDB 开源项目也正式上线,新集成了时序数据库、多分区管理,多数据库实例等功能,也从一定程度上提升了整体性能。

来源:灵动微电子

围观 348

灵动股份推出全新主流型 MM32F3270系列 MCU。MM32F3270系列基于Arm Cortex-M3 内核,适用于要求高集成度的高性能控制领域,如:工业控制、消防监控、家电、电源管理、打印机和扫描仪、通信转换模块等应用。MM32F3270支持工业级(-40℃ ~ 85℃)和扩展工业级(-40℃ ~ 105℃)工作温度。

MM32F3270系列MCU

MM32F3270在性能和外设集成度配置方面具有显著的特点,其中包括:

  • Arm Cortex-M3内核,运行频率高达120MHz
  • 2.0 – 5.5V 宽压设计,适用于各种电源供电场合
  • 内置1KB Cache,提高代码执行效率
  • 内置多达512KB Flash和128KB SRAM
  • 多达8个UART、3个SPI (含I2S功能) 、2个I2C,支持需要多外设连接的应用
  • 以太网 10/100M MAC,带RMII接口
  • USB 2.0 FS OTG
  • CAN 2.0B 控制器
  • 外扩存储器接口 FSMC
  • 1个SDIO接口
  • 3组12bit 1Msps ADC,多达21通道
  • 2个12bit DAC
  • 2个模拟比较器
  • 2个支持死区控制、6通道PWM的高级定时器
  • 6个通用定时器
  • 1个实时时钟,2个看门狗定时器
  • 与经典MM32F103引脚保持兼容

灵动股份发布全新主流型MM32F3270系列MCU

产品供货情况和支持

MM32F3270提供128KB 到512KB Flash的不同选择。先期发布LQFP144、LQFP100和LQFP64三种封装形式,LQFP48、QFN48和QFN40为可选封装形式。全系列提供工业级和扩展工业级产品型号。随芯片同时发布MM32F3270的官方开发套件:eMiniBoard MB-036(基于LQFP64的基础功能开发板)和EVB MB-039(基于LQFP144的全功能开发板)。主流开发设计工具和编程器厂家也已实现对MM32F3270的支持。

MM32F3270系列现已提供样片,并将于7月量产。有关芯片购买事宜,请洽灵动股份的销售、官方代理商和方案设计公司。

更多详细信息,请访问 www.mm32mcu.com

来源:灵动MM32MCU

围观 112

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——万年历》

转自:灵动微电子

围观 208

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产生一次中断,在中断中我们将当前的日期信息通过串口打印在显示终端软件上:

转自:灵动微电子

围观 74

页面

订阅 RSS - 灵动微电子