硬件开发板:STM32G0B1RET6
软件平台:cubemax+keil+VScode
1 新建cubemax工程
1.1 配置系统时钟RCC
1.2 配置定时器
1.2.1 配置输入捕获
选择通用定时器TIM2-Channel 1
为输入捕获引脚,对应IO口是PA0
,时钟源选择内部时钟源Internal clock
,工作模式选择直接输入捕获Input Capture direct mode
,预分频系数选择63
,定时器向上计数,重装载值选择最大值,减少溢出次数;输入捕获选择上升沿直接触发。
使能中断
1.2.2 配置PWM输出
选择通用定时器TIM3-Channel 1
为PWM输出引脚,对应IO口是PA6
,时钟源选择内部时钟源Internal clock
,工作模式选择PWM输出PWM Generation CH1
,预分频系数选择63
,定时器向上计数,重装载值选择999
,此时对应产生的PWM信号周期是 T = 64 ∗ 1000 64 M = 1 m s ; 频率为 1 K H z T=\frac{64*1000}{64M}=1ms;频率为1KHz T=64M64∗1000=1ms;频率为1KHz,占空比设置为100
,即高电平所占总周期的10%
1.3 配置串口
2 代码
2.1 printf重定向
在usart.h
中添加头文件和函数声明
# include "stdio.h"int fgetc(FILE *f);
int fputc(int ch,FILE *f);
在usart.c
中重定向
//重定向scanf
int fgetc(FILE *f)
{uint8_t ch=0;HAL_UART_Receive(&huart2,&ch,1,0xffff);return ch;
}//重定向printf
int fputc(int ch,FILE *f)
{uint8_t temp[1]={ch};HAL_UART_Transmit(&huart2,temp,1,2);return ch;
}
由于该实验并未使用到scanf函数,可不必重定向scanf
2.2 定义变量
uint32_t capture_value[3]; /*定义数组变量,存放捕获到的值*/
uint32_t diff_value1; /*计数差值*/
uint32_t diff_value2; /*计数差值*/uint8_t capture_state=0; /*捕获状态:0表示未开始捕获;1表示完成一次捕获;2表示完成两次捕获*/
uint8_t capture_flag=0; /*捕获标志位:0表示未完成,1表示已完成*/
2.3 中断回调函数
/* USER CODE BEGIN 4 */
//回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if (htim==&htim2){switch (capture_state){case 0:{capture_value[0]=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1); /*读取捕获开始时间*/__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING); /*切换为下降沿捕获*/capture_state=1; /*标志完成第一次捕获*/}break;case 1:{capture_value[1]=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1); /*读取第二次捕获时间*/__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING); /*切换为上升沿捕获*/capture_state=2; /*标志完成第二次捕获*/}break;case 2:{capture_value[2]=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1); /*读取捕获结束时间*/HAL_TIM_IC_Stop_IT(htim,TIM_CHANNEL_1); /*停止捕获*/capture_state=0; /*标志完成第三次捕获,重新置0,准备下一轮捕获*/capture_flag=1; /*标志捕获完成*/}break;default:printf("running error!\n");break;}}}
/* USER CODE END 4 */
2.4 main函数
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM2_Init();MX_TIM3_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); /*开启PWM输出*/HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); /*开启输入捕获*//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){if (capture_flag==1) /* 捕获完成 */{printf("\n******capture start******\n");if (capture_value[2] >= capture_value[0]) /* 计时没有溢出 */{diff_value1=capture_value[2]-capture_value[0]; /* 取差值 */}else /* 计时有溢出 */{diff_value1=(0xffffffff+1)+capture_value[2]-capture_value[0]; /* 取差值 */}printf("周期:%.2fms\n",diff_value1/1000.0); /*周期=差值*单次计数时间*/printf("频率:%.2fKHz\n",1000.0/diff_value1);if (capture_value[1] >= capture_value[0]) /* 计时没有溢出 */{diff_value2=capture_value[1]-capture_value[0]; /* 取差值 */}else /* 计时有溢出 */{diff_value2=(0xffffffff+1)+capture_value[1]-capture_value[0]; /* 取差值 */}printf("高电平:%.2fms\n",diff_value2/1000.0); /*高电平时间=差值*单次计数时间*/printf("占空比:%d%%\n",diff_value2*100/diff_value1);printf("******capture over******\n");capture_flag=0; /*清除标志位,准备下一轮捕获*/HAL_Delay(1000);HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); /*开启下一轮捕获*/}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
周期计算公式: T = 计数差值 ∗ 单次计数时间 = 计数差值 ∗ 1 计数频率 = 计数差值 ∗ 1 时钟频率 / 预分频系数 = 计数差值 ∗ 预分频系数 时钟频率 T=计数差值*单次计数时间=计数差值*\frac{1}{计数频率}=计数差值*\frac{1}{时钟频率/预分频系数}=计数差值*\frac{预分频系数}{时钟频率} T=计数差值∗单次计数时间=计数差值∗计数频率1=计数差值∗时钟频率/预分频系数1=计数差值∗时钟频率预分频系数
比如该实验中,时钟频率为64MHz,预分频系数为63(64分频),得到的计数频率为1MHz,即单次计数时间为1us
3 实验现象
用杜邦线连接PA0
和PA6
引脚,打开串口,观察打印情况