时钟树、SYSTICK、printf函数重定向

一.时钟树介绍

时钟和时钟树

时钟是具有周期性的脉冲信号,最常用的是占空比为50%的方波。

时钟源名称频率材料用途
高速外部振荡器(HSE)4~16MHz晶体/陶瓷SYSCLK/RTC
低速外部振荡器(LSE)32.768KHz晶体/陶瓷RTC
高速内部振荡器(HSI)8MHz晶体/陶瓷SYSCLK
低速内部振荡器(LSI)40KHz晶体/陶瓷RTC/IWDG

时钟安全系统(CSS):如果HSE启动失败,切换到HSI,可进NMI(不可屏蔽)中断。

自由运行时钟(FCLK):用于采样中断和调试模块计时,休眠仍有效。

二.配置系统时钟 

<1>系统时钟配置步骤

  1. 配置HSE_VALUE:告诉HAL库外部晶振频率,stm32xxxx_hal_conf.h;
  2. 调用SytemInit()函数(可选):在启动文件中调用,在system_stm32xxxx.c定义;
  3. 选择时钟源,配置PLL:通过HAL_RCC_OscConfig()函数配置;
  4. 选择系统时钟源,配置总线分频器:通过HAL_RCC_ClockConfig()函数设置;
  5. 配置扩展外设时钟(可选):通过HAL_RCCEx_PeriphCLKConfig()函数设置,只有H7系列有。

<2>外设时钟使能和失能 

要使用某个外设,必须先使能该外设时钟。

使能:_HAL_RCC_XXXX_CLK_ENABLE()

失能: _HAL_RCC_XXXX_CLK_DISENABLE()

<3>sys_stm32_clock_init函数

1.HAL_RCC_OscConfig()函数

2. HAL_RCC_ClockConfig()函数

3.sys_stm32_clock_init()函数

三.SYSTEM文件夹

 <1>sys文件夹

1.中断类函数:

  • sys_nvic_set_vector_table():设置中断向量表地址;
  • sys_intx_enable():开启所有中断;
  • sys_intx_disable():关闭所有中断(但是不包括fault和NMI中断)。

2.低功耗类函数:

  • sys_wfi_set():执行:WFI指令(执行完该指令进入低功耗状态);
  • sys_standby():进入待机模式;
  • sys_soft_reset(): 系统软复位。

3.设置栈顶地址函数: 

  • sys_msr_msp():设置栈顶地址;

4.系统时钟初始化函数: 

  • sys_stm32_clock_init():设置系统时钟;

5.Cache配置函数(F7/H7): 

  • sys_cache_enable():使能I-Cache和D-Cache,开启D-Cache强制透写。

<2>delay文件夹

1.函数简介

  • delay_init():初始化系统嘀答定时器;
  • delay_us():用系统嘀嗒定时器实现微秒延时;
  • delay_ms():用微秒延时函数实现毫秒延时。

2.SysTick工作原理 

SysTick,即系统嘀答定时器,包含在M3/4/7内核里面,核心是一个24位的递减计数器。

每次VAL到0时,VAL自动从LOAD重载!开始新一轮递减计数。

3.SysTick寄存器介绍 

SysTick控制及状态寄存器(CTRL)

CLKSOURCE位ST公司将其设置为外部时钟源,可以选择8分频或1分频。

4.delay_init()函数(F1)

void delay_init(uint16_t sysclk)
{
    SysTick->CTRL = 0;//将HAL库设置的寄存器参数归0
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);//选择8分频
    g_fac_us = sysclk/8;//定义全局变量,1us要数9次:9/9000000 (72MHz)
}

5.delay_us()函数(F1)

void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus * g_fac_us;//时间加载
    SysTick->VAL = 0x00;//清空计数器
    SysTick->CTRL |= 1<<0;//开始倒数
    do 
    {
        temp = SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));//CTRL.ENABLE位必须为1,并等待时间到达
    
    SysTick->CTRL &= ~(1<<0);//关闭SYSTICK
    SysTick->VAL = 0x00;//清空计数器
}

6.delay_ms()函数(F1) 

void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 1000;/*这里用1000,是考虑到可能有超频应用,比如128MHz时,delay_us最大只能延时1048576us*/  
    uint32_t remain = nms % 1000;
    while(repeat)
    {
        delay_us(1000*1000);//利用delay_us实现1000ms延时
        repeat--;
    }                         
    if(remain)
    {
        delay_us(remain*1000);//利用delay_us,把尾数延时(remain ms)给做了
    }
}

<3>usart文件夹

1.printf函数输出流程

2.printf的使用

  • printf("字符串\r\n");
  • printf("输出控制符",输出参数);
  • printf("输出控制符1 输出控制符2... ...",输出参数1,输出参数2,... ...);
  • printf("非输出控制符 输出控制符 非输出控制符",输出参数);
  • 输出&、\和双引号:printf("%%\r\n");   printf("\\\r\n");   printf("\"\"\r\n");

3.printf函数支持

1>避免使用半主机模式(两种方法:微库法,代码法)

半主机模式:通过仿真器实现开发板在电脑上的输入和输出。

微库法

优点:简单;

缺点:某些标准C库函数运行慢、兼容性差。

在魔法棒->Target选项卡,勾选:Use Micro LIB,即可以避免半主机模式。

代码法 

优点:标准C库函数运行快、兼容性好;

缺点:稍微复杂。

/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */

#if 1

#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}


/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART_UX->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    USART_UX->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
#endif
/******************************************************************************************/

2>实现fputc函数:实现单个字符输出
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART_UX->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    USART_UX->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值