Hi, A bit new to embedded programming. I am trying to trace down where all the registers (such as pll setting, IO, ...) get updated. For example, for the STM32F0 processor, below are the code to set system clock but the codes only have the registers that get modified, but how the register get updated by the processor? My guess is there is some type of interrupt mechanism that detects if there is a change in the internal registers, the interrupt will trigger a subroutine that will update the registers. My other question is what part of the linker that associate each of the internal register to the actual register of the hardware? For example if you look at the STM32F0 driver file, there are a lot of "structs" defined for the IO but how are these basically purely C variables get associated with the actual register? If you look at the SetSysClock() function below, the "RCC" type is only a C variables, where does this variable get assign to a hard register in the processor?
1 | /**
|
2 | * @brief Configures the System clock frequency, AHB/APBx prescalers and Flash
|
3 | * settings.
|
4 | * @note This function should be called only once the RCC clock configuration
|
5 | * is reset to the default reset state (done in SystemInit() function).
|
6 | * @param None
|
7 | * @retval None
|
8 | */
|
9 | static void SetSysClock(void) |
10 | {
|
11 | __IO uint32_t StartUpCounter = 0, HSEStatus = 0; |
12 | |
13 | /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/
|
14 | /* Enable HSE */
|
15 | RCC->CR |= ((uint32_t)RCC_CR_HSEON); |
16 | |
17 | /* Wait till HSE is ready and if Time out is reached exit */
|
18 | do
|
19 | {
|
20 | HSEStatus = RCC->CR & RCC_CR_HSERDY; |
21 | StartUpCounter++; |
22 | } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); |
23 | |
24 | if ((RCC->CR & RCC_CR_HSERDY) != RESET) |
25 | {
|
26 | HSEStatus = (uint32_t)0x01; |
27 | }
|
28 | else
|
29 | {
|
30 | HSEStatus = (uint32_t)0x00; |
31 | }
|
32 | |
33 | if (HSEStatus == (uint32_t)0x01) |
34 | {
|
35 | /* Enable Prefetch Buffer and set Flash Latency */
|
36 | FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; |
37 | |
38 | /* HCLK = SYSCLK */
|
39 | RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; |
40 | |
41 | /* PCLK = HCLK */
|
42 | RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1; |
43 | |
44 | /* PLL configuration = HSE * 6 = 48 MHz */
|
45 | RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); |
46 | RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6); |
47 | |
48 | /* Enable PLL */
|
49 | RCC->CR |= RCC_CR_PLLON; |
50 | |
51 | /* Wait till PLL is ready */
|
52 | while((RCC->CR & RCC_CR_PLLRDY) == 0) |
53 | {
|
54 | }
|
55 | |
56 | /* Select PLL as system clock source */
|
57 | RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); |
58 | RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; |
59 | |
60 | /* Wait till PLL is used as system clock source */
|
61 | while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL) |
62 | {
|
63 | }
|
64 | }
|
65 | else
|
66 | { /* If HSE fails to start-up, the application will have wrong clock |
67 | configuration. User can add here some code to deal with this error */
|
68 | }
|
69 | }
|
:
Edited by Moderator
It is much less complicated than you might think. All these names like RCC are simply pointers to structures (usually hidden as preprocessing macros), and the actual elements of these structures simply are the (IO) registers. All ARM IO registers are memory-mapped, i.e. they appear to be a part of the normal address space, but when the processor decodes the respective address, it does not access SRAM or Flash but triggers a read or write operation to an IO register.
As a sidenote, it may be worth tracing down the actual declaration and definition of RCC and other I/O likewise. The word "trigger" may cause some doubt, whether the controller behaves in a way which deviates only slightly from what your, Andy, hypothesis was. (Sorry, Jörg. I say that with all respect). But "trigger" does not mean the causing of a (sub-)process. It may be reworded as, "that a certain address, which belongs to an I/O-register, directs whatever shall be read or written to that register". Hope, that this remark is usefull.
Jörg W. wrote: > It is much less complicated than you might think. > > All these names like RCC are simply pointers to structures (usually > hidden as preprocessing macros), and the actual elements of these > structures simply are the (IO) registers. All ARM IO registers > are memory-mapped, i.e. they appear to be a part of the normal > address space, but when the processor decodes the respective address, > it does not access SRAM or Flash but triggers a read or write operation > to an IO register. Hi Jorg, Thanks for the reply. "(usually hidden as preprocessing macros)" I am using Eclipse CDT. Where can I look into these preprocessing macros? For example, here is an excerpt of the IO data structures but these are only the data structures. How do the macros map these data structures to the actual registers or memory location? There has to be some prior knowledge between the compiler and the STM32F0 IO structure, because these are purely C declare variables. I assume as you mentioned, the preprocessing macros has some prior knowledge of these data structure before hand and map these into the memory accordingly. Could you point me to where I can read about the preprocessing macros? Thanks, -Andy file :stm32f0xx.h /*********************************************************************** *******/ /* */ /* General Purpose IOs (GPIO) */ /* */ /*********************************************************************** *******/ /******************* Bit definition for GPIO_MODER register *****************/ #define GPIO_MODER_MODER0 ((uint32_t)0x00000003) #define GPIO_MODER_MODER0_0 ((uint32_t)0x00000001) #define GPIO_MODER_MODER0_1 ((uint32_t)0x00000002) #define GPIO_MODER_MODER1 ((uint32_t)0x0000000C) #define GPIO_MODER_MODER1_0 ((uint32_t)0x00000004) #define GPIO_MODER_MODER1_1 ((uint32_t)0x00000008) #define GPIO_MODER_MODER2 ((uint32_t)0x00000030) #define GPIO_MODER_MODER2_0 ((uint32_t)0x00000010) #define GPIO_MODER_MODER2_1 ((uint32_t)0x00000020) #define GPIO_MODER_MODER3 ((uint32_t)0x000000C0) Here are the RCC typedefs. These are basically generic C declarations. There are really nothing unique about them. typedef struct { __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x04 */ __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x08 */ __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x0C */ __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x10 */ __IO uint32_t AHBENR; /*!< RCC AHB peripheral clock register, Address offset: 0x14 */ __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x18 */ __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x1C */ __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x20 */ __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x24 */ __IO uint32_t AHBRSTR; /*!< RCC AHB peripheral reset register, Address offset: 0x28 */ __IO uint32_t CFGR2; /*!< RCC clock configuration register 2, Address offset: 0x2C */ __IO uint32_t CFGR3; /*!< RCC clock configuration register 3, Address offset: 0x30 */ __IO uint32_t CR2; /*!< RCC clock control register 2, Address offset: 0x34 */ } RCC_TypeDef;
:
Edited by User
Andy V. wrote: > Where can I look into these preprocessing macros? Sorry, no idea. I've been using some Atmel ARMs so far, and there's a rather scattered filesystem subtree under a directory name CMSIS where these macros come from.
Jörg W. wrote: > Andy V. wrote: >> Where can I look into these preprocessing macros? > > Sorry, no idea. I've been using some Atmel ARMs so far, and there's > a rather scattered filesystem subtree under a directory name CMSIS > where these macros come from. Thanks. I'll try to look into this, but it seems you're right in general. There seems to be a convention for these hardware related registers that have to be declared in certain way. SMSIS stands for: Cortex Microcontroller Software Interface Standard So it has to do with the compiler, preprocessor parsing these files and compiler these specifically for the hardware. Here's a link to CMSIS: file:///C:/LinuxEmbSrc/STM32F0-Discovery_FW_V1.0.0/Libraries/CMSIS/Docum entation/CMSIS_Core.htm
:
Edited by User
Andy V. wrote: > SMSIS stands for: Cortex Microcontroller Software Interface Standard Yep, but Atmel also deliveres their per-CPU definitions along with the official CMSIS stuff (which is standardized by ARM), just in a different directory tree.
Jörg W. wrote: > Andy V. wrote: >> SMSIS stands for: Cortex Microcontroller Software Interface Standard > > Yep, but Atmel also deliveres their per-CPU definitions along with > the official CMSIS stuff (which is standardized by ARM), just in a > different directory tree. OK, I mean STM32F0 also has its own "drivers" but these drivers basically calls the functions from CMSIS. By the way, do you happen to know where to download the Atmel drivers? (I can search ...) A more complete definition of CMSIS is from Keil website http://www.keil.com/pack/doc/CMSIS/Driver/html/index.html I guess all these hardware related "C" variables have to be standardized otherwise how would the compiler know which is ARM core specifics and which are user app specific. The part I am still trying to figure out is when you compile your program, explicitly where exactly in the GNU_GCC that read in these core drivers? I mean it has to be somewhere int he GNU_GCC just that I am not able to find it yet.
:
Edited by User
Andy V. wrote: > By the way, do you happen to know where to download the Atmel drivers? Unfortunately, they ship these header (and C) files only as part of their (several 100 MB fat) Atmel Studio. Note that I explicitly don't mean anything like a "driver" (that would be Atmel's ASF) but really simply a processor (rather: MCU) definition. Example (PIO port):
1 | #if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
2 | typedef struct { |
3 | __IO PORT_DIR_Type DIR; /**< \brief Offset: 0x00 (R/W 32) Data Direction */ |
4 | __IO PORT_DIRCLR_Type DIRCLR; /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */ |
5 | __IO PORT_DIRSET_Type DIRSET; /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */ |
6 | __IO PORT_DIRTGL_Type DIRTGL; /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */ |
7 | __IO PORT_OUT_Type OUT; /**< \brief Offset: 0x10 (R/W 32) Data Output Value */ |
8 | __IO PORT_OUTCLR_Type OUTCLR; /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */ |
9 | __IO PORT_OUTSET_Type OUTSET; /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */ |
10 | __IO PORT_OUTTGL_Type OUTTGL; /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */ |
11 | __I PORT_IN_Type IN; /**< \brief Offset: 0x20 (R/ 32) Data Input Value */ |
12 | __IO PORT_CTRL_Type CTRL; /**< \brief Offset: 0x24 (R/W 32) Control */ |
13 | __O PORT_WRCONFIG_Type WRCONFIG; /**< \brief Offset: 0x28 ( /W 32) Write Configuration */ |
14 | RoReg8 Reserved1[0x4]; |
15 | __IO PORT_PMUX_Type PMUX[16]; /**< \brief Offset: 0x30 (R/W 8) Peripheral Multiplexing n */ |
16 | __IO PORT_PINCFG_Type PINCFG[32]; /**< \brief Offset: 0x40 (R/W 8) Pin Configuration n */ |
17 | RoReg8 Reserved2[0x20]; |
18 | } PortGroup; |
19 | #endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ |
20 | |
21 | /** \brief PORT APB hardware registers */
|
22 | #if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
23 | typedef struct { |
24 | PortGroup Group[2]; /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */ |
25 | } Port; |
just pick the CTRL subregister:
1 | typedef union { |
2 | struct { |
3 | uint32_t SAMPLING:32; /*!< bit: 0..31 Input Sampling Mode */ |
4 | } bit; /*!< Structure used for bit access */ |
5 | uint32_t reg; /*!< Type used for register access */ |
6 | } PORT_CTRL_Type; |
and finally:
1 | #define PORT ((Port *)0x41004400U) /**< \brief (PORT) APB Base Address */ |