cjtdz 发表于 7 天前

请详细讲解一下不同时钟下延时、定时自适配问题

请高手们详细讲解一下,在不同时钟频率下,不用改代码,有关延时函数、定时时间、波特率等自适配问题,代码怎么写?最好出一期详细的视频讲解。

国芯人工智能 发表于 7 天前

在不同时钟频率下实现延时、定时及波特率等参数的自适配,是嵌入式系统开发中常见的问题。尤其在使用不同主频的微控制器(如STM32、ESP32、RISC-V等)时,若希望代码具备良好的可移植性与通用性,需设计出不依赖固定时钟频率的自适应机制。

一、延时函数的自适应

1. 延时原理
延时函数的核心在于基于系统时钟周期进行循环计数。例如,在标准的 for 循环延时中,通常会根据时钟频率计算所需循环次数,从而达到预期的延时时间。

2. 自适应方法
为实现不依赖固定时钟频率的延时,可以采用以下方式:
使用系统时钟频率变量:在程序中定义一个全局变量 SystemCoreClock 或类似变量,用于存储当前系统的主频。
动态计算延时周期:根据目标延时时间与当前系统时钟频率,动态计算所需的循环次数。

c
void Delayms(uint32t ms) {
    uint32t ticks = ms(SystemCoreClock / 1000);
    for (uint32t i = 0; i < ticks; i++);
}
注意:此方法适用于低精度延时场景,高精度延时建议使用定时器或看门狗模块。

3. 使用定时器实现自适应延时
更可靠的方式是使用定时器(如STM32的TIMx),通过配置定时器的预分频值和自动重载寄存器,实现精确延时。该方式不受主频影响,只需根据当前时钟频率动态设置定时器参数。

c
void TimerInit(uint32t freq, uint32t ms) {
    uint32t arr = (freq / 1000)ms;
    TIMTimeBaseInitTypeDef TIMInitStruct;
    TIMInitStruct.TIMPrescaler = 0;
    TIMInitStruct.TIMPeriod = arr;
    TIMInitStruct.TIMCounterMode = TIMCounterModeUp;
    TIMTimeBaseInit(TIMx, &TIMInitStruct);
    TIMCmd(TIMx, ENABLE);
}

二、定时功能的自适应

1. 定时器的基本原理
定时器的核心是根据系统时钟进行计数,并在计数值达到设定值时触发中断或事件。因此,定时器的配置需要根据系统时钟频率进行调整。

2. 自适应配置策略
动态计算预分频值和自动重载值:

c
void AdaptiveTimerConfig(uint32t targetfreq, uint32t periodms) {
      uint32t ticks = (targetfreq / 1000)periodms;
      uint32t prescaler = (SystemCoreClock / targetfreq) - 1;
      TIMTimeBaseInitTypeDef TIMInitStruct;
      TIMInitStruct.TIMPrescaler = prescaler;
      TIMInitStruct.TIMPeriod = ticks;
      TIMTimeBaseInit(TIMx, &TIMInitStruct);
}
使用系统时钟变量:确保所有定时器配置都基于当前系统时钟频率,避免硬编码。

三、波特率的自适应

1. 波特率计算原理
串口通信的波特率由系统时钟、分频系数、以及USART的寄存器配置决定。其公式如下:

$$
\text{BaudRate} = \frac{\text{SystemClock}}{16 \times (\text{USARTDIV})}
$$

其中,USARTDIV 是由寄存器配置决定的分频值。

2. 自适应配置方法
动态计算分频值:根据目标波特率和当前系统时钟频率,计算合适的分频系数。

c
void USARTBaudrateSet(USARTTypeDef USARTx, uint32t baudrate) {
    uint32t usartdiv = SystemCoreClock / (16baudrate);
    USARTInitStruct.USARTBaudRate = baudrate;
    USARTInitStruct.USARTParity = USARTParityNo;
    USARTInitStruct.USARTWordLength = USARTWordLength8b;
    USARTInitStruct.USARTStopBits = USARTStopBits1;
    USARTInitStruct.USARTMode = USARTModeRx | USARTModeTx;
    USARTInitStruct.USARTHardwareFlowControl = USARTHardwareFlowControlNone;
    USARTInitStruct.USARTFIFOMode = USARTFIFOModeDisable;

    USARTInit(USARTx, &USARTInitStruct);
    USARTCmd(USARTx, ENABLE);
}
注意:部分MCU的USART模块支持自动分频,可通过配置寄存器实现自适应波特率。

四、总结

| 功能       | 实现方式                     | 关键点                         |
|------------|------------------------------|--------------------------------|
| 延时函数   | 动态计算循环次数             | 使用系统时钟变量               |
| 定时器   | 配置预分频和自动重载         | 根据当前时钟频率动态计算       |
| 波特率   | 计算分频系数               | 确保基于当前系统时钟频率       |

以上方法均实现了“不改代码”即可适应不同主频的方案,适用于多平台、多时钟频率的嵌入式系统开发。如需进一步深入讲解,可结合具体MCU型号(如STM32F1/F4、ESP32、RISC-V等)进行示例分析,也可制作详细视频教程进行演示。

[本文内容由国芯人工智能辅助生成,仅供参考]

_奶咖君_ 发表于 6 天前

总感觉你问了很多类似的问题。。。。。

ISP软件上的是根据生成代码的时钟数来完成确定时间的延时,不考虑延时当中中断的情况。

如果你也想弄一个的话,把你上述的需求实现的时钟数都考虑进去,大概也行。。。

最简单的现在不是有T11定时器了么,你可以用这个定时器来做延时。。
页: [1]
查看完整版本: 请详细讲解一下不同时钟下延时、定时自适配问题