STM32-定时器&PWM应用编程

STM32-定时器&PWM应用编程

实验任务

  1. 使用STM32F103的Tim2~Tim5其一定时器的某一个通道pin,连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
  2. 采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,并使用Keil虚拟示波器,观察pwm输出波形。
  3. 采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。

实验过程

定时器控制LED亮灭

打开Keil,创建好工程后,在Hardware文件夹中添加两个文件:LED.hLED.c,用来编写LED相关代码。

LED.h中,进行相关函数的声明:

#ifndef __LED_H
#define __LED_H

void LED_Init(void); // LED初始化
void LED_ON(void); // 亮
void LED_OFF(void); // 灭
void LED_Turn(void); // 电平反转

#endif

LED.c中,负责上述函数的实现,这里选择PA1作为LED引脚输出。

#include "stm32f10x.h"                  // Device header

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
}

void LED_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}

void LED_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
}

void LED_Turn(void)
{
	if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_1);
	}
	else
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);
	}
}

LED部分完成后,在System文件夹中添加两个文件:Timer.hTimer.c,用来编写定时器相关代码。

Timer.h中,负责声明定时器初始化函数Timer_Init

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

Time.c中,负责实现定时器初始化函数Timer_Init

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
    // 启用 TIM2 的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 配置 TIM2 的时钟源为内部时钟
    TIM_InternalClockConfig(TIM2);

    // 定义并初始化 TIM_TimeBaseInitTypeDef 结构体
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频,不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 定时器模式为向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // 设置定时器的自动重载值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 14400 - 1; // 设置预分频器,控制计数频率
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器,一般不使用
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 将配置应用到 TIM2 定时器

    // 清除 TIM2 的更新标志位
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    // 启用 TIM2 的更新中断
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    // 配置 NVIC 中断优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // 定义并初始化 NVIC_InitTypeDef 结构体,配置 TIM2 的中断
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 指定中断通道为 TIM2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 启用中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级
    NVIC_Init(&NVIC_InitStructure); // 将配置应用到 NVIC

    // 启用 TIM2 定时器
    TIM_Cmd(TIM2, ENABLE);
}

这段代码的主要作用是初始化 TIM2 定时器,配置其时钟源、计数模式、预分频值、自动重载值、以及中断相关设置。这样,TIM2 定时器就可以用于产生定时中断,执行相关任务或操作。

其中,定时器 TIM2 的中断触发频率由以下参数决定:

  • 预分频器 (TIM_Prescaler):设置为 14400 - 1。
  • 自动重载值 (TIM_Period):设置为 10000 - 1。
  • 时钟分频 (TIM_ClockDivision):设置为 TIM_CKD_DIV1,表示不分频。

中断触发的频率可以通过以下公式计算:

中断触发频率 = TIM2的时钟频率 / (TIM_Prescaler + 1) / (TIM_Period + 1)

根据上述设置,中断触发频率 = 72,000,000 Hz / (14400) / (10000) = 0.5 Hz

因此,这段代码配置了 TIM2 定时器以每2秒触发一次中断

最后,在main.c中,进行相关的初始化工作。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "LED.h"

int main(void)
{
	LED_Init();
	Timer_Init();
	
	while (1)
	{
	}
}

这里的while循环内不需要进行其他操作,全部交给定时器中断函数去实现。

在main函数后面,添加上定时器中断函数,在中断函数中实现LED的亮灭反转:

void TIM2_IRQHandler(void)
{
	// 检查是否是 TIM2 的更新中断(TIM_IT_Update)触发了中断
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		LED_Turn(); // LED1(PA1)的电平反转
		// 清除 TIM2 的更新中断标志位,以重置中断状态
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

代码全部编写完成,编译烧录后,效果如下:

PWM实现呼吸灯

要实现呼吸灯效果,需要把定时器TIM2设置成PWM(脉冲宽度调制)输出模式。

Hardware文件夹中,创建两个文件:PWM.hPWM.c,用来实现PWM相关代码的实现。

PWM.h中声明相关函数:

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void); // PWM初始化
void PWM_SetCompare1(uint16_t Compare); // 改变 PWM 波形的占空比

#endif

PWM.c中进行实现:

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    // 启用 TIM2 的时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    // 启用 GPIOA 的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置 GPIOA 的引脚为复用模式,并设置为推挽输出,用于 PWM 输出
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 配置使用的引脚,这里是GPIOA的引脚0
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO 速度
    GPIO_Init(GPIOA, &GPIO_InitStructure); // 将配置应用到 GPIOA

    // 配置 TIM2 的时钟源为内部时钟
    TIM_InternalClockConfig(TIM2);

    // 定义并初始化 TIM_TimeBaseInitTypeDef 结构体
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频,不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 定时器模式为向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 设置定时器的自动重载值(ARR)
    TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 设置预分频器(PSC),控制计数频率
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器,一般不使用
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 将配置应用到 TIM2 定时器

    // 定义并初始化 TIM_OCInitTypeDef 结构体
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure); // 初始化结构体

    // 配置 TIM2 的输出比较通道 1(TIM2_CH1)为 PWM 模式
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出脉冲的极性为高
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 启用输出
    TIM_OCInitStructure.TIM_Pulse = 0; // 设置输出脉冲的占空比(CCR)
    TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 将配置应用到 TIM2_CH1

    // 启用 TIM2 定时器
    TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare1(uint16_t Compare)
{
    // 使用 TIM_SetCompare1 函数来设置定时器 TIM2 的输出比较通道 1 的比较值
    TIM_SetCompare1(TIM2, Compare);
}

PWM_Init()的主要作用是初始化 TIM2 定时器,配置其为 PWM 输出模式。具体做法包括:

  1. 启用 TIM2 和 GPIOA 的时钟,以使这些外设可用。
  2. 配置 GPIOA 的引脚为复用模式,用于 PWM 输出,并设置为推挽输出。
  3. 配置 TIM2 的时钟源为内部时钟。
  4. 初始化 TIM_TimeBaseInitStructure 结构体,设置定时器 TIM2 的计数模式、自动重载值(ARR)、预分频值(PSC)等参数。
  5. 初始化 TIM_OCInitStructure 结构体,设置输出比较通道 1(TIM2_CH1)为 PWM 模式,定义输出脉冲的极性和初始占空比。
  6. 启用 TIM2 定时器。

这样,TIM2 就被配置成了一个产生 PWM 输出的定时器,可以通过适当配置 TIM_OCInitStructure 结构体中的 TIM_Pulse 参数来调整 PWM 脉冲的占空比。

其中ARR和PSC是两个非常重要的寄存器,用于控制定时器的计数和定时器产生的时间间隔。

  • ARR(Auto-Reload Register)

    • ARR 寄存器定义了定时器的自动重载值,也称为周期值。它决定了定时器在计数到达自动重载值后会自动重新开始计数。当计数器计数到ARR的值时,定时器产生一个更新事件(UEV),并且计数器重新从零开始计数。
    • ARR 寄存器决定了定时器的周期,也就是定时器从开始计数到自动重载的值之间所经过的时间。通过更改 ARR 寄存器的值,可以调整定时器的计数周期,从而改变定时器的工作频率和时间间隔。
  • PSC(Prescaler Register)

    • PSC 寄存器是预分频器寄存器,它用于控制定时器的输入时钟频率。定时器每次计数都会将输入时钟除以 PSC 寄存器中的值。这意味着 PSC 寄存器允许你减小或增加定时器的计数频率。
    • 通过更改 PSC 寄存器的值,可以改变定时器的计数速度,实现不同的计时和延时效果。通常,将 PSC 设置为较大的值将减小计数速度,而设置为较小的值将增加计数速度。

总的来说,ARR 决定了定时器的周期,而 PSC 决定了输入时钟频率和计数速度。这两个寄存器的配置对于实现定时、延时和PWM等功能非常关键,因为它们直接影响了定时器的行为和性能。

PWM_SetCompare1()用于设置定时器 TIM2 的输出比较通道 1 (TIM2_CH1) 的比较值,从而改变 PWM 波形的占空比。

这个函数是一个简单的封装函数,其主要作用是设置 TIM2 定时器的输出比较通道 1 (TIM2_CH1) 的比较值。比较值决定了 PWM 波形的占空比。通过传递参数 Compare,可以设置输出比较通道 1 的比较值,从而调整 PWM 波形的占空比。Compare 参数是一个 16 位无符号整数(uint16_t),通常在范围 [0, ARR-1] 内,其中 ARR 是定时器的自动重载值。占空比可以通过 Compare 的值与 ARR 的值的比值来确定。

通过调用 TIM_SetCompare1 函数,将 Compare 参数传递给 TIM2 定时器的输出比较通道 1,从而实时调整 PWM 波形的占空比。

最后是main.c的实现:

#include "PWM.h"

uint8_t i;

int main(void)
{
    OLED_Init(); // 初始化OLED显示屏
    PWM_Init();  // 初始化PWM定时器

    while (1)
    {
        for (i = 0; i <= 100; i++)
        {
            PWM_SetCompare1(i); // 逐渐增加PWM的占空比
            Delay_ms(10);       // 延时10毫秒,控制LED亮度的变化速度
        }
        for (i = 0; i <= 100; i++)
        {
            PWM_SetCompare1(100 - i); // 逐渐减小PWM的占空比,实现呼吸灯效果
            Delay_ms(10);             // 延时10毫秒,控制LED亮度的变化速度
        }
    }
}

在循环中,首先通过 for 循环逐渐增加 PWM 的占空比,从0增加到100,实现LED的逐渐亮起效果。

随后,通过另一个 for 循环逐渐减小 PWM 的占空比,从100递减到0,实现LED的逐渐熄灭效果。

在每个 for 循环内部,使用 PWM_SetCompare1 函数来设置 PWM 的占空比,从而实现LED的亮度控制。

代码编写完成,看看实际效果:

使用Keil的仿真逻辑分析仪,观察PA0的波形如下:

可以看到,PA0输出波形占空比是随着事件在不断变化的,最终实现了LED的呼吸灯效果。

测量PWM输出信号的频率和占空比

接下来将实现对PWM输出信号的采集,获得其频率和占空比,并重定向输出到串口显示。

在刚才的PWM.h中添加两个函数:PWM_SetPrescalerPWM_SetCompare1,分别用来设置PSC和占空比。

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
void PWM_SetPrescaler(uint16_t Prescaler);

#endif

PWM.c中,实现这两个函数:

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}

void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}

Hardware文件夹下,新建四个文件:IC.hIC.c以及Serial.hSerial.c

IC文件中进行输入捕获的配置,负责实现对频率和占空比的采集,Serial文件中负责串口通信,把读取到的值发送给上位机。

IC.h声明下列函数:

#ifndef __IC_H
#define __IC_H

void IC_Init(void); // 初始化
uint32_t IC_GetFreq(void); // 读取频率
uint32_t IC_GetDuty(void); // 读取占空比

#endif

IC.c的实现如下:

#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // PWM的输出信号输入到PA6,就可以采集频率和占空比
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter = 0xF;
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);

	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
	
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}

uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}

Serial.h中声明如下:

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

#endif

Serial.c实现如下:

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

接下来,再配置一个定时器TIM4,实现每隔一个固定时间进行依次频率和占空比的采集。Timer.c中的Timer_Init如下:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	TIM_InternalClockConfig(TIM4);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM4, ENABLE);
}

TIM4配置完毕后,就可以每隔1秒触发一次中断,在中断处理函数中进行数据的读取并通过串口发送。

main.c中输入以下代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
#include "Serial.h"
#include "Timer.h"

uint16_t i;
uint32_t freq, duty;

int main(void)
{
	PWM_Init();
	IC_Init();
	Serial_Init();
	Timer_Init();
	
	PWM_SetPrescaler(7200 - 1);			//Freq = 72M / (PSC + 1) / 100
	
	while (1)
	{		
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);
			Delay_ms(10);
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);
			Delay_ms(10);
		}
	}
}

void TIM4_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)
	{
		freq = IC_GetFreq(); // 读取频率
		duty = IC_GetDuty(); // 读取占空比
		Serial_Printf("Freq: %d Hz  ", freq); // 给上位机发送频率
		Serial_Printf("Duty: %d %\r\n", duty); // 给上位机发送占空比
		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
	}
}

代码编写完毕,编译运行后,在单片机的PA0PA6之间连接一根杜邦线,打开串口助手,波特率调到9600,就可以看到效果,每隔1秒读取一次频率和占空比。

总结

通过本次实验,学会了定时器的基本配置和使用,以及PWM波形的生成以及波形的频率和占空比的计算。

遇到的困难:PWM波形生成以后,得到了频率和占空比,并通过串口发送到电脑后,发现占空比的值一直不变,看完视频教程后,才发现PA0和PA6之间没连线,导致PA6一直没接收到PWM信号。

引用 & 参考

江科大STM32入门

posted @ 2023-11-04 17:39  回文串多放孜然  阅读(130)  评论(0编辑  收藏  举报