本节主要讲3部分的内容:pwm 输出,输入捕获,ADC的电压捕获
1.PWM输出
-
PWM 本质上是一个数字信号,通过控制它的高低电平持续时间的比例,可以实现对模拟量的控制
-
pwm输出一般在引脚不冲突的情况下就可以自由输出脉冲信号了,不需要特定的引脚,但是引脚必须要打开定时器
-
PWM波一般是板子由内发向外界的数字信号,一般在确定哪一个引脚输出后连接示波器观察波形的频率和占空比了
1.1PWM输出在CubeMax的基础配置
-
PWM输出的波形必须依靠定时器,本节以PA15为例,设置一个初始化占空比为20,频率为1KHz的脉冲信号
-
f = 80000000 / 800 / 100 = 1000Hz = 1KHz,一般脉冲信号的ARR为100,这样好设置pwm波形的占空比
1.2PWM输出在keil5的底层代码编写
-
PWM输出只需要打开定时器即可,不需要打开中断,也不需要中断回调函数就可以设置PWM输出波形
-
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
LCD_Init(); //LCD初始化
HAL_TIM_Base_Start_IT(&htim6) ;//开启定时器中断
HAL_TIM_PWM_Start (&htim2,TIM_CHANNEL_1 );//开启定时器2,通道1输出脉冲信号
__HAL_TIM_SetCompare(&htim2 ,TIM_CHANNEL_1 ,30);//设置定时器2,通道1的占空比为30
__HAL_TIM_SET_PRESCALER(&htim2,400-1);//设置定时器2的预分频值为400,则此时频率为2KHz
/* USER CODE END 2 */
2.输入捕获相关原理图
-
蓝桥杯的板子内置了一个滑动电阻R40和R39,可以通过调节他们2个的阻值可以改变波形的输出频率
-
同时板子还通过跳线帽将PB4和PA15的输入捕获与滑动电阻相连接,这样就可以直接捕获R39和R40的输出频率和占空比
-
不过R40和R39的占空比已经确定都为50了,不能通过阻值改变
2.1输入捕获在CubeMax的基础配置
-
输入捕获的本质是在硬件上对输入信号的时序特征进行精准记录,例如PWM是输出波形则输入捕获是捕获pwm的波形
-
一般省赛的输入捕获只需要捕获波形频率,但是以防万一我们顺便把波形的占空比一起测量了
-
因为刚刚PWM输出我们使用了PA15引脚输出脉冲,因此这次我们用PB4捕获R39的频率和占空比好了
-
直接捕获的是波形频率,间接捕获就可以利用2个之间的差值计算出占空比
-
别忘记打开定时器中断
2.2输入捕获在keil5的底层代码编写
-
因为输入捕获需要在定时器中断中进行,所以我们需要先在之前的中断函数中继续编写代码
-
interrupt.c
#include "interrupt.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器中断回调函数
{
if(htim->Instance ==TIM6 )//判断定时器是否是定时器6
{
key_1();
}
}
float ARR1,ARR2;
float frq,duty;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//输入捕获中断回调函数
{
if(htim ->Instance ==TIM3 )//判断定时器是否是定时器3
{
if(htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_1)//中断消息来源,设置直接输入的通道
{
ARR1 =HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1 );//读取定时器3通道1的重装载值
ARR2 =HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2 );
if(0==ARR1 | 0==ARR2)
{
frq =0;
}
else
{
frq = (80000000/80.0)/ARR1 ;//计算波形频率
}
duty = (ARR2/ARR1)*100.0f; //计算占空比
__HAL_TIM_SetCounter(htim ,0);//清空计数器
HAL_TIM_IC_Start(htim ,TIM_CHANNEL_1 );//启动定时器直接输入捕获功能
HAL_TIM_IC_Start(htim ,TIM_CHANNEL_2 );
}
}
}
-
interrupt.h
#ifndef _INTERRUPT_H__
#define _INTERRUPT_H__
#include "main.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
extern float ARR1,ARR2;
extern float frq,duty;
#endif
接着我们在主函数去测试输入捕获R39的频率和占空比是否正确
main.c
/* USER CODE BEGIN WHILE */
while (1)
{
if(1==key[2].duan_flag)
{
led_dan (7,1);
key[2].duan_flag=0;
}
if(1==key[3].long_flag)
{
led_dan (7,0);
key[3].long_flag=0;
}
LCD_DisplayStringLine(Line0, (uint8_t *)" ");
sprintf (text," ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf (text," frq:%0.1f ",frq);
LCD_DisplayStringLine(Line2, (uint8_t *)text);
sprintf (text," duty:%0.1f%% ",duty);
LCD_DisplayStringLine(Line3, (uint8_t *)text);
LCD_DisplayStringLine(Line4, (uint8_t *)" ");
LCD_DisplayStringLine(Line5, (uint8_t *)" ");
LCD_DisplayStringLine(Line6, (uint8_t *)" ");
LCD_DisplayStringLine(Line7, (uint8_t *)" ");
LCD_DisplayStringLine(Line8, (uint8_t *)" ");
LCD_DisplayStringLine(Line9, (uint8_t *)" ");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
-
这个时候将代码下载到板子里可以看到LCD屏显示出R39的频率和占空比
-
通过改变R39的阻值,波形频率会不断改变但是占空比不变,始终都是50%左右
3.ADC的电压捕获原理图
-
蓝桥杯的板子还内置了一个滑动电阻R37和R38,通过改变他们的阻值可以改变电压值从0~3.3V
3.1ADC的电压捕获在CubeMax的基础配置
-
PB15的配置
-
PB12的配置
3.2ADC的电压捕获在keil5的底层代码编写
-
adc电压捕获的新建模块名称不能是adc否则会和HAL库冲突
-
myadc.c
#include "myadc.h"
double getadc(ADC_HandleTypeDef *pin)
{
double adc;
HAL_ADC_Start(pin );//开启ADC
adc = HAL_ADC_GetValue(pin );//读取最近一次 ADC 转换的原始数值
return adc * 3.3 / 4096.0;//转换为电压
}
-
myadc.h
#ifndef __MYADC_H
#define __MYADC_H
#include "main.h"
double getadc(ADC_HandleTypeDef *pin);
#endif
别忘记将新建的模块名称添加到main.h里
/* Includes ------------------------------------------------------------------*/
#include "stm32g4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdbool.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "interrupt.h"
#include "myadc.h"
/* USER CODE END Includes */
-
别忘记将新建的模块名称添加到main.h里
/* Includes ------------------------------------------------------------------*/
#include "stm32g4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "stdbool.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "interrupt.h"
#include "myadc.h"
/* USER CODE END Includes */
-
main.c
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
char text[20];
double V1,V2;//新建变量储存电压
/* USER CODE END Includes */
~
~
~
/* USER CODE BEGIN WHILE */
while (1)
{
if(1==key[2].duan_flag)
{
led_dan (7,1);
key[2].duan_flag=0;
}
if(1==key[3].long_flag)
{
led_dan (7,0);
key[3].long_flag=0;
}
V1 = getadc (&hadc1);
V2 = getadc (&hadc2);
sprintf (text," ");
LCD_DisplayStringLine(Line0, (uint8_t *)text);
sprintf (text," ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf (text," frq:%0.1fHz ",frq);
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf (text," duty:%0.1f%% ",duty);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf (text," V:%0.1fV ",V1);
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf (text," V:%0.1fV ",V2);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
sprintf (text," ");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
sprintf (text," ");
LCD_DisplayStringLine(Line7,(uint8_t *)text);
sprintf (text," ");
LCD_DisplayStringLine(Line8,(uint8_t *)text);
sprintf (text," ");
LCD_DisplayStringLine(Line9,(uint8_t *)text);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
-
这个时候将代码下载到板子里可以看到LCD屏显示出R37和R38的电压值了
-
通过改变R37和R38的阻值,ADC捕获的电压会在0~3.3V不断改变
-
本节就结束啦,谢谢大家观看