ARM - STM32 使用11.0592MHz晶振
2013-07-05 11:41
260 查看
这里探究了以下stm32 外部时钟的用法。
这里用的是stm32f103rbt6,usart2,外部晶振11.0592Mhz。
先看时钟树吧
这是上图的配置:
这里的配置和上面的时钟树意义对应,下面读取时钟的函数可以得到各种时钟的值了。可以验证一下,你自己推算的对不对了。
其中读取时钟的函数RCC_GetClocksFreq函数如下:
stm32f10x.h
发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别(TE和RE)置位时,分别为其产生时钟。接收器和发送器的波特率应设置成相同。
Tx / Rx 波特率 = PCLKx / (16 * USARTDIV)
PCLKx可以是APB1的时钟PCLK1(用于USART1),也可以是APB2的时钟PCLK2(用于USART2、3、4、5) 。
USARTDIV = DIV_Maintissa[11:0] + DIV_Fraction[3:0] / 16
USARTDIV是一个无符号的定点数,转换成对应的二进制小数后,整数部分存放在USART_BRR寄存器中的DIV_Maintissa[11:0],小数部分存放在USART_BRR寄存器中的DIV_Fraction[3:0]。
注:在写入USART_BRR之后,波特率计数器会被波特率寄存器的新值替换。因此,不要在通信进行中改变波特率寄存器的数值。
以下部分来自
http://www.cnblogs.com/TrueElement/archive/2012/09/14/2684298.html
新的设置位:CR1_OVER8_Set,位于CR1的第15位,但是手册里面没有提到(rev14);根据手册里面提到的波特率计算公式,估计默认这个位是0,代表每个bit采样16次,而置位的时候,每bit采样8次。手册里的计算公式:
这个计算公式即按照每bit采样16周期来计算的,所以上面的代码在采样8周期的时候,除以2了。
b、integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate))); 的计算与上面公式不一致,这是因为这行代码下面还有这么一句:
所以再除以100,合起来就相当于除以16了,为什么这样子做?因为integerdivider是一个uint32,为了提高精度,所以那里乘以100了。
c、这个精度够不够?乘以100,相当于保留到小数点后两位,而我们知道BRR的分数部分有4位,精度为1/16 = 0.0625,所以可以看到这个精度是够的。那么上面可以不可以改成乘以250再除以1000呢?不行,因为加入apbclock用最高的频率(72M),那么250*apbclock/4将溢出(uint32).
d、如果波特率特别高,而apbclock又较低,导致按照上面公式计算,结果USARTDIV小于1,则这样是不行的,你不能指望USART还能自己提时钟速度。这个时候,CR1_OVER8应该会有些作用,可以将在目前APB时钟下的最大的波特率提升一倍。
我补充一点:
在 stm32 reference manual中
而在stm32L1系列中
发现区别了吧:
over8是这里的,而非stm32L系列的读出来就是0,可以说是兼容的。
这里用的是stm32f103rbt6,usart2,外部晶振11.0592Mhz。
先看时钟树吧
这是上图的配置:
void RCC_Init(void) { ErrorStatus HSEStartUpStatus; RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); //设置外部高速晶振(HSE) HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE起振 if(HSEStartUpStatus == SUCCESS) //SUCCESS:HSE晶振稳定且就绪 { RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置AHB时钟(HCLK),RCC_SYSCLK_Div1——AHB时钟 = 系统时钟 RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速AHB时钟(PCLK2),RCC_HCLK_Div1——APB2时钟 = HCLK RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1),RCC_HCLK_Div2——APB1时钟 = HCLK / 2 FLASH_SetLatency(FLASH_Latency_2); //设置FLASH存储器延时时钟周期数,FLASH_Latency_2 2延时周期 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); // 选择FLASH预取指缓存的模式,预取指缓存使能 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_2); // 设置PLL时钟源及倍频系数,PLL的输入时钟 = HSE时钟频率;RCC_PLLMul_2——PLL输入时钟x 2 RCC_PLLCmd(ENABLE);//使能PLL while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//检查指定的RCC标志位(PLL准备好标志)设置与否 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //设置系统时钟(SYSCLK),RCC_SYSCLKSource_PLLCLK——选择PLL作为系统时钟,11.0592x2 while(RCC_GetSYSCLKSource() != 0x08); //PLL返回用作系统时钟的时钟源,0x08:PLL作为系统时钟 } RCC_GetClocksFreq(&RCC_ClocksStatus); }
这里的配置和上面的时钟树意义对应,下面读取时钟的函数可以得到各种时钟的值了。可以验证一下,你自己推算的对不对了。
RCC_GetClocksFreq(&RCC_ClocksStatus); if (usartxbase == USART1_BASE) { apbclock = RCC_ClocksStatus.PCLK2_Frequency; } else { apbclock = RCC_ClocksStatus.PCLK1_Frequency; } /* Determine the integer part */ if ((USARTx->CR1 & CR1_OVER8_Set) != 0) { /* Integer part computing in case Oversampling mode is 8 Samples */ integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate))); }
其中读取时钟的函数RCC_GetClocksFreq函数如下:
void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks) { uint32_t tmp = 0, pllmull = 0, pllsource = 0, presc = 0; #ifdef STM32F10X_CL uint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0; #endif /* STM32F10X_CL */ #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) uint32_t prediv1factor = 0; #endif /* Get SYSCLK source -------------------------------------------------------*/ tmp = RCC->CFGR & CFGR_SWS_Mask; switch (tmp) { case 0x00: /* HSI used as system clock */ RCC_Clocks->SYSCLK_Frequency = HSI_VALUE; break; case 0x04: /* HSE used as system clock */ RCC_Clocks->SYSCLK_Frequency = HSE_VALUE; break; case 0x08: /* PLL used as system clock */ /* Get PLL clock source and multiplication factor ----------------------*/ pllmull = RCC->CFGR & CFGR_PLLMull_Mask; pllsource = RCC->CFGR & CFGR_PLLSRC_Mask; #ifndef STM32F10X_CL pllmull = ( pllmull >> 18) + 2; if (pllsource == 0x00) {/* HSI oscillator clock divided by 2 selected as PLL clock entry */ RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull; } else { #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1; /* HSE oscillator clock selected as PREDIV1 clock entry */ RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull; #else /* HSE selected as PLL clock entry */ if ((RCC->CFGR & CFGR_PLLXTPRE_Mask) != (uint32_t)RESET) {/* HSE oscillator clock divided by 2 */ RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE >> 1) * pllmull; } else { RCC_Clocks->SYSCLK_Frequency = HSE_VALUE * pllmull; } #endif } #else pllmull = pllmull >> 18; if (pllmull != 0x0D) { pllmull += 2; } else { /* PLL multiplication factor = PLL input clock * 6.5 */ pllmull = 13 / 2; } if (pllsource == 0x00) {/* HSI oscillator clock divided by 2 selected as PLL clock entry */ RCC_Clocks->SYSCLK_Frequency = (HSI_VALUE >> 1) * pllmull; } else {/* PREDIV1 selected as PLL clock entry */ /* Get PREDIV1 clock source and division factor */ prediv1source = RCC->CFGR2 & CFGR2_PREDIV1SRC; prediv1factor = (RCC->CFGR2 & CFGR2_PREDIV1) + 1; if (prediv1source == 0) { /* HSE oscillator clock selected as PREDIV1 clock entry */ RCC_Clocks->SYSCLK_Frequency = (HSE_VALUE / prediv1factor) * pllmull; } else {/* PLL2 clock selected as PREDIV1 clock entry */ /* Get PREDIV2 division factor and PLL2 multiplication factor */ prediv2factor = ((RCC->CFGR2 & CFGR2_PREDIV2) >> 4) + 1; pll2mull = ((RCC->CFGR2 & CFGR2_PLL2MUL) >> 8 ) + 2; RCC_Clocks->SYSCLK_Frequency = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull; } } #endif /* STM32F10X_CL */ break; default: RCC_Clocks->SYSCLK_Frequency = HSI_VALUE; break; } /* Compute HCLK, PCLK1, PCLK2 and ADCCLK clocks frequencies ----------------*/ /* Get HCLK prescaler */ tmp = RCC->CFGR & CFGR_HPRE_Set_Mask; tmp = tmp >> 4; presc = APBAHBPrescTable[tmp]; /* HCLK clock frequency */ RCC_Clocks->HCLK_Frequency = RCC_Clocks->SYSCLK_Frequency >> presc; /* Get PCLK1 prescaler */ tmp = RCC->CFGR & CFGR_PPRE1_Set_Mask; tmp = tmp >> 8; presc = APBAHBPrescTable[tmp]; /* PCLK1 clock frequency */ RCC_Clocks->PCLK1_Frequency = RCC_Clocks->HCLK_Frequency >> presc; /* Get PCLK2 prescaler */ tmp = RCC->CFGR & CFGR_PPRE2_Set_Mask; tmp = tmp >> 11; presc = APBAHBPrescTable[tmp]; /* PCLK2 clock frequency */ RCC_Clocks->PCLK2_Frequency = RCC_Clocks->HCLK_Frequency >> presc; /* Get ADCCLK prescaler */ tmp = RCC->CFGR & CFGR_ADCPRE_Set_Mask; tmp = tmp >> 14; presc = ADCPrescTable[tmp]; /* ADCCLK clock frequency */ RCC_Clocks->ADCCLK_Frequency = RCC_Clocks->PCLK2_Frequency / presc; }
stm32f10x.h
#if !defined HSE_VALUE #ifdef STM32F10X_CL #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */ #else #define HSE_VALUE ((uint32_t)11059200) /*!< Value of the External oscillator in Hz */ #endif /* STM32F10X_CL */ #endif /* HSE_VALUE */
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) { uint32_t tmpreg = 0x00, apbclock = 0x00; uint32_t integerdivider = 0x00; uint32_t fractionaldivider = 0x00; uint32_t usartxbase = 0; RCC_ClocksTypeDef RCC_ClocksStatus; /* Check the parameters */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_BAUDRATE(USART_InitStruct->USART_BaudRate)); assert_param(IS_USART_WORD_LENGTH(USART_InitStruct->USART_WordLength)); assert_param(IS_USART_STOPBITS(USART_InitStruct->USART_StopBits)); assert_param(IS_USART_PARITY(USART_InitStruct->USART_Parity)); assert_param(IS_USART_MODE(USART_InitStruct->USART_Mode)); assert_param(IS_USART_HARDWARE_FLOW_CONTROL(USART_InitStruct->USART_HardwareFlowControl)); /* The hardware flow control is available only for USART1, USART2 and USART3 */ if (USART_InitStruct->USART_HardwareFlowControl != USART_HardwareFlowControl_None) { assert_param(IS_USART_123_PERIPH(USARTx)); } usartxbase = (uint32_t)USARTx; /*---------------------------- USART CR2 Configuration -----------------------*/ tmpreg = USARTx->CR2; /* Clear STOP[13:12] bits */ tmpreg &= CR2_STOP_CLEAR_Mask; /* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/ /* Set STOP[13:12] bits according to USART_StopBits value */ tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits; /* Write to USART CR2 */ USARTx->CR2 = (uint16_t)tmpreg; /*---------------------------- USART CR1 Configuration -----------------------*/ tmpreg = USARTx->CR1; /* Clear M, PCE, PS, TE and RE bits */ tmpreg &= CR1_CLEAR_Mask; /* Configure the USART Word Length, Parity and mode ----------------------- */ /* Set the M bits according to USART_WordLength value */ /* Set PCE and PS bits according to USART_Parity value */ /* Set TE and RE bits according to USART_Mode value */ tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity | USART_InitStruct->USART_Mode; /* Write to USART CR1 */ USARTx->CR1 = (uint16_t)tmpreg; /*---------------------------- USART CR3 Configuration -----------------------*/ tmpreg = USARTx->CR3; /* Clear CTSE and RTSE bits */ tmpreg &= CR3_CLEAR_Mask; /* Configure the USART HFC -------------------------------------------------*/ /* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */ tmpreg |= USART_InitStruct->USART_HardwareFlowControl; /* Write to USART CR3 */ USARTx->CR3 = (uint16_t)tmpreg; /*---------------------------- USART BRR Configuration -----------------------*/ /* Configure the USART Baud Rate -------------------------------------------*/ RCC_GetClocksFreq(&RCC_ClocksStatus); if (usartxbase == USART1_BASE) { apbclock = RCC_ClocksStatus.PCLK2_Frequency; } else { apbclock = RCC_ClocksStatus.PCLK1_Frequency; } /* Determine the integer part */ if ((USARTx->CR1 & CR1_OVER8_Set) != 0) { /* Integer part computing in case Oversampling mode is 8 Samples */ integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate))); } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ { /* Integer part computing in case Oversampling mode is 16 Samples */ integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate))); //apbclock=0xA8C000=11059200 } tmpreg = (integerdivider / 100) << 4; // 得到integerdivider /* Determine the fractional part */ fractionaldivider = integerdivider - (100 * (tmpreg >> 4)); //得到fractionaldivider /* Implement the fractional part in the register */ if ((USARTx->CR1 & CR1_OVER8_Set) != 0) { tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07); } else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */ { tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F); } /* Write to USART BRR */ USARTx->BRR = (uint16_t)tmpreg; }
小数波特率
发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别(TE和RE)置位时,分别为其产生时钟。接收器和发送器的波特率应设置成相同。Tx / Rx 波特率 = PCLKx / (16 * USARTDIV)
PCLKx可以是APB1的时钟PCLK1(用于USART1),也可以是APB2的时钟PCLK2(用于USART2、3、4、5) 。
USARTDIV = DIV_Maintissa[11:0] + DIV_Fraction[3:0] / 16
USARTDIV是一个无符号的定点数,转换成对应的二进制小数后,整数部分存放在USART_BRR寄存器中的DIV_Maintissa[11:0],小数部分存放在USART_BRR寄存器中的DIV_Fraction[3:0]。
注:在写入USART_BRR之后,波特率计数器会被波特率寄存器的新值替换。因此,不要在通信进行中改变波特率寄存器的数值。
以下部分来自
http://www.cnblogs.com/TrueElement/archive/2012/09/14/2684298.html
新的设置位:CR1_OVER8_Set,位于CR1的第15位,但是手册里面没有提到(rev14);根据手册里面提到的波特率计算公式,估计默认这个位是0,代表每个bit采样16次,而置位的时候,每bit采样8次。手册里的计算公式:
这个计算公式即按照每bit采样16周期来计算的,所以上面的代码在采样8周期的时候,除以2了。
b、integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate))); 的计算与上面公式不一致,这是因为这行代码下面还有这么一句:
tmpreg = (integerdivider / 100) << 4;
所以再除以100,合起来就相当于除以16了,为什么这样子做?因为integerdivider是一个uint32,为了提高精度,所以那里乘以100了。
c、这个精度够不够?乘以100,相当于保留到小数点后两位,而我们知道BRR的分数部分有4位,精度为1/16 = 0.0625,所以可以看到这个精度是够的。那么上面可以不可以改成乘以250再除以1000呢?不行,因为加入apbclock用最高的频率(72M),那么250*apbclock/4将溢出(uint32).
d、如果波特率特别高,而apbclock又较低,导致按照上面公式计算,结果USARTDIV小于1,则这样是不行的,你不能指望USART还能自己提时钟速度。这个时候,CR1_OVER8应该会有些作用,可以将在目前APB时钟下的最大的波特率提升一倍。
我补充一点:
在 stm32 reference manual中
而在stm32L1系列中
发现区别了吧:
over8是这里的,而非stm32L系列的读出来就是0,可以说是兼容的。
相关文章推荐
- STM32使用16M外部晶振时出现异常
- STM32学习之路:day1-arm内核结构 下载编译程序和keil的使用
- STM32使用非8M晶振时如何修改代码
- 在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法
- 不用晶振,STM32内部HSI时钟的倍频使用
- IAR下stm32使用12M外部晶振
- IAR下stm32使用12M外部晶振
- STM32使用外部16MHZ晶振修改程序点
- STM32上不使用外部晶振,OSC_IN和OSC_OUT的接法
- STM32使用内部晶振和外部晶振
- stm32变更外部晶振时如何配置时钟、以及HSI的使用
- 在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法
- STM32不使用外部晶振的接法
- STM32使用外部16MHZ晶振总结
- STM32使用16M外部晶振时串口乱码
- 在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法
- STM32使用外部16MHZ晶振总结
- STM32使用不同的晶振需修改的代码
- ARM DS-5开发STM32程序(Eclipse配合Keil插件使用)
- arm-linux-gcc/ld/objcopy/objdump使用总结