ARM - STM32 使用11.0592MHz晶振

来源:互联网 发布:重置网络命令 编辑:程序博客网 时间:2024/04/28 04:42

这里探究了以下stm32 外部时钟的用法。

这里用的是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时钟 = HCLKRCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1),RCC_HCLK_Div2——APB1时钟 = HCLK / 2FLASH_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 2RCC_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,可以说是兼容的。

原创粉丝点击