AFVN越南通讯网
Article Cover

时钟延迟函数-总体架构和时钟介绍

122浏览 2026-1-27 软件教程 MA115804

1. 写时钟延迟函数

原理是72MHZ对应72次振动1微秒。


AHB这里是最高频率72MHZ


STK_LOAD寄存器只能存储24位的。

代码如下:


注意这个函数的调用方式,要避免超出2^24的数值,2^16是25535。

PS:如果在工作当中需要写微秒级别的函数可以参考上面的代码(HAL库自动生成代码无法实现微秒),在51芯片当中一个指令1.08微秒无法做到这个精度。

用hal库实现毫秒级延时

(后续: CSDN你M死了 我看了半天一调试,只要超过 1MS 也就是 1000ms)

我看了半天一调试,只要超过 1MS 也就是 大于1000us 就是 大于 递减 72000 次 就无法准确计数

例如我想定时 1ms + 1us delays = 72000 + 72 = 72072

wait = 72000 + startval - delays;

wait = 72000 + 72000 - 72072

wait = 72072

SysTick->VAL 重装载值 是 72000

while(wait < SysTick->VAL);👈这条空转语句执行都不执行了 我草泥马 那些这个if(delays > startval)语句不是脱裤放屁吗??? 你妈死了 浪费我这么多时间!草!!!


void delay_us(uint32_t udelay)
{
  uint32_t startval,tickn,delays,wait;
 
  startval = SysTick->VAL; //获取未开始前定时器计数值(已设定的值)
  tickn = HAL_GetTick();//获取当前计数器值(运行中)
  //sysc = 72000;  //SystemCoreClock / (1000U / uwTickFreq);
  delays =udelay * 72; //sysc / 1000 * udelay;
  if(delays > startval)
    {
      while(HAL_GetTick() == tickn)
        {
 
        }
      wait = 72000 + startval - delays;
      while(wait < SysTick->VAL)
        {
 
        }
    }
  else
    {
      wait = startval - delays;
      while(wait < SysTick->VAL && HAL_GetTick() == tickn)
        {
 
        }
    }

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/u010779035/article/details/104102469


// 这是一个简化的实现,帮助你理解
volatile uint32_t uwTick; // 定义在某个地方,'volatile'防止编译器优化

__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;//uwTickFreq = 1
}

uint32_t HAL_GetTick(void)
{
  return uwTick;
}

1. 嵌入式分层思想和代码实践


垂直分层 (Vertical Tiers): 从硬件到功能

  • 驱动层 (Dri):
  • 核心任务: 直接操作硬件。
  • 作用: 封装对芯片、传感器等硬件的具体读写,向上层提供统一的硬件访问接口,屏蔽硬件差异。是连接硬件和软件的桥梁。
  • 系统层 (int):
  • 核心任务: 提供运行环境。
  • 作用: 集成操作系统(如 FreeRTOS),提供任务调度、内存管理和通信机制。它是整个软件运行的基石和调度中心。
  • 中间件层 (mid):
  • 核心任务: 提供通用软件服务。
  • 作用: 实现与具体业务无关的复杂功能,如网络协议栈 (TCP/IP)、文件系统、图形库等。它让应用层可以专注于业务,而无需重新发明轮子。
  • 应用层 (APP):
  • 核心任务: 实现产品最终功能。
  • 作用:产品的灵魂。调用所有下层服务(驱动、系统、中间件)来组合实现设备的核心业务逻辑和用户交互。它定义了产品“是什么”和“做什么”。

横向模块化 (Horizontal Components): 可重用的积木

  • 组件/通用模块 (COM):
  • 核心任务: 功能封装与复用。
  • 作用: 将某个特定功能(如按键处理、日志系统)打包成一个独立的、可配置的“软件积木”。这些组件可以被不同项目、不同层级复用,极大地提高了开发效率和代码质量。

实际代码如下头文件能写的部分:



2. 总体架构和时钟系统

2.1. 总体架构

3个被动单元(无法主动发出信号):


4个主动(驱动)单元(主动发出信号):


其他单元:


2.2. 时钟系统


两个无源的晶振:

HSE:8Mhz

LSE:32.768khz(2^15)

内部两个RC振荡器:

LSI(Low Speed Internal)-40khz

HSI(Hight Speed Internal)-8Mhz

3. HEL库创建项目流程

3.1. pinout & configuration

SYS选择 systick 是24位 向下计数的定时器,Debug选择 Serial Wire


RCC设置 HSE,LSE 选择Crystal/Ceramic Resonator (无源晶振)


GPIO设置


3.2. Clock Configuration


3.3. Project Manager


3.4. HAL库自动生成DRI底层代码原理-看头文件总结:


没事自己去尝试翻一下HAL库生成的代码。

3.5. HAL库常用函数-GPIO相关


这次常用的HAL库函数有:

HAL_GPIO_WritePin();//以set和reset为主

HAL_GPIO_ReadPin();

HAL_Delay();//ms级别

4. 代码附录:

#ifndef __DRI_LED_H__
#define __DRI_LED_H__
#include "stm32f10x.h"
#define __Dri_Led_Led0__ GPIO_ODR_ODR0
#define __Dri_Led_Led1__ GPIO_ODR_ODR1
#define __Dri_Led_Led2__ GPIO_ODR_ODR8

void Dri_Led_Init(void);
void Dri_Led_On(uint16_t led);
void Dri_Led_Off(uint16_t led);
void Dri_Led_AllOn(uint16_t led[],uint8_t len);
void Dri_Led_AllOff(uint16_t led[],uint8_t len);
void Dri_Led_Toggle(uint16_t Led);
#endif /* __DRI_LED_H__ */
#include "Dri_Led.h"

void Dri_Led_Init(void)
{
    RCC->APB2ENR |=RCC_APB2ENR_IOPAEN;//advanced peripheral bus 2 enable rigistor
    GPIOA->CRL |=(GPIO_CRL_MODE0|GPIO_CRL_MODE1);//start led0 and led1 mode,this is mode is output 50mhz
    GPIOA->CRH|=GPIO_CRH_MODE8;//start led2 mode,this is mode is output 50mhz
    GPIOA->CRL &=~(GPIO_CRL_CNF0|GPIO_CRL_CNF1);//use general purpose push-pull output in led0 and led1
    GPIOA->CRH &=~GPIO_CRH_CNF8;//use general purpose push-pull output in led2

    GPIOA->ODR |=(__Dri_Led_Led0__|__Dri_Led_Led1__|__Dri_Led_Led2__);//use define led0,1,2 to high level to prevent led0,1,2 to light,it retain dark at begin.
}

void Dri_Led_On(uint16_t led)//because GPIO_ODR_ODR0 is 16bit,so we use 16bit suit to define.
{
    GPIOA->ODR &=~ led;
}

void Dri_Led_Off(uint16_t led)
{
    GPIOA->ODR |=led;
}

void Dri_Led_AllOn(uint16_t led[], uint8_t len)
{
    for (uint8_t i = 0; i < len; i++)
    {
        GPIOA->ODR&=~led[i];
    }
}

void Dri_Led_AllOff(uint16_t led[], uint8_t len)
{
    for (uint8_t i = 0; i < len; i++)
    {
        GPIOA->ODR|=led[i];
    }
}

void Dri_Led_Toggle(uint16_t Led)
{
    ((GPIOA->IDR&Led)==0)?(GPIOA->ODR|=Led):(GPIOA->ODR&=~Led);
}
#ifndef __DRI_DELAY_H__
#define __DRI_DELAY_H__
#include "stm32f10x.h"

void Dri_Delay_us(uint16_t us);
void Dri_Delay_ms(uint16_t ms);
void Dri_Delay_s(uint16_t s);
#endif /* __DRI_DELAY_H__ */
#include "Dri_Delay.h"
void Dri_Delay_us(uint16_t us)
{
    SysTick->LOAD|=us*72;//systemtick*72,because 72mhz corresponding 10^6 is correct for 1us.
    SysTick->CTRL|=SysTick_CTRL_ENABLE;
    SysTick->CTRL&=~SysTick_CTRL_TICKINT;
    SysTick->CTRL&=~SysTick_CTRL_COUNTFLAG;
    SysTick->CTRL|=SysTick_CTRL_CLKSOURCE;
    while ((SysTick->CTRL&SysTick_CTRL_COUNTFLAG)==0)
    {};
}

void Dri_Delay_ms(uint16_t ms)
{
    while (ms--)
    {
        Dri_Delay_us(1000);
    }
}

void Dri_Delay_s(uint16_t s)
{
    while (s--)
    {
        Dri_Delay_ms(1000);
    }
    
}

5. 代码TIP-粑粑巴士



6. 英文附录

STK_CTRL 的全称就是 SysTick Control Register

AHBAdvanced High-performance Bus 的缩写。翻译成中文是 “高级高性能总线”。

SRAM: Static Random-Access Memory, 静态随机存取存储器, 用作高速内存,存放程序运行时的变量和堆栈。

AHB: Advanced High-performance Bus, 先进高性能总线, 连接CPU、SRAM、Flash等高速系统部件的总线。

APB: Advanced Peripheral Bus, 先进外设总线, 连接UART、I2C、SPI、定时器等低速外设的总线。

内部RC振荡器中的RC : Resistor, 电阻器, 在电路中用于限制电流的流动,并控制电容的充电速度。Capacitor, 电容器, 在电路中用于储存和释放电荷,通过周期性的充放电产生振荡基础。

未经作者允许,禁止转载
#STM32
7