STM32使用定时器与PWM实现串口通信和点灯


一、定时器介绍

1.定时器概念

(1)能够对内部时钟信号或外部输入信号进行计数,数值达到设定要求时,向CPU发起中断请求,完成外部程序的运行。

(2)本质就是进行计数,选择内部时钟脉冲,作为计数器时,技术信号的来源选择非周期脉冲信号。

(3)STM32中定时器可分为高级定时器、通用定时器、基本定时器三类,他们都是由一个可编程的16位预分频器(TIMX_PSC)驱动的16位。

(4)在大容量的 STM32F103xx增强型系列产品包含最多2个高级控制定时器、4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器,本次的目标芯片(STM32F103C8T6)也是大容量系列,所以具备以上所述的定时器种类

2.STM32定时器分类

定时器可分为3类:
(1)基本定时器:功能最少,只能充当基本的时基,甚至都没有外部引脚
(2)通用定时器:拥有基本定时器的全部功能,同时有输入捕获模式,用以接收外部的PWM,脉冲之类的信息
(3)高级定时器:又有通用定时器的全部功能,又有互补输出模式,功能最为强大
在这里插入图片描述
通常我们使用的都是通用定时器
通用定时器特点:
(1)位于ABP1低速总线上
(2)16位向下,向上/向下(中心对齐模式)计数模式,自动重装载计数器(TIMx_CNT)
(3)16位可编程(可以实现修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535任意数值
(4)四个独立通道(TIMx_CH1~4),通道用来支持:
①输入捕获
②输出比较
③PWM生成
④单脉冲模式输出
(5)可使用外部信号(TIM_ETR)控制定时器和定时器互连的同步电路

3.计数器模式

  • 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
  • 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
  • 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

4.定时时钟计算方法

Tout = ((arr+1)(psc+1))/Tclk

其中:
Tclk:定时器的输入时钟频率(单位MHZ)
Tout:定时器溢出时间(单位为us)
arr: 计数装载值
psc: 时钟分频系数

二、通过定时器Timer方式实现串口通信和LED等周期性地闪烁

1.通过STM32CubeMX配置项目

(1) 实验过程

  • 点击File,New Project,创建新的工程项目
    请添加图片描述
  • 选择芯片STM32F103C8T6
    请添加图片描述
  • 进行SYS选择,选择Senal Wire
    请添加图片描述
  • 进行RCC选择,选择Crystal/Ceramic Resonator
    请添加图片描述
  • 设置GPIO,这里选择的是PC13
    在这里插入图片描述
  • 配置定时器2和定时器3
    这里我们使用定时器2和定时器3来实现定时的功能。
    如图所示,配置定时器2和定时器3的时钟源为内部时钟;分频系数为71,向上计数模式,计数周期为5000,使能自动重载模式。
    在这里插入图片描述
    在这里插入图片描述
  • 配置中断
    如下图所示,开启定时器2和定时器3的中断。
    在这里插入图片描述
  • 配置定时器2和定时器3中断优先级
    在这里插入图片描述
  • 选择串口USART1,设置MODE为异步通信(Asynchronous)
    基础参数:波特率为115200 Bits/s,传输数据长度为8 Bit。无校验位,1位停止位,接收和发送都使能。
    在这里插入图片描述
  • 配置时钟
    在这里插入图片描述
  • 设置项目名称,存储路径以及选择所用IDE,最后创建项目工程文件
    在这里插入图片描述
    请添加图片描述
  • 配置完成后生成代码并打开
    请添加图片描述

2.结果运行

(1)代码修改

在int main() 中添加以下代码
该函数表示启动相应的定时器

	HAL_TIM_Base_Start_IT(&htim2);
	HAL_TIM_Base_Start_IT(&htim3);

在这里插入图片描述
在main函数中添加串口通信代码
在main.c中定义STM32需要给上位机发送的消息 “hello windows!”

	uint8_t hello[20]="hello windows!\r\n";

在这里插入图片描述
在main函数中添加以下代码
用于定时器中断回调

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint32_t time_cnt =0;
	static uint32_t time_cnt3 =0;
	if(htim->Instance == TIM2)
	{
		if(++time_cnt >= 400)
		{
			time_cnt =0;
			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
		}
	}
	if(htim->Instance == TIM3)
	{
		if(++time_cnt3 >= 1000)
		{
			time_cnt3 =0;
    HAL_UART_Transmit(&huart1,hello,20,100000);
		}
			
	}
}

在这里插入图片描述

  • 该函数为定时器的中断回调函数,当产生定时中断的时候,会自动调用这个函数。在函数内部定义了定时器的一个静态变量:time_cnt与定时器3 的time_cnt3。
    例如:time_cnt,当它大于等于400的时候,才会执行if里面的代码。也就是说需要发生400次中断,才会让LED的状态翻转。前面已经算过了,一次定时中断的时间是0.005秒,所以400次中断的时间是0.005400=2秒。也就是说每隔2秒,LED的状态翻转一次。
    例如:time_cnt3,当它大于等于1000的时候,才会执行if里面的代码。也就是说需要发生1000次中断,才会让串口发一次消息。0.0051000=5秒,符合题目要求。

(2)运行视频

三、PWM介绍

(1)定义
  PWM(Pulse Width Modulation)即脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术;它是一种模拟控制方式,根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源输出的改变。

(2)基本原理
  PWM就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也可以这样理解,PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。
  该信号在预定义的时间和速度中设置为高(5v或3.3v)和低(0v)。通常,我们将PWM的高电平称为1,低电平为0。

(3)优点及应用范围
  由于其控制简单、灵活和动态响应好等优点而成为电力电子技术应用最广泛的控制方式,其应用领域包括测量,通信, 功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些音频放大器,因此学习PWM具有十分重要的现实意义。

(4)主要参数

  • PWM占空比:
    PWM信号保持高电平的时间百分比称为占空比。如果信号始终为高电平,则它处于100%占空比,如果它始终处于低电平,则占空比为0%。
  • PWM的频率:
    PWM信号的频率决定PWM完成一个周期的速度。STM32的MDK编译器可以选择5MHZ,10MHZ,20MHZ和50MHZ。

(5)PWM的产生
  STM32的定时器除了TIM6和7,其他的定时器都可以用来产生PWM输出。其中高级定时器TIM1和TIM8可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。
  通过STM32控制板,有两种方式能产生PWM,第一是利用普通IO口输出PWM,第二种是利用定时器的PWM的IO口或复用IO口。一般能够输出PWM的端口都会在主要功能那一栏出现CHx的标志,而普通定时器没有出现这种标志。如图所示,上面的红框就是普通的定时器,不是专用的PWM端口

注意:一般而言,尽量选用PWM口进行PWM输出,因为普通IO口模拟PWM的输出频率越高,进入定时器中断的次数就越快,中断间隔的时间越短,如果再有其他类型的中断也要处理时,会因为中断的优先级嵌套等待响应,影响控制精度,PWM输出误差增大,也会影响其他如ADC等中断处理,甚至会较出现单片机逻辑出错,死机或者跑飞的情况。

四、使用TIM3和TIM4实现2个LED呼吸灯

1.通过STM32CubeMX配置项目

(1) 实验过程

  • 点击File,New Project,创建新的工程项目
    请添加图片描述
  • 选择芯片STM32F103C8T6
    请添加图片描述
  • 进行SYS选择,选择Senal Wire
    请添加图片描述
  • 进行RCC选择,选择Crystal/Ceramic Resonator
    请添加图片描述
  • 配置定时器3和定时器4
    这里我们使用定时器3和定时器4来实现定时的功能。
    如图所示,配置定时器3和定时器4的时钟源为内部时钟;分频系数为71,向上计数模式,计数周期为5000,使能自动重载模式。
    在这里插入图片描述
    在这里插入图片描述
  • 选择串口USART1,设置MODE为异步通信(Asynchronous)
    基础参数:波特率为115200 Bits/s,传输数据长度为8 Bit。无校验位,1位停止位,接收和发送都使能。
    在这里插入图片描述
  • 配置时钟
    在这里插入图片描述
  • 设置项目名称,存储路径以及选择所用IDE,最后创建项目工程文件
    在这里插入图片描述
    请添加图片描述
  • 配置完成后生成代码并打开
    请添加图片描述

2.结果运行

(1)代码修改

在main函数中设置占空比
定义一个变量,用来存储占空比:初值设为0

uint16_t duty_num3 = 0;

在这里插入图片描述
在main函数中添加开启PWM信道代码
开启TIM3的通道3,输出PWM。
开启TIM4的通道4,输出PWM。

	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);

请添加图片描述
在int main() 函数的while循环中添加以下代码
设置每隔200毫秒,进入while循环
当占空比小于5000(设置的PWM周期),占空比加10。(即灯会逐渐从亮到暗)
当占空比超过5000(设置的PWM周期),占空比减10。(即灯会逐渐从暗到亮)

    HAL_Delay(200);
		
		  while (duty_num<5000)
			{
				duty_num = duty_num + 10;
			__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,duty_num);
			__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,duty_num);
				HAL_Delay(10);
			}
			while (duty_num)
			{
				duty_num = duty_num - 10;
			__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,duty_num);
			__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,duty_num);
				HAL_Delay(10);
			}

在这里插入图片描述

(2)硬件连接

查询数据手册可发现定时器3,4的一通道分别对应引脚PA6,PB6
题目要求需要直接驱动PC13(最小开发板上已焊接的LED(固定接在 PC13 GPIO端口)),因此我们将对应PWM波作为输入,连接其对应引脚即可实现所需完成功能。
在这里插入图片描述
在这里插入图片描述
将引脚PA6与引脚PC13相连

PA6 —> PC13

在这里插入图片描述

(3)运行视频


总结

通过本次作业,深入理解了定时器在嵌入式系统中的重要性和强大功能。
在实现多任务并发运行方面,定时器提供了一种高效且精准的解决方案。相比于传统的循环和 delay 函数,定时器能够让 CPU 在定时任务等待期间去处理其他任务,提高了系统的整体效率和响应能力。例如,在同时实现串口发送和 LED 闪烁任务时,定时器中断机制确保了这两个任务能够按照预定的时间间隔独立运行,互不干扰。如果不采用定时器而仅依靠循环和 delay 函数来实现上述两个周期性任务,会面临一些问题。在执行 delay 函数时,CPU 会被阻塞,无法进行其他操作,导致系统资源浪费且无法实现真正的多任务并发。而且,使用循环和 delay 函数难以保证任务执行的精准性,尤其是在涉及多个任务且时间要求严格的情况下。
在实现 PWM 呼吸灯效果时,进一步熟悉了 TIM 定时器的 PWM 输出功能。通过动态调整 PWM 的占空比,可以轻松实现 LED 亮度的渐变效果。同时,使用不同的 TIM 定时器来控制不同的 LED,展示了定时器资源在多外设控制中的灵活应用。这也让我认识到在嵌入式系统设计中,合理利用硬件定时器资源能够创造出丰富多样的功能和效果,对于提升系统的性能和用户体验具有重要意义。
总的来说,本次作业不仅提升了在定时器使用方面的技术能力,还加深了对嵌入式系统多任务处理和外设控制的理解,为今后更复杂的嵌入式项目开发奠定了坚实的基础。

### 回答1: 好的,您需要使用STM32的串口接收功能来控制点灯。 首先,您需要配置STM32的串口接收功能。您可以通过调用HAL库中的函数来实现。例如,如果您使用的是USART2,可以使用以下代码进行初始化: ``` /* USART2 init function */ void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } ``` 在这个例子中,我们将USART2的模式设置为UART_MODE_RX,这样它就可以接收数据了。 接下来,您需要编写一个函数来读取接收到的数据并控制LED的状态。例如,以下代码可以实现这个功能: ``` void USART2_IRQHandler(void) { uint8_t data = 0; if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) != RESET) { __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE); HAL_UART_Receive(&huart2, &data, 1, 1000); // 读取接收到的数据 if(data == '1') // 如果接收到的数据是字符'1' { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // 控制LED亮起 } else if(data == '0') // 如果接收到的数据是字符'0' { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // 控制LED熄灭 } } } ``` 在这个例子中,我们使用了HAL库中的函数来读取接收到的数据,并根据接收到的数据控制LED的状态。当接收到字符'1'时,LED亮起;当接收到字符'0'时,LED熄灭。 最后,您需要在main函数中启用中断功能,以便可以通过串口接收数据并控制LED的状态。例如,以下代码可以实现这个功能: ``` int main(void) { /* ... */ MX_USART2_UART_Init(); HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); while (1) { /* ... */ } } ``` 在这个例子中,我们使用了HAL库中的函数来启用USART2的中断功能,并设置了中断的优先级。然后,在main函数的无限循环中,您可以执行其他任务或等待接收到的数据。 希望这个例子可以帮助您了解如何使用STM32的串口接收功能控制LED的状态。 ### 回答2: 要实现stm32串口接收点灯的功能,需要进行以下步骤: 1. 首先,需要在stm32的开发环境中进行串口的初始化配置。设置串口的波特率、数据位、停止位校验位等参数,并使能串口的接收中断。 2. 接着,编写一个中断服务函数,用于处理串口接收中断。在该函数中,可以通过查询接收寄存器的状态位来判断是否接收到了数据,如果接收到数据,则可以读取接收寄存器的值。 3. 在中断服务函数中,可以根据接收到的数据来控制点灯的逻辑。比如,可以使用if语句或switch语句来根据接收到的特定字符或指令来控制点灯的开关。 4. 在主函数中,需要开启全局中断使能,以使得串口接收中断能够正常触发,从而进入中断服务函数进行处理。 5. 在主函数的无限循环中,可以添加其他逻辑或功能代码。比如,可以通过串口发送数据来触发串口接收中断从而控制点灯的开关。 总结起来,实现stm32串口接收点灯的功能,即根据串口接收到的特定字符或指令来控制点灯,需要进行串口初始化配置、编写中断服务函数、开启中断使能等操作。这样,当串口接收到数据时,会触发中断服务函数进行处理,从而实现点灯控制的功能。 ### 回答3: 要在STM32实现串口接收点灯的功能,首先需要配置串口以及GPIO。 首先,我们需要在STM32的引脚配置中选择合适的引脚作为串口接收引脚,并确保STM32的串口功能被使能。然后,我们需要配置串口的波特率、数据位、停止位以及奇偶校验等参数。为了简化操作,我们可以使用STM32提供的库函数进行配置。 接下来,我们需要配置用于控制的GPIO引脚。选择合适的引脚并设置为输出模式。然后,我们可以使用库函数来控制GPIO引脚的状态,例如设置为高电平点亮,或者设置为低电平熄灭。 在代码中,我们可以使用中断的方式接收串口数据。当有数据接收到时,中断将触发,我们可以在中断处理函数中读取接收到的数据。在读取数据后,我们可以根据接收到的数据来控制的状态。例如,当接收到字符'A'时,点亮;当接收到字符'B'时,熄灭。 最后,我们需要在主循环中启用中断并保持程序的运行。当有数据接收到时,中断将触发并处理接收到的数据。 综上所述,我们可以通过配置串口GPIO以及使用中断的方式实现STM32串口接收点灯的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值