(这里仅对部分外设最基础的概念和操作列出,寄存器也只是列出一部分,详细的学习请参考官方手册和查看源码学习)
对于外设使用,一般需要了解外设的基本概念和原理,了解相关寄存器。
使用时,首先初始化时钟,接着初始化(配置)外设,如果使用到GPIO要初始化相应端口并复用为对应功能,最后如果需要使用中断,应当开启中断,编写中断处理函数。
初始化完成后我们就可以在主逻辑代码中利用相应的库函数使用相应的外设完成我们的功能需求。
1、前言: STM32F4具有14个定时器,TIM1和TIM8为高级定时器,TIM2 ~ TIM5、TIM9 ~ TIM14为通用定时器,TIM6和TIM7为基本定时器。
定时的基本原理是利用时钟信号,对预设的计数装载值进行倒计数,计数至0产生溢出,可以重装载计数值,这样利用装载的计数值和具体时钟频率就可以知道计数时间了。
通用TIMx定时器功能包括:
● 16位/32位(仅TIM2和TIM5)向上、向下、向上/向下自动装载计数器,TIM9 ~ TIM14只支持向上。
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1 ~65536之间的任意数值
● 4个独立通道(TIMx_CH1~4,其中TIM9 ~ TIM14最多2个通道)可以用作如下功能:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA:
─ 更新: 计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
─ 支持针对定位的增量(正交)编码器和霍尔传感器电路(TIM9 ~ TIM14不支持)
─ 触发输入作为外部时钟或者按周期的电流管理(TIM9 ~ TIM14不支持)
2、关键寄存器(通用计时器):
TIMx_CR1: 控制寄存器,比如最低位CEN计数器使能位,控制寄存器的开始计数。
TIMx_DIER: DMA/中断使能寄存器,比如最低位的更新中断允许位UIE,可以设置允许由于更新事件产生的中断。
TIMx_PSC: 预分频寄存器,设置时钟分频因子提供给计数器作为计数器时钟。
TIMx_SMCR: 从模式控制器,可以设置定时器的时钟来源。
TIMx_CNT: 计数器,存储了当前计数器的计数值。
TIMx_ARR: 自动重装载寄存器,存在影子寄存器,可以简单当成将一个寄存器分为两个,影子寄存器不可见(不可操作,实际起作用的寄存器),预设的值可以与影子寄存器实时相通,也可以设置为仅更新发生时才将值传递到影子寄存器。
TIMx_SR: 状态寄存器,标记各种事件和中断是否发生。
3、库函数操作:
下面的代码完成对定时器3的初始化以及使能了定时器更新中断,对于更新事件发生后的中断处理可以在stm32f4xx_it.c文件中编写相应的中断处理函数。
//通用定时器3中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,即输入时钟频率,单位:Mhz
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///使能TIM3时钟
TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式2
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器3更新中断
TIM_Cmd(TIM3,ENABLE); //使能定时器3
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
实际上是定时器的输出比较功能。
比如计时器工作在向上计数模式时,当计数值大于设定的CCRx时,输出高电平,小于CCRx时输出低电平,这样就会产生一个固定占空比的方波信号,改变预设重装载计数ARR大小可以改变PWM输出频率,改变CCRx值可以改变输出PWM的占空比。
寄存器:
在之前所讲的寄存器基础上,我们还使用到如下寄存器(捕获/比较):
TIMx_CCMR1/2: 捕获/比较模式寄存器,
TIMx_CCER: 捕获/比较使能寄存器,
TIMx_CCR1~4: 捕获/比较寄存器,
库函数:
相比前面定时器设置,这里因为要输出,所以要配置IO口复用,然后又因为用到比较,这里需要初始化输出比较。
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //TIM14时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能PORTF时钟
/*初始化GPIO复用为TIM输出引脚*/
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9
...... //省略前面已经讲到的定时器初始化TIM_TimeBaseInit过程,但是这个步骤是必须的
/*初始化TIM14 Channel1 PWM模式*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能
TIM_Cmd(TIM14, ENABLE); //使能TIM14
这里补充解释下最后两行加入的使能预装载寄存器(主要是理解影子寄存器,详细可见这篇文章):
简单的说,使能了预装载寄存器结果是使得影子寄存器和我们操作的寄存器直接相通,如果失能则两则要在更新事件发生后才会相通。
即使能预装载使得我们的修改可以实时的反馈到影子寄存器(立即更新)。
定时器输入捕获功能,可以用来测量脉冲宽度或者频率。
定时器可以通过监测边沿信号的跳变(如上升沿)记录当前定时计数值存放在捕获比较寄存器中,这个过程叫做输入捕获。
下面初始化代码仅列出输入捕获不同于前面PWM输出等的地方:
/*定时器基本参数初始化*/
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
......
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //设置输入捕获的滤波长度,不是对时钟的再分频
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
/*初始化TIM5输入捕获参数*/
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
一些常用的函数:
TIM_GetITStatus(TIM5, TIM_IT_Update); //获取更新中断状态,TIM_IT_CC1为捕获中断标志
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
TIM_SetCounter(TIM5,0); //计时器清空
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
1、前言: 我们的系统运行期间,如果代码的设计存在问题或者硬件故障(设计缺陷或外界干扰)都有可能使系统死机,看门狗的存在则使得系统在长时间无响应时自动恢复(复位),其原理为使用独立定时器计时,超过预设时间便会产生复位信号(正常情况会在预设时间到达之前重装载预设值进行计数)。
2、关键寄存器: 我们对于看门狗操作的主要寄存器有3个
IWDG_KR: 关键字寄存器,写入0xCCCC开启看门狗计时,写入0xAAAA自动重装载计数值。
IWDG_PR: 预分频寄存器,设置看门狗时钟的分频系数。
IWDG_RLR: 重装载寄存器,保存用于重装载到计数器的值。
(注: IWDG_RLR与IWDG_PR具有写保护,需要先向IWDG_KR写入0x5555才能修改这两个寄存器)
3、库函数操作:
初始化看门狗的操作如下:
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对IWDG->PR IWDG->RLR的写
IWDG_SetPrescaler(prer); //设置IWDG分频系数
IWDG_SetReload(rlr); //设置IWDG装载值
IWDG_ReloadCounter(); //reload
IWDG_Enable(); //使能看门狗
}
/*使用下面的函数对看门狗计数重装载,防止复位信号产生*/
IWDG_ReloadCounter(); //reload
这里需要补充下看门狗复位周期的计算:
对32kHz的LSI时钟分频,分频系数大小为 4 × 2 p r e r 4 \times 2 ^ {prer} 4×2prer , 结合计数值就可计算(大概,时钟并不精确): T o u t = ( ( 4 × 2 p r e r ) × r l r ) / 32 T_{out}=((4 \times 2^{prer}) \times rlr)/32 Tout=((4×2prer)×rlr)/32 (ms).