此延时用在freertos运行前和运行后 均可以,已在F407和F767上实测.
并且在不可用DWT的芯片上也能自动使用最基本的模拟延时delay_us_simulate。在freertos运行起来以后会自动切换到非堵塞的vTaskDelay上。
注意事项:
关于DWT_Init函数开始的解锁操作,实际F4不需要此操作,因为默认没锁,但加上也没问题。但F7必须进行此操作。
//可根据有没有使用freeftos来决定是否注释掉下面的这个宏定义
#define USE_FREEFTOS 1
#ifdef USE_FREEFTOS
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#endif
#define DWT_CTRL (*(volatile uint32_t *)0xE0001000)
#define DWT_CYCCNT (*(volatile uint32_t *)0xE0001004)
#define DEMCR (*(volatile uint32_t *)0xE000EDFC)
#define DEMCR_TRCENA (1 << 24)
// DWT Lock 相关寄存器
#define DWT_LAR (*(volatile uint32_t *)0xE0001FB0) // Lock Access Register
#define DWT_LSR (*(volatile uint32_t *)0xE0001FB4) // Lock Status Register
static uint8_t dwt_available = 0;
void DWT_Init(void) {
DWT_LAR = 0xC5ACCE55; // Magic key to unlock DWT
__DSB();
__ISB();
// 检查DWT是否可用
if ((DWT_CTRL & 1) == 0) {
DEMCR |= DEMCR_TRCENA;
DWT_CYCCNT = 0;
DWT_CTRL |= 1;
}
dwt_available = (DWT_CTRL & 1) ? 1 : 0;
if (!dwt_available) {printf("API:DWT error");}
}
void delay_us(uint32_t us) {
if (dwt_available) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while ((DWT_CYCCNT - start) < cycles);
}
else {
delay_us_simulate(1);
}
}
void delay_ms(uint32_t ms) {
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
// FreeRTOS未启动,使用DWT延时
for (uint32_t i = 0; i < ms; i++) {
delay_us(1000);
}
}
else {
// FreeRTOS已启动,使用vTaskDelay
vTaskDelay(pdMS_TO_TICKS(ms));
}
}
// 微秒级延时(阻塞式,适用于168MHz HCLK)
void delay_us_simulate(uint16_t us) {
volatile uint32_t cycles = us * (168 / 4); // 168MHz下,每个循环约4周期
while (cycles--);
}
main.c的main()中,基本的初始化完成后:
DWT_Init();
printf("APP:start1:%d,%d\n",API_TIM7_Count,SystemCoreClock);
delay_ms(200);
delay_ms(200);
printf("APP:start2:%d\n",API_TIM7_Count);
F407上运行结果:
[17:15:19.452]APP:start1:1,168000000
[17:15:20.191]APP:start2:403
解释:
API_TIM7_Count是TIM7定时器的计数值,1ms增加一次。即以上表明,延时400ms时TIM7定时器中断了406次。还是比较准确的。