目录
正点原子库函数
stm32f103rct6(库函数版例程)使用内部晶振8M,倍频64M
参考
https://blog.csdn.net/oHuanCheng/article/details/105112884
http://www.openedv.com/forum.php?mod=viewthread&tid=286233
https://blog.csdn.net/ll148305879/article/details/106138302
1.void SystemInit(void)
修改system_stm32f10x.c文件中的void SystemInit(void)函数
void SystemInit(void) //mychange
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
RCC_DeInit(); //set RCC to initialization
RCC_HSICmd(ENABLE);//Enable HSI
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);//wait HSI enable
//FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
// Set PLL clock source and frequency doubling coefficient
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16);//4*16=64M RCC_PLLMul_9 36M
RCC_PLLCmd(ENABLE);
// Waiting for the specified RCC flag bit settings to succeed
// and waiting for the PLL initialization to succeed
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// Setting PLL as System Clock Source
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//Waiting for PLL to be successfully used as clock source for system clock
// 0x00:HSI
// 0x04:HSE
// 0x08:PLL
while(RCC_GetSYSCLKSource() != 0x08);
}
2.FLASH
内部时钟用到了FLASH,需要添加stm32f10x_flash.c
3.宏定义
将宏定义注释
system_stm32f10x.c
4.查看
配置后的时钟频率
main.c
int main(void)
{
RCC_ClocksTypeDef RCC_Clocks; //初始化时钟频率结构体
RCC_GetClocksFreq(&RCC_Clocks); //获取各个时钟频率
while(1)
{
printf("SYSCLK_Frequency %d \r\n",RCC_Clocks.SYSCLK_Frequency);//串口输出主频
delay_ms(1000);//延迟1s
}
}
5.延时
设置延时函数
参考
https://blog.csdn.net/sky_Buddha/article/details/106093426
delay.c中
void delay_us(u32 nus)
{
u16 i=0;
while(nus--)
{
i=7;
while(i--) ;
}
}
void delay_ms(u16 nms)
{
u16 i=0;
while(nms--)
{
i=7000;
while(i--) ;
}
}
6.最终结果
(虽然这样时间不是很准,但都使用内部晶振了,谁会在意精度呢?)
7.精准延时尝试(失败)
没来得及看的参考链接
https://blog.csdn.net/qq_20222919/article/details/103820476
精准延时的一些尝试,但是失败。参考
https://blog.csdn.net/coderdd/article/details/97612514
失败的尝试如下:
直接操作寄存器,查看手册说明:
SysTick->CTRL&=0xfffffffb;这一句把 SysTick 的时钟选择外部时钟,这里需要注意的是:
SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,然后倍频到 72M,那么 SysTick
的时钟即为 9Mhz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。所以fac_us应该就是72M/8000000=9。
fac_us=SystemCoreClock/8000000; //ΪϵͳʱÖÓµÄ1/8
//延时 nus
//nus 为要延时的 us 数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时 nms
//注意 nms 的范围
//SysTick->LOAD 为 24 位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK 单位为 Hz,nms 单位为 ms
//对 72M 条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD 为 24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
虽然没找到内核时钟是多少,但应该是8M,就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/8us,所以fac_us=8。
使用内核时钟,即
SysTick->CTRL=0x05 ; //开始倒数 0000 0101
SysTick->CTRL=0x04; //关闭计数器0000 0100
//延时 nus
//nus 为要延时的 us 数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*8; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x05 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x04; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时 nms
//注意 nms 的范围
//SysTick->LOAD 为 24 位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK 单位为 Hz,nms 单位为 ms
//对 72M 条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*8000;//时间加载(SysTick->LOAD 为 24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x05 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL=0x04; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
但是,还是失败了,不知道问题出在哪,有大神知道的可以在评论区指导一下!!!
HAL库函数
官方用的HAL库函数,所以直接问ChatGPT就行
1 宏定义
在stm32f1xx_hal_conf.h文件中,将HSE_VALUE宏定义的值设置为0,表示不使用外部晶振。
/* #define HSE_VALUE ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
#define HSE_VALUE ((uint32_t)0U) /*!< Value of the External oscillator in Hz */
2 时钟配置
在主函数中,使用RCC_ClkInitTypeDef结构体进行时钟配置,将AHB、APB1和APB2总线的分频系数都设置为1,以确保这些总线的时钟与系统时钟同步。代码如下:
#include "main.h"
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* Private functions ---------------------------------------------------------*/
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Infinite loop */
while (1)
{
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
/* Initializes the CPU, AHB and APB buses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Initializes the CPU, AHB and APB buses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
在上述代码中,将RCC_OscInitStruct结构体中的OscillatorType设置为RCC_OSCILLATORTYPE_HSI,表示使用HSI内部晶振。将RCC_OscInitStruct结构体中的HSIState设置为RCC_HSI_ON,表示开启HSI内部晶振。将RCC_OscInitStruct结构体中的PLLClockSource设置为RCC_PLLSOURCE_HSI_DIV2,表示使用HSI内部晶振除以2作为PLL的时钟源。将RCC_OscInitStruct结构体中的PLLCMUL设置为RCC_PLL_MUL16,表示将HSI内部晶振倍频16倍作为PLL的输出时钟,最终的系统时钟频率为64MHz。在RCC_ClkInitStruct结构体中,将SYSCLKSource设置为RCC_SYSCLKSOURCE_PLLCLK,表示将PLL输出的时钟作为系统时钟源。将AHBCLKDivider、APB1CLKDivider和APB2CLKDivider都设置为RCC_SYSCLK_DIV1,表示不分频,保持与系统时钟同步。
3 main函数中调用
在main函数中调用HAL_RCC_OscConfig和HAL_RCC_ClockConfig函数进行时钟初始化,如上所示。
4 例子代码
下面是一个简单的例子代码,演示了如何在使用STM32F103RCT6内部晶振的情况下进行时钟初始化,并在进入while循环前延时50ms等待外设初始化完成,然后向串口一打印最终的系统时钟频率:
#include "stm32f1xx_hal.h"
#include "stdio.h"
void SystemClock_Config(void);
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Wait for external peripherals to initialize */
HAL_Delay(50);
/* Infinite loop */
while (1)
{
/* Print system clock frequency to USART1 */
printf("System clock frequency: %dHz\n", (int)HAL_RCC_GetSysClockFreq());
HAL_Delay(1000);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* Configure the internal oscillator */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Configure the PLL */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
在该代码中,首先使用HAL_RCC_OscConfig函数将HSI内部晶振配置为系统时钟源。然后使用HAL_RCC_ClockConfig函数将PLL配置为使用HSI作为输入时钟源,输出时钟频率为64MHz,并将其作为系统时钟源。在进入while循环前延时50ms,等待外设初始化完成。在while循环中,使用printf函数向串口一打印最终的系统时钟频率,并使用HAL_Delay函数延时1秒。
注意,在使用printf函数打印时钟频率之前,需要在代码中配置USART1,并使用HAL_UART_Init函数进行初始化。
本人Error_Handler();会报错,注释掉就能跑通,实验可行。
寄存器版本(跑通串口)
之前的版本,跑在mini板子上是没问题,显示的系统频率是64MHz,但到了自己做的板子上烧录,发现串口通信显示乱码,gg!
经排查,是通信时钟的问题,想把寄存器版本中的串口初始化函数(应为可以直接设置频率)移植到HAL版本中,失败,无奈只能再战寄存器版本。
//pclk2:PCLK2 时钟频率(Mhz)
//bound:波特率
void uart_init(u32 pclk2,u32 bound)
代码
问下ChatGPT,只要加一个函数直接跑通。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "24cxx.h"
#include <string.h>
void SystemInit(void)
{
// 选择 HSI 作为系统时钟源
RCC->CFGR &= ~(RCC_CFGR_SW);
RCC->CFGR |= RCC_CFGR_SW_HSI;
// 等待 HSI 稳定
while((RCC->CR & RCC_CR_HSIRDY) == 0);
// 配置 PLL
RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
RCC->CFGR |= RCC_CFGR_PLLSRC_HSI_Div2 | RCC_CFGR_PLLMULL16;
// 启用 PLL
RCC->CR |= RCC_CR_PLLON;
// 等待 PLL 启动完成
while((RCC->CR & RCC_CR_PLLRDY) == 0);
// 设置 FLASH 读取延迟
FLASH->ACR |= FLASH_ACR_LATENCY_2;
// 设置系统时钟源为 PLL
RCC->CFGR &= ~(RCC_CFGR_SW);
RCC->CFGR |= RCC_CFGR_SW_PLL;
// 等待 PLL 作为系统时钟源设置成功
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
// 设置 APB1 时钟为 HCLK/2
RCC->CFGR &= ~(RCC_CFGR_PPRE1);
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
// 设置系统时钟频率为 64MHz
RCC->CFGR &= ~(RCC_CFGR_HPRE);
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// 打开 GPIOC 时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
}
int main(void)
{
uint32_t sysClockFreq = SystemCoreClock;
char buf[32];
int len,i;
SystemInit(); //ϵͳʱÖÓÉèÖÃ
delay_init(64); //ÑÓʱ³õʼ»¯
delay_ms(10);
uart_init(64,9600); //´®¿Ú³õʼ»¯Îª9600 7246
AT24CXX_Init(); //IIC³õʼ»¯
delay_ms(10);
while(1)
{
sprintf(buf, "System clock frequency: Hz\r\n");
len = strlen(buf);
for( i = 0; i < len; i++)
{
USART1->DR = buf[i]; // ???????
while(!(USART1->SR & USART_SR_TXE)); // ??????
}
delay_ms(500); // ??1?
//USART1->DR = 0x55;
}
}
在 SystemInit() 函数中配置 RCC 寄存器,以选择内部时钟源并设置系统时钟频率为 64MHz。
在上述代码中,我们使用 HSI 作为系统时钟源,并使用 PLL 将时钟频率提高到 64MHz。我们还设置了FLASH读取延迟以确保在高频率下稳定工作,并将APB1时钟设置为HCLK / 2。最后,我们打开了 GPIOC 时钟。
示波器查看波特率
还是乱码,没办法,直接上示波器。
参考
https://blog.csdn.net/ybhuangfugui/article/details/120480189
https://blog.csdn.net/Old_Street/article/details/127454235
在主函数一直发数据:0x55
while(1)
{
USART1->DR = 0x55;
}
终于成了,虽然没有改到9600,但实在不想改了,就这样吧。
(另提一句,hal版本改为7246也没有乱码了,说明hal设置波特率的时候确实是按64M来算)