一.时钟树介绍
时钟和时钟树
时钟是具有周期性的脉冲信号,最常用的是占空比为50%的方波。
时钟源名称 | 频率 | 材料 | 用途 |
高速外部振荡器(HSE) | 4~16MHz | 晶体/陶瓷 | SYSCLK/RTC |
低速外部振荡器(LSE) | 32.768KHz | 晶体/陶瓷 | RTC |
高速内部振荡器(HSI) | 8MHz | 晶体/陶瓷 | SYSCLK |
低速内部振荡器(LSI) | 40KHz | 晶体/陶瓷 | RTC/IWDG |
时钟安全系统(CSS):如果HSE启动失败,切换到HSI,可进NMI(不可屏蔽)中断。
自由运行时钟(FCLK):用于采样中断和调试模块计时,休眠仍有效。
二.配置系统时钟
<1>系统时钟配置步骤
- 配置HSE_VALUE:告诉HAL库外部晶振频率,stm32xxxx_hal_conf.h;
- 调用SytemInit()函数(可选):在启动文件中调用,在system_stm32xxxx.c定义;
- 选择时钟源,配置PLL:通过HAL_RCC_OscConfig()函数配置;
- 选择系统时钟源,配置总线分频器:通过HAL_RCC_ClockConfig()函数设置;
- 配置扩展外设时钟(可选):通过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;
}