一、需求概述
实现使用外部输入的 PWM 触发内部定时器输出自定义的 m 序列,涉及tim.c
和main.c
文件的编写与配置,同时解决代码中函数重定义的问题。
二、代码关键部分分析
(一)tim.c
文件
1. 定时器初始化
- 功能:完成 TIM2 定时器的初始化配置,将其设置为外部触发模式(外部触发源为 ETR)。
- 关键代码:
c
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void) {
// 定时器参数设置
htim2.Instance = TIM2;
htim2.Init.Prescaler = 1000; // 根据实际情况调整
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000; // 根据实际情况调整
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
// 外部触发配置
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
sSlaveConfig.InputTrigger = TIM_TS_ETRF;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;
sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig) != HAL_OK) {
Error_Handler();
}
// 主模式配置
TIM_MasterConfigTypeDef sMasterConfig = {0};
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
// 使能定时器中断
HAL_TIM_Base_Start_IT(&htim2);
}
- 注意事项:
Prescaler
和Period
参数需根据实际的外部 PWM 频率和想要的定时器输出序列的时间间隔进行调整。
2. 底层硬件初始化和反初始化
- 功能:配置 TIM2 的时钟、GPIO 引脚和中断。
- 关键代码:
c
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) {
if(tim_baseHandle->Instance==TIM2) {
// 使能TIM2时钟
__HAL_RCC_TIM2_CLK_ENABLE();
// 配置GPIO引脚
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF14_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置TIM2中断
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) {
if(tim_baseHandle->Instance==TIM2) {
// 禁用TIM2时钟
__HAL_RCC_TIM2_CLK_DISABLE();
// 反初始化GPIO引脚
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0);
// 禁用TIM2中断
HAL_NVIC_DisableIRQ(TIM2_IRQn);
}
}
3. 中断处理函数(可能导致重定义问题)
- 功能:处理 TIM2 的中断。
- 关键代码:
c
void TIM2_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim2);
}
(二)main.c
文件
1. m 序列生成和输出函数
- 功能:生成和输出 m 序列。
- 关键代码:
c
// m序列生成函数
uint8_t generate_mbo(void) {
static uint8_t m = 0x01;
uint8_t feedback;
feedback = ((m >> 0) ^ (m >> 2) ^ (m >> 3) ^ (m >> 4)) & 0x01;
m = (m >> 1) | (feedback << 7);
return m & 0x01;
}
// m序列输出函数
void m_seq(void) {
uint8_t mbo = generate_mbo();
if (mbo) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
}
__asm__ volatile("nop");
__asm__ volatile("nop");
__asm__ volatile("nop");
}
2. 主函数和中断回调函数
- 功能:初始化系统和外设,在定时器溢出中断发生时调用 m 序列输出函数。
- 关键代码:
c
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2);
while (1) {
// 主循环
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
m_seq();
}
}
三、常见问题及解决办法
(一)函数重定义问题
- 问题描述:出现
L6200E: Symbol TIM2_IRQHandler multiply defined (by stm32g4xx_it.o and tim.o)
错误,表明TIM2_IRQHandler
函数被重复定义。 - 可能原因:在多个文件中重复定义,或者自动生成代码与手动添加代码冲突。
- 解决办法:
- 方法一:移除
tim.c
文件中的TIM2_IRQHandler
定义,将中断处理逻辑移到stm32g4xx_it.c
文件中。 - 方法二:使用条件编译,在
tim.c
文件中添加条件编译,同时在stm32g4xx_it.c
文件里注释掉或者删除TIM2_IRQHandler
函数的定义。
- 方法一:移除
四、复习要点总结
- 理解定时器初始化的各个参数的含义和作用,能够根据实际需求调整
Prescaler
和Period
参数。 - 掌握定时器外部触发模式的配置方法,包括
TIM_SlaveConfigTypeDef
结构体的使用。 - 熟悉 GPIO 引脚和中断的初始化和反初始化操作。
- 理解 m 序列的生成和输出原理,掌握相关函数的编写。
- 学会处理函数重定义问题,避免代码中出现类似错误。