ARM - STM32 使用11.0592MHz晶振

这里探究了以下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时钟 = 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,可以说是兼容的。

d:\stm32dev\stm32cubeide_1.12.1\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.200.202301161003\tools\arm-none-eabi\bin\ld.exe: ./Core/Src/main.o: in function `main': main.c:(.text.startup.main+0x16): undefined reference to `TMC2660_DirectSet' d:\stm32dev\stm32cubeide_1.12.1\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.200.202301161003\tools\arm-none-eabi\bin\ld.exe: main.c:(.text.startup.main+0x1e): undefined reference to `TMC2660Enable' d:\stm32dev\stm32cubeide_1.12.1\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.200.202301161003\tools\arm-none-eabi\bin\ld.exe: main.c:(.text.startup.main+0x26): undefined reference to `TMC2660_MicrostepSet' d:\stm32dev\stm32cubeide_1.12.1\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.200.202301161003\tools\arm-none-eabi\bin\ld.exe: main.c:(.text.startup.main+0x2e): undefined reference to `TMC2660_CurrentScaleSet' d:\stm32dev\stm32cubeide_1.12.1\stm32cubeide\plugins\com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.10.3-2021.10.win32_1.0.200.202301161003\tools\arm-none-eabi\bin\ld.exe: main.c:(.text.startup.main+0x34): undefined reference to `TMC2660_SPIMoveStep' collect2.exe: error: ld returned 1 exit status make: *** [makefile:64: TMC2660_tset.elf] Error 1 "make -j32 all" terminated with exit code 2. Build might be incomplete.
07-13
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值