MM32

在我们应用开发时,经常会有一些程序运行参数需要保存,如一些修正系数或一些自定义数据。这些数据的特点是:数量少而且不需要经常修改,但又不能定义为常量,因为每台设备可能不一样而且在以后还有修改的可能。将这类数据存在指定的位置,需要修改时直接修改存储位置的数值,需要使用时则直接读取,会是一种方便的做法。考虑到这些数据量比较少,使用专门的存储单元既不经济,也没有必要,而MM32L0系列产品内部的Flash容量较大,且擦写次数以万为单位,使用部分的Flash空间存储用户数据实现起来既方便又实惠。

MM32L0系列产品嵌入式闪存特性:

• 高达 128K 字节闪存存储器
• 存储器结构:
- 主闪存模块:最大 32K 字(32K×32 位)
- 信息模块: 系统存储器:高达 1K 字节(1K x 8 位)
选项字节:高达 2 x 8 字节
保密空间:高达 3K 字节(3K x 8 位)
保护字节:高达 512 字节(512 x 8 位)
闪存接口的特性为:
• 带预取缓冲器的读接口(每字为 2 × 64 位 )
• 选择字节加载器
• 闪存编程/擦除操作
• 访问/写保护
• 低功耗模式

MM32 FLASH操作

闪存空间由 64 位宽的存储单元组成,既可以存代码又可以存数据。主闪存块按 128 页(每页 1K 字节)或 32 扇区(每扇区 4K 字节)分块,以扇区为单位设置写保护。

主存储器的起始地址就是0x0800 0000,结束地址是0x0801 FFFF,共128K字节, B0、B1都接GND的时候,就是从0x08000000开始运行代码的。

选项字节的地址空间是0x1FFF F800 - 0x1FFF F80F,选项字节为16个字节(有效数据为低 8 位,而高 8 位为低 8 位的反码)。用于配置读写保护、软件/硬件看门狗以及器件处于待机或停止模式下的复位。

1、 读保护

在产品发布时,如果想对MCU的程序做一些保护性措施,防止他人通过仿真器把Flash中的程序读取回来,得到bin文件或hex文件,然后去模仿产品。所以我们需要对程序进行保护,一种比较简单可靠的方法就是把Flash设置成读保护。

读保护后,只允许从用户代码中对主闪存存储器的读操作(以非调试方式从主闪存存储器启动),调试模式下(Sram boot 和 debug 模式)禁止对 flash 进行操作, flash 本身的程序禁止写低 4KB 空间。第 0 ~ 3 页被自动加上了写保护,其它部分的存储器可以通过在主闪存存储器中执行的代码进行编程(实现 IAP 或数据存储等功能),但不允许在调试模式下或在从内部 SRAM 启动后执行写或擦除操作(整片擦除除外)。

读保护操作方法:

FLASH_Unlock();//解锁
FLASH_ReadOutProtection(ENABLE);//读保护使能
FLASH_Lock();//上锁
注意:如果在设置了读保护时,调试器仍然连接到 JTAG/SWD 接口,需要执行一次上电复位,而不是(没有调试器时的)系统复位。

解除读保护操作方法:

FLASH_Unlock();//解锁
FLASH_ReadOutProtection(DISABLE);//读保护失能
FLASH_Lock();//上锁
注意:当解除读保护后MM32会自动擦除整片的Flash。

2、 FLASH写和擦除操作

只要MCU的供电正常,就可以完成烧写和擦除功能操作,但是在写/擦除 Flash 的同时不可以对它取指和访问数据,因为在对 Flash进行写/擦除操作的同时,任何对 Flash 的访问都会令总线停顿。

对FLASH进行写和擦除操作的功能主要由下列7个寄存器完成:
• 关键字寄存器(FLASH_KEYR)
• 选项字节关键字寄存器(FLASH_OPRKEYR)
• Flash 控制寄存器(FLASH_CR)
• Flash 状态寄存器(FLASH_SR)
• Flash 地址寄存器(FLASH_AR)
• 选项字节寄存器(FLASH_OBR)
• 写保护寄存器(FLASH_WRPR)

MM32 FLASH操作

第一步:FLASH解锁,访问闪存控制寄存器(FLASH_CR)的锁状态, Flash 存储器默认是受保护状态的,这样可以防范意外的擦除动作。 FLASH_CR 寄存器不允许被改写,除非执行一串针对 FLASH_KEYR 寄存器的解锁操作才能开启对 FLASH_CR 的访问权限。当LOCK位为“1”时表示FPEC和FLASH_CR被锁住,不能对FLASH进行读写或者擦除操作。在检测到正确的解锁序列后,硬件会自动清除此位为“0”,该位为“0”时才能对FLASH进行正常的操作。

第二步:清除相关标志位,清EOP(操作结束位)、WRPRTERR(写保护错误)和PGERR(编程错误)位,EOP(操作结束位)在需对FLASH进行读写操作时,需等存储器的操作结束和该位当闪存操作完成,然后硬件设置该位为“1”,再写“1”清除该状态位。 WRPRTERR(写保护错误)在试图对写保护的闪存地址编程时,硬件设置为“1”,写“1”清除状态。PGERR(编程错误)在试图对内容不是0XFFFF的地址编程时,该位置为“1”。

第三步:擦除FLASH(先擦除后写入),MM32的Flash 存储器可以按页(1K字节)为单位擦除,也可以整片擦除。
页(1K字节)擦除步骤:
• 检查 FLASH_SR 中的 BSY 位,以确认上一操作已经结束,当 FLASH_SR 中得 BSY 位为 1 的时候,这些寄存器不能写
• 置 FLASH_CR 寄存器中得 PER 位为 1
• 写 FLASH_AR 寄存器以选择待擦除的页
• 置 FLASH_CR 寄存器中的 STRT 位为 1
• 等待 FLASH_SR 中的 BSY 归零
• 读取已擦除页以校验
库函数操作函数:
FLASH_ErasePage(Page_Address);//页擦除,Page_Address表示需要擦除的页地址

整片擦除步骤:
可以用整片擦除命令一次擦除整个 Flash 用户区,但信息块不会受这个命令影响,注意用户在使用整片擦除时执行该函数会将写入的应用程序也会擦除,具体步骤如下:
• 检查 FLASH_SR 中的 BSY 位,以确认上一操作已经结束,当 FLASH_SR 中得 BSY 位为 1 的时候,这些寄存器不能写
• 置 FLASH_CR 寄存器中的 MER 位为 1
• 置 FLASH_CR 寄存器中的 STRT 位为 1
• 等待 BSY 位归零
• 读取全部页并校验
库函数操作函数:
FLASH_EraseAllPages();//整片擦除

选项字节的擦除步骤:
选项字节的编程与常规用户地址不同,总共 4 个字节(2 个写保护, 1 个读保护, 1 个硬件配置)。
• 检查 FLASH_SR 寄存器中的 BSY 位,以确保上一操作结束,当 FLASH_SR 中得 BSY 位为 1 的时候,这些寄存器不能写
• 解锁 FLASH_CR 寄存器中的 OPTWRE 位
• 置 FLASH_CR 寄存器中的 OPTER 位为 1
• 置 FLASH_CR 寄存器中的 STRT 位为 1
• 等待 BSY 位归零
• 读取并校验
库函数操作函数:
status = FLASH_EraseOptionBytes();//Option空间擦除

第四步:清除相关标志位,清EOP(操作结束位),等待擦除操作结束。

第五步:写入FLASH,存储器按页(1K字节)为单位写入,FLASH的写入地址必须是偶数(FLASH机制决定的FLASH写入的时候只能是偶数地址写入,必须写入半字或字,也就是2个字节或是4字节的内容)。
主闪存编程步骤:
• 检查 FLASH_SR 中的 BSY 位,以确认上一操作已经结束
• 置 FLASH_CR 寄存器中的 PG 位
• 以半字为单位向目标地址写入数据
• 等待 FLASH_SR 寄存器中的 BSY 归零
• 读数据以校验
在指定地址编写一个字库函数操作:
FLASH_ProgramWord(Address, Data);// Address表示待编写的地址,Data表示带写入数据

在指定地址编写半字库函数操作:
ProgramHalfWord(Address, Data); // Address表示待编写的地址,Data表示带写入数据

在指定FLASH选项字节地址(0x1FFF F800 - 0x1FFF F80F)编写半字库函数操作:
ProgramOptionByteData(Address, Data); // Address表示待编写的地址,Data表示带写入数据

第六步:FLASH上锁,设置Flash存储器保护状态,这样可以防范意外的擦除动作。

读取FLASH数据
我们要从地址addr,读取一个字(字节为8位,半字为16位,字为32位),可以通过如下的语句读取:
data= (*(__IO uint32_t*) addr));
将addr强制转换为u32指针,然后取该指针所指向的地址的值,即得到了addr地址的值。

小结:

1、 为了准确读取 Flash 数据,必须根据 CPU 时钟 (HCLK) 频率和器件电源电压,在 Flash 存取控制寄存器 (FLASH_ACR) 中正确地设置等待周期数 (LATENCY),有很多用户在自己配置时钟时,配置等待周期数错误导致程序进入HardFault。

MM32 FLASH操作

2、有些用户在测试读保护功能后,发现无法Download程序,这个时候MCU是处在读保护状态,所以程序无法下载,所以有两个解决办法:(1)在设置读保护之前加一个延时函数为程序擦除预留时间,延时时间以秒为单位,在MCU复位后,使用仿真器迅速擦除MCU程序。(2)从内置 SRAM 解除读保护,选择BOOT0接GND,使MCU从SRAM启动,Download程序,对读保护进行解除,然后重新将BOOT0接高电平。

3、如选项字节块对应的地址值为非 0xFFFF, 需先执行擦除选项字节块的动作,执行擦除选项字节块的动作不会导致自动的整片擦除操作, 不会改变读保护状态。

4、MM32的Flash主闪存块按 128 页(每页 1K 字节)或 32 扇区(每扇区 4K 字节)分块,存储器可以按页(1K字节)为单位擦除,也可以整片擦除,存储器按页(1K字节)为单位写入。

5、如果在设置了读保护时,调试器仍然连接到 JTAG/SWD 接口,需要执行一次上电复位读保护功能才会起作用,而不是(没有调试器时的)系统复位。

转自: 灵动微电子

围观 1107

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM最基本的调节就是频率和占空比,通过调节频率和占空比对外部元件进行控制或者捕捉外部信号源。

在大功率电机、变频器、开关电源等方案中,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。

MM32L0系列MCU有多达9个定时器,高级定时器TIM1可以产生互补的PWM,高级定时器TIM1是1 个 16 位 4 通道高级控制定时器,有 4 通道 PWM 输出,以及死区生成和紧急停止功能,可以通过相关寄存器的设置使能或关闭PWM的输出。

高级控制定时器(TIM1)能够输出两路互补信号,并且能够管理输出的瞬时关断和接通。这段时间通常被称为死区,用户应该根据连接的输出器件和它们的特性(电平转换的延时、电源开关的延时等)来调整死区时间。

使用紧急停止功能,使能输出信号和无效电平都会被改变,关闭PWM输出,能够有效的预防突发事件造成外部电机等不可控的状态,刹车源既可以是刹车输入管脚又可以是一个时钟失败事件。时钟失败事件由复位时钟控制器中的时钟安全系统产生。在安全方面,你可以把刹车输入连到电源驱动的报警输出、热敏传感器或者其他安全器件上。

在编写电机的驱动程序时,需要利用TIM1的Channel1,2,3三个通道生成三路互补的PWM波形,TIM1需要配置的寄存器有:捕捉/比较模式寄存器 1(TIMx_CCMR1)、捕捉/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~3)和刹车和死区寄存器(TIMx_BDTR).
程序配置流程如下:

void TIM1_PWM_Init(u16 arr,u16 psc)
{ 
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef  TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStruct;
      
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB , ENABLE);
 
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; //TIM3_CH1  TIM3_CH1N
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_13 | GPIO_Pin_14 | GPIO_Pin_15; //TIM3_CH1  TIM3_CH1N
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_12; //TIM3_CH1  TIM3_CH1N
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
      
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_PinAFConfig(GPIOB, GPIO_PinSource13,GPIO_AF_2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14,GPIO_AF_2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15,GPIO_AF_2);
             
GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_2);
      
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_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
 
TIM_BDTRInitStruct.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStruct.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStruct.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
TIM_BDTRInitStruct.TIM_DeadTime = 0x99;
//TDTS = 125nS(8MHz)
//DTG[7: 5] = 0xx => DT = DTG[7: 0] * Tdtg, Tdtg = TDTS;
//DTG[7: 5] = 10x => DT =(64+DTG[5: 0]) * Tdtg, Tdtg = 2 * TDTS;
//DTG[7: 5] = 110 => DT =(32+DTG[4: 0]) * Tdtg, Tdtg = 8 * TDTS;
//DTG[7: 5] = 111=> DT =(32 + DTG[4: 0]) *  Tdtg, Tdtg = 16 * TDTS;
 
TIM_BDTRInitStruct.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStruct.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig( TIM1, &TIM_BDTRInitStruct);     
      
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState=TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity=TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState=TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState=TIM_OCNIdleState_Reset;
             
TIM_OC1Init(TIM1, &TIM_OCInitStructure); 
TIM_OC2Init(TIM1, &TIM_OCInitStructure); 
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_Cmd(TIM1, ENABLE);
      
}

第一步:开定时器TIM1的时钟和GPIOA\GPIOB的时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB , ENABLE);

第二步:配置GPIO的模式,根据UM_MM32L0系列产品手册的8.1.11外设的 GPIO 配置查表可以知配置CHx的输出比较通道x和互补输出通道x功能时,需将GPIO配置成推挽复用输出模式,配置定时器的刹车输入功能时GPIO需配置成浮空输入功能。

MM32 MCU互补PWM输出使能带死区、刹车功能

根据DS_MM32L0系列产品手册的表4和表5的端口功能复用列表可知,PA8\PA9\PA10的AF2是TIM1的CH1\CH2\CH3通道,PB13\PB14\PB15的AF2是TIM1的CH1N\CH2N\CH3N通道,所以在配置GPIO的复用功能选择时通过软件写入配置IO的复用功能为AF2。

在本试验中,通过配置PB12作为刹车信号源的输入脚。根据端口功能复用列表可知,PB12的AF2是TIM1的刹车输入功能。

MM32 MCU互补PWM输出使能带死区、刹车功能

MM32 MCU互补PWM输出使能带死区、刹车功能

第三步:配置定时器的基本功能,首先来了解定时器初始化参数结构体指针,结构体类型是TIM_TimeBaseInitTypeDef,该结构体主要包含5个成员变量:

typedef struct
{
        uint32_t TIM_Prescaler;
        uint32_t TIM_CounterMode;
uint32_t TIM_Period;
        uint32_t TIM_ClockDivision;
        uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;

第一个参数TIM_Prescaler是用来设置分频系数的,MM32L0系列产品的高级定时器TIM1是挂在APB2上。在MM32所有例程中,在每次一启动时,在程序中就已经帮助客户配置好了APB的时钟,在system_MM32L0系列.c中如果您配置相对应的系统时钟,在时钟配置函数中,您可以看到已经对APB1\APB2\AHB的时钟做了配置,默认APB2的时钟是48MHz,所以定时器的时钟需用户根据自己的实际需求进行分频或者不分频操作。

MM32 MCU互补PWM输出使能带死区、刹车功能

第二个参数是TIM_CounterMode用来配置PWM模式,MM32L0系列产品的PWM模式有三种:向上计数模式、向下计数模式和中央对齐模式。
1、向上计数模式:例:当 TIMx_CNT < TIMx_CCRx 时,PWM 参考信号 OCxREF 为高,
否则低。如果 TIMx_CCRx 中的比较值大于自动重装载值(TIMx_ARR),则 OCxREF 保持为‘1’。
2、向下计数模式:当 TIMx_CNT > TIMx_CCRx 时参考信号 OCxREF 为低,否则为高。如果 TIMx_CCRx中的比较值大于 TIMx_ARR 中的自动重装载值,则 OCxREF 保持为‘1’。
3、中央对齐模式:中央对齐模式又分为三种模式,根据不同的 CMS 位的设置,比较标志可以在计数器向上计数时被置‘1’、在计数器向下计数时被置‘1’、或在计数器向上和向下计数时被置‘1’。

中央对齐模式选择
MM32 MCU互补PWM输出使能带死区、刹车功能

第三个参数是TIM_Period用来设置自动重载计数周期值,自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在 TIMx_CR1 寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件 UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMx_CR1 寄存器中的 UDIS位等于 0 时,产生更新事件。

第四个参数是TIM_ClockDivision用来设置时钟分频因子,该变量与后面死区持续时间配置有关,该变量是设置控制寄存器1(TIMx_CR1)的时钟分频因子CKD[1:0],这 2 位定义在定时器时钟(CK_INT) 频率、死区时间和由死区发生器与数字滤波器(ETR,TIx) 所用的采样时钟之间的分频比例。

第五个参数是TIM_RepetitionCounter用来设置重复计数值,该功能在上一篇文章有详细讲解。

第四步:配置定时器的刹车和死区,首先来了解定时器刹车和死区参数结构体指针,结构体类型是TIM_BDTRInitTypeDef,共7个成员变量:

    typedef struct
    {
        uint16_t TIM_OSSRState;
        uint16_t TIM_OSSIState;
        uint16_t TIM_LOCKLevel;
        uint16_t TIM_DeadTime;
        uint16_t TIM_Break;
        uint16_t TIM_BreakPolarity;
        uint16_t TIM_AutomaticOutput;
} TIM_BDTRInitTypeDef;

第一个参数是TIM_OSSRState用来配置运行模式下“关闭状态”选择。
第二个参数是TIM_OSSIState用来配置空闲模式下“关闭状态”选择。
第三个参数是TIM_LOCKLevel用来定时器的寄存器锁定保护,锁定级别分为3个级别,只能写入一次LOCK位,一旦写入TIMx_BDTR寄存器,则内容冻结至复位。
第四个参数是TIM_DeadTime设置死区时间,用户可根据UM_MM32L0xx手册的刹车和死区寄存器(TIM1x_BDTR)寄存器计算死区时间。
第五个参数是TIM_Break设置是否使能刹车功能。
第六个参数是TIM_BreakPolarity用来设置刹车输入电平。
第七个参数是TIM_AutomaticOutput设置BDTR寄存器的主输出使能(MOE)位。

第五步:配置定时器的PWM输出功能,

 typedef struct
    {
        uint16_t TIM_OCMode;
        uint16_t TIM_OutputState; 
        uint16_t TIM_OutputNState;          
        uint16_t TIM_Pulse;
        uint16_t TIM_OCPolarity;
        uint16_t TIM_OCNPolarity;
        uint16_t TIM_OCIdleState;
        uint16_t TIM_OCNIdleState;
    } TIM_OCInitTypeDef;

第一个参数是TIM_OCMode用来设置模式是输入捕获功能或者输出比较功能。
PWM 模式 1 - 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为有效电平,否则为 无效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为无效电平(OC1REF= 0) ,否 则为有效电平(OC1REF = 1)
PWM 模式 2 - 在向上计数时,一旦 TIMx_CNT < TIMx_CCR1 时通道 1 为无效电平,否则为有效电平;在向下计数时,一旦 TIMx_CNT > TIMx_CCR1 时通道 1 为有效电平,否则为无效电平。
第二个和第三个参数TIM_OutputState和TIM_OutputNState是配置互补通道输出使能。
第四个参数是TIM_Pulse用来设置待装入捕获比较寄存器的脉冲值,它的取值必须在 0x0000 和 0xFFFF之间。
第五和第六个参数TIM_OCPolarity和TIM_OCNPolarity是设置互补通道的极性是高还是低。
第七和第八个参数是TIM_OCIdleState和TIM_OCNIdleState用来设置刹车后的互补通道的电平状态。

第六步:初始化TIM1的OC1\OC2\OC3通道,使能定时TIM1。

逻辑分析仪抓取PWM波形
MM32 MCU互补PWM输出使能带死区、刹车功能
(逻辑分析仪精度不高,建议使用示波器抓波性)

小结:

1、PWM的频率和占空比计算:
//Fpwm = 48M / ((arr+1)*(psc+1))(单位:Hz)
//duty circle = TIM1->CCR1 / arr(单位:%)
本次配置参数:arr=999,psc=47,Pulse=500
Fpwm = 48M / ((arr+1)*(psc+1))=48M/ ((999+1)*(47+1))=1KHz
duty circle = (TIM1->CCR1 / arr )*100= (500/999)*100 = 50%

2、PWM互补通道电平设置:
通过上面截图可以看到互补通道的输出电平极性是相反的,如果用户需要配置互补通道输出相同的电平极性,需要配置的TIMx_CCER寄存器,通过库函数设置TIM_OCPolarity和TIM_OCNPolarity即可改变互补通道的电平输出极性。

MM32 MCU互补PWM输出使能带死区、刹车功能

3、刹车电平配置:
在配置刹车输入有效电平时,需先配置刹车和死区寄存器(TIMx_BDTR)的BKP位,配置为0时表示刹车输入低电平有效,配置为1时表示刹车输入高电平有效,在库函数中通过设置TIM_BreakPolarity变量即可实现有效电平设置,用户需根据外面信号来源配置对应的极性。

MM32 MCU互补PWM输出使能带死区、刹车功能

如上图所示,中间表示有刹车信号输入,关断PWM输出。在刹车信号关闭时,PWM又恢复输出,用户可以根据实际需求在刹车信号后是否恢复PWM输出,需要配置寄存器刹车和死区寄存器(TIMx_BDTR)的AOE位,在设置AOE位为0的情况下,触发刹车时,MOE关断,不再开启。

4、死区时间设置:
用户如果需要配置插入互补输出之间的死区持续时,需要配置刹车和死区寄存器(TIMx_BDTR)的死区发生器设置DTG[7:0],如下图所示,DT表示死区持续时间,Tdts为系统时钟周期,Tdtg表示乘以倍数后死区设置时间步进值。

MM32 MCU互补PWM输出使能带死区、刹车功能

Tdts = 1/48M = 20.83ns,TIM1_BDTR = 0xFF,高三位是0,所以选择第一个公式:
DT=(32+0x1F)*16* Tdts=(32+32)*16*20.83 = 21.3us。

MM32 MCU互补PWM输出使能带死区、刹车功能
(逻辑分析仪精度不高,建议使用示波器抓波性)

转自: 灵动微电子

围观 1730

UART(Universal Asynchronous Receiver and Transmitter)通用异步收发器(异步串行通信口)是MCU的一个重要的数字接口,市面上很多的传感器、通信模块等外围器件都采用了UART接口,同时工程师在软件开发调试过程中UART打印输出作为一种最直观的输出方式可以检查程序的运行情况,所以UART在MCU中的作用不言而喻。

首先普及一下并行通信、串行通信(同步通信和异步通信)两种通信方式的特点:
并行通信:并行通信是指数据的各个位同时传送,可以字或字节为单位并行进行。
-传输原理:数据各个位同时传输。
-优点:速度快,位数多
-缺点:占用引脚资源多,线路复杂,成本高

串行通信:串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度,其只需要少数几条线就可以在系统间交换信息。
-传输原理:数据按位顺序传输。
-优点:占用引脚资源少,传输线少
-缺点:速度相对较慢,耗时长

串行通信的通信方式又分为:同步通信和异步通信两种方式
同步通信:带时钟同步信号传输,发送方和接收方时钟需要建立连接,使双方的时钟
到完全同步。比如:SPI,IIC通信接口等。
异步通信:接收器和发送器使用各自的时钟,不带时钟同步信号。每一个字符要用起始位和停止位作为字符开始和结束的标志,以字符为单位的一个个地发送和接收。比如:UART通信接口等。

MM32系列MCU的通用异步收发器(UART)提供了一种灵活的方法与使用工业标准 NRZ 异步串行数据格式的外部设备之间进行全双工数据交换。UART 利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,以及调制解调器(CTS/RTS)操作。

有很多工程师在使用UART时,在设计PCB的UART接口时,一直对UART采用两线、三线、四线的问题一直都是很模糊情况,在这里我简单讲一下这个问题。

有些客户采用两线主要是单工模式,两线分别是:GND, TX 或者 RX,相当于MCU是做发送或者接收功能,将接收设备和发送设备共地,是要把参考电压调节成一致,避免接收设备和发送设备双方对高低电平的判断不一致的情况。

采用两线主要是双工模式,三线分别是:GND,TX,RX,接收设备和发送设备都是双向通信设备,且都有各自的供电电源,只需要将双方的基准电压调节一致就可以实现双方的串口通信功能,客户在使用ISP下载程序时一般都采用这种方式,预留一个三线接口。

采用四线主要是通信双方有一方需要为另一方提供电源,供另一方芯片运行,所以四线分别为:GND,TX,RX,VDD。
GND:共地,提供基准电压。
RX:接收数据串行输入。通过过采样技术来区别数据和噪音,从而恢复数据。
TX:发送数据串行输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
VDD:供电源。

一般的通信模块还会有另外两个引脚在硬件流控模式中需要使用:
nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送。
nRTS:发送请求,若是低电平,表明 UART 准备好接收数据。

两个UART间的通信接线方法

MM32 UART中断通信

两个UART间的硬件流控
MM32 UART中断通信

UART特征:

字长可以通过编程 UART_CCR 寄存器中的 CHAR 位,选择 5 ~ 8 位。在起始位期间, TX 脚处于低电平,在停止位期间处于高电平。

空闲符号被视为完全由‘1’组成的一个完整的数据帧,后面跟着包含了数据的下一帧的开始位(‘1’的位数也包括了停止位的位数)。

断开符号被视为在一个帧周期内全部收到‘0’(包括停止位期间,也是‘0’)。在断开帧结束时,发送器再插入 1 或 2 个停止位(‘1’)来应答起始位。

发送和接收由一个共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。

UART时序

MM32 UART中断通信

串口设置的步骤可分为如下几个流程:

1) 串口复位,GPIO复位
2) 串口时钟使能, GPIO 时钟使能
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 编写中断处理函数

1、串口复位,GPIO复位

在系统开始配置外设时,建议先执行复位相对应的外设,然后重新配置该外设,使其达到自己所期望的工作模式。

UART_DeInit(UART1);//复位串口1
GPIO_DeInit(GPIOA);//复位GPIOA

2、串口时钟使能, GPIO 时钟使能

串口1(UART1)是挂载在 APB2 下面的外设,GPIOA的时钟是挂载在AHB上,所以使能函数为:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);//使能UART1
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//使能GPIOA的时钟

3、GPIO 端口模式设置

在上一章节讲述了GPIO的使用,使用GPIO的UART功能需要配置端口复用功能,根据DS_MM32L073_Ver1.7手册表4.PA端口功能复用可知PA9和PA10的UART功能的复用配置AF1。
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1);
外设的GPIO配置:

MM32 UART中断通信

接下来的两段代码就是将 TX(PA9)设置为推挽复用输出模式,将 RX(PA10)设置为浮空输入模式:
GPIO_InitTypeDef GPIO_InitStructure;
//UART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 GPIOA.9

//UART1_RX GPIOA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化 GPIOA.10

4、串口参数初始化

4.1、分数波特率发生器

接收器和发送器的波特率在 BRR 的整数寄存器和 FRA 的小数寄存器中的值应设置成相同。
Tx/Rx 波特率 = fCK /(16 *UARTDIV )

这里的 fCK 是给外设的时钟(PCLK1 用于 UART2, PCLK2 用于 UART1)。UARTDIV 是一个无符号的定点数。这 16 位的值设置在 UART_BRR 寄存器。

例:如果BaudRate=9600,PCLK2=72MHz
则UARTDIV= 468.75

4.2、字长配置

字长可以通过编程 UART_CCR 寄存器中的 CHAR 位,选择 5 ~ 8 位。

4.3、奇偶校验位

奇偶控制(发送时生成一个奇偶位,接收时进行奇偶校验)可以通过设置 UART_CCR 寄存器上的 PEN位而激活。如果奇偶校验出错,无效数据不会从移位寄存器传送到 UART_RDR 寄存器。

偶校验:校验位使得一帧中的数据以及校验位中‘1’的个数为偶数。

例如:数据 = 00110101,有 4 个‘1’,如果选择偶校验(在 UART_CCR 中的 PSEL = 0),校验位将是‘0’。

奇校验:此校验位使得一帧中的数据以及校验位中‘1’的个数为奇数。

例如:数据=00110101,有 4 个‘1’,如果选择奇校验(在 UART_CCR 中的 PSEL = 1),校验位将是‘1’。

传输模式:如果 UART_CCR 的 PEN 位被置位,写进数据寄存器的数据的 MSB 位被校验位替换后发送出去(如果选择偶校验偶数个‘1’,如果选择奇校验奇数个‘1’)。如果奇偶校验失败, UART_ISR 寄存器中的 RXPERR_INTF 标志被置‘1’,并且如果 RXPERREN 在被预先设置的话,中断产生。

4.4、硬件数据流流控

RTS 流控制

如果 RTS 流控制被使能,只要 UART 接收器准备好接收新的数据, nRTS 就变成有效(接低电平)。当接收寄存器内有数据到达时, nRTS 被释放,由此表明希望在当前帧结束时停止数据传输。下图是一个启用 RTS 流控制的通信的例子。

MM32 UART中断通信

CTS 流控制

如果 CTS 流控制被使能,发送器在发送下一帧前检查 nCTS 输入。如果 nCTS 有效(被拉成低电平),则下一个数据被发送(假设那个数据是准备发送的),否则下一帧数据不被发出去。若 nCTS 在传输期间被变成无效,当前的传输完成后停止发送。下图是一个 CTS 流控制被启用的通信的例子。

MM32 UART中断通信

UART_InitTypeDef UART_InitStructure;
UART_InitStructure.UART_BaudRate = 115200;//波特率设置
UART_InitStructure.UART_WordLength= UART_WordLength_8b;//字长为8
UART_InitStructure.UART_StopBits = UART_StopBits_1;//一个停止位
UART_InitStructure.UART_Parity = UART_Parity_No;//无奇偶校验位
UART_InitStructure.UART_HardwareFlowControl=UART_HardwareFlowControl_None;//无硬件数据流流控
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;//开启收发模式
UART_Init(UART1, &UART_InitStructure); //初始化串口
UART_Cmd(UART1, ENABLE); //UART1使能

4.5、开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)

NVIC_InitTypeDef NVIC_InitStructure;
UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);//开启串口接收中断
NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

4.6、编写中断处理函数

void UART1_IRQHandler(void) //中断服务函数
{
u8 Res;
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET) //接收中断产生
{
UART_ClearITPendingBit(UART1,UART_IT_RXIEN);//清中断标志
Res =UART_ReceiveData(UART1);
UART_SendData (Res);
}
}

按照上述配置完成后,在main函数中将上述函数调用,将程序下载到MiniBorad中,打开串口助手,在对话框中输入您需要输入的内容,MCU的UART将您发送的数据转发并且打印在串口助手对话框,可以在显示串口看到您输入的内容。打印结果如下图所示:

MM32 UART中断通信

转自: 灵动微电子

围观 672

前段时间有一个客户需要用到4线电阻触摸屏,为了节省一片触摸屏控制芯片,客户决定使用MCU直接控制4线电阻触摸屏,主要使用到了MM32的ADC外设资源,通过ADC采集触摸屏的X+\Y+的电压,算出相对应的坐标轴,并且显示在显示屏上。所以今天将结合该例程和大家一起熟悉、了解MM32的ADC配置流程。

一、触摸屏操作原理

电阻触摸屏利用压力感应进行控制。电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层,它的内表面也涂有一层涂层,在他们之间有许多细小的(小于1/1000英寸)的透明隔离点把两层导电层隔开绝缘。当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在X和Y两个方向上产生信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出(X,Y)的位置,再根据模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。

二、MM32驱动电阻触摸屏的实现

MM32系列MCU的I/O口可以编程配置成带上拉电阻的模式,这个特点可以用来检测有无触摸。MM32的GPIO管脚连接触摸屏的Y+和X- ,连接Y+的管脚配置成内部电阻上拉模式,连接X-的管脚配置成推挽输出低电平。如果Y+为高电平,那么就是没有触摸。如果Y+为低电平,触摸屏被压下。从高到低电平的跳变可以检测触摸,可以用外部中断,在本例程中将使用ADC的单次扫描模式采集X+\Y+电压。

图1. 4线电阻触摸屏坐标读取

 MM32通过ADC控制4线电阻触摸屏

三、MM32 ADC介绍

12 位 ADC 是逐次逼近式的模拟-数字转换器(SAR A/D 转换器),且ADC转换数据分辨率可设置8-12位有效。

高达1Msps转换速率,有很多用户在计算采样频率设置时会觉得很难理解,在这里将为大家讲一下采样频率计算方法,ADC 的时钟 ADCLK 由 PCLK2 分频得到ADC 的输入时钟(不得超过 15MHz,它是由 PCLK2 经分频产生)。分频系数可通过设置 ADCFG 寄存器的 ADCPRE 位来确定,即 PCLK2/(N+1)/2 分频后作为 ADC 时钟。设置 ADC 分辨率为 n 位(n=8,9,10,11,12),每个通道采样时间为 m, Fsample = FADCLK/(m + n + 1.5)。假设分辨率配置为 12bit,每个通道采样时间为 1.5T, 则 Fsample =FADCLK/15。

例如:设置 ADC 分辨率为 n =12,每个通道采样时间m =1.5个周期,FADCLK=15MHz
Fsample = FADCLK/(m + n + 1.5)=15/(12+1.5+1.5)=1Msps

根据此配置可以得到1Msps转换速率,也就是转换时间为1us,用户可以根据项目的需求配置相对应的转换速率。

A/D 转换器支持多种工作模式:单次转换、单周期扫描模式和连续转换模式。
- 单次转换模式: A/D 转换在指定通道完成一次转换。
- 单周期扫描模式: A/D 转换在所有指定通道完成一个周期(从低序号通道到高序号通道)转换。

- 连续扫描模式: A/D 转换连续执行单周期扫描模式直到软件停止 A/D 转换。

支持DMA传输,单周期扫描和连续扫描时通道转换的值存储在各自通道的数据寄存(ADDRn)中,最近一次转换的结果也会保存在 ADDATA 寄存器中。DMA 传输时可以选择传输某个特定通道的数据,或者传输所有扫描通道的结果。

A/D 转换的启动方式有软件设定(即在配置相关寄存器时,直接开启采样)、外部引脚触发(例如定时器捕获,EXTI线)以及各个定时器启动(Timer1/2/3/4 匹配或者 TRGO 信号,在配置电机应用时需要使用定时器触发ADC采样)。

窗口比较器(模拟看门狗)允许应用程序检测输入电压是否超出了用户设定的高/低阀值值,转换结果可和指定的值相比较,当转换值和设定值相匹配时,用户可设定是否产生中断请求。

四、用MM32L373读触摸屏参数
在硬件中使用了MM32L373的ADC单周期扫描功能,使用到的GPIO分别是:PA1\PA3\PA4\PA5,分两步读取X,Y坐标值。

//测量X+电压的GPIO配置
void XP_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//将X+配置模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//将X-配置浮空模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//将Y+、Y-配置为通用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);//Y-输出低电平
GPIO_SetBits(GPIOA,GPIO_Pin_5); //Y+输出高电平
}
//测量Y+电压的GPIO配置
void YP_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//将Y+ 配置模拟输入
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);
//将Y- 配置浮空模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//将X+、X-配置为通用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_3);//X-输出低电平
GPIO_SetBits(GPIOA,GPIO_Pin_1); //X+输出高电平
}
第一步,驱动Y+为高电平,Y-为低电平,接X+管脚配置成AD输入模式,检测X+的电压,此电压与驱动电压的比例即Y坐标和整个屏的高度比率。
/***************************************************************************
** 函数信息:void XP_ADC1_SingleChannel(uint8_t ADC_Channel_x)
**功能描述:测量X+电压,配置ADC单次扫描通道
**输入函数:ADC_Channel_x , x为0~8
**输出函数:无
***************************************************************************/
void XP_ADC1_SingleChannel(uint8_t ADC_Channel_x)
{
ADC_InitTypeDef ADC_InitStructure;
XP_GPIO_Configuration();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC1时钟
/* Initialize the ADC_PRESCARE values */
ADC_InitStructure.ADC_PRESCARE = ADC_PCLK2_PRESCARE_16;//16分频
/* Initialize the ADC_Mode member */
ADC_InitStructure.ADC_Mode = ADC_Mode_Single;//配置单通道扫描模式
/* Initialize the ADC_ContinuousConvMode member */
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换模式
/* Initialize the ADC_DataAlign member */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
/* Initialize the ADC_ExternalTrigConv member */
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T1_CC1;//外部触发通道选择
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_x, 0, ADC_SampleTime_1_5Cycles); //设置换换顺序和采样时间
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件转换启动
if(ADC_Channel_x==ADC_Channel_8)
{
ADC1->ADCFG|=0x04;
}
}

第二步,驱动X+为高电平,X-为低电平,接Y+的管脚配置成AD输入模式,检测Y+的电压,此电压与驱动电压的比例即X坐标和整个屏的宽度比率。
/***************************************************************************
** 函数信息:void YP_ADC1_SingleChannel(uint8_t ADC_Channel_x)
**功能描述:测量Y+电压,配置ADC单次扫描通道
**输入函数:ADC_Channel_x , x为0~8
**输出函数:无
***************************************************************************/
void YP_ADC1_SingleChannel(uint8_t ADC_Channel_x)
{
ADC_InitTypeDef ADC_InitStructure;
YP_GPIO_Configuration();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1时钟
/* Initialize the ADC_PRESCARE values */
ADC_InitStructure.ADC_PRESCARE = ADC_PCLK2_PRESCARE_16; //16分频
/* Initialize the ADC_Mode member */
ADC_InitStructure.ADC_Mode = ADC_Mode_Single; //配置单通道扫描模式
/* Initialize the ADC_ContinuousConvMode member */
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续转换模式
/* Initialize the ADC_DataAlign member */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
/* Initialize the ADC_ExternalTrigConv member */
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T1_CC1;//外部触发通道选择
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_x,0,ADC_SampleTime_1_5Cycles); //设置换换顺序和采样时间
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件转换启动
if(ADC_Channel_x==ADC_Channel_8)
{
ADC1->ADCFG|=0x04;
}
}

第三步:根据ADC采集到的数据做校验算法,得到触摸点坐标,并且显示在显示屏对应的区域。

图2 实验效果

 MM32通过ADC控制4线电阻触摸屏

转自: 灵动微电子

围观 739

电源对电子设备的重要性不言而喻,它是保证系统稳定运行的基础,而保证系统稳定运行后,又有低功耗的要求。

在很多应用场合中,对电子设备的功耗要求非常苛刻,如某些传感器信息采集设备,仅靠小型的电池提供电源,要求工作长达数年之久,且期间不需要任何维护;由于智慧穿戴设备的小型化要求,电池体积不能太大导致容量也比较小,所以很有必要从控制功耗入手,提高设备的续行时间。

在系统或电源复位以后,MCU处于运行状态。运行状态下的时钟源为 CPU 提供时钟,内核执行程序代码。当 CPU 不需继续运行时,可以利用多个低功耗模式来降低功耗,例如等待某个外部事件时。

MM32L0产品支持三种低功耗模式:睡眠模式、停止模式和待机模式,可以在要求低功耗、短启动时间和多种唤醒事件之间达到最佳的平衡,可以满足用户对低功耗的要求。

• 睡眠模式

在睡眠模式,只有 CPU 停止,所有外设处于工作状态并可在发生中断/事件时唤醒 CPU。

有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt)和 WFE(wait for event),即等待“中断”唤醒和“事件”唤醒。

进入睡眠模式例:
__WFE();//等待事件,等待事件是一个暂停执行指令暂停至任意事件产生后被唤醒。__WFI();//等待中断,等待中断是一个暂停执行指令暂停至任意中断产生后被唤醒。

关于退出睡眠模式:

如何使用MM32的三种低功耗模式?

配置外部事件唤醒函数例:
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init( GPIOA, &GPIO_InitStruct);
SYSCFG_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Event;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
}

• 停止模式

在保持 SRAM 和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部 1.8V 部分的供电, PLL 、HSI 的振荡器和 HSE 晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。

有两种方式进入停止模式,可以通过设置独立的控制位,选择以下待机模式的功能:

独立看门狗(IWDG):可通过写入看门狗的键寄存器或硬件选择来启动 IWDG。一旦启动了独立看门狗,除了系统复位,它不能再被停止。

内部振荡器(LSI 振荡器):通过控制/状态寄存器 (RCC_CSR)的 LSION 位来设置。在停止模式下,如果在进入该模式前 ADC 和 DAC 没有被关闭,那么这些外设仍然消耗电流。

进入停止模式例:
void Sys_Stop(void)
{
PWR_EnterSTOPMode(0, PWR_STOPEntry_WFI);
}
注:MM32L0xx在进入停止模式前,需将系统时钟切换到HSI。

关于退出停止模式:

当一个中断或唤醒事件使MCU退出停止模式时, HSI 振荡器被选为系统时钟。当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。

如何使用MM32的三种低功耗模式?

配置中断唤醒函数例:
void WKUP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
PWR_WakeUpPinCmd(ENABLE);
}

• 待机模式

待机模式可实现系统的最低功耗。该模式是在 CPU 深睡眠模式时关闭电压调节器。整个 1.8V 供电区域被断电。 PLL、 HSI 和 HSE 振荡器也被断电。 SRAM 和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。在进入待机模式后,除了被用于唤醒 I/O,其余 I/O 都进入高阻态,而从待机模式唤醒后,相当于复位MM32芯片,程序重新从头开始执行。

有两种方式进入待机模式,可以通过设置独立的控制位,选择以下待机模式的功能:

独立看门狗(IWDG):可通过写入看门狗的键寄存器或硬件选择来启动 IWDG。一旦启动了独立看门狗,除了系统复位,它不能再被停止。

内部振荡器(LSI 振荡器):通过控制/状态寄存器(RCC_CSR)的 LSION 位来设置。

进入待机模式例:
void Sys_Standby(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
PWR_WakeUpPinCmd(ENABLE);
PWR_EnterSTANDBYMode();
}

关于退出待机模式:

当一个外部复位(NRST 引脚)、 IWDG 复位、 WKUP 引脚上的上升沿, 微控制器从待机模式退出。从待机唤醒后,除了电源控制/状态寄存器(PWR_CSR),所有寄存器被复位。

从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚、读取复位向量等)。电源控制/状态寄存器(PWR_CSR)将会指示内核由待机状态退出。

如何使用MM32的三种低功耗模式?

配置WKUP引脚上升沿函数例:
u8 Check_WKUP(void)
{
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
{
return 1;
}
else
{
return 0;
}
}
}

void WKUP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
if(Check_WKUP()==0) Sys_Standby();
}

当系统处于低功耗状态时,使用下载器是无法给芯片下载程序的。有很多用户使用低功耗功能,但是唤醒部分配置有问题,导致MCU无法唤醒,发现无法对芯片进行下载程序。在这里告诉大家一个调试低功耗模式的小秘密,您在测试低功耗模式时可在主函数启动时加一个较长的延时函数,按住板子的复位键,使系统处于复位状态,然后点击电脑端的下载按钮下载程序,这时再释放复位键,这样MCU上电后执行延时函数期间对低功耗程序进行擦除。

还有一种方法是选择启动方式,通过将BOOT0拉高,重新上电使MCU从SRAM启动,重新选择一个闪灯程序对flash进行擦除,擦除完成后将BOOT0拉低,然后重新上电即可重新下载程序。

转自: 灵动微电MMCU

围观 756

(2016年8月23日深圳)今天,上海灵动微电子股份有限公司在深圳成功举办2016年秋季新品发布会,众多MCU领域的知名厂商代表、专业人士以及国内30多家知名门户与科技媒体云集现场,共同见证灵动微电子新品发布,灵动微电子董事长兼CEO吴忠洁博士、灵动微电子MCU事业部总经理娄方超先生、ARM 物联网应用市场经理耿立锋先生以及云汉芯城产品副总裁程凯先生等嘉宾还给大家带来了精彩的演讲,并与现场观众一起分享交流经验,共同探讨本土MCU如何突破形成大发展势态。

活动主持人电子创新网CEO张国斌在开场致辞中指出,随着物联网和智能硬件的快速发展,32位MCU的需求呈现快速上升趋势。据相关数据统计,2015年全球MCU出货量255亿颗,比2014年提升了12.4%。虽然今年全球的经济相对低迷,但是MCU的需求仍然有望增长20%左右,预计2016年全球MCU市场出货量将突破300亿颗大关,其中32位MCU市场未来几年会保持30%左右的高速增长!在这一大背景下,本土MCU厂商必然强势崛起,重演PC、手机、汽车领域发展格局。

灵动微电子董事长兼CEO吴忠洁博士详细介绍了灵动微电子多年来的发展历程,强调灵动微电子多年来一直专注MCU设计,并表示灵动微电子给用户提供“保姆式”的服务,灵动微的愿景是要成为中国本土MCU第一推动力。

灵动微电子MCU事业部总经理娄方超先生在演讲中宣布灵动微电子三款新品,它们分别是则是基于Cortex-M3内核的MM32F103系列MCU,基于Cortex-M0内核的MM32F031系列MCU和MM32P011系列的MCU,其中MM32F031系列MCU有突出的性价比特性,主打IoT及消费类市场。他通过数据的分析对比展示了这几款新品的强大优越性能以及其相关的应用领域。”我们的MCU采用的是国际领先的封测供应商的封测服务,这表明灵动对MCU产品品质和可靠性有着严格的要求。”他举例说。

他还揭秘了灵动微电子MCU的十大构成要素,详细介绍了灵动微电子提供的开发套件、调试工具和技术支持服务,并分享了灵动微电子技术的发展路线图。他表示灵动微电子未来会逐步推出支持超低功耗、无线连接的MCU产品系列,在制造工艺上也会过渡到55nm,甚至40nm节点。

作为灵动微电子的重要合作伙伴,ARM物联网应用市场经理耿立锋先生也到场,并发表了以《ARM助力国产MCU腾飞》为主题的演讲,他从工具、生态系统建设、商业模式等各个发展介绍了ARM如何更好地与本土公司合作,并以奥运冠军中国女排中的外国队医形象地比喻ARM与灵动微电子的合作。

好的产品离不开好的渠道,此次灵动微电子渠道合作伙伴云汉芯城也来到现场。云汉芯城产品副总裁程凯发表了以《云汉芯城,打造电子产业第一服务平台》为主题的演讲,详细介绍了云汉芯城独特的服务模式,并表示大数据显示本土MCU受到更多关注。

灵动微电子的方案合作伙伴深圳蚁石(OCTANT)科技有限公司总经理朱才智发表了《多旋翼飞行器的发展、机会和挑战》的主题演讲,他表示,多旋翼飞行器的算法需求加大,未来基于Cortex-M3 MCU方案将走热。

在最后的圆桌论坛环节中,演讲嘉宾以及周立功单片机创始人周立功、南京万利电子创始人刘强、深圳智慧家庭协会秘书长蔡锦江等与观众面对面互动,为灵动微电子的发展出谋划策。

大家一致认为本土MCU在未来的发展机遇很大,要抓住发展机遇就要专注细分领域一战成名!周立功教授还结合自己的经验指出灵动微电子要提供其他厂商不能提供的差异化服务,超越客户预期,并要放眼未来,提供前瞻性的方案。

蔡锦江则表示,随着中国厂商具备物联网架构的定义能力,本土公司面临绝佳的发展机遇。

娄方超表示MCU市场潜力巨大,未来几年物联网就有近千亿颗的MCU需求,面对如此巨大的市场,灵动微电子将以高品质高可靠的产品以及高质量的服务和开放的精神赢得用户!

灵动微电子还将在24日到26日开幕的深圳国际嵌入式展展出新品和方案,欢迎业者观摩交流,展位号:三号馆3B38,这是今天展示的部分方案。

围观 498


2016年6月30日,上海灵动微电子股份有限公司(公司简称“灵动微电”,证券代码:833448)MCU事业部总经理娄方超,应邀参加了“2016中国(上海)物联网大会暨中国(上海)国际物联网博览会”,并做了主题为“MM32 MCU,基于ARM内核的物联网灵动之风”的演讲。

灵动微电MCU事业部总经理娄方超演讲中


娄方超表示,MM32 包含MM32x031和MM32x103两大系列,该系列具备“低功耗”、“高性能通用”和“高可靠性”等性能。此外,该系列产品涵盖四大领域:IOT/低功耗应用;工业控制及高可靠性应用;传感器/MEMS应用;加密安全应用。公司在这四个领域都有标志性的合作方和客户。

娄方超透露:“MM32 MCU产品从规格和设计上面跟欧美的一线大厂的同类产品是很有可比性的,我们的诸多关键技术指标已经等同甚至优于同类标的。”MM32第一个标准产品MM32F103从去年下半年在市场推广以来,目前有合作意向的合作方超过300家。目前有意向订单的已经超过30家,已经下订单的有十余家,而且我们的客户、供应商及合作伙伴都对这个市场都非常乐观。

娄方超还表示,在产品的推广方面,公司提供的不仅仅是孤立的产品,公司提供的是完整的产品及设计方案。MM32 MCU有十大产品构成要素,分别是:模拟模块、数字模块、闪存控制器、架构、库函数、固件与例程、文档与手册、开发调试烧录、加密及安全、原型验证,这十大要素共同构成了灵动MM32 MCU生态链所倡导的优质产品和精致服务的基础。

公开资料显示,灵动微电是国内专注于MCU产品与MCU应用方案的领先供应商,是中国工业及信息化部及上海市信息化办公室认定的集成电路设计企业,同时也是上海市认定的高新技术企业。自2011年3月成立至今,灵动微电子已经成功完成十余款MCU产品设计及推广,包括基于8051、ARM Cortex-M0、Cortex-M3内核的Flash MCU、OTP MCU、EEPROM MCU等产品、产品及方案广泛应用于工业控制、智能家电、智慧家庭、可穿戴式设备、汽车电子、仪器仪表等领域。

围观 409

页面

订阅 RSS - MM32