一、TIM通道捕获配置函数
1. 编码器相关函数
HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
功能: 启动定时器的编码器接口模式
参数:
htim:定时器句柄指针
Channel:通道选择
**应用:**用于旋转编码器的位置计数
__HAL_TIM_GET_COUNTER(HANDLE)
功能: 获取定时器的当前计数值
参数: 定时器句柄
**返回值:**当前计数器值(16位或32位)
__HAL_TIM_IS_TIM_COUNTING_DOWN(HANDLE)
功能: 检查定时器是否正在向下计数
参数: 定时器句柄
**返回值:**TRUE(向下计数)或FALSE(向上计数)
__HAL_TIM_SET_COUNTER(HANDLE, COUNTER)
功能: 设置定时器的计数值
参数:
__HANDLE__:定时器句柄
__COUNTER__:要设置的计数值
2. 输入捕获相关函数
HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
功能: 启动输入捕获模式
参数:
htim:定时器句柄
Channel:输入捕获通道
HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
功能: 停止输入捕获模式
参数:
htim:定时器句柄
Channel:输入捕获通道
HAL_TIM_ReadCapturedValue(const TIM_HandleTypeDef *htim, uint32_t Channel)
功能: 读取输入捕获的值
参数:
htim:定时器句柄
Channel:通道选择
**返回值:**捕获到的计数值
3. 标志位操作宏
__HAL_TIM_GET_FLAG(HANDLE, FLAG)
功能: 获取定时器状态标志
参数:
__HANDLE__:定时器句柄
__FLAG__:标志位
__HAL_TIM_CLEAR_FLAG(HANDLE, FLAG)
功能: 清除定时器状态标志
参数:
__HANDLE__:定时器句柄
__FLAG__:要清除的标志位
二、编码器模式实验
1. 实验现象
当旋转编码器时,串口会实时显示转动方向和计数值变化(如"顺时针移动"或"逆时针移动");当编码器停止转动超过2秒后,串口会持续显示"编码器静止超过2s"的提示信息。
2. 示例代码
TIM初始化
/**
* @brief TIM3初始化函数,配置为编码器模式
*/
void MX_TIM3_Init(void)
{
TIM_Encoder_InitTypeDef sConfig = {0}; // 编码器配置结构体
TIM_MasterConfigTypeDef sMasterConfig = {0}; // 主模式配置结构体
// 定时器基础参数配置
htim3.Instance = TIM3; // 使用TIM3定时器
htim3.Init.Prescaler = 4-1; // 预分频器,4分频
htim3.Init.CounterMode = TIM_COUNTERMODE_UP; // 计数器模式:向上计数
htim3.Init.Period = 65535; // 自动重装载值,16位最大值
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频:不分频
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 自动重装载预装载禁止
// 编码器接口配置
sConfig.EncoderMode = TIM_ENCODERMODE_TI12; // 编码器模式:在TI1和TI2边沿计数
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; // 通道1极性:上升沿
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; // 通道1输入:直接输入
sConfig.IC1Prescaler = TIM_ICPSC_DIV1; // 通道1预分频:不分频
sConfig.IC1Filter = 0; // 通道1滤波器:无滤波
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; // 通道2极性:上升沿
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; // 通道2输入:直接输入
sConfig.IC2Prescaler = TIM_ICPSC_DIV1; // 通道2预分频:不分频
sConfig.IC2Filter = 0; // 通道2滤波器:无滤波
// 初始化编码器接口
if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK)
{
Error_Handler(); // 初始化失败,进入错误处理
}
// 主模式配置
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; // 主模式触发输出:复位
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; // 主从模式:禁止
// 配置主模式同步
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler(); // 配置失败,进入错误处理
}
}
主函数
// 全局变量定义
int16_t cnt = 0; // 当前编码器计数值
int16_t last = 0; // 上一次编码器计数值
uint32_t time = 0; // 时间戳,用于超时检测
uint8_t sta = 0; // 状态标志位
/**
* @brief 主函数
*/
int main(void)
{
// 系统初始化
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
// 外设初始化
MX_GPIO_Init(); // 初始化GPIO
MX_TIM3_Init(); // 初始化TIM3编码器
MX_USART1_UART_Init(); // 初始化USART1串口
// 启动编码器接口
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
// 主循环
while (1)
{
// 读取当前编码器计数值
cnt = __HAL_TIM_GET_COUNTER(&htim3);
// 检测编码器是否发生移动
if(cnt != last)
{
sta = 0; // 重置静止状态标志
time = HAL_GetTick(); // 记录当前时间戳
int16_t diff = cnt - last; // 计算计数值变化量
// 根据计数方向判断旋转方向并输出
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim3))
printf("逆时针移动%d\r\n", diff); // 向下计数:逆时针
else
printf("顺时针移动%d\r\n", diff); // 向上计数:顺时针
last = cnt; // 更新上一次计数值
}
else
{
// 编码器静止状态处理
if(HAL_GetTick() - time > 2000 && sta == 0)
sta = 1; // 设置静止状态标志
printf("编码器静止超过2s"); // 输出静止提示
}
}
}
三、超声波测距实验
1. 实验现象
系统会周期性地通过串口输出当前测量的距离值(单位:米),当超声波传感器前方物体距离发生变化时,输出的距离数值会相应变化,准确反映出物体与传感器之间的实时距离,测量周期为500毫秒。
2. 示例代码
GPIO初始化
/**
* @brief GPIO初始化函数
* 配置超声波模块相关引脚:
* - PA15: 超声波TRIG触发信号输出引脚
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIO端口时钟 */
__HAL_RCC_GPIOD_CLK_ENABLE(); // 使能GPIOD时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
/* 配置GPIO引脚初始输出电平 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // PA15初始化为低电平
/* 配置PA15引脚为超声波TRIG信号输出 */
GPIO_InitStruct.Pin = GPIO_PIN_15; // 引脚15
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速输出
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化GPIO配置
}
TIM初始化
/**
* @brief TIM2定时器初始化函数
* 配置为输入捕获模式,用于超声波测距的时间测量
*/
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0}; // 时钟源配置结构体
TIM_MasterConfigTypeDef sMasterConfig = {0}; // 主模式配置结构体
TIM_IC_InitTypeDef sConfigIC = {0}; // 输入捕获配置结构体
// 定时器基础参数配置
htim2.Instance = TIM2; // 使用TIM2定时器
htim2.Init.Prescaler = 72-1; // 预分频器72分频,1MHz计数频率(72MHz/72)
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
htim2.Init.Period = 65535; // 自动重装载值,16位最大值
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频:不分频
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁止自动重装载预装载
// 初始化定时器基础配置
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
// 配置时钟源为内部时钟
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
// 初始化输入捕获模式
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
// 配置主模式
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; // 主模式触发输出:复位
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; // 禁止主从模式
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
// 配置通道1输入捕获参数(用于测量回波下降沿)
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; // 下降沿捕获
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; // 间接TI输入
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 输入捕获不分频
sConfigIC.ICFilter = 0; // 无输入滤波
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 配置通道2输入捕获参数(用于测量回波上升沿)
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; // 上升沿捕获
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接TI输入
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
}
主函数
// 超声波测距相关变量定义
uint32_t start_time = 0; // 回波开始时间(上升沿捕获值)
uint32_t end_time = 0; // 回波结束时间(下降沿捕获值)
float pluse_width = 0.0f; // 回波脉冲宽度(单位:秒)
float distance = 0.0f; // 计算得到的距离值(单位:米)
/**
* @brief 超声波TRIG触发信号生成函数
* 产生一个10us的高电平脉冲触发超声波模块
*/
void CS_TRIG_Start(void)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET); // TRIG引脚置高
for(uint16_t i = 0; i < 90; i++); // 短暂延时约10us
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // TRIG引脚置低,结束触发
}
/**
* @brief 主函数 - 超声波测距应用
*/
int main(void)
{
// 系统初始化
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
// 外设初始化
MX_GPIO_Init(); // 初始化GPIO(TRIG引脚)
MX_TIM2_Init(); // 初始化TIM2输入捕获
MX_USART1_UART_Init(); // 初始化USART1串口
// 主循环 - 持续进行距离测量
while (1)
{
// 初始化定时器计数器
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清零TIM2计数器
// 启动输入捕获通道
HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_1); // 启动通道1(下降沿捕获)
HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_2); // 启动通道2(上升沿捕获)
// 发送超声波触发脉冲
CS_TRIG_Start(); // 产生10us触发信号
// 等待回波上升沿(通道2捕获标志)
while(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_CC2) == RESET); // 阻塞等待上升沿
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_CC2); // 清除捕获标志
start_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2); // 读取上升沿时间
// 等待回波下降沿(通道1捕获标志)
while(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_CC1) == RESET); // 阻塞等待下降沿
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_CC1); // 清除捕获标志
end_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 读取下降沿时间
// 停止输入捕获
HAL_TIM_IC_Stop(&htim2, TIM_CHANNEL_1); // 停止通道1捕获
HAL_TIM_IC_Stop(&htim2, TIM_CHANNEL_2); // 停止通道2捕获
// 计算距离
uint32_t delta = end_time - start_time; // 计算时间差(微秒数)
pluse_width = (float)delta * 1e-6f; // 转换为秒
distance = pluse_width * 340.0f / 2.0f; // 计算距离(声速340m/s,往返除以2)
// 输出测量结果
printf("当前距离:%.2f m\r\n", distance); // 串口输出距离值
// 延时500ms后进行下一次测量
HAL_Delay(500);
}
}