须知
开发板:STM32F103C8T6最小系统板
编译环境:Keil5 MDK
辅助软件:STM32 CubeMX
课程教学:基于正点原子HAL库学习教程
其余配件:江科大STM32配件包 和 示波器一台
备注: 因为这块开发板没有基本定时器,所以本文也没有基本定时器的内容
本文1.3和2.1部分的标题不知道为什么显示不对
大家凑合一下应该还是看得懂标题的
一. 通用定时器
1.1 定时器中断
1.1.1 实现目标
我们这里目标为用定时器2实现LED以500ms为间隔亮灭
注意LED接PA6
1.1.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置GPIOA6
配置定时器2
注意Psc和Arr都需要减1所以应该设置为7199和4999
配置NVIC
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
1.1.3 程序编写
这里我们要先在主函数中给中断使能
HAL_TIM_Base_Start_IT(&htim2); //使能中断
然后我们在主函数下面加上
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) //判断是否为定时器2产生的中断
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);//GPIOA6电平翻转
}
}
再然后我们编译并下载程序,我们就会发现在PA6上的LED以500ms为间隔亮灭了
1.2 定时器输入比较(PWM)
1.2.1 实现目标
我们的目标是实现LED的呼吸灯,同时我们在旁边点亮一个LED来作为对比
注意一个LED接PA0,另一个LED接PA6
1.2.2 CubeMX配置
这里的配置和上面的定时器中断开始和结尾是一样的
所以看过上面部分的朋友可以直接看定时器部分的配置
但需要注意这里不用配置NVIC
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置GPIOA6
配置定时器2
这里附上一张总的引脚定义图,可以作为参考
接了下来我们配置定时器2 PWM
具体原理我就不讲解了,不懂的去百度都有
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
1.2.3 程序编写
首先我们需要定义一个全局变量
uint16_t Pwm; //PWM控制
记下来我们在主程序while前面加入这句代码
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //开启 定时器2 PWM通道1
然后我们在主程序while循环中加入下面的代码
while (Pwm < 999)
{
Pwm++;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Pwm); //设置比较值
HAL_Delay(1); //延时1ms不然改变太快
}
while (Pwm)
{
Pwm--;
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, Pwm); //设置比较值
HAL_Delay(1); //延时1ms不然改变太快
}
接下来我们编译下载就能看到LED呼吸灯的效果了
1.3 定时器输入捕获
1.3.1 实现目标
我们的实现目标为:按键下,松手后将脉宽值通过串口发送给电脑,并显示在OLED上
1.3.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器
配置GPIO
配置I2C
配置串口
这里开不开中断都无所谓,因为我们只发送数据
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
1.3.3 程序编写
usart.c
我们将使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
int fputc(int ch, FILE * f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
return ch;
}
注意我们还需要设置一下
首先我们要打开魔法棒,然后勾上就行了
main.c
首先一样是包含头文件
#include <stdio.h>
#include "./OLED/OLED.h"
然后在include下面加上
/* 输入捕获状态(g_timxchy_cap_sta)
* [7] :0,没有成功的捕获;1,成功捕获到一次.
* [6] :0,还没捕获到高电平;1,已经捕获到高电平了.
* [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303
* 注意:为了通用,我们默认ARR和CCRy都是16位寄存器,对于32位的定时器(如:TIM1),也只按16位使用
* 按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒
*
* (说明一下:正常32位定时器来说,1us计数器加1,溢出时间:4.294秒)
*/
uint8_t g_timxchy_cap_sta = 0; /* 输入捕获状态 */
uint16_t g_timxchy_cap_val = 0; /* 输入捕获值 */
接着在主函数里定义一个变量
uint32_t temp = 0;
再然后我们在while循环内加入
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
OLED_ShowString(0, 4, "Down", 16, 0);
else
OLED_ShowString(0, 4, " UP ", 16, 0);
if (g_timxchy_cap_sta & 0X80) /* 成功捕获到了一次高电平 */
{
temp = g_timxchy_cap_sta & 0X3F;
temp *= 65536; /* 溢出时间总和 */
temp += g_timxchy_cap_val; /* 得到总的高电平时间 */
g_timxchy_cap_sta = 0; /* 开启下一次捕获*/
OLED_ShowNum(32, 0, temp, 7, 16, 0);
printf("LOW: %4d ms", temp/1000);
}
再然后我们在主函数下面加上回调函数
/* 定时器输入捕获回调函数 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
if ((g_timxchy_cap_sta & 0X80) == 0) /* 还未成功捕获 */
{
if (g_timxchy_cap_sta & 0X40) /* 捕获到一个下降沿 */
{
g_timxchy_cap_sta |= 0X80; /* 标记成功捕获到一次高电平脉宽 */
g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); /* 获取当前的捕获值 */
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原来的设置 */
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 配置TIM2通道1上升沿捕获 */
}
else /* 还未开始,第一次捕获上升沿 */
{
g_timxchy_cap_sta = 0; /* 清空 */
g_timxchy_cap_val = 0;
g_timxchy_cap_sta |= 0X40; /* 标记捕获到了上升沿 */
__HAL_TIM_SET_COUNTER(&htim2, 0); /* 定时器2计数器清零 */
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原来的设置!! */
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 定时器2通道1设置为下降沿捕获 */
}
}
}
}
/* 定时器更新中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
if ((g_timxchy_cap_sta & 0X80) == 0) /* 还未成功捕获 */
{
if (g_timxchy_cap_sta & 0X40) /* 已经捕获到高电平了 */
{
if ((g_timxchy_cap_sta & 0X3F) == 0X3F) /* 高电平太长了 */
{
TIM_RESET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1); /* 一定要先清除原来的设置 */
TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);/* 配置TIM2通道1上升沿捕获 */
g_timxchy_cap_sta |= 0X80; /* 标记成功捕获了一次 */
g_timxchy_cap_val = 0XFFFF;
}
else /* 累计定时器溢出次数 */
{
g_timxchy_cap_sta++;
}
}
}
}
}
再然后我们就可以下载编译就能实现现象了
二. 高级定时器
2.1 定时器输出多路PWM
2.1.1 实现目标
用定时器1产生多路PWM实现LED的不同亮度做对比
这里因为设备问题拍照看不出来,但实物还是呢看出来的
2.1.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
2.1.3 程序编写
这里的很简单
在while循环前开启PWM就行
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
然后就是编译下载就完成了
2.2 定时器OC输出比较产生相位差
2.2.1 实现目标
使用定时器1产生4路占空比为50%的PWM并具有相位差
相位差为45度
相位差为90度
相位差为135度
2.2.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
2.2.3 程序编写
我们需要使能输出捕获所以在while循环之前加入这段代码
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_3);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_4);
然后我们就可以编译下载了
2.3 定时器OC互补输出
2.3.1 实现目标
OC互补输出用2个通道产生4路占空比为50%的PWM
具体原理我这里不多赘述,可以去看正点原子的HAL库教学视频
2.3.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
2.3.3 程序编写
我们只需要在while循环前面加上使能OC和OCN的代码就行了
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_OCN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_OCN_Start(&htim1, TIM_CHANNEL_2);
然后我们就可以编译下载了
2.4 定时器PWM互补输出加死区生成
2.4.1 实现目标
开启两路的PWM和其互补输出,并有死区生成
2.4.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
2.4.3 程序编写
我们只需要在while前加上
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
然后我们就可以编译下载了
2.5 定时器PWM输入捕获
2.5.1 实现目标
用定时器1对定时器2产生的PWM测量,并用串口打印出来
有兴趣的可以用示波器测 ,我这里就不展示效果图了
注意在不考虑溢出的情况下这里的配置测量周期最大为910us
周期为100us,高电平脉宽为60us
周期为910us,高电平脉宽为60us
周期为911us(注意已经超出),高电平脉宽为60us
明显看出周期不对
2.5.2 CubeMX配置
配置系统线
配置时钟
这里用高速就行了,所以低速就不设置了
配置定时器1
配置定时器2
配置串口
这里开不开中断都无所谓,因为我们只发送数据
设置时钟频率并生成工程
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
2.5.3 程序编写
usart.c
我们将使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
int fputc(int ch, FILE * f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
return ch;
}
注意我们还需要设置一下
首先我们要打开魔法棒,然后勾上就行了
main.c
首先我们在前创建3个全局变量
uint8_t Tim1_IC_Flag; //0-未捕获 1-捕获
uint16_t Tim2_Pwm_H_Val; //PWM的高电平脉宽
uint16_t Tim2_Pwm_C_Val; //PWM的周期宽度
然后再主函数内创建4个局部变量
double ht, ct, f, tpsc;
再就是在while循环前添加代码
// 定时器2PWM使能 产生PWM波
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
//定时器1输入捕获使能
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_2);
如果我们要修改定时器2通道1的比较值和定时器的重装载值可以用这个函数和宏定义
//修改参数
TIM2->CCR1 = 60;
__HAL_TIM_SetAutoreload(&htim2, 100);
再然后我们就可以写while循环内的逻辑了
HAL_Delay(500); //延时500ms不然打印数据太快
if (Tim1_IC_Flag) /* 捕获了一次数据 */
{
printf("\r\n"); /* 输出空,另起一行 */
printf("PWM Hight:%d\r\n", Tim2_Pwm_H_Val); /* 打印高电平脉宽 */
printf("PWM Cycle:%d\r\n", Tim2_Pwm_C_Val); /* 打印周期 */
tpsc = ((double)0 + 1) / 72; /* 得到PWM采样时钟周期时间 */
ht = Tim2_Pwm_H_Val * tpsc; /* 计算高电平时间 */
ct = Tim2_Pwm_C_Val * tpsc; /* 计算周期长度 */
f = (1 / ct) * 1000000; /* 计算频率 */
printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高电平脉宽长度 */
printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期时间长度 */
printf("PWM Frequency :%.3fHz\r\n", f); /* 打印频率 */
HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_1); //停止定时器中断
Tim1_IC_Flag = 0;/* 清零状态,重新开始检测 */
Tim2_Pwm_H_Val=0;
Tim2_Pwm_C_Val=0;/* 重启PWM输入检测 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); //开启定时器中断
}
接下来就是在主函数后面写定时器输入捕获中断回调函数了
/* 定时器输出捕获中断回调函数 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1) //判断是否为定时器1中断
{
if (Tim1_IC_Flag == 0) //未捕获
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) //判断是否是通道一
{
Tim2_Pwm_H_Val = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_2) + 1 + 1; //读取高脉宽
Tim2_Pwm_C_Val = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_1) + 1 + 1; //读取周期
Tim1_IC_Flag = 1; //标志位置1
}
}
}
}
然后我们就可以编译下载看现象了,大家也可以多尝试PWM看看效果
注意上面实现目标的内容就行了
三、结束语
这篇文章主要也是我自己为了我自己复习用的笔记
所以没有过多的原理解释,写得也一般
主要是因为正点原子并没有出配置CubeMX的教程
所以我写了这个,原理部分看正点原子就行了文章来源:https://www.toymoban.com/news/detail-859151.html
希望大家越学越有,早日成为嵌入式巨佬文章来源地址https://www.toymoban.com/news/detail-859151.html
到了这里,关于STM32 HAL库 CubeMX配置 定时器学习 F103C8T6的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!