赞
踩
编译环境:Keil5 MDK
辅助软件:STM32 CubeMX
课程教学:基于正点原子HAL库学习教程
其余配件:江科大STM32配件包 和 示波器一台
备注: 因为这块开发板没有基本定时器,所以本文也没有基本定时器的内容
本文1.3和2.1部分的标题不知道为什么显示不对
大家凑合一下应该还是看得懂标题的
我们这里目标为用定时器2实现LED以500ms为间隔亮灭
注意LED接PA6
这里用高速就行了,所以低速就不设置了
注意Psc和Arr都需要减1所以应该设置为7199和4999
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
这里我们要先在主函数中给中断使能
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为间隔亮灭了
我们的目标是实现LED的呼吸灯,同时我们在旁边点亮一个LED来作为对比
注意一个LED接PA0,另一个LED接PA6
这里的配置和上面的定时器中断开始和结尾是一样的
所以看过上面部分的朋友可以直接看定时器部分的配置
但需要注意这里不用配置NVIC
这里用高速就行了,所以低速就不设置了
这里附上一张总的引脚定义图,可以作为参考
接了下来我们配置定时器2 PWM
具体原理我就不讲解了,不懂的去百度都有
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
首先我们需要定义一个全局变量
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呼吸灯的效果了
我们的实现目标为:按键下,松手后将脉宽值通过串口发送给电脑,并显示在OLED上
这里用高速就行了,所以低速就不设置了
这里开不开中断都无所谓,因为我们只发送数据
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
我们将使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
- int fputc(int ch, FILE * f)
- {
- HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
- return ch;
- }
注意我们还需要设置一下
首先我们要打开魔法棒,然后勾上就行了
首先一样是包含头文件
#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++;
- }
- }
- }
- }
- }
再然后我们就可以下载编译就能实现现象了
用定时器1产生多路PWM实现LED的不同亮度做对比
这里因为设备问题拍照看不出来,但实物还是呢看出来的
这里用高速就行了,所以低速就不设置了
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
这里的很简单
在while循环前开启PWM就行
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
然后就是编译下载就完成了
使用定时器1产生4路占空比为50%的PWM并具有相位差
相位差为45度
相位差为90度
相位差为135度
这里用高速就行了,所以低速就不设置了
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
我们需要使能输出捕获所以在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);
然后我们就可以编译下载了
OC互补输出用2个通道产生4路占空比为50%的PWM
具体原理我这里不多赘述,可以去看正点原子的HAL库教学视频
这里用高速就行了,所以低速就不设置了
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
我们只需要在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);
然后我们就可以编译下载了
开启两路的PWM和其互补输出,并有死区生成
这里用高速就行了,所以低速就不设置了
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
我们只需要在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);
然后我们就可以编译下载了
用定时器1对定时器2产生的PWM测量,并用串口打印出来
有兴趣的可以用示波器测 ,我这里就不展示效果图了
注意在不考虑溢出的情况下这里的配置测量周期最大为910us
周期为100us,高电平脉宽为60us
周期为910us,高电平脉宽为60us
周期为911us(注意已经超出),高电平脉宽为60us
明显看出周期不对
这里用高速就行了,所以低速就不设置了
这里开不开中断都无所谓,因为我们只发送数据
这里有个便捷方法,就是在红框里面输入72,然后按下enter,再点击OK
这样CubeMX就会帮我们自动配好其他的
这样我们的CubeMX就写好了,接下来是代码部分
我们将使用串口的重定向
首先包含#include <stdio.h>
然后在末尾加上
- int fputc(int ch, FILE * f)
- {
- HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF);
- return ch;
- }
注意我们还需要设置一下
首先我们要打开魔法棒,然后勾上就行了
首先我们在前创建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的教程
所以我写了这个,原理部分看正点原子就行了
希望大家越学越有,早日成为嵌入式巨佬
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。