Content:
CR and CFGR registers
Coefficients
Registers in CMSIS
Customization
CR and CFGR registers
Open the Reference manual for the STM32F103x8 microcontroller and go to section 7: Low-, medium-, high- and XL-density reset and clock control (RCC). The RCC has quite a few registers, as many as 10 of them. However, we need only 2 of them to configure the clock source and bus dividers.
Description of the main bits of the register:
PLLRDY - PLL Ready Flag. It is set by hardware and signals that the PLL is locked.
PLLON - Enable PLL. Set and reset by software. Reset by hardware when going to Stop or Standby mode. This bit cannot be reset if the PLL is used as a system clock source.
CSSON - enable CSS system
HSEBYP - If we want to use an external rectangular clock signal instead of the HSE crystal resonator, this bit must be set to 1.
HSERDY - HSE generator ready flag. It is set to 1 after successful start-up and stabilization of the HSE generator frequency.
HSEON - Start HSE generator. It is set and reset programmatically. When going to Stop or Standby mode, it is reset by hardware and the HSE generator is stopped. This bit cannot be reset if the HSE is used as a system clock source.
HSIRDY - Same as HSERDY, only for the on-board RC oscillator of the HSI
HSION - same as HSEON, only for the built-in HSI RC oscillator.
Description of the main bits of the register:
MCO - supplying a clock signal to the MCO pin of the microcontroller.
- 0xx: The function is disabled
- 100: System clock (SYSCLK) selected
- 101: HSI signal selected
- 110: HSE signal selected
- 111: Selected signal from PLL, which is divided by 2.
- 0000: Input frequency PLL multiplied by 2
- 0001: -//- by 3
- 0010: -//- by 4
- 0011: -//- by 5
- 0100: -//- by 6
- 0101: -//- by 7
- 0110: -//- by 8
- 0111: -//- on 9
- 1000: -//- on 10
- 1001: -//- on 11
- 1010: -//- on 12
- 1011: -//- on 13
- 1100: -//- on 14
- 1101: -//- on 15
- 1110: -//- on 16
- 1111: -//- on 16
PLLXTPRE - Frequency divider from the HSE of the oscillator before feeding the PLL. This bit cannot be changed if the PLL is running. If set to 1, the HSE frequency will be divided by 2. If 0, the divider is disabled.
PLLSRC - Source of the PLL input frequency. Cannot be changed if the PLL is running.
- 0: HSI oscillator frequency divided by 2
- 1: HSE oscillator frequency. The divider can be selected by the PLLXTPRE bit.
0xx: HCLK without division
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
PPRE1 - APB1 prescaler bus divider. The APB1 bus frequency must not exceed 36 MHz.
0xx: HCLK without division
100: HCLK / 2
101: HCLK / 4
110: HCLK / 8
111: HCLK / 16
HPRE - AHB prescaler
- 0xxx: SYSCLK without division
- 1000: SYSCLK / 2
- 1001: SYSCLK / 4
- 1010: SYSCLK / 8
- 1011: SYSCLK / 16
- 1100: SYSCLK / 64
- 1101: SYSCLK / 128
- 1110: SYSCLK / 256
- 1111: SYSCLK / 512
- 00: HSI oscillator is used as system clocking source
- 01: HSE oscillator is used as system clock source
- 10: PLL is used as the system clocking source.
- 00: HSI selected as system clock source
- 01: HSE selected as system clock source
- 10: PLL selected as system clock source
In the previous article we looked at clocking the clocking system from the HSE oscillator via a PLL, for convenience I will copy that here:
- 8 MHz HSE quartz
- PLLXTPRE: without division
- PLLSRC: HSE oscillator
- PLLMUL = 9
- SW = PLLCLK
- AHB Prescaler = 1
- APB1 Prescaler = 2
- APB2 Prescaler = 1
Registers in CMSIS
In the second part we learned how to connect CMSIS library to IAR, now we will need this project, as we are moving on to practice. But before that, let's talk a bit about how the access to peripheral registers in CMSIS is organized.
Each instance of a peripheral is a structure that holds all the registers pertaining to that device. In almost all cases, the name of the structure is the same as the name of the peripheral module. For the STM32F103C8 microcontroller, all peripheral module structures are declared in the stm32f103xb.h file:
Code: Select all
#define TIM2 ((TIM_TypeDef *)TIM2_BASE)
#define TIM3 ((TIM_TypeDef *)TIM3_BASE)
#define TIM4 ((TIM_TypeDef *)TIM4_BASE)
#define RTC ((RTC_TypeDef *)RTC_BASE)
#define WWDG ((WWDG_TypeDef *)WWDG_BASE)
#define IWDG ((IWDG_TypeDef *)IWDG_BASE)
#define SPI2 ((SPI_TypeDef *)SPI2_BASE)
#define USART2 ((USART_TypeDef *)USART2_BASE)
#define USART3 ((USART_TypeDef *)USART3_BASE)
#define I2C1 ((I2C_TypeDef *)I2C1_BASE)
#define I2C2 ((I2C_TypeDef *)I2C2_BASE)
#define USB ((USB_TypeDef *)USB_BASE)
#define CAN1 ((CAN_TypeDef *)CAN1_BASE)
#define BKP ((BKP_TypeDef *)BKP_BASE)
#define PWR ((PWR_TypeDef *)PWR_BASE)
#define AFIO ((AFIO_TypeDef *)AFIO_BASE)
#define EXTI ((EXTI_TypeDef *)EXTI_BASE)
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define ADC2 ((ADC_TypeDef *)ADC2_BASE)
#define ADC12_COMMON ((ADC_Common_TypeDef *)ADC1_BASE)
#define TIM1 ((TIM_TypeDef *)TIM1_BASE)
#define SPI1 ((SPI_TypeDef *)SPI1_BASE)
#define USART1 ((USART_TypeDef *)USART1_BASE)
#define SDIO ((SDIO_TypeDef *)SDIO_BASE)
#define DMA1 ((DMA_TypeDef *)DMA1_BASE)
#define DMA1_Channel1 ((DMA_Channel_TypeDef *)DMA1_Channel1_BASE)
#define DMA1_Channel2 ((DMA_Channel_TypeDef *)DMA1_Channel2_BASE)
#define DMA1_Channel3 ((DMA_Channel_TypeDef *)DMA1_Channel3_BASE)
#define DMA1_Channel4 ((DMA_Channel_TypeDef *)DMA1_Channel4_BASE)
#define DMA1_Channel5 ((DMA_Channel_TypeDef *)DMA1_Channel5_BASE)
#define DMA1_Channel6 ((DMA_Channel_TypeDef *)DMA1_Channel6_BASE)
#define DMA1_Channel7 ((DMA_Channel_TypeDef *)DMA1_Channel7_BASE)
#define RCC ((RCC_TypeDef *)RCC_BASE)
#define CRC ((CRC_TypeDef *)CRC_BASE)
#define FLASH ((FLASH_TypeDef *)FLASH_R_BASE)
#define OB ((OB_TypeDef *)OB_BASE)
#define DBGMCU ((DBGMCU_TypeDef *)DBGMCU_BASE)
Code: Select all
RCC->CR |= RCC_CR_HSEON_Msk;
RCC->CR |= (1 << RCC_CR_HSEON_Pos);
I think those who used to program for AVR microcontrollers will see something familiar in these records. Let's consider the first case:
First comes the name of the peripheral module, in our case "RCC". Then the "->" character followed by the register name "CR". RCC_CR_HSEON_Msk is a #define like this:
Code: Select all
#define RCC_CR_HSEON_Msk (1<<16)
Code: Select all
#define RCC_CR_HSEON RCC_CR_HSEON_Msk
The second case looks similar:
where
Code: Select all
#define RCC_CR_HSEON_Pos 16
What about parameters that have several bits? For example, in the CFGR register we want to set the PLL multiplier value equal to nine, which has the code 0111 (see Fig. 2 PLLMUL bits). Here is the solution:
Code: Select all
RCC->CFGR |= RCC_CFGR_PLLMULL_0 | RCC_CFGR_PLLMULL_1 | RCC_CFGR_PLLMULL_2;
RCC->CFGR &= ~(RCC_CFGR_PLLMULL_3);
Code: Select all
RCC->CFGR |= RCC_CFGR_PLLMULL_0 | RCC_CFGR_PLLMULL_1 | RCC_CFGR_PLLMULL_2;
Code: Select all
RCC->CFGR |= (7 << RCC_CFGR_PLLMULL_Pos);
So, here we go! Let's take as a basis the project we created in Part 2, when we connected CMSIS: Let's create a function to which we will add the initialization code:
int ClockInit(void)
{
}
The first thing to do is to start the HSE generator:
Code: Select all
RCC->CR |= (1<<RCC_CR_HSEON_Pos); //Start the HSE generator
Code: Select all
__IO int StartUpCounter;
//Wait for a successful start or timeout expiration
for(StartUpCounter=0; ; StartUpCounter++)
{
//If it was successfully started, then
//exit the loop
if(RCC->CR & (1<<RCC_CR_HSERDY_Pos))
break;
//If it did not start, then
//disable everything that was enabled
//and return an error
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Stop HSE
return 1;
}
}
So, the HSE has been started. Let's move on to PLL setting:
Code: Select all
//Customize PLL
RCC->CFGR |= (0x07<<RCC_CFGR_PLLMULL_Pos) //PLL multiplier is equal to 9
| (0x01<<RCC_CFGR_PLLSRC_Pos); //Tactivate PLL from HSE.
RCC->CR |= (1<<RCC_CR_PLLON_Pos); //Start the PLL
After that we wait for a successful start. Here the waiting is implemented in the same way as for HSE:
Code: Select all
//Wait for a successful start or timeout expiration
for(StartUpCounter=0; ; StartUpCounter++)
{
//If it was successfully started, then
//exit the loop
if(RCC->CR & (1<<RCC_CR_PLLRDY_Pos))
break;
//If for some reason PLL did not start, then
//disable everything that was enabled
//and return an error
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Stop HSE
RCC->CR &= ~(1<<RCC_CR_CR_PLLON_Pos); //Stop PLL
return 2;
}
}
Code: Select all
//Set 2 wait cycles for Flash
//since the kernel frequency will be 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= (0x02<<FLASH_ACR_LATENCY_Pos);
//Delivers
RCC->CFGR |= (0x00<<RCC_CFGR_PPRE2_Pos) //The APB2 bus divider is disabled (leave 0 by default).
| (0x04<<RCC_CFGR_PPRE1_Pos) //The APB1 niche divider is 2
| (0x00<<RCC_CFGR_HPRE_Pos); //AHB tire splitter disabled (leave 0 by default)
RCC->CFGR |= (0x02<<RCC_CFGR_SW_Pos); //Switch to PLL operation.
Wait for switching to be completed:
Code: Select all
//Waiting for switching
while((RCC->CFGR & RCC_CFGR_SWS_Msk) != (0x02<<RCC_CFGR_SWS_Pos))
{
}
Code: Select all
//After switching to an
//external tac source
//disable the internal RC generator
//to save power
RCC->CR &= ~(1<<RCC_CR_HSION_Pos);
Code: Select all
//Customize the system clocking from the external quartz
//through PLL at the highest possible frequencies.
//The external quartz should be at 8MHz
//Returns:
// 0 - completed successfully
// 1 - quartz oscillator failed to start
// 2 - PLL did not start
int ClockInit(void)
{
__IO int StartUpCounter;
////////////////////////////////////////////////////////////
//Start the crystal oscillator
////////////////////////////////////////////////////////////
RCC->CR |= (1<<RCC_CR_HSEON_Pos); //Start HSE generator
//Wait for successful start or end of timeout
for(StartUpCounter=0; ; StartUpCounter++)
{
//If it was successfully started, then
//exit the loop
if(RCC->CR & (1<<RCC_CR_HSERDY_Pos))
break;
//If it did not start, then
//disable everything that was enabled
//and return an error
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Stop HSE
return 1;
}
}
////////////////////////////////////////////////////////////
//Customize and run PLL
////////////////////////////////////////////////////////////
//Customize PLL
RCC->CFGR |= (0x07<<RCC_CFGR_PLLMULL_Pos) //PLL multiplier is equal to 9
| (0x01<<RCC_CFGR_PLLSRC_Pos); //Tactivate PLL from HSE
RCC->CR |= (1<<RCC_CR_PLLON_Pos); //Start PLL
//Wait for a successful start or timeout expiration
for(StartUpCounter=0; ; StartUpCounter++)
{
//If it has successfully started, then
//exit the loop
if(RCC->CR & (1<<RCC_CR_PLLRDY_Pos))
break;
//If for some reason PLL did not start, then
//disable everything that was enabled
//and return an error
if(StartUpCounter > 0x1000)
{
RCC->CR &= ~(1<<RCC_CR_HSEON_Pos); //Stop HSE
RCC->CR &= ~(1<<RCC_CR_CR_PLLON_Pos); //Stop PLL
return 2;
}
}
////////////////////////////////////////////////////////////
//Customize FLASH and dividers
////////////////////////////////////////////////////////////
//Set 2 wait cycles for Flash
//since the core frequency will be 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= (0x02<<FLASH_ACR_LATENCY_Pos);
//Delivers
RCC->CFGR |= (0x00<<RCC_CFGR_PPRE2_Pos) // APB2 bus divider disabled
| (0x04<<RCC_CFGR_PPRE1_Pos) //The APB1 niche divider is 2
| (0x00<<RCC_CFGR_HPRE_Pos); //AHB bus divider is disabled
RCC->CFGR |= (0x02<<RCC_CFGR_SW_Pos); //Switch to PLL operation
//Wait until we switch
while((RCC->CFGR & RCC_CFGR_SWS_Msk) != (0x02<<RCC_CFGR_SWS_Pos))
{
}
//After switching to the
//external tucking source
//disable the internal RC generator
//to save power
RCC->CR &= ~(1<<RCC_CR_HSION_Pos);
//System setup and reclocking
//to an external crystal oscillator
//and the PLL has succeeded.
//Exit
return 0;
}