最近利用HAL库的F4低功耗模式(STOP),利用CAN总线来唤醒F4MCU 先吐槽HAL库的坑还是有点多,在清除中断的顺序方面还是做的不够好。
大体思路:利用CAN总线RX引脚 触发外部中断来激活MCU。
由于CAN唤醒实验用到了基本定时器TIM7,做个100ms的中断检测CAN总线是否有信号。
这里涉及到了一个先进中断后进中断的问题:
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
{
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
/* Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}
函数中的 使能TIM 更新中断和使能外设函数的顺序决定了是先中断一次还是先计数
HAL库中的是先进入一次中断。
配置CAN的函数这里就不说了,见上一个帖子
方向是:如果CAN总线没有信号进来,则认为总线空闲,进入低功耗。
初始化先初始化CAN TIM7 开启更新中断直接进入一次中断,在中断中会检测CAN的FIFO的邮箱中是否有报文,
如果有报文则说明CAN总线处于ongoing状态,否则则进入低功耗模式,这里我们直接在TIM7的CALLBACK中进入低功耗模式。
进入低功耗函数如下:
void STOP_MODE_ENTER(void)
{
printf("\r\nSTOP模式");
HAL_TIM_Base_Stop_IT(&htim7);//关闭TIM7定时器
HAL_CAN_DeInit(&hcan1);//失能 CAN1
HAL_CAN_DeInit(&hcan2);//失能CAN2
__HAL_RCC_GPIOH_CLK_DISABLE();//失能GPIO时钟
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOC_CLK_DISABLE();
__HAL_RCC_GPIOA_CLK_DISABLE();
GPIO_InitTypeDef GPIO_InitStruct1;
GPIO_InitStruct1.Pin = GPIO_PIN_12;
GPIO_InitStruct1.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct1.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct1);
HAL_NVIC_SetPriority(EXTI15_10_IRQn,3,0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_12);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);
SystemClock_Config();
MX_GPIO_Init();
HAL_GPIO_DeInit(GPIOB,GPIO_PIN_12);
HAL_NVIC_DisableIRQ(EXTI15_10_IRQn);
MX_CAN1_Init();
MX_CAN2_Init();
printf("\r\nCAN2 ERROR IS %d",HAL_CAN_GetError(&hcan2));
printf("\r\nCAN1 ERROR IS %d",HAL_CAN_GetError(&hcan1));
printf("\r\n启动CAN总线");
__HAL_TIM_ENABLE(&htim7);
__HAL_TIM_ENABLE_IT(&htim7, TIM_IT_UPDATE);
}
函数中在进入低功耗模式之前先关闭GPIO的时钟,如果追求极限低功耗可以把所有的外设都失能,GPIO全部反初始化。
在开启外部中断时一定要先清理一次中断标志位,这个是为了意外没有清除EXTI的PR 导致程序进不去停止模式,手册上有详细说明。
必须先清除外部中断标志位和RTC的闹钟标志位否则程序将跳过停止模式。
关键的函数:
void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry)
Regulator选择PWR_LOWPOWERREGULATOR_ON :让电压调节器处于低功耗模式
和STOPEntry:PWR_STOPENTRY_WFI : 外部中断触发
再选择外部晶振
之后开启CAN 以及其他外设 最后开启定时器。
这里我们选择先计数再进入更新中断的选择是因为在第一个上升沿到来时,这一帧CAN数据要舍弃,在下一帧数据进行读取。
在外部中断到来时,切记要在中断回调函数中失能外部中断,因为CAN总线上的信号是一段高低电平,否则会一直触发中断。
HAL_GPIO_DeInit(GPIOB,GPIO_PIN_12);//失能GPIO12
HAL_NVIC_DisableIRQ(EXTI15_10_IRQn);失能15-10中断线
下面来看一下定时器中断函数的回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==&htim7)
{
printf("\r\ntim7 IS %d",((&hcan2)->Instance->RF1R & 0x03) );
if (((&hcan2)->Instance->RF1R & 0x03) == RESET)//如果邮箱没有报文
{
STOP_MODE_ENTER();
}
}
}
简单的判断邮箱值是否为零,这里需要注意的是我选择的波特率是500kps 取样为90%
如果数据的传输速率不够快的化这个邮箱值会一直为零,因为在CAN中断函数中已经处理了。处理完毕之后会清零,就不会进入停止模式。
如果有问题 欢迎滴滴我,通过此次的低功耗,通过操作寄存器的方式比HAL库更便捷,但是HAL库降低了门槛,更快速上手。
|