在MCU资源不足时,我们经常会用普通IO模拟串行时序,例如:i2c,uart等。此时,就会需要一个比较精确一点的延时来提供通讯成功率。STM32MCU调试单元提供了一个时钟周期计数器,利用该计数器我们可以实现一个高精度的延时计时器。
概念:频率fclk(cpu时钟)与时间的关系,t=1/fclk,例如:系统时钟为72MHZ,那么一个时钟周期的执行时间为:1/72(us),也就是72个时钟周期的执行时间为1us,这样就可以实现一个us级别的高精度延时。假设,uart的波特率为9600,那么每接收一位数据之间的间隔为1/9600≈104us,那么,只需延时104us*72个时钟周期就可以精确的接收串口数据,共地情况下,误码率基本上为0。
下面为delay模块的源文件,仅供参考。
一 delay.h
#ifndef _DELAY_H
#define _DELAY_H
#include "stm32f1xx.h"
#define CYCCNT_US (SystemCoreClock/1000000)
void delay_init(void);
uint32_t get_cyccnt(void);
void udelay(uint32_t us);
void mdelay(uint32_t ms);
void sdelay(uint32_t s);
/* 延时指定个时钟周期数 */
void delay_cyc(uint32_t cyc);
#endif
二、delay.c
#include "delay.h"
#define DWT_CR *(uint32_t *)0xE0001000 //数据观察点和跟踪控制寄存器
#define DWT_CYCCNT *(uint32_t *)0xE0001004 //DWT当前PC采样器周期计数寄存器
#define DEM_CR *(uint32_t *)0xE000EDFC //调试异常和监控控制寄存器
#define DBGMCU_CR *(uint32_t *)0xE0042004 //MCU调试模块控制寄存器,详细内容参考《stm32中文参考手册》调试支持(DBG)章节,747页
#define DEM_CR_TRCENA (1 << 24) //DEM_CR调试异常和监控控制寄存器的Bit24,该位必须为1,使能调试和跟踪模块的使用
#define DWT_CR_CYCCNTENA (1 << 0) //置为DWT_CR控制寄存器的CYCCNTENA位,使能DWT_CYCCNT计数器
void delay_init(void)
{
DEM_CR |= (uint32_t)DEM_CR_TRCENA; /* Enable Cortex-M3's DWT CYCCNT reg. 使能调试和跟踪模块的使用 */
DWT_CYCCNT = (uint32_t)0u; //初始化当前PC采样器周期计数器的计数值为0
DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA; //使能DWT_CYCCNT计数器。如果不使能,则计数器不执行计
//数操作,因此不会产生PC采样或CYCCNTENA事件。在正常
//使用时,CYCCNT计数器应由调试器初始化为0。
}
uint32_t get_cyccnt(void)
{
return (uint32_t)(DWT_CYCCNT);
}
void udelay(uint32_t us)
{
uint32_t udly = (uint32_t)(DWT_CYCCNT) + us*CYCCNT_US;
while((uint32_t)(DWT_CYCCNT) < udly);
}
void mdelay(uint32_t ms)
{
udelay(ms *1000);
}
void sdelay(uint32_t s)
{
udelay(s*1000000);
}
void delay_cyc(uint32_t cyc)
{
uint32_t cycdly = (uint32_t)(DWT_CYCCNT) + cyc;
while((uint32_t)(DWT_CYCCNT) < cycdly);
}
三、实例,请看下一篇,基于高精度延时的软件串口模拟