• PY32F003F18之通用定时器MspInit函数


    PY32F003F18高级定时器有TIM1,通用定时器有TIM3,TIM14,TIM16和TIM17。在初始化定时器前,要先写好MspInit函数,才可以调用与之对应的初始化函数。

    1、TIM1更新事件的MspInit函数

    //函数功能:在初始化定时器时,HAL库使用该函数
    //使能TIMx时钟,设置中断优先级,使能TIMx中断

    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM1)//初始化TIM1
        {
        __HAL_RCC_TIM1_CLK_ENABLE();                          //使能TIM1时钟
        HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0, 0); //设置中断优先级
        HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);         //使能TIM1中断
        }
        if(htim->Instance == TIM3)//初始化TIM3
        {
        __HAL_RCC_TIM3_CLK_ENABLE();           //使能TIM3时钟
        HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0); //设置中断优先级
        HAL_NVIC_EnableIRQ(TIM3_IRQn);         //使能TIM3中断
        }
    }

    TIM1更新事件,需要调用HAL_TIM_Base_Init ()进行定时器初始化。

    2、TIM1采用OC1互补输出的MspInit函数

    //函数功能:使能TIM1时钟,GPIOA时钟,将PA0复用为TIM1_CH1N,将PA3复用为TIM1_CH1
    void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
    {
      GPIO_InitTypeDef   GPIO_InitStruct;

      if(htim->Instance == TIM1)//初始化TIM1
        {
        __HAL_RCC_TIM1_CLK_ENABLE();  //TIM1时钟使能
        __HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能

        /*初始化PA0/PA7为TIM1_CH1N/TIM1_CH1*/
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;       //复用功能推挽模式
        GPIO_InitStruct.Pull = GPIO_PULLUP;           //引脚上拉被激活
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //引脚速度为高速
        GPIO_InitStruct.Alternate = GPIO_AF14_TIM1;   //选择AF14通道
        GPIO_InitStruct.Pin = GPIO_PIN_0;             //选择第0引脚
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);       //A0-TIM1_CH1N
          //根据GPIO_InitStruct结构参数初始化GPIOA的外设寄存器
          //将PA0复用为TIM1_CH1N

        GPIO_InitStruct.Alternate = GPIO_AF13_TIM1;//选择AF13通道
        GPIO_InitStruct.Pin = GPIO_PIN_3;          //选择第3引脚
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);         //A3-TIM1_CH1
          //根据GPIO_InitStruct结构参数初始化GPIOA的外设寄存器
          //将PA3复用为TIM1_CH1

        }
        if(htim->Instance == TIM3)//初始化TIM3
        {
        }
    }

    TIM1采用OC1互补输出则需要调用HAL_TIM_PWM_Init()。

    3、TIM1采用IC1输入捕获的MspInit函数

    void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
    {
        GPIO_InitTypeDef   GPIO_InitStructure;

        if(htim->Instance == TIM1)//初始化TIM1
        {
        __HAL_RCC_TIM1_CLK_ENABLE();  //使能TIM1时钟
          __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟

          GPIO_InitStructure.Pin = GPIO_PIN_3;       //选择第3脚,PA3是为TIM1_CH1
        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
        GPIO_InitStructure.Pull = GPIO_PULLUP;     //引脚上拉被激活
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
        GPIO_InitStructure.Alternate = GPIO_AF13_TIM1;//选择AF13,将PA3引脚复用为TIM1_CH1
        HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
          //根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
          //将PA3初始化为TIM1_CH1功能,用作IC11输入捕获引脚

        HAL_NVIC_SetPriority(TIM1_CC_IRQn,1, 0); //设置"捕获/比较"的中断优先级为1
        HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);        //开启"捕获/比较"总中断
        }
        if(htim->Instance == TIM3)//初始化TIM3
        {
        }
    }

    TIM1采用IC1捕获,则需要调用HAL_TIM_IC_Init()

    通过上面的举例,我们发现MspInit()有很多分支,显然HAL库采用这种方式管理程序,降低了程序的可读性,且给移植也带来不便,因为移植时需要将不要的内容删除。同时,MspInit()很庞大。所以,我个人觉得不需要这么设计,也是可以实现的。举例如下:

    1. //函数功能:TIM1中基本计数功能,并使能了更新中断,每次重装ARR值时会产生一次更新中断
    2. //arr:自动重装值。
    3. //psc:时钟预分频数
    4. //TIM1_COUNTERMODE_UP_IC1_Init(20000,240);//若使用HSE,当arr=20000,psc=240时,则为200ms,误差为10us;
    5. //TIM1_COUNTERMODE_UP_IC1_Init(20000,80);//若使用HSI,当arr=20000,psc=80时,则为200ms,误差为10us;
    6. void TIM1_COUNTERMODE_UP_IC1_Init(uint16_t arr,uint16_t psc)
    7. {
    8. GPIO_InitTypeDef GPIO_InitStructure;
    9. TIM_HandleTypeDef TIM1_HandleStructure;
    10. TIM_IC_InitTypeDef TIM1_IC_InitStructure;
    11. //HAL_TIM_IC_MspInit开始/
    12. __HAL_RCC_TIM1_CLK_ENABLE(); //使能TIM1时钟
    13. __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
    14. GPIO_InitStructure.Pin = GPIO_PIN_3; //选择第3脚,PA3是为TIM1_CH1
    15. GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
    16. GPIO_InitStructure.Pull = GPIO_PULLUP; //引脚上拉被激活
    17. GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //引脚速度为最高速
    18. GPIO_InitStructure.Alternate = GPIO_AF13_TIM1;//选择AF13,将PA3引脚复用为TIM1_CH1
    19. HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
    20. //根据GPIO_InitStructureure结构变量指定的参数初始化GPIOA的外设寄存器
    21. //将PA3初始化为TIM1_CH1功能,用作IC11输入捕获引脚
    22. HAL_NVIC_SetPriority(TIM1_CC_IRQn,1, 0); //设置"捕获/比较"的中断优先级为1
    23. HAL_NVIC_EnableIRQ(TIM1_CC_IRQn); //开启"捕获/比较"总中断
    24. //HAL_TIM_IC_MspInit结束/
    25. TIM1_HandleStructure.Instance = TIM1; //选择TIM1
    26. TIM1_HandleStructure.Init.Period = arr-1;
    27. //设置在下一个更新事件产生时,装入"自动重载入寄存器TIMx_ARR"的值
    28. //将(arr-1)写入"自动重载入寄存器TIMx_ARR",设置自动重装载值
    29. TIM1_HandleStructure.Init.Prescaler = psc-1;
    30. //设置用来作为TIMx时钟频率除数的预分频值
    31. //将(psc-1)写入"预装载寄存器TIMx_PSC",的PSC[15:0],设置预分频值
    32. //计数器的时钟频率CK_CNT=fCK_PSC/(PSC[15:0]+1)
    33. TIM1_HandleStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;//时钟不分频,则tDTS=tCK_INT
    34. //若使用HSE,计算公式:arr*psc/24000000/1,当arr=20000,psc=240时,则为200ms,误差为10us;
    35. //若使用HSI,计算公式:arr*psc/8000000/1,当arr=20000,psc=80时,则为200ms,误差为100us;
    36. TIM1_HandleStructure.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数
    37. TIM1_HandleStructure.Init.RepetitionCounter = 1 - 1;
    38. //不重复计数
    39. //将(1-1)写入"重复计数寄存器TIMx_RCR"中的REP[7:0],设置"重复计数器值"
    40. TIM1_HandleStructure.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    41. //"自动重装载寄存器"没有缓冲
    42. //不允许将"TIMx自动重新加载寄存器TIMx_ARR"的值被装入缓冲区;
    43. HAL_TIM_Base_Init(&TIM1_HandleStructure); //TIM1初始化
    44. //选择计数器模式:向上计数
    45. //设置时钟分频因子:时钟不分频,则tDTS=tCK_INT
    46. //设置自动重装载:"自动重装载寄存器"没有缓冲
    47. //设置自动重装载值:TIM_Base_InitStructure.Period
    48. //设置预分频值:TIM_Base_InitStructure.Prescaler
    49. //设置"重复计数器值":TIM_Base_InitStructure.RepetitionCounter
    50. //启动更新事件:将TIMx_EGR中的UG位置1,由软件产生更新事件
    51. TIM1_IC_InitStructure.ICPolarity = TIM_ICPOLARITY_RISING; //上升沿捕获
    52. TIM1_IC_InitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI;//CC1通道配置为输入
    53. TIM1_IC_InitStructure.ICPrescaler = TIM_ICPSC_DIV1; //输入不分频
    54. TIM1_IC_InitStructure.ICFilter = 0; //输入无滤波
    55. HAL_TIM_IC_ConfigChannel(&TIM1_HandleStructure, &TIM1_IC_InitStructure, TIM_CHANNEL_1);
    56. //配置通道1输入捕获
    57. HAL_TIM_IC_Start_IT(&TIM1_HandleStructure, TIM_CHANNEL_1);
    58. //使能输入捕获1中断,使能输入捕获1通道
    59. }

    HAL库这种做法确实有点坑,若你不了解程序的管理方法,就不知道MspInit()是什么东西。我就是采用自己的方式来实现,程序的可读性更强。其次Callback()也是一样,把初学者带入坑中。我不用Callback函数,直接在中断中调用自己中断处理程序。还是距离说明,免得不放心。

    1. //函数功能:TIM1输入捕获中断服务程序
    2. //IC1为上升沿捕获,所以捕获周期是TIM3周期的2
    3. void TIM1_CC_IRQHandler(void)
    4. {
    5. HAL_TIM_PeriodElapsedCallback开始/
    6. if (_HAL_TIM_GET_FLAG(TIM1,TIM_FLAG_CC1) != RESET)
    7. {//根据TIM_FLAG_CC1,读"TIMx状态寄存器(TIMx_SR)"中输入捕获中断标志位
    8. _HAL_TIM_CLEAR_IT(TIM1, TIM_IT_CC1);
    9. //TIM_IT_CC1,令CC1IF=0,清除输入捕获中断标志位
    10. TIM1_LED_Toggle();
    11. }
    12. HAL_TIM_PeriodElapsedCallback结束/
    13. }

    为了能说明问题,我把其它程序贴上来。
     

    1. //函数功能:TIM3中基本计数功能,并使能了更新中断,每次重装ARR值时会产生一次更新中断
    2. //arr:自动重装值。
    3. //psc:时钟预分频数
    4. //TIM3_COUNTERMODE_UP_Init(20000,240);//若使用HSE,当arr=20000,psc=240时,则为200ms,误差为10us;
    5. //TIM3_COUNTERMODE_UP_Init(20000,80);//若使用HSI,当arr=20000,psc=80时,则为200ms,误差为10us;
    6. void TIM3_COUNTERMODE_UP_Init(uint16_t arr,uint16_t psc)
    7. {
    8. TIM_HandleTypeDef TIM3_HandleStructure;
    9. //HAL_TIM_Base_MspInit开始/
    10. __HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟
    11. HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0); //设置中断优先级
    12. HAL_NVIC_EnableIRQ(TIM3_IRQn); //使能TIM3中断
    13. //HAL_TIM_Base_MspInit开始/
    14. TIM3_HandleStructure.Instance = TIM3; //选择TIM3
    15. TIM3_HandleStructure.Init.Period = arr-1;
    16. //设置在下一个更新事件产生时,装入"自动重载入寄存器TIMx_ARR"的值
    17. //将(arr-1)写入"自动重载入寄存器TIMx_ARR",设置自动重装载值
    18. TIM3_HandleStructure.Init.Prescaler = psc-1;
    19. //设置用来作为TIMx时钟频率除数的预分频值
    20. //将(psc-1)写入"预装载寄存器TIMx_PSC",的PSC[15:0],设置预分频值
    21. //计数器的时钟频率CK_CNT=fCK_PSC/(PSC[15:0]+1)
    22. TIM3_HandleStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;//时钟不分频,则tDTS=tCK_INT
    23. //若使用HSE,计算公式:arr*psc/24000000/1,当arr=20000,psc=240时,则为200ms,误差为10us;
    24. //若使用HSI,计算公式:arr*psc/8000000/1,当arr=20000,psc=80时,则为200ms,误差为100us;
    25. TIM3_HandleStructure.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数
    26. TIM3_HandleStructure.Init.RepetitionCounter = 1 - 1;
    27. //不重复计数
    28. //将(1-1)写入"重复计数寄存器TIMx_RCR"中的REP[7:0],设置"重复计数器值"
    29. TIM3_HandleStructure.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    30. //"自动重装载寄存器"没有缓冲
    31. //不允许将"TIMx自动重新加载寄存器TIMx_ARR"的值被装入缓冲区;
    32. HAL_TIM_Base_Init(&TIM3_HandleStructure); //TIM3初始化
    33. //选择计数器模式:向上计数
    34. //设置时钟分频因子:时钟不分频,则tDTS=tCK_INT
    35. //设置自动重装载:"自动重装载寄存器"没有缓冲
    36. //设置自动重装载值:TIM_Base_InitStructure.Period
    37. //设置预分频值:TIM_Base_InitStructure.Prescaler
    38. //设置"重复计数器值":TIM_Base_InitStructure.RepetitionCounter
    39. //启动更新事件:将TIMx_EGR中的UG位置1,由软件产生更新事件
    40. HAL_TIM_Base_Start_IT(&TIM3_HandleStructure);
    41. //允许计数器产生"更新中断";
    42. //如果计数器不是工作在触发模式中,则开始计数
    43. }
    44. //函数功能:TIM3中断服务程序
    45. void TIM3_IRQHandler(void)
    46. {
    47. HAL_TIM_PeriodElapsedCallback开始/
    48. if (_HAL_TIM_GET_FLAG(TIM3,TIM_FLAG_UPDATE) != RESET)
    49. {//根据TIM_FLAG_UPDATE,读"TIMx状态寄存器(TIMx_SR)"中更新中断标志位
    50. _HAL_TIM_CLEAR_IT(TIM3, TIM_IT_UPDATE);
    51. //TIM_IT_UPDATE,令UIF=0,清除定时器更新中断标志位
    52. TIM3_LED_Toggle();
    53. }
    54. HAL_TIM_PeriodElapsedCallback结束/
    55. }

    由于没有将结构定义为全局变量,有些宏定义,需要自己重写或重构。这个可以去HAL库里找,修改也很容易。见下面的代码,可以仿照对比一下就知道怎么改了,很容易做。

    1. #ifndef __TIM1_EdgeAligned_InputCapture_H
    2. #define __TIM1_EdgeAligned_InputCapture_H
    3. #include "py32f0xx_hal.h"
    4. #define _HAL_TIM_GET_FLAG(__INSTANCE__, __FLAG__) (((__INSTANCE__)->SR &(__FLAG__)) == (__FLAG__))
    5. //根据__FLAG__,读"TIMx状态寄存器(TIMx_SR)"中相应的中断标志位
    6. //TIM_FLAG_UPDATE,若UIF=1,建立"更新事件"
    7. //TIM_FLAG_CC1,若CC1IF=1,如果通道CC1配置为输出模式,则建立"CC1输出事件"
    8. //TIM_FLAG_CC1,若CC1IF=1,如果通道CC1配置为输入模式,则建立"CC1捕获事件"
    9. //TIM_FLAG_CC2,若CC2IF=1,如果通道CC2配置为输出模式,则建立"CC2输出事件"
    10. //TIM_FLAG_CC2,若CC2IF=1,如果通道CC2配置为输入模式,则建立"CC2捕获事件"
    11. //TIM_FLAG_CC3,若CC3IF=1,如果通道CC3配置为输出模式,则建立"CC3输出事件"
    12. //TIM_FLAG_CC3,若CC3IF=1,如果通道CC3配置为输入模式,则建立"CC3捕获事件"
    13. //TIM_FLAG_CC4,若CC4IF=1,如果通道CC4配置为输出模式,则建立"CC4输出事件"
    14. //TIM_FLAG_CC4,若CC4IF=1,如果通道CC4配置为输入模式,则建立"CC4捕获事件"
    15. //TIM_FLAG_COM,若COMIF=1,则建立"COM事件"
    16. //TIM_FLAG_TRIGGER,若TIF=1,则建立"触发事件"
    17. //TIM_FLAG_BREAK,若BIF=1,则建立"刹车事件"
    18. //TIM_FLAG_CC1OF,若CC1OF=1,则表示"计数器x的值被捕获到TIMx_CCR1寄存器"
    19. //TIM_FLAG_CC2OF,若CC2OF=1,则表示"计数器x的值被捕获到TIMx_CCR2寄存器"
    20. //TIM_FLAG_CC3OF,若CC3OF=1,则表示"计数器x的值被捕获到TIMx_CCR3寄存器"
    21. //TIM_FLAG_CC4OF,若CC4OF=1,则表示"计数器x的值被捕获到TIMx_CCR4寄存器"
    22. #define _HAL_TIM_CLEAR_IT(__INSTANCE__, __INTERRUPT__) ((__INSTANCE__)->SR = ~(__INTERRUPT__))
    23. //根据__INTERRUPT__,将"TIMx状态寄存器(TIMx_SR)"中相应的中断标志位置0
    24. //TIM_IT_UPDATE,令UIF=0,清除定时器更新中断标志位
    25. //TIM_IT_CC1,令CC1IF=0,清除捕获/比较1中断标志位
    26. //TIM_IT_CC2,令CC2IF=0,清除捕获/比较2中断标志位
    27. //TIM_IT_CC3,令CC3IF=0,清除捕获/比较3中断标志位
    28. //TIM_IT_CC4,令CC4IF=0,清除捕获/比较4中断标志位
    29. //TIM_IT_COM,令COMIF=0,清除COM事件中断标志位
    30. //TIM_IT_TRIGGER,令TIF=0,清除触发中断标志位
    31. //TIM_IT_BREAK,令BIF=0,清除刹车中断标志位
    32. extern void TIM1_COUNTERMODE_UP_IC1_Init(uint16_t arr,uint16_t psc);
    33. extern void TIM3_COUNTERMODE_UP_Init(uint16_t arr,uint16_t psc);
    34. #endif /* __TIM1_EdgeAligned_InputCapture_H */
    1. #include "py32f0xx_hal.h"
    2. #include "SystemClock.h"
    3. #include "delay.h"
    4. #include "LED.h"
    5. #include "TIM1_EdgeAligned_InputCapture.h"
    6. int main(void)
    7. {
    8. HSE_Config();
    9. // HAL_Init();//systick初始化
    10. delay_init();
    11. HAL_Delay(1000);
    12. TIM1_LED_Init();
    13. TIM3_LED_Init();
    14. TIM1_COUNTERMODE_UP_IC1_Init(20000,240);//若使用HSE,当arr=20000,psc=240时,则为200ms,误差为10us;
    15. TIM3_COUNTERMODE_UP_Init(20000,240);//若使用HSE,当arr=20000,psc=240时,则为200ms,误差为10us;
    16. while (1)
    17. {
    18. delay_ms(100);
    19. // TIM1_LED_Toggle();
    20. // TIM3_LED_Toggle();
    21. }
    22. }

    在程序设计时,别人好的的地方,我们要学习,要借鉴。不清楚HAL库为什么会这么干,也许他们认为这样做很好。经过实践证明的,肯定是好的。

  • 相关阅读:
    三入职场!你可以从我身上学到这些(附毕业Vlog)
    微服务框架:一招实现降本、提质、增效办公!
    ContentProvider的执行时机
    企业架构HA-Keepalived服务器高可用
    数据分析-Pandas的Andrews曲线可视化解读
    2023年中国制服需求量、市场规模及行业细分需求现状分析[图]
    python隶属关系图模型:基于模型的网络中密集重叠社区检测方法
    WebGl-Blender:建模 / 想象成形 / 初识 Blender
    专业课-代码题记录
    [Java·算法·困难]LeetCode124.二叉树中的最大路径和
  • 原文地址:https://blog.csdn.net/weixin_42550185/article/details/132897942