Skip to main content
eidetech
Associate II
September 17, 2021
Solved

Problem with outputting LSE clock on PA8 pin as MCO

  • September 17, 2021
  • 4 replies
  • 1823 views

Hi!

I'm trying to output the LSE clock on the PA8 pin on my Nucleo64 STM32L476 board.

The code I have written is the following:

#include<stdint.h>
 
#define RCC_BASE_ADDR 0x40021000UL
 
#define RCC_CFGR_REG_OFFSET 0x08UL
 
#define RCC_CFGR_REG_ADDR (RCC_BASE_ADDR + RCC_CFGR_REG_OFFSET )
 
#define GPIOA_BASE_ADDR 0x48000000UL
 
int main(void)
{
	uint32_t *pRccCfgrReg = (uint32_t*) RCC_CFGR_REG_ADDR;
 
 
	//1. Configure the RCC_CFGR MCOSEL bit fields to select LSE as clock source: 0111: LSE clock selected
	*pRccCfgrReg &=~(0U << 27);
	*pRccCfgrReg |= (1U << 26);
	*pRccCfgrReg |= (1U << 25);
	*pRccCfgrReg |= (1U << 24);
 
	//2. Configure PA8 to AF0 mode to behave as MCO signal
 
	//a ) Enable the peripheral clock for GPIOA peripheral
	// RCC_AHB2ENR
	 uint32_t *pRCCAhb2Enr = (uint32_t*)(RCC_BASE_ADDR + 0x4C);
	*pRCCAhb2Enr |= (1U << 0); //Enable GPIOA peripheral clock
 
	//b ) Configure the mode of GPIOA pin 8 as alternate function mode
// GPIOx_MODER
	uint32_t *pGPIOAModeReg = (uint32_t*)(GPIOA_BASE_ADDR + 0x00UL);
	*pGPIOAModeReg &=~(1U << 16); 	// clear bit 16
	*pGPIOAModeReg |= (1U << 17); // set bit 17
 
	//c ) Configure the alternation function register to set the mode 0 for PA8
	// GPIOx_AFRH
	uint32_t *pGPIOAAltFunHighReg = (uint32_t*)(GPIOA_BASE_ADDR + 0x24);
	*pGPIOAAltFunHighReg &=~(1U << 0);
	*pGPIOAAltFunHighReg &=~(1U << 1);
	*pGPIOAAltFunHighReg &=~(1U << 2);
	*pGPIOAAltFunHighReg &=~(1U << 3);
 
	uint32_t *pRCC_BDCR = (uint32_t*)(RCC_BASE_ADDR + 0x90);
 
	// Turn on LSE clock (LSEON)
	*pRCC_BDCR |= (1U << 0);
	for (;;);
}

Can anyone see an obvious mistake? Is there something I have forgotten? I have read that the LSE clock might take some time to start, how can I improve my code to get around this?

An observation I have made is that the LSERDY bit is never set to 1, and I suspect this to be the issue. What can cause this bit to always stay 0?

Sincerely,

Marcus

This topic has been closed for replies.
Best answer by Tesla DeLorean

Don't you need to unlock the low power domain before you can change RCC->BDCR ?

4 replies

Tesla DeLorean
Tesla DeLoreanBest answer
Guru
September 17, 2021

Don't you need to unlock the low power domain before you can change RCC->BDCR ?

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
eidetech
eidetechAuthor
Associate II
September 17, 2021

That could be, although I have not read anything about it in the RM. How do I unlock the low power domain? I'm new to STM32 :)

waclawek.jan
Super User
September 17, 2021

Read the description of RCC_BDCR register - it directs you to PWR_CR1.DBP.

Don't forget to enable PWR's clock in RCC.

JW

eidetech
eidetechAuthor
Associate II
September 17, 2021

Ah! Found it, thanks a lot @Community member​ , now I get what you referred to @Community member​ .

What do you mean by "Don't forget to enable PWR's clock in RCC." @Community member​ ?

EDIT:

Nvm, I got it @Community member​ .

eidetech
eidetechAuthor
Associate II
September 17, 2021

Fantastic! I got it working now that you guys told me how it's done. Thanks a lot for the help both of you.

Here is the final code for anyone stumbling across this post at a later date:

#include<stdint.h>
 
#define RCC_BASE_ADDR 0x40021000UL
#define PWR_BASE_ADDR 0x40007000UL
 
#define RCC_CFGR_REG_OFFSET 0x08UL
 
#define RCC_CFGR_REG_ADDR (RCC_BASE_ADDR + RCC_CFGR_REG_OFFSET )
 
#define GPIOA_BASE_ADDR 0x48000000UL
 
int main(void)
{
	uint32_t *pRccCfgrReg = (uint32_t*) RCC_CFGR_REG_ADDR;
 
 
	// 1. Configure the RCC_CFGR MCOSEL bit fields to select LSE as clock source: 0111: LSE clock selected
	*pRccCfgrReg &=~(0U << 27);
	*pRccCfgrReg |= (1U << 26);
	*pRccCfgrReg |= (1U << 25);
	*pRccCfgrReg |= (1U << 24);
 
	// 2. Configure PA8 to AF0 mode to behave as MCO signal
	// 2.1 Enable the peripheral clock for GPIOA peripheral - RCC_AHB2ENR
	uint32_t *pRCCAhb2Enr = (uint32_t*)(RCC_BASE_ADDR + 0x4C);
	*pRCCAhb2Enr |= (1U << 0); //Enable GPIOA peripheral clock
 
	// 2.2 Configure the mode of GPIOA pin 8 as alternate function mode - GPIOx_MODER
	uint32_t *pGPIOAModeReg = (uint32_t*)(GPIOA_BASE_ADDR + 0x00UL);
	*pGPIOAModeReg &=~(1U << 16); 	// clear bit 16
	*pGPIOAModeReg |= (1U << 17); // set bit 17
 
	// 2.3 Configure the alternation function register to set the mode 0 for PA8 - GPIOx_AFRH
	uint32_t *pGPIOAAltFunHighReg = (uint32_t*)(GPIOA_BASE_ADDR + 0x24);
	*pGPIOAAltFunHighReg &=~(1U << 0); // clear bit 0
	*pGPIOAAltFunHighReg &=~(1U << 1); // clear bit 1
	*pGPIOAAltFunHighReg &=~(1U << 2); // clear bit 2
	*pGPIOAAltFunHighReg &=~(1U << 3); // clear bit 3
 
	// 3. Turn on PWR peripheral clock
	uint32_t *pRCC_APB1ENR1 = (uint32_t*)(RCC_BASE_ADDR + 0x58);
	*pRCC_APB1ENR1 |= (1U << 28);
 
	// 4. Disable backup domain write protection
	uint32_t *pPWR_CR1 = (uint32_t*)(PWR_BASE_ADDR + 0x00);
	*pPWR_CR1 |= (1U << 8);
 
	// 5. Turn on LSE clock (LSEON)
	uint32_t *pRCC_BDCR = (uint32_t*)(RCC_BASE_ADDR + 0x90);
	*pRCC_BDCR |= (1U << 0);
	for (;;);
}

waclawek.jan
Super User
September 18, 2021

Glad you got it working.

Please select Clive's (Tesla's) post as Best so that thread is marked as solved.

[stylistics]

You may want to change registers' content in one operation rather than bit by bit. Here it does not matter but I'm runtime code it would be rather inefficient.

JW

eidetech
eidetechAuthor
Associate II
September 18, 2021

Good, I have done that now.

In regards to the bit by bit style of programming: I am quite new to bare metal programming, and therefore I find it easier to understand all the operations by splitting them up, and I know that it is not the most efficient way of doing things. I guess I'll combine operations and write more efficient code as I get more familiar with the concepts. But thanks for the heads up anyways!