曲轴凸轮轴位置信号对于发动机电控十分重要,通过它们可以确定曲轴当前转速,以及判定x缸活塞到达上止点(判缸),是喷油和点火的重要依据。本次实验我们尝试基于STM32F103的最小系统板,来模拟脉冲型曲轴和凸轮轴信号。
曲轴每旋转2圈,凸轮轴旋转1圈,完成4次点火(1-3-4-2)。4缸汽油机常见的曲轴信号为60-2,即一周58个周期脉冲加两个周期低电平;4缸汽油机常见凸轮轴信号为每转4个脉冲(对应x缸活塞上止点,曲轴换向),且在一缸脉冲后30°额外添加一个脉冲,作为1缸信号(类似于磁场定向中的Z脉冲信号)。实际的曲轴与凸轮轴通过正时链条(或皮带)连接,具有同步性。
本次实验采用两个定时器(定时器1,定时器2),一个定时器中断服务(定时器1更新中断)。定时器1通过PWM(硬件动作,节省计算资源)来模拟曲轴脉冲信号,脉冲计数则在定时器1中断服务中完成。定时器2通过PWM来模拟凸轮轴脉冲信号,而脉冲计数则在定时器1中断服务中完成。
首先给出两个定时器的配置,
void TIMER1_INIT(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//输出PWM需要配置为复用推挽
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM1);//定时器1启用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//时基单元配置
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;//向下计数
TIM_TimeBaseInitStructure.TIM_Period = 100-1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//初始化其他未设置的变量
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式(高有效)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);//(第1通道)
TIM_ClearFlag(TIM1, TIM_FLAG_Update);//清中断标志位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//开定时器中断
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清定时器1中断标志位
TIM_Cmd(TIM1, DISABLE);//关定时器
TIM_CtrlPWMOutputs(TIM1, DISABLE);//关pwm主使能(高级定时器独有)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);//配置中断通道
}
void TIMER2_INIT(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//定时器1启用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//时基单元配置
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 200-1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 1080-1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//初始化其他未设置的变量
// TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式(高有效)
// TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
// TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;//关下路互补,做单(上)桥臂斩波
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//(第1通道)
TIM_SetCounter(TIM2, 197);//同步预装
TIM_Cmd(TIM2, DISABLE);//等待软件开定时器2
}
为了保证两个定时的PWM脉冲尽量对齐,在主程序中依次开启两个定时器,
int main()
{
TIMER1_INIT();
TIMER2_INIT();
TIM_Cmd(TIM1, ENABLE);//曲轴信号
TIM_Cmd(TIM2, ENABLE);//凸轮轴信号
TIM_SetCompare1(TIM1, 0);
TIM_SetCompare1(TIM2, 0);
while(1)
{
}
}
考虑到曲轴和凸轮轴信号并非单纯的脉冲,其存在大周期,所以两定时器比较器的装载值在定时器1中断服务中根据脉冲计数值来给定。
这里有一些需要注意的地方:
PWM是硬件动作,软件对比较器装载值的实时修改有时是“不及时”的。具体来讲,以向上计数,上溢中断为例,当计数器周期为100,比较器装载50,我们将得到0.5占空比的PWM,当完成58个脉冲输出后,我们需要在下个中断服务中将比较器装载值改为1,或直接关PWM,这里就体现的软件“不及时”的问题,如下图
可以看出,原本是完全低电平的最后两个周期出现了两个窄脉冲,这是因为由上溢中断到来至配置比较器装置(或关PWM),程序执行需要时间,而在这段时间中比较器值大于计数器,所以PWM会出高电平。考虑到上述问题,本次实验采用向下计数,这样当更新(下溢)中断到来时,比较器值小于计数器,PWM不会出高电平。如下图所示
对于曲轴信号,脉冲计数器值58,59和118,119为全周期低电平,大于119则归零复位(曲轴转2圈,凸轮轴转1圈,进入下一周期)。
凸轮轴信号通过通过定时器2的PWM来模拟,曲轴完成两个60-2,凸轮轴完成一个4脉冲,+一个1后30°单脉冲,所以定时器1的PWM频率时定时器2的30倍,通过设置分频及计数器周期可以对定时器2的PWM周期灵活调整;通过定时器2计数器的预装值来微调其相位。最后对于1缸上止点后30°的单脉冲,可通过在中断服务中修改定时器2比较器装载值来实现,如下图,
这里30°可以通过曲轴脉冲数来确定,凸轮轴30°对应曲轴60°,即第10脉冲,至于凸轮轴脉冲宽度,可以自定。中断服务如下
void TIM1_UP_IRQHandler(void)//定时器1中断服务函数
{
static uint16_t num;//曲轴脉冲计数器
if(TIM_GetITStatus(TIM1, TIM_IT_Update)==SET)//查定时器1中断标志位
{
TIM_CtrlPWMOutputs(TIM1, ENABLE);//开pwm主使能(高级定时器独有)
num++;
if(num>=10&&num<=12)
{
TIM_SetCompare1(TIM2, 100);
}
else if(num==58|num==59|num==118|num==119)
{
TIM_SetCompare1(TIM2, 20);
TIM_CtrlPWMOutputs(TIM1, DISABLE);//关pwm主使能(高级定时器独有)
}
else if(num>119)
{
num = 0;
TIM_SetCompare1(TIM2, 20);
TIM_SetCompare1(TIM1, 50);
}
else
{
TIM_SetCompare1(TIM2, 20);
TIM_SetCompare1(TIM1, 50);
}
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清定时器1中断标志位
}
}
最终效果如下,
介于本人水平有限,本次实验目的仅仅是抛砖引玉,希望大家能够贴上更简单的代码。