Air001 TIM16通用定时器作PWM输出和延时使用配置方法

Air001 TIM16通用定时器作PWM输出和延时使用配置方法


  • ✨本文将通过2个工程配置案例分别实现:TIM16通用定时器作PWM输出和延时使用。
  • 👉🏻本次利用STM32CubeMX工具,选择一款M0内核的芯片,配置一个所需功能的工程,然后从所生成的工程中将所需的目标驱动文件拷贝到Air001工程中使用,这样就快速部署完成了一个所需功能的工程框架。可以避免自己手写配置出bug的可能,通用性还是非常高,甚至PWM输出映射引脚都不需要做修改,当然也不排除STM32CubeMX生成的代码有bug问题。
  • 🔰对于代码通用性,一定要参照Air001数据手册上的资源外设描述对比来确定,不能盲从。

🔖通过现有的SDK资源,所能提供的公开资料里面,只有定时器1和定时器3的相关内容,结合数据手册上看,定时器16/17作为通用定时器,还是有一些差异。
在这里插入图片描述

  • 🌿查看Air001芯片数据手册,定时器16 PWM输出通道1,PA6引脚复用AF5,与STM32F030R8,一致,如果是其他定时器,不一样的话,需要注意修改对应的复用引脚。
    在这里插入图片描述
    在这里插入图片描述
  • 🌿对应生成的代码:
    在这里插入图片描述

🛠STM32CubeMX配置定时器16 PWM功能选项

  • 🔖这里以STM32F030R8T6为例。

  • 🌿配置定时器16参数,对应PWM功能,主要是使能定时器以及选择通道,其他参数根据需求配置即可。
    在这里插入图片描述
    在这里插入图片描述

  • 🔧配置好后,生成独立的.c和.h文件
    在这里插入图片描述

  • 🌿找到工程生成文件目录,驱动文件分别在srcinc文件夹内,拷贝对应的tim.ctim.h文件到,Air001工程项目中使用。

  • 🌿回到Air001工程,将tim.c添加进来,将头文件路径包含进来,需要修改的地方,主要是调整主时钟频率,对应PWM频率和占空比根据个人需求修改即可。
    在这里插入图片描述

  • 🔧这样一个基本的工程框架就搭建好了。
  • 📝main主程序
int main(void)
{
    uint16_t plusewidth = 5000;//脉冲宽度;f=1000 000/5000=200Hz
    uint16_t plusedelay = 500;//脉宽
    /* 初始化所有外设,Flash接口,SysTick */
    HAL_Init();

    /* 系统时钟配置 */
    APP_SystemClockConfig();
    MX_GPIO_Init();
    MX_TIM16_Init();
    __HAL_TIM_SET_AUTORELOAD(&htim16, plusewidth - 1); //调整分频系数,可以改变arr以改变频率
    __HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, plusedelay); //PWM脉冲宽度,修改占空比比较值

        HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1);
//    if(HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1) != HAL_OK)
//    {
//        Error_Handler();
//    }

    while(1)
    {
    }
}
  • 🌿PA7引脚PWM信息:
    在这里插入图片描述
  • 📚工程代码
链接:https://pan.baidu.com/s/1FboUjQMLb1pQu2coVDlh3Q 
提取码:xqpn

📓Air001 TIM16通用定时器作延时使用

  • 🔖配置工程方法,可以如法炮制。
  • STM32CubeMX工具,配置定时器相关参数:
    在这里插入图片描述
  • 🌿步骤步骤重复,参考上面的方法导入到Air001工程中即可。
  • 🔨在tim.c中添加如下延时函数内容,并将函数名拷贝到tim.h中。
/* USER CODE BEGIN 1 */
//us级延时函数
//us需要延时的us数
void delay_us(uint16_t us)
{
	uint16_t n;
	n = us/100;//修正误差值
	//设置定时器预分频系数,TIM16时钟为8MHz,分频后时钟为1MHz即1us
    //不同CPU的时钟可能不一样,PSC的值=定时器时钟/1MHz -1
    //8M的定时器设置PSC为8-1
	TIM16->PSC = (8 -1);
	//设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间
	
	TIM16->ARR = us - 2*n;
	
	//重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用,此时定时器将采用刚刚写入的预分频值,如果此处不更新,那么定时器需要等待下次更新事件的到来才会重新加载预分频值
	TIM16->EGR |= (1<<0);
	//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
	TIM16->SR = 0;
	
	//CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式
	TIM16->CR1 |= (1<<3);
	//CR1的bit0(CEN)置一,启动定时器开始计数
	TIM16->CR1 |= (1<<0);
	//等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到
	while((TIM16->SR & 0x01)==0);
	//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
	TIM16->SR &= ~(1<<0);
}
 
//ms级延时函数
//最大延时时间 65535ms
void delay_ms(uint16_t ms)
{
	//设置定时器预分频系数,TIM16时钟为8MHz,分频后时钟为1KHz即1ms
    //不同CPU的时钟可能不一样
	TIM16->PSC = (8000-1);//8000 000/8000=1KHz
	//设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间的一半
	TIM16->ARR = ms;
	
	//重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用,此时定时器将采用刚刚写入的预分频值,如果此处不更新,那么定时器需要等待下次更新事件的到来才会重新加载预分频值
	TIM16->EGR |= (1<<0);
	//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
	TIM16->SR = 0;
	
	//CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式
	TIM16->CR1 |= (1<<3);
	//CR1的bit0(CEN)置一,启动定时器开始计数
	TIM16->CR1 |= (1<<0);
	//等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到
	while((TIM16->SR & 0x01)==0);
	//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
	TIM16->SR &= ~(1<<0);
}

/* USER CODE END 1 */
  • 📝main函数:
int main(void)
{

    /* 初始化所有外设,Flash接口,SysTick */
    HAL_Init();
    /* 初始化GPIO */
    MX_GPIO_Init();
    /* 系统时钟配置 */
    APP_SystemClockConfig();
    MX_TIM16_Init();
    while(1)
    {
        /* LED翻转 */
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
        delay_us(500);
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
        delay_ms(250); /* 延时250ms */
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
        delay_ms(250); /* 延时250ms */
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
        delay_us(500);
    }
}
  • 🎞采集的PB3引脚的波形:
    在这里插入图片描述
  • 📚定时器16作延时函数使用工程
链接:https://pan.baidu.com/s/132W6L3jC1196mrcDzrC3qg 
提取码:onsu
✨按照上述方法和实现原理,对于Air001 定时器17,配置相同功能的方法一样,这里不再赘述。

🌼定时器16 微秒延时配置

#define DLY_TIM_Handle  (&htim16)
TIM_HandleTypeDef htim16;
/* TIM16 init function */
void MX_TIM16_Init(void)
{

    /* USER CODE BEGIN TIM16_Init 0 */

    /* USER CODE END TIM16_Init 0 */
    __HAL_RCC_TIM16_CLK_ENABLE();

    /* USER CODE BEGIN TIM16_Init 1 */

    /* USER CODE END TIM16_Init 1 */
    htim16.Instance = TIM16;
    htim16.Init.Prescaler = 8 - 1;//配置8MHz时钟主频情况下
    htim16.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim16.Init.Period = 65535;
    htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim16.Init.RepetitionCounter = 0;
    htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    if(HAL_TIM_Base_Init(&htim16) != HAL_OK) {
        Error_Handler();
    }


    /* USER CODE BEGIN TIM16_Init 2 */

    /* USER CODE END TIM16_Init 2 */


}
void delay_us(uint16_t nus)
{
    __HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0);
    __HAL_TIM_ENABLE(DLY_TIM_Handle);
    while(__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus) {
    }
    __HAL_TIM_DISABLE(DLY_TIM_Handle);
}

📗利用滴答定时器实现微秒级延时

  • 📍参考:https://blog.csdn.net/qq153471503/article/details/102930097
#define CPU_FREQUENCY_MHZ    8		// 时钟主频
void delay_us(__IO uint32_t delay)
{
    int last, curr, val;
    int temp;

    while (delay != 0)
    {
        temp = delay > 900 ? 900 : delay;
        last = SysTick->VAL;
        curr = last - CPU_FREQUENCY_MHZ * temp;
        if (curr >= 0)
        {
            do
            {
                val = SysTick->VAL;
            }
            while ((val < last) && (val >= curr));
        }
        else
        {
            curr += CPU_FREQUENCY_MHZ * 1000;
            do
            {
                val = SysTick->VAL;
            }
            while ((val <= last) || (val > curr));
        }
        delay -= temp;
    }
}

在这里插入图片描述


  • 🔖此文章仅作为个人学习探索知识的总结,不作为他人引用者的理论依据,由于学识所限,难免会出现错误或纰漏,欢迎大家指正。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值