0
\$\begingroup\$

I am writing a program on STM32F4 Discovery where I have to store address of Timer's CCR register into a pointer and then change register via this pointer, ie

volatile uint32_t* tim_ccr_reg = reinterpret_cast<uint32_t*>(TIM4_BASE + 0x34);
// 0x34 = offset of CCR1 register
*tim_ccr_reg = 1999; // Set CCR1 to 1999

When I test this alone in an "empty program" it works, but when I use my entire program (along with all other features I implemented) it causes hard fault at one of those assignments. Now Im figuring out whether its my other code which displaces something in memory or I'm saving register address the wrong way and it only shows when more complex program is loaded. In documentation I read

The simplest way to implement memory-mapped variables is to use pointers to fixed addresses.

#define PORTBASE 0x40000000
unsigned int volatile * const port = (unsigned int *) PORTBASE;

The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:

*port = value; /* write to port */
value = *port; /* read from port */

This approach can be used to access 8, 16 or 32 bit registers, but be sure to declare the variable with the appropriate type for its size, i.e., unsigned int for 32-bit registers, unsigned short for 16-bit, and unsigned char for 8-bit. You should also ensure that the memory-mapped registers lie on appropriate address boundaries, e.g. either all word-aligned, or aligned on their natural size boundaries, i.e., 16-bit registers must be aligned on half-word addresses (but note that ARM recommends that all registers, whatever their size, be aligned on word boundaries.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html

Then I went to see documentation for CCR registers for timers and it says

enter image description here

so now I'm not certain, is the register always 32-bit and only 16-bits are used in some timers or is register in some cases 16-bit only and I have to use 16-bit pointer to write to it?

In any case, I would like to know what is the proper way of saving timer CCR register to a variable. If I second click on CCR1 in following command

TIM4->CCR1 = 1999;

and click "go to definition" it takes me to some weird chunk of code

/** \brief  ITM Send Character

    This function transmits a character via the ITM channel 0.
    It just returns when no debugger is connected that has booked the output.
    It is blocking when a debugger is connected, but the previous character send is not transmitted.

    \param [in]     ch  Character to transmit
    \return             Character to transmit
 */
static __INLINE uint32_t ITM_SendChar (uint32_t ch)
{
  if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)  &&      /* Trace enabled */
      (ITM->TCR & ITM_TCR_ITMENA_Msk)                  &&      /* ITM enabled */
      (ITM->TER & (1UL << 0)        )                    )     /* ITM Port #0 enabled */
  {
    while (ITM->PORT[0].u32 == 0);
    ITM->PORT[0].u8 = (uint8_t) ch;
  }
  return (ch);
}

Im (very) confused.

\$\endgroup\$
  • \$\begingroup\$ If you consult the header files for the appropriate version of the STM32 standard peripheral library you will find a working definition of the timer register you are interested in compatible with a C or C++ compiler. You may be over-relying on contextual help from your IDE, you should probably consult the sources directly yourself. \$\endgroup\$ – Chris Stratton Jan 8 '17 at 18:18
  • \$\begingroup\$ So far everything looks good to me. Hard fault can happen if you try to write to a incalid memory address. Recheck all memory pointers which your code tries to dereference and write to. And also: since STM32F4 uses a 32-bit processor, you should be able to use 32-bit variables for all register reads and writes. Unused bits usually read as zeroes. (dont trust this though without checking the datasheet) \$\endgroup\$ – olltsu Jan 8 '17 at 19:34
  • \$\begingroup\$ By observing registers while executing step by step I noticed that when I test this alone it has correct IO register address loaded, but when I execute it along with other code it doesn't. Something changes the value of pointers, even though there is only one place they get set and when I checked there they are set correctly. I will check all the code now. Thanks! \$\endgroup\$ – Terraviper-5 Jan 8 '17 at 20:24
  • \$\begingroup\$ @olitsua - actually the STM32 series has memory mapped registers which can only be accessed at certain access widths, which are not always 32 bits. You're talking to dedicated logic. \$\endgroup\$ – Chris Stratton Jan 8 '17 at 20:24
  • \$\begingroup\$ @ChrisStratton So that means I am using uint32_t* to access uint16_t because the timer 4 has 16-bit CCR. What I dont understand is why it doesnt cause a hard fault when executed alone. But wouldnt that mean that different timer's CCR1 registers can't be offset by same 0x34 value because of different sizes? Great, I thought I solved the problem of how to universally access all timer's CCRs but looks like I havent... \$\endgroup\$ – Terraviper-5 Jan 8 '17 at 20:26
0
\$\begingroup\$

At its most basic way, you cast the address as a "pointer to 32 bit value", dereference it and get the value.

x=*((int*)(0x40000800+0x34))

(int*) is the casting, making the number a pointer which points to an int type.

*(....) is to dereference the pointer.

0x40000800+0x34 is in the parenthesis to prevent an addition error. Casting has predecence over addition and addition an integer to a pointer not the same as addition of integers.

For taking a step further, define the peripherals as structs. It has to be guaranteed by the compiler that it will take place in memory as the sequence you write. Don't forget that structs are virtual definitions like 'int'. For example:

struct TIMg_st {volatile u32 CR1,bos0,SMCR,DIER,SR,EGR,CCMR1,bos1,CCER,CNT,PSC,ARR,bos2,CCR1,bos3[6],OR;};

When you make a pointer which points to that struct (so not an int type this time), for example:

(struct TIMg_st*)0x40000800

You can reach its elements without hassle. For example:

*((struct TIMg_st*)0x40000800).CCR1

Or, for an easier scripting;

((struct TIMg_st*)0x40000800)->CCR1

To make things easier just turn it into a definition:

#define TIM4 ((struct TIMg_st*)0x40000800)

So now it is just

TIM4->CCR1

and this is simply an unsigned 32 bit value. The value in the register might be 16 bit or 8 bit, that is not a problem. The important thing is their allocation width and you design the struct according to that. All the elements of structure are uint32 because they are all 32 bit allocated.

\$\endgroup\$
  • \$\begingroup\$ @Terraviper-5 thanks but I have misunderstood your question and totally missed the main concern, right? I think you should better unaccept it. \$\endgroup\$ – Ayhan Jan 8 '17 at 21:56
  • \$\begingroup\$ No you didn't, you showed me how those structures work and how to get address of a register the proper way. That way I could be sure the mistake was elsewhere in my code and indeed it was, I made the stupidest mistake ever, assigned object to reference and when object went out of scope it was deleted. Then I called functions on this reference and it was trashing around in memory, thats why a wrong register address of CCR was loaded and accessed, which caused a hard fault. I was afraid that I cant use uint32_t* to access 16-bit register, but looks like I can do it that way. \$\endgroup\$ – Terraviper-5 Jan 8 '17 at 22:36
  • \$\begingroup\$ For reference, ARM peripheral registers are usually defined as 32 bit registers (except for the 64 bit ones, which you can access as 2 separate 32 bit ones), even if the register only has one or two real bits underneath. There are exceptions, but these are usually clearly noted. \$\endgroup\$ – Sean Houlihane Jan 8 '17 at 23:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.