13.增改源代码,实现LED的控制
初学STM FOC或者需要尽快应用到工程项目中,在电机能大致驱动运转时,需要加入其它交互的接口方式,使得能更容易控制电机和判断电机状态。这时,可以加入LED灯的闪烁状态,加入通讯接口并拟定通讯协议实现控制和监控。
本文主要介绍在workbench生成好的代码中,直接添加LED控制的代码,来表达电机当前的运行状态。我会用到一个PA8引脚控制LED,用TIM3来产生LED闪烁的时钟频率,根据电机的STM结构体来判断电机状态。
1.使能LED控制接口
//初始化PB1为输出.并使能时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_8; //PA8
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET); //PA8置0,默认初始化后灯灭
}
初始化LED灯的函数调用很简单
2.使能TIM3作为时基
使能TIM3时,我只用了一个函数来实现定时时间的设定,这样方便调用和修改。
定时器初始化函数在main中LED的初始化后面调用就行。
我设置的是100ms时基:TIM3_Init(1000-1,8400-1); //定时器3初始化,定时器时钟为84M,分频系数为8400-1,
其实,如果单是点亮LED的闪烁,完全可以加到电机控制的中频任务函数块中去。但我后续还会用到TIM时基,就使能TIM3来做LED的定时控制了。
//通用定时器3中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!(定时器3挂在APB1上,时钟为HCLK/2)
void TIM3_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //通用定时器3
TIM3_Handler.Init.Prescaler=psc; //分频系数
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM3_Handler.Init.Period=arr; //自动装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
HAL_TIM_Base_Init(&TIM3_Handler);
HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE
}
在stm32f3xx_hal_msp.c文件中的
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)函数里加入了这段代码来开启TIM3的中断
if(htim_base->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3时钟
HAL_NVIC_SetPriority(TIM3_IRQn,1,3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM3_IRQn); //开启ITM3中断
}
3.TIM3中断服务完成指定闪烁
这段代码是,TIM3中断函数的回调、LED闪烁控制的结构体定义、TIM3实际的中断服务函数、设置LED闪烁规律的函数。
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler);
}
typedef struct
{
u8 uDelay100ms; //亮灭间隔时间
u8 uLightOnNb; //点亮次数
u8 uLightOffNb; //熄灭间隔
}LightCtr;
LightCtr LightCtruser={5,1,0};
//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
static u8 LedFlg=0; //这里其实可以读PA8的寄存器来判断亮灭状态
static LightCtr LightCtrCnt={0,0,0}; //一个静态变量 记录次数信息
LightCtrCnt.uDelay100ms++;
if(LightCtrCnt.uDelay100ms>=LightCtruser.uDelay100ms) //这个判断来执行闪烁快慢
{
LightCtrCnt.uDelay100ms=0;
if(LightCtruser.uLightOffNb==0) //如果没有熄灭间隔时间,那就得一直闪烁下去
LightCtrCnt.uLightOnNb=0;
if(LightCtrCnt.uLightOnNb<LightCtruser.uLightOnNb) //判断闪烁次数是否达够指定的次数
{
if(LedFlg)
{
LightCtrCnt.uLightOnNb++; //点亮一次 则闪烁次数增加一次
LedFlg=0;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET);
}
else
{
LedFlg=1;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET);
}
}
else //达够闪烁次数后 进行一定延时的熄灭
{
LightCtrCnt.uLightOffNb++; //熄灭的延时计数
LedFlg=0;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET);
//达够熄灭的延时计数之后 清零LED闪烁控制的变量 开始下个回合
if(LightCtrCnt.uLightOffNb>=LightCtruser.uLightOffNb)
{
LightCtrCnt.uLightOffNb=0;
LightCtrCnt.uLightOnNb=0;
}
}
}
}
}
void SetFlashingSpeed( u8 uDelay100ms,u8 uLightOnNb,u8 uLightOffNb)
{
LightCtruser.uDelay100ms=uDelay100ms;
LightCtruser.uLightOnNb=uLightOnNb;
LightCtruser.uLightOffNb=uLightOffNb;
}
4.根据STM来判断LED闪烁频率
/*
指示灯闪烁判断 根据有无故障来解析闪烁次数和频率
*/
void LightTwinkle(void)
{
uint16_t hFaultOccurred;
hFaultOccurred=STM[0].hFaultOccurred|STM[1].hFaultOccurred; //按位与 不论哪个电机有故障 都报告
if(hFaultOccurred)
{
u8 Twinkle=0;
while(hFaultOccurred) //按照最高位的优先级来闪烁指示灯
{
Twinkle++; //移位累加 获取到最高位的错误 值 ,闪烁次数 即哪个bit位的故障
hFaultOccurred>>=1;
}
if((STM[0].hFaultNow|STM[1].hFaultNow)==0) //当前没有故障了,错误已经解除
SetFlashingSpeed(2,Twinkle,5); //故障解除 则熄灭状态延长节拍
else
SetFlashingSpeed(2,Twinkle,2); //故障保持 则熄灭节拍短促
}
else
SetFlashingSpeed(5,1,0);
}
这个函数在主函数的while(1)里面被调用就可以了。
做如上的增改之后,LED正常情况下会0.5s的闪烁;如果有故障产生,且故障没有解除,则LED快速闪烁指定的次数后,短暂熄灭;如果故障已经消除,但没通过人为确认,则闪烁后是一段长时间的熄灭。这样,与ST FOC 代码中对故障的Now 和hFaultOccurred 思路一致。
ST的开源方案–空间矢量控制,驱动永磁同步电机的学习及分享计划CSDN链接