MM32

一、WWDG 简介

窗口看门狗通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在T6 位变成 0前被刷新,看门狗电路在达到预置的时间周期时,会产生一个MCU 复位。在递减计数器达到窗口寄存器数值之前,如果 7位的递减计数器数值(在控制寄存器中)被刷新,那么也将产生一个MCU 复位。这表明递减计数器需要在一个有限的时间窗口中被刷新。

二、WWDG 主要特征

  •   可编程的自由运行递减计数器

  •   条件复位

      - 当递减计数器的值小于 0x40,(若看门狗被启动)则产生复位。
      - 当递减计数器在窗口外被重新装载,(若看门狗被启动)则产生复位。

  •   如果启动了看门狗并且允许中断,当递减计数器等于0x40 时产生早期唤醒中断(EWI),它可以被用于重装载计数器以避免WWDG 复位。

三、WWDG功能描述

如果看门狗被启动(WWDG_CR寄存器中的WDGA 位被置1),并且当 7位(T[6:0])递减计数器从0x40 翻转到0x3F(T6位清零)时,则产生一个复位。如果软件在计数器值大于窗口寄存器中的数值时重新装载计数器,将产生一个复位。

看门狗框图
MM32之窗口看门狗(WWDG)

应用程序在正常运行过程中必须定期地写入WWDG_CR 寄存器以防止MCU 发生复位。只有当计数器值小于窗口寄存器的值时,才能进行写操作。储存在WWDG_CR 寄存器中的数值必须在0xFF 和0xC0 之间:

1、启动看门狗

在系统复位后,看门狗总是处于关闭状态,设置WWDG_CR 寄存器的WDGA 位能够开启看门狗,随后它不能再被关闭,除非发生复位。

2、控制递减计数器

递减计数器处于自由运行状态,即使看门狗被禁止,递减计数器仍继续递减计数。当看门狗被启用时,T6 位必须被设置1,以防止立即产生一个复位。

T[5:0]位包含了看门狗产生复位之前的计时数目;复位前的延时时间在一个最小值和一个最大值之间变化,这是因为写入WWDG_CR寄存器时,预分频值是未知的。

配置寄存器(WWDG_CFR)中包含窗口的上限值:要避免产生复位,递减计数器必须在其值小于窗口寄存器的数值并且大于0x3F 时被重新装载,上图描述了窗口寄存器的工作过程。

另一个重装载计数器的方法是利用早期唤醒中断(EWI)。设置WWDG_CFR 寄存器中的WEI 位开启该中断。当递减计数器到达0x40 时,则产生此中断,相应的中断服务程序(ISR)可以用来加载计数器以防止WWDG 复位。

在WWDG_SR 寄存器中写0可以清除该中断。注:T6 位可以被用来产生一个软件复位(WDGA 位被置位,T6 位清零)

四、如何编写看门狗超时程序

下图显示了装载到看门狗计数器(CNT)中的 6 位计数值和看门狗的延迟时间之间的线性关系(以 mS为单位)。此图可用来做为快速计算的参考,而未将时间的偏差考虑在内。如果需要更高的精度,可以使用下图提供的计算公式。

当写入 WWDG_CR 寄存器时,始终置 T6 位为1以避免立即产生一个复位。

窗口看门狗时序图
MM32之窗口看门狗(WWDG)

五、设置WWDG实验分析

  •   实验内容简介
环境MM32L073PF Miniboard、MDK,设置WWDG,通过观察喂狗与不喂狗的复位现象。

  •   实验代码分析
首先看main函数
MM32之窗口看门狗(WWDG)
先初始化一个串口,方便我们观察复位现象,然后通过函数Wwdg_reset_ON 进行设置WWDG。最后在循环里通过WWDG_SetCounter函数不停的喂狗。
MM32之窗口看门狗(WWDG)
在函数Wwdg_reset_ON中设置窗口值,和计数器的初值。

  •   实验现象
1、当注释掉喂狗函数时,通过串口在不断打印可以看出,MCU一直在复位。
2、当不注释喂狗函数时,串口只打印一次,MCU没有复位。

来源:灵动微电子

围观 419

一、MM32 BKP简介及功能描述

在使用MCU的过程中,当系统在待机模式下被唤醒,或者系统复位或电源复位时,会导致我们在RAM中的一些重要数据丢失,此时该怎么处理呢?MM32为我们提供了备份寄存器(BKP), 备份寄存器是 10 个 16 位的寄存器,可用来存储 20 个字节的用户应用程序数据。他们处在备份域里,当 1.5V 电源被切断,他们仍然由 VDD维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。
复位后,对备份寄存器的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。通过设置寄存器 RCC_APB1ENR 的 PWREN 位来打开电源和后备接口的时钟,可以用半字(16 位)或字(32 位)的方式操作这些外设寄存器。

二、BKP实验分析

  •   实验内容简介

以MM32L073PF为例,往BKP写数据,如果成功则LED慢闪,如果失败则LED快闪。

  •   实验代码分析

首先我们来看main函数:

MM32 MCU之BKP备份寄存器

1、 初始化delay_init, LED_Init 函数;
2、 通过函数BKP_DATA往BKP写数据,成功则返回0;
3、 写入成功则LED慢闪,写入失败则快闪。
MM32 MCU之BKP备份寄存器

在BKP_DATA函数中打开PWR时钟,使能BKP, 先通过WriteToBackupReg函数写入数据,在通过CheckBackupReg读取写入的数据是否正确。

两个函数代码如下:

MM32 MCU之BKP备份寄存器

MM32 MCU之BKP备份寄存器

  •   实验现象

向MM32 Miniboard里下载好程序后,启动板子,发现LED快闪,说明写入成功。

转自:灵动微电子

围观 409

MM32L0系列MCU内嵌两个通用比较器 COMP1 和 COMP2,为通用的可编程电压比较器,可独立使用(适用所有终端上的 I/O 口),也可与定时器结合使用, 支持两个独立的比较器。它们可用于多种功能,包括:由模拟信号触发低功耗模式唤醒事件调节模拟信号与 DAC 和定时器输出的 PWM 相结合,组成逐周期的电流控制回路。

比较器框图
MM32 定时器捕获比较器输出

COMPx信号宽度需要测量的输入信号连接到 PA0-PA7。参考信号可通过以下方式供电:

● 内部参考电压(VREFINT、 3/4 VREFINT、 1/2 VREFINT 或 1/4 VREFINT)

● PA4\ PA5\ PA0\ PA7的外部引脚(COMP1),PA4\ PA5\ PA2\ PA7的外部引脚(COMP2)COMPx 输出重映射通过COMP1_CSR[13:10],COMP2_CSR[13:10]比较器 x 输出选择位实现。

在 MM32L0系列MCU中,COMP1 \COMP2 输出可以重映射到内置定时器 TIM1 的 BKIN(刹车输入)和IC4上。重映射 COMP21\COMP2 输出时,可以测量具有特定高/低电平的信号宽度或频率(例如,移位信号)。

定时器输入捕获通道应配置为同时在上升沿和下降沿保存定时计数器值。当输入信号高于参考电压时, COMPx 输出处于高电平,此时会在定时器输入捕获上生成一个上升沿。当输入信号低于参考电压时, COMPx输出处于低电平,此时会生成一个下降沿。两个连续事件之间经过的时间(下降沿到上升沿或者上升沿到下降沿)表示脉冲宽度。因此,只需对计数器值做减法即可测量脉冲宽度。

COMPx输出重映射到定时器
MM32 定时器捕获比较器输出

1、将定时器输入捕获通道配置为仅在上升沿或下降沿保存计数器值,即可获得信号频率。

2、参考电压可以使用内部参考电压(0.4v、0.6v、0.9v和1.2v),也可以使用外部 GPIO 接到某一个电压值(0-5v)作为反相输入。

脉冲宽度测量应用:

将 COMPx 输出连接到定时器的输入捕获通道,则可以测量电容值,电容测量过程包括通过电阻对电容进行充电和放电。测量原理基于电阻、电容 (RC) 网络充电时间的测量过程,方法如下:

● 测量充电时间
● 已知充电电阻 (R)
● 可计算未知的电容 (C)

测量电容的电路图
MM32 定时器捕获比较器输出

通过 PWM 模式下配置的定时器输出比较通道 (TIMx OC),可确保 RC 网络周期性的充电和放电过程。

输入电压连接到 COMPx 同相输入,而阈值连接到 COMPx 反相输入。当输入电压超过阈值时, COMPx 输出切换为高电平,同时发生保存计数器值的捕获事件。

MM32 定时器捕获比较器输出

充电函数如下所示:

Input voltage =VDD(1- exp(- t /T))

其中:

● VDD 为正电源电压
● t 为时间
● T 为 RC 常数

根据上述公式可以推算:

C = -t / ( R x In( 1-threshold /(VDD) ))

比较器配置程序:

void Comp_Config(uint32_t COMP_Selection_COMPx)
{
COMP_InitTypeDef COMP_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_COMP, ENABLE);//开比较器时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//开GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_7);// 复用AF7,比较器输出

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//内部参考电压1.2v(可以内部基准设置0.4v、0.6v、0.9v和1.2v)
COMP_InitStructure.COMP_InvertingInput = COMP_InvertingInput_VREFINT;
//PA5为正相输入引脚
COMP_InitStructure.COMP_NonInvertingInput = COMP_NonInvertingInput_IO6;
//输出重印射到TIM1的输入捕获通道4
COMP_InitStructure.COMP_Output = COMP_Output_TIM1IC1;
//比较器不上锁
COMP_InitStructure.COMP_BlankingSrce = COMP_BlankingSrce_None;
//比较器输出极性:同相输出
COMP_InitStructure.COMP_OutputPol = COMP_OutputPol_NonInverted;
//比较器迟滞电压:0mV(可设置迟滞电压0-27mV)
COMP_InitStructure.COMP_Hysteresis = COMP_Hysteresis_No;
//比较器模式:中等速率(可设置极低速率、低速率、中等速率、高速率)
COMP_InitStructure.COMP_Mode = COMP_Mode_MediumSpeed; COMP_Init(COMP_Selection_COMPx, &COMP_InitStructure);
//使能比较器
COMP_Cmd(COMP_Selection_COMPx, ENABLE);
}

定时器TIM1配置程序:

void TIM1_PWMINPUT_INIT(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM1_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//TIM1时钟使能
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);GPIOA时钟使能

TIM_TimeBaseStructure.TIM_Period = arr; //设置重装载值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;// TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

NVIC_InitStructure.NVIC_IRQChannel = TIM1_IRQn; //配置中断优先级
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

TIM1_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端 IC1映射到TI1 上
TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到 TI1 上
TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //不分频
TIM1_ICInitStructure.TIM_ICFilter = 0x0; //不滤波
TIM_PWMIConfig(TIM1, &TIM1_ICInitStructure);

TIM_ITConfig(TIM1, TIM_IT_CC1|TIM_IT_Update, ENABLE); //使能捕获和更新中断
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1|TIM_IT_Update); //清中断
TIM_Cmd(TIM1, ENABLE); //定时器使能
}

定时器TIM1中断服务函数:

void TIM1_CC_IRQHandler (void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET)
{
period = TIM_GetCapture1(TIM1);
duty = TIM_GetCapture2(TIM1);
CollectFlag = 1;
}
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1|TIM_IT_Update);
}

小结:

1、为保证恶劣环境下比较器设置不能被无效寄存器访问或者程序计数器破坏所改变,比较器控制和状态寄存器可以设为写保护(只读),一旦设置完成, LOCK 位必须设为 1,这导致整个 COMPx_CSR 寄存器变成只读,包括 LOCK 位在内。写保护只能被 MCU 复位所清除。

2、在某些用电池供电的应用中,MCU需要在环境明亮时上电;其它情况下,微控制器必须保持断电状态。对于此类应用,可以使用电阻随光强度变化的光敏电阻 (LDR) 来控制MCU的状态。使用 LDR 传感器时,MCU可根据 LDR 电阻提供的电压切换到低功耗模式或退出低功耗模式。MM32L0系列MCU 的比较器COMP1和COMP2的可在内部将该输出分别连接到 EXTI 线19和20。

转自:灵动微电子

围观 498

一、IWDG简介

MM32 MCU内置两个看门狗,提供了更高的安全性、时间的精确性和使用的灵活性。两个看门设备(独立看门狗和窗口看门狗)可用来检测和解决由软件错误引起的故障;当计数器达到给定的超时值时,触发一个中断(仅适用于窗口型看门狗)或产生系统复位。

独立看门狗(IWDG)由专门的低速时钟(LSI)驱动,即使主时钟发生故障它也仍然有效。窗口看门狗从APB1时钟分频后得到的时钟驱动,通过可配置的时间窗口来检测应用程序非正常的过迟或过早的操作。

IWDG最适合应用于那些需要看门狗作为一个正在主程序外,能够完全独立工作,并且对时间精度要求低的场合。WWDG最适合那些要求看门狗在精确计时窗口起作用的应用程序。

二、IWDG主要性能

自由运行的递减计数器

时钟由独立的振荡器提供(可在停止和待机模式下工作)
看门狗被激活后,则在计数器计数至 0x0000 时产生复位。

三、IWDG 功能描述

下图为独立看门狗模块的功能框图。

在键寄存器(IWDG_KR)中写入 0xCCCC。开始启动独立看门狗;此时计数器开始从其复位值 0xFFF递减计数。当计数器计数到末尾 0x000 时,会产生一个复位信号(IWGD_RESET)。

无论何时,只要在键寄存器 IWDG_KR 中写入 0xAAAA, IWDG_RLR 中的值就会被重新加载到计数器,从而避免产生看门狗复位。

独立看门狗框图

独立看门狗框图

看门狗超时时间(40KHz 的输入时钟(LSI))

独立看门狗框图

此外,即使振荡器的频率是精确的,确切的时序仍然依赖于 APB 接口时钟与振荡器时钟之间的相位差,因此总会有一个完整的振荡器周期是不确定的。

IWDG_PR 和 IWDG_RLR 寄存器具有写保护功能。要修改这两个寄存器的值,必须先向 IWDG_KR 寄存器中写入 0x5555。以不同的值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即写入 0xAAAA)也会启动写保护功能。

状态寄存器指示预分频值和递减计数器是否正在被更新。

当微控制器进入调试模式时(CPU 核心停止),根据调试模块中的 DBG_IWDG_STOP 配置位的状态,IWDG 的计数器能够继续工作或停止。

四、独立看门狗代码配置

MM32L073系列独立看门狗初始化和启动设置:
MM32 独立看门狗(IWDG)
MM32 独立看门狗(IWDG)
MM32 独立看门狗(IWDG)
喂狗函数:
MM32 独立看门狗(IWDG)

五、实验实现窗口看门狗

测试方法:在main函数中先进行串口打印后初始和打开IWDG,然后让程序进入while(1)的死循环。

测试代码main函数如下:
MM32 独立看门狗(IWDG)

测试结果:
1、当我们在while(1)里进行喂狗操作时,串口只会进行一次打印。。
2、当我们在while(1)里不进行喂狗操作时,串口每隔大约看门狗复位的时间1.6s进行一次打印。

实验总结:实现了看门狗复位的功能。

转自:灵动微电子

围观 354

一、应用简介

在实际应用的一些产品上可能需要使用到对脉冲的个数进行计数,本文小编将给大家介绍如何使用TIM来做一个脉冲计数的功能。在MM32 TIM中正好有一个外部时钟模式1可以来帮助我们实现这个功能。

二、外部时钟源模式1描述

首先我们来了解一下外部时钟源模式1,当 TIMx_SMCR 寄存器的 SMS = 111 时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。下图是TI2外部时钟连接例子。

如何使用定时器做脉冲计数

例如,要配置向上计数器在 T12 输入端的上升沿计数,使用下列步骤:
1. 配置 TIMx_CCMR1 寄存器 CC2S = 01,配置通道 2 检测 TI2 输入的上升沿。
2. 配置 TIMx_CCMR1 寄存 器的 IC2F[3: 0],选择输入滤波器带宽(如果不需要滤波器,保持 IC2F= 0000) 。
3. 配置 TIMx_CCER 寄存器的 CC2P = 0,选定上升沿极性。
4. 配置 TIMx_SMCR 寄存器的 SMS = 111,选择定时器外部时钟模式 1。
5. 配置 TIMx_SMCR 寄存器中的 TS = 110,选定 TI2 作为触发输入源。
6. 设置 TIMx_CR1 寄存器的 CEN = 1,启动计数器。

注:捕获预分频器不用作触发,所以不需要对它进行配置。

当上升沿出现在 TI2,计数器计数一次,且 TIF 标志被设置。

在 TI2 的上升沿和计数器实际时钟之间的延时取决于TI2输入端的重新同步电路。

外部时钟模式1下的控制电路

如何使用定时器做脉冲计数

三、定时器代码配置

如何使用定时器做脉冲计数

四、实验结果

实验信号发生器从PA1输入1HZ的方波,进入KEIL的调试模式观察TIM->CNT的变化,TIM的计数器以每秒加1的速度向上计数,停止输入方波,计数器停止计数。说明我们实现了使用TIM进行计数的功能

转自:灵动MM32

围观 412

针对电动马达方案中需要使用到六步PWM输出功能,本篇文章将向大家介绍如何使用MM32L0系列MCU实现六步PWM输出功能。

MM32高级控制定时器TIM1有互补输出的功能,我们便可以利用定时器 TIM1 来产生 3 对 6 路的互补 PWM 输出。MM32高级控制定时器TIM1产生六步PWM输出,用于驱动三相电机,对应着直流无刷电机的六步换相。

六步 PWM 产生:当在一个通道上应用了互补输出时, OCxM、CCxE 和 CCxNE位的预载位有效,这些预装载位被传送到影子寄存器,因此可以预先设置好下一步的配置,并在同一时间更改所有通道的配置。COM 事件可以通过硬件(在 TRGI的上升沿) 设置或者软件修改TIM1_EGR 寄存器的 COM 位来产生。

当 COM 事件发生时会设置一个标志位(TIM1_SR 寄存器中的 COMIF 位),这时如果已设置了TIM1_DIER 寄存器的 COMIE 位,则产生一个中断;如果已设置了 TIMx_DIER寄存器的COMDE位,则产生一个DMA请求。

下图显示当发生 COM 事件时,三种不同配置下OCx和OCxN 输出。

MM32 六步PWM输出

在本次实验中主要教大家如何配置PWM的输出状态以及输出有效电平设置,在主函数的循环中更新PWM状态输出,将不使用中断方式,用户在实际电机配置程序中可以直接采用该配置方式移植到TIM1_BRK_UP_TRG_COM_IRQHandler函数中。

程序配置:

MM32 六步PWM输出

1) 开启定时器TIM1的时钟
2) 初始化 TIM1,设置 TIM1 的 ARR 和 PSC,向上计数模式
3) 设置BDTR,使能刹车输入信号,高电平有效
4) 设置 TIM1_CH1/CH1N,TIM1_CH2/CH2N,TIM1_CH3/CH3N的 PWM 模式,PWM 模式2,使能 TIM1 的 CHx 输出
5) 使能TIM1_CR1的自动重装载预装载允许位
6) 使能定时器TIM1
MM32 六步PWM输出

main函数配置流程:
1) systick延时函数初始化
2) GPIO口配置,PA8/PB13,PA9/PB14,PA10/PB15,分别为TIM1的三组互补通道输出:CH1/CH1N,CH2/CH2N,CH3/CH3N,复用推挽输出,最大输出速度50MHz, 除此之外,还有一个引脚可以配置也可以不配置,那就是TIM1_BKIN对应的引脚PB12,TIM1_BKIN的功能是检测故障,如果当PB12检测到高电平(取决于刹车有效电平的设置)时,就表示检测到故障,然后它会自动关闭定时器。
3) 定时器TIM1初始化及输出配置
4) PWM输出模式配置,在我配置的程序中大家可以看到对TIM1_CCMR1\TIM1_CCMR2\TIM1_CCER三个寄存器进行操作即可实现需要的功能

实验结果:

MM32 六步PWM输出

从逻辑分析仪抓的波形可以看到在6个通道中,如果一个通道处在PWM输出模式,另外的5个通道处在关闭状态,依次轮询该过程。

需要弄清楚我对上述三个寄存器做了什么操作需要参考UM_MM32L0xx文档的第13.4.7章节、13.4.8章节、13.4.9章节三个章节寄存器

MM32 六步PWM输出

如上图所示:位3是输出比较1预装载使能位,TIMx_CCR1的预装载值在更新事件到来时加载到当前寄存器,该位需要置1,位6:4是PWM输出比较模式配置,如果CCMR1的输出比较1配置0x48表示强制为无效电平,0x58表示强制为有效电平,0x68表示PWM模式1。

定时器的配置模式如上所示,但是输出到GPIO口的电平状态还需要查表34,根据表中的定义配置所需要的电平状态。

MM32 六步PWM输出

用户在配置电机所需要的PWM功能时,只需要根据对TIM1_CCMR1 \ TIM1_CCMR2 \ TIM1_CCER 三个寄存器进行操作,如果对寄存器的操作值不是很清楚,建议大家在调试模式下,直接通过修改该三个寄存器的值,然后查看对应的GPIO得状态变化,然后记录下来,在程序中将测试值写入到相对应的寄存器中,重新下载程序到MM32L0系列 MCU中观察对应的GPIO的电平状态。

转自: 灵动微电子

围观 536

一、比较器简介

在实际应用过程中有时候我们需要去判断两个变化的电压大小,在不同变化时需要做出不同的反应,这时候我们就可以用到比较器。MM32系列芯片内嵌两个通用比较器COMP1和COMP2, 比较器为通用的可编程电压比较器,支持两个独立的比较器。可独立使用(适合所有终端上的I/O),也可与定时器结合使用。它们可用于多种功能,包括:
• 由模拟信号触发低功耗模式唤醒事件
• 调节模拟信号
• 与 DAC 和定时器输出的 PWM 相结合,组成逐周期的电流控制回路

本文主要介绍一下如何通过比较器产生中断。

二、比较器功能描述

1、比较器输入输出介绍:

比较器框图如下,以COMP1为例,从图中可以看出PA0 – PA7口可连接到比较器的正向输入端,PA4 - PA7口及内部参考电压和三个等分电压值(1/4, 1/2, 3/4)可连接到比较器的反向输入端。比较器输入的 I/O 引脚必须在 GPIO 寄存器中设置为模拟模式。输出端可以重定向到一个 I/O 端口或多个定时器输入端,从而触发不同事件。

MM32如何使用比较器产生中断

2、 比较器时钟:

COMP 时钟控制器提供的时钟与 PCLK 同步(APB2 时钟)。在使用比较器之前,要先使能 RCC 控制器中的时钟使能控制位。

3、 比较器的中断:

比较器的输出可以内部连接到外部中断和事件控制器。每个比较器有自己的 EXTI 信号,能产生中断或事件。COMP1对应外部中断线19,COMP2对应外部中断线20。

4、 功耗模式:

在具体应用中可以通过调整比较器功耗和响应时间得到最优的结果。

COMPx_CSR 寄存器的 MODE[1: 0]位有下面几种设置:
• 00:高速/高功耗
• 01:中速/中等功耗
• 10:低速/低功耗
• 11:极低速/极低功耗

5、 比较器锁定机制:

比较器能用于安全的用途,比如过流或者过热保护。在某些特定的安全需求的应用中,有必要保证比较器设置不能被无效寄存器访问或者程序计数器破坏所改变。为了这个目的,比较器控制和状态寄存器可以设为写保护(只读)。一旦设置完成, LOCK 位必须设为 1,这导致整个 COMPx_CSR 寄存器变成只读,包括 LOCK 位在内。写保护只能被 MCU 复位所清除。

6、 迟滞现象:

比较器的可配置迟滞电压能防止无效的输出变化产生的噪声信号。在不需要强制迟滞电压的情况下迟滞现象可以被禁止。通过配置COMPx_CSR 寄存器 HYST[1:0]可以设置比较器迟滞电压。

MM32如何使用比较器产生中断

比较器的迟滞现象如下图:
MM32如何使用比较器产生中断

三、比较器触发中断实验主要代码分析

本实验以MM32L073为例,比较器配置代码如下图:

MM32如何使用比较器产生中断

中断配置及中断服务子函数如下图所示:
MM32如何使用比较器产生中断

四、实验结果

理论分析:使用信号发生器通过PA1输入频率为1Hz,高电平1.2V,低电平0V的方波,在输入信号由低电平变化为高电平(大于1/4Vrefint)时比较器会产生一个上升沿信号输出高电平,在输入信号由高电平变化为低电平(小于/4Vrefint)时比较器会产生一个下降沿输出低电平,由于设置的外部中断为上升下降沿触发,所以Led会以每0.5S翻转一次。

实验现象:LED以0.5s闪烁,可以通过示波器观察时间,与理论分析符合。

转自: 灵动MM32

围观 405

有客户需要用到MM32L073,需要通过IAP进行固件升级,在FLASH里面要烧录两份代码:一个Boot loader,一个用户应用程序。在开发应用程序时,使用中断函数不能相应中断。

在开发IAP的用户应用程序时,必须得重新映射中断向量表,中断向量表即某个中断服务程序的入口地址的集合。

在Cortex-M3内核的MCU上可以通过设置SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;该寄存器的值来实现中断向量表的重定义。但用户反馈在MM32L0xx系列以Cortex-M0为内核的单片机中却怎么也找不到这个设置中断向量表的寄存器,用户可以通过以下方法来实现中断向量表重定义。

实现方法基本思想:

1、将中断向量表放入到RAM的起始地址(只需要在应用程序中保留RAM起始地址的0x100大小不使用即可)。
2、在bootload中将应用程序的中断向量表从Flash中拷贝到RAM中。
3、设置MM32L073中断向量表位于RAM中。

MM32 IAP中断向量表重定义

0x20000000是SRAM的起始地址,0x08010000是应用程序的起址地址,从0x08010000开始的字节,存放应用程序的中断向量表。

应用程序代码及自身中断矢量表存放在离0X08000000加某个地址偏移量的地方,即从0x08000000+偏移量的地址开始存放APP代码及中断矢量。这个偏移量要大于IAP的程序空间,防止程序覆盖重定义的中断向量表的数据。在本程序中的偏移量为0x10000,即APP程序的起始地址为0x08010000。

当应用程序发生中断时,内核就从地址0x00处的向量表取相应中断的入口地址,即相当于从0x20000000处的向量表取中断入口地址,当然也相当于从0x08010000处的向量表取中断入口地址,然后去执行相应中断程序。

可以根据startup_MM32L0xx.s的中断函数的入口地址数计算需要预留的空间大小。

MM32 IAP中断向量表重定义

如上图所示,每一个DCD都代表一个中断向量。
例如:
DCD WWDG_IRQHandler ; Window Watchdog
“WWDG_IRQHandler "其实就是WWDG中断服务函数WWDG_IRQHandler的入口地址。

中断向量的集合定义了一张中断向量表,这张表包括48个元素,每个元素是一个长度为4字节的地址。除了第一个地址是SP(堆栈指针)外,其它的地址都是某个中断服务程序的入口地址。中断向量表的所占内存大小为48*4=180(0xC0)个字节。

转自: 灵动MM32

围观 658

有客户需要用到高精度的DAC模块,MM32L0系列产品内部没有集成DAC模块,考虑到外接DAC芯片会增加成本,所以在本实验中将为大家介绍使用PWM输出,经过简单的变换电路即可实现DAC,这将大量降低电子设备的成本、减少体积,并提高精度。本实验在PWM到DAC转换关系的理论分析基础上,设计出输出为0~5V电压的DAC。

MM32L0系列产品包含1个高级控制定时器、5个通用定时器(1个32 位定时器和5个16 位定时器),以及 2个看门狗定时器和1个系统嘀嗒定时器。

每个定时器都有 PWM 输出或单脉冲模式输出,所以MM32L0系列产品任意一款型号都可以用PWM做DAC输出功能。

PWM波形的分段函数:

MM32 基于PWM做DAC输出设计

其中:k为谐波次数,N是PWM波一个周期的计数脉冲个数,T是单片机中计数脉冲的基本周期,即MCU每隔T时间记一次数(计数器的值增加或者减少1),t为时间, n是PWM波一个周期中高电平的计数脉冲个数,VH和VL分别是PWM波中高低电平的电压值。

PWM的高低电平分别为VH和VL,理想的情况VL等于0,但是实际中一般不等于0,所以用户在处理PWM的VL时需注意,出现较大误差一般都是因为这个地方。

将上述函数展开成傅里叶级数得到:

MM32 基于PWM做DAC输出设计

从上式可以看出,上式中第1个方括弧为直流分量,第2项为1次谐波分量,第3项为大于1次的高次谐波分量。上式中的直流分量与n成线性关系,并随着n从0到N,直流分量从VL到VL+VH之间变化,这正是电压输出的DAC所需要的。因此,如果能把式中除直流分量的谐波过滤掉,则可以得到从PWM波到电压输出DAC的转换,即:PWM波可以通过一个低通滤波器进行解调。式中的第2项的幅度和相角与n有关,频率为1/(NT),该频率是设计低通滤波器的依据。如果能把1次谐波很好过滤掉,则高次谐波就应该基本不存在了。

在DAC的应用中,分辨率是一个很重要的参数,傅里叶级数公式中的分辨率计算直接与N和n的可能变化有关:

MM32 基于PWM做DAC输出设计

从上式中可看出:
N越大DAC的分辨率越高,但是NT也越大,即 PWM的周期或者傅里叶级数公式中的1次谐波周期也越大,相当于1次谐波的频率也越低,需要截止频率很低的低通滤波器,DAC输出的滞后也将增加。为了使T减少,即减少单片机的计数脉冲宽度(这往往需要提高单片机的工作频率),达到不降低1次谐波频率的前提下提高精度。

MM32L0系列产品最高工作频率可达 48MHz,TIM2是32位的定时器,PWM频率计算公式:
Fpwm = 48M / ((arr+1)*(psc+1))(单位:Hz)
公式中psc就是分频系数,arr就是计数值。预分频器可以将计数器的时钟频率按 1 到 65536 之间的任意值分频。计数值可以从1-4294967295(2的32次方减1)中任意选取。

本次实验采用两阶RC滤波,使用两个电阻和两个电容组成一个具有DAC功能的引脚,PB10是32位的定时器TIM2_CH3通道,PA3和PA4两个通道分别去采集1阶RC滤波和2阶RC滤波后的电压值。

MM32 基于PWM做DAC输出设计

R29和C10的具体参数可根据傅里叶级数公式的第2部分的一次谐波频率来选择,实际应用中一般选择阻容滤波器的截止频率为傅里叶级数公式的基波频率的1/4左右。

RC滤波器的截止频率计算公式:

f = 1/(2πRC)

滤波器是频率选择电路,只允许输入信号中的某些频率成分通过,而阻止其他频率成分到达输出端。在电路中需要考虑到芯片引脚输出端到RC滤波电路之间的存在阻值等问题,上图中的电阻和电容值需要根据实际情况计算调整。

PB10引脚能将不同占空比的PWM信号转换为不同电压值的模拟信号。为了能更准确的获取DAC转换值,电路中还使用了2个ADC通道用来检测DAC转换值。在转换过程中PWM信号频率越快DAC输出的电压值越稳定,PWM位数越高DAC输出的电压值精度越高,32位PWM比16位PWM精度高。

实验程序:

TIM2定时器配置:
u32 OutCnt;

void InitTIM2_PWM(u16 t1, u16 t2, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //TIM2_CH3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //TIM2_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource10,GPIO_AF_2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11,GPIO_AF_2);

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_Period = t1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC4Init(TIM2, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_Pulse = t2;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);

TIM_OCInitStructure.TIM_Pulse = t2;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}

获取 SysTick 计数器的值:
u32 GetSysTickCount(void)
{
return SysTick_Count;
}

设置 SysTick 重装载值:
void SysTick_Configuration(void)
{
SysTick_Config(48000);
}

SysTick中断配置:
u32 pwm = 150;
void SysTick_Handler(void)
{
if (SysTick_Count ++ > 500)
{
SysTick_Count = 0;
InitTIM2_PWM(1024, pwm, 1);
}
}

主函数:
int main(void)
{
SystemInit();
SysTick_Count = 0;
SysTick_Configuration();
while(1)
{
}
}

操作方法:按照上述硬件搭建实验环境后,上电接上调试器,进入Debug状态,在IAR的Live watch窗口修改pwm值可以实现占空比可调。

实验结果:

MM32 基于PWM做DAC输出设计MM32 基于PWM做DAC输出设计

根据实验现象:
从PWM到DAC输出的信号处理有许多电路实现方法,上述电路实现方法DAC输出的负载能力比较差,适合具有高输入阻抗的后续电路连接,对精度和负载能力要求较高的场合,建议增加基准电压、负载驱动等电路。在MCU的应用中还可以通过软件的方法进行精度调整和误差的进一步校正。

PWM 外设结合本电路所实现DAC 有非常好的差分非线性(DNL)、线性度(INL),8位分辨率的情况下,PWM 频率为50KHz,实测精度在 0.5LSB 以内,适合于输出低频、高精度的模拟信号。

转自: 灵动微电子

围观 564

呼吸灯,就是指电子产品上的LED灯的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰减,有节奏感地一起一伏,就像是在呼吸一样,因而被广泛应用于手机、电脑等电子设备的指示灯中。在使用MCU开发相关的应用产品中也可以加入呼吸灯功能,增强用户的体验感,在本实验中将介绍如何使用MM32L0系列产品芯片做呼吸灯功能。

在平时应用中可以知道,MCU的GPIO输出高低电平变化可以实现LED灯的亮、灭两个过程,如果GPIO的电平一直维持高电平或者低电平,LED灯就处于长亮或长灭的状态,呼吸灯就是通过较高频率的电平变化来实现亮灭的切换,由于人的视觉暂留效应,肉眼无法迅速捕捉快速亮灭变化的过程,所以在视觉中一直出现一直亮或者灭的状态,通过调整占空比可以控制LED灯的亮度,给人视觉上一种灯光由暗到亮逐渐增强,然后又由亮到暗逐渐衰减。

正常的成年人的吸气呼气时间整个过程持续大约3秒时间,即吸气时间(LED灯亮度逐渐变亮)时间为1.5S,吸、呼气时间(LED灯亮度逐渐变暗)时间为1.5S。

亮度随着时间逐渐变强再衰减,可以用两种算数方式实现:半周期的正弦函数曲线和指数上升曲线及对称的下降沿曲线。

指数方式曲线图
指数方式曲线图

要控制 LED 灯达到呼吸灯的效果,实际上就是要控制 LED 灯的亮度拟合呼吸特性曲线,在本次实验中,将采用指数上升曲线及对称的下降沿曲线方式,用户如果对正弦方式感兴趣也可以进行尝试。在本次实验中,我们使用MM32L0xx输出较高频率的PWM信号,通过调制信号的占空比,控制LED灯的亮度。

生成指数方式的曲线图主要因素:
TIMPeriod:定时器的计数周期,它的值必须与PWM 表中的极大值相等(应用中赋值需要减 1),而 PWM表的极大值决定了控制的分辨率。例如极大值为 10时,PWM 占空比只有10个等级,精确到0.1,当极大值为1000 时,PWM 占空比1000个等级,精确到0.001。在本次实验中设置定时器的计数周期值为255+1,即PWM表中的极大值也是256。

TIM_Prescaler:定时器时钟分频因子,它控制定时器计数器 CNT计数加1所需要的时间,它的值太大会导致输出的单个PWM波周期过长,影响控制的动态特性。如控制LED灯时,该值太大会导致LED灯开关时间变长,闪烁明显。一般来说,该值越小越好。在本次实验中设置定时器时钟分频因子为1757+1,即对时钟1758分频。

PWM 表的点数:PWM表的点数即对拟合曲线的采样点数,即把LED灯的亮度分为0-255个等级,采样点越多,能更好地还原拟合曲线,采样点太少,可能会导致失真。在本次实验中设置PWM 表的点数为40。

Period_class:周期倍数,即 PWM 表中每个元素的循环次数,它影响拟合曲线的周期。
在本次实验中设置设置周期倍数为8。

本次实验程序直接从MM32L073的定时器PWM输出的例程的基础上修改得来,TIM3配置成向上计数,PWM通道输出也被配置成当计数器 CNT 的值小于输出比较寄存CCR1的值时,PWM通道输出低电平,点亮 LED 灯。在函数的最后还使能了定时器中断,每当定时器的一个计数周期完成时,产生中断,配合中断服务函数,即可切换CCR1 比较寄存器的值。

程序配置如下:
TIM3初始化配置
//LED亮度等级列表
uint8_t PWM_Wave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,255
,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};
TIM3的初始化、中断及PWM输出配置:
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能TIM3时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//使能GPIOB时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //TIM3_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断源
NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01; //设置中断优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器

TIM_TimeBaseStructure.TIM_Period = arr;//设置自动装载寄存器值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision=0; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化TIM3寄存器

TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置PWM模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置初始脉冲宽度为0
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//小于CCR1值为低电平
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //初始化TIM3_OC1寄存器

TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //预装载使能

TIM_ARRPreloadConfig(TIM3, ENABLE); //使能 TIM 重载寄存器 ARR
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能TIM3的更新中断

TIM_Cmd(TIM3, ENABLE);//使能TIM3
}
中断服务函数:
void TIM3_IRQHandler(void)
{
static uint8_t a = 0;//PWM表的成员数,用于PWM查表
static uint8_t b = 0;//计算周期数
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//判断更新中断标志位
{
b++;
if(b >= 8) //周期倍数
{
TIM3->CCR1 = PWM_Wave [a]; //修改定时器比较寄存器值
a++;//查表指向下一个成员变量
if( a >= 40) //查表查到最后一位,重新指向表头
{
a=0;
}
b=0; //重置周期计数标志
}
TIM_ClearITPendingBit (TIM3, TIM_IT_Update);
}
}
主程序:
int main(void)
{
TIM3_PWM_Init(255,1757);
while(1)
{
}
}

本次实验配置拟合曲线周期计算:
TIMPeriod=255+1;
TIM_Prescaler=1757+1;
PWM 表的点数(a)=40;
Fpwm = 48M / ((arr+1)*(psc+1))(单位:Hz)
定时器 update 事件周期,即定时器中断周期:t1= 1/ Fpwm = 9376us
每个 PWM 点的时间:t2= t1*8= 75008us
遍历PWM表的周期时间为:t3=t2*40= 3000320us
通过公式的计算可知本工程的配置可使得输出的拟合曲线周期约等于3秒,符合成年人的吸气呼气时间整个过程持续大约3秒时间。

利用GPIO模拟PWM波形设计呼吸灯功能:
void LedOnOff(uint16_t t,uint16_t i)
{
LED1_ON();
delay_us(i);
LED1_OFF();
delay_us(t-i);
}
int main(void)
{
int i,j;
LED_Init();
TIM3_PWM_Init(255,1757);
while(1)
{
for(i=0;i for(j=0;j {
LedOnOff(100,i);
}
}
for(i=100;i>0;i--) {
for(j=0;j {
LedOnOff(100,i);
}
}
}
}

使用MM32L0系列MCU上述两种方式都可以实现呼吸灯功能,两种方法GPIO分别是PB4和PB5,download到MiniBoard中,可以对比测试两种实现方法的差异性,选择合适的呼吸灯实现方式。

来源: 灵动MM32

围观 642

页面

订阅 RSS - MM32