已經實現 ,2018年11月17日11:56:42,具體 如下:
第一步 : 修改 void vPortSetupTimerInterrupt( void ) 函數 ,修改原來的 systick 定時器初始化 改為 RTC 初始化
void vPortSetupTimerInterrupt( void ) { NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /* Enable PWR and BKP clocks */ /* PWR時鍾(電源控制)與BKP時鍾(RTC后備寄存器)使能 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Allow access to BKP Domain */ /*使能RTC和后備寄存器訪問 */ PWR_BackupAccessCmd(ENABLE); RCC_LSICmd(ENABLE); /* 使能內部32.768K時鍾 */ /* 等待內部32.768K時鍾就緒 */ while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET); /* 選擇內部32.768K時鍾為RTC時鍾 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* Set RTC prescaler: set RTC period to 1 millisecond */ /* suozhang,使用外部32.768K晶振,設置32分頻,計數1024次為1S,2018年11月15日09:28:52 */ RTC_SetPrescaler( 40 ); /* Enable the RTC Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configKERNEL_INTERRUPT_PRIORITY; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Enable the RTC Alarm Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configKERNEL_INTERRUPT_PRIORITY; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //鬧鍾中斷接到第17線外部中斷 EXTI_ClearITPendingBit(EXTI_Line17); EXTI_InitStructure.EXTI_Line = EXTI_Line17; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* 使能電源管理時鍾 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR , ENABLE); /* Enable RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* 使能RTC根據選擇的時鍾源以及分頻值的設置產生的定時中斷,當做FreeRTOS時鍾源 */ RTC_ITConfig(RTC_IT_SEC, ENABLE); RTC_WaitForLastTask(); }
第二步:增加 RTC 中斷服務函數
void RTCAlarm_IRQHandler( void ) { //必須加上這個中斷服務函數,否則RTC鬧鍾喚醒后,只能執行中斷服務函數,suozhang,2018年11月15日13:47:34 } void RTC_IRQHandler( void ) { if(RTC_GetITStatus(RTC_IT_SEC)!= RESET)//RTC定時中斷 { RTC_WaitForLastTask(); RTC_ClearITPendingBit(RTC_IT_SEC); //清除RTC定時中斷 /* The SysTick runs at the lowest interrupt priority, so when this interrupt executes all interrupts must be unmasked. There is therefore no need to save and then restore the interrupt mask value as its value is already known - therefore the slightly faster vPortRaiseBASEPRI() function is used in place of portSET_INTERRUPT_MASK_FROM_ISR(). */ vPortRaiseBASEPRI(); { /* Increment the RTOS tick. */ if( xTaskIncrementTick() != pdFALSE ) { /* A context switch is required. Context switching is performed in the PendSV interrupt. Pend the PendSV interrupt. */ portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } vPortClearBASEPRIFromISR(); } if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//鬧鍾中斷 { RTC_WaitForLastTask(); RTC_ClearITPendingBit(RTC_IT_ALR); //清鬧鍾中斷 RTC_WaitForLastTask(); EXTI_ClearITPendingBit(EXTI_Line17); if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET) { RTC_WaitForLastTask(); PWR_ClearFlag(PWR_FLAG_WU); //清自動喚醒中斷( AWU ) } } }
第三步:因為要實現 tickless 模式,因此要實現 void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) 這個函數接口
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; TickType_t xModifiableIdleTime; uint32_t exitTime = 0; /* 停止RTC產生 1ms 的定時中斷 */ RTC_ITConfig(RTC_IT_SEC, DISABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Calculate the reload value required to wait xExpectedIdleTime tick periods. -1 is used because this code will execute part way through one of the tick periods. */ //根據參數xExpectIdleTime來計算滴答定時器的重載值,進入低功耗之后,計時由滴答定時器計算。 ulReloadValue = xExpectedIdleTime - 1UL ; if( ulReloadValue > ulStoppedTimerCompensation ) { ulReloadValue -= ulStoppedTimerCompensation;//上面已經停止RTC了,下面一直到啟動RTC鬧鍾的 程序運行時間補償,小於1mS,因此這里為0 } /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ __disable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */ if( eTaskConfirmSleepModeStatus() == eAbortSleep ) { /* 再次啟動 RTC產生 1ms 的定時中斷*/ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Re-enable interrupts - see comments above __disable_irq() call above. */ __enable_irq(); } else { /* RTC 計數器清0 */ RTC_SetCounter( 0 ); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* RTC 計數器清0 */ RTC_SetAlarm( ulReloadValue ); // 設置經過ulReloadValue 計時后,產生鬧鍾 /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); RTC_ClearITPendingBit(RTC_IT_ALR); //清鬧鍾中斷 /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); PWR_ClearFlag(PWR_FLAG_WU); //清自動喚醒中斷( AWU ) /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* 啟動 RTC 鬧鍾中斷. */ RTC_ITConfig(RTC_IT_ALR, ENABLE); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can set its parameter to 0 to indicate that its implementation contains its own wait for interrupt or wait for event instruction, and so wfi should not be executed again. However, the original expected idle time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { __enable_irq(); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE);//進入停止模式 } configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); /* 關閉 RTC 鬧鍾中斷. */ RTC_ITConfig(RTC_IT_ALR, DISABLE); /* 再次啟動 RTC產生 1ms 的定時中斷*/ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Re-enable interrupts to allow the interrupt that brought the MCU out of sleep mode to execute immediately. see comments above __disable_interrupt() call above. */ __enable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); /* Disable interrupts again because the clock is about to be stopped and interrupts that execute while the clock is stopped will increase any slippage between the time maintained by the RTOS and calendar time. */ __disable_irq(); __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); /* 讀取RTC計數值,用於補償真正的系統時間,有可能其他事件喚醒,而不是鬧鍾喚醒,2018年11月16日11:07:31,suozhang */ exitTime = RTC_GetCounter(); /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); if( exitTime ) vTaskStepTick(exitTime-0); //補償真正的系統時間,進入鬧鍾定時之前,已經把計數器清0 因此這里減0 /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard value. */ /* Exit with interrpts enabled. */ __enable_irq(); } }
最后 有 兩個 函數 說明一下:
configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); // 進入低功耗之前 要執行的函數,用戶可自定義!
configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); // 退出低功耗 第一個 執行的函數,用戶可自定義!
最后測試一下: 測試代碼 如下
void vLedTask( void *pvParameters ) { for(;;) { //PC13 LED1 bsp_LedToggle(1); vTaskDelay( 1000*5 ); } }
功耗測試結果 如下: 5S 高電流 即 LED 點亮時間, 5S低電流 是 13uA 正好達到裸機 進入 STOP 模式 的電流!