give specific address to variable in Computer and STM32 - c

i have been working with STM32 hal drivers and i noticed when they want to give a value to a register , they use some code like this :
#define __IO volatile
typedef struct
{
__IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */
__IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */
__IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */
__IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */
__IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */
__IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */
__IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */
__IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */
__IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */
__IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */
__IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */
__IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */
} USART_TypeDef;
#define D2_APB1PERIPH_BASE (0x40000000UL)
#define UART4_BASE (D2_APB1PERIPH_BASE + 0x4C00UL)
#define UART4 ((USART_TypeDef *) UART4_BASE)
UART4->CR = whatever!
so i tried to test on computer with C but i got a little confused , the program below is the one i wrote :
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define startaddress ((mytype*) (0x6d9ff93b))
typedef struct
{
volatile uint32_t myTypeValue;
}mytype;
int main()
{
mytype* inst;
inst = startaddress;
inst->myTypeValue = 0x10;
printf("address is : %x \n",inst);
return 0;
}
output :
Process finished with exit code -1073741819 (0xC0000005)
the program will crash at inst->myTypeValue = 0x10; . otherwise it will show the address which has been assign to it . for example this is the output :
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define startaddress ((mytype*) (0x6d9ff93b))
typedef struct
{
volatile uint32_t myTypeValue;
}mytype;
int main()
{
mytype* inst;
inst = startaddress;
// inst->myTypeValue = 0x10;
printf("address is : %x \n",inst);
return 0;
}
output :
address is : 6d9ff93b
Process finished with exit code 0
now i have some question , dose the stm32 library works because the address is a register and not the SRAM ? because i remember when i use SDRAM on FMC i can do the same at address 0xC0000000 and it works perfectly.
if the assignment is wrong why does the compiler works on second example and only crashes when i am trying to use the variable inside the struct ? why not crash at the assignment inst = startaddress; it self ?

Those addresses in STM32 are defined and they reference memory-mapped hardware registers.
Your code is different. To be equivalent to the STM one it has to be:
#define inst ((mytype*) (0x6d9ff93b))
typedef struct
{
volatile uint32_t myTypeValue;
}mytype;
int main()
{
inst -> mytepevalue = 10;
.....
dose the stm32 library works because the address is a register and not
the SRAM ?
Yes. Those addresses you see in the STM CMSIS headers (not the library!!!!) are defined by the manufacturer and are hardware related.
because i remember when i use SDRAM on FMC i can do the same at
address 0xC0000000 and it works perfectly.
It is because one of banks of memory managed by the Flexible Memory Controller is exposed to the core at this address. The chip manufacturer also defines those addresses.
if the assignment is wrong why does the compiler works on second
example and only crashes when i am trying to use the variable inside
the struct ? why not crash at the assignment inst = startaddress; it
self ?
Because you define a pointer and assign it with value and it is OK. But this pointer contains an invalid reference and when you try to dereference it - the system fails.
I think you need to reread the pointers chapter in your favourite C book as your understanding of pointers is rather limited. It is quite important knowledge if you want to bare-metal program uCs

Related

#define statement for address of memory

What does this #define statement do? It is used to define an address of memory. But I don't understand the (uint32_t *) part
#define GPxDAT (uint32_t *) 0x6FC0
It is usually used to access hardware registers mapped into the address space, or some particular memory addresses
Hardware registers should be defined as volatile as registers can change without any program activity (as they are changed by the hardware).
#define GPIOREGA ((volatile uint32_t *) 0x6FC0)
then you can assign or read this memory location (*GPIOREGA = something; something = *GPIOREGA )
Sometimes much more complex data structures are used this way (example from the STM32 headers)
#define __IO volatile
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
#define PERIPH_BASE 0x40000000U /*!< Peripheral base address in the alias region */
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000U)
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000U)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
And you can use it is as any normal pointer
GPIOA -> MODER |= (1 << 15);
The (uint32_t *) part is what is called a cast, it converts the integer number 0x6FC0 to a pointer(an address) to an unsigned integer, 32bits wide (which is what the uint32_t type represents.) Basically a type conversion to a pointer to an unsigned integer. In my opinion, the expression is not completely safe. While the cast operator is high precedence, it should be written as:
#define GPxDAT ((uint32_t *) 0x6FC0)
rounding the whole expression in parenthesis, so it will not fail, wherever you put it.
Writing this kind of defines, you can access GPxDAT[0] as a 32bit unsigned integer at the address 0x6fc0, GPxDAT[1] as the next 32bit address (above the previous one) all of them based at the address represented by the number.

How compiler knows if a pointer address in C is port mapped or RAM?

I know that port mapped IO are accessed thorugh in/out CPU instructions and memory (& memory mapped registers) are accessed through load/store CPU instructions (similar to memory). But, with a pointer in C code, how compiler knows if a address is port mapped IO register or memory and then inserts the correct CPU instructions?
Example:
uint16_t const* uart_reg = 0x8000c000;
uint16_t const* ram_addr = 0x4000c000;
*uart_reg = 0x1;
*ram_addr = 0x12;
If you have non-memory-mapped I/O, you're not going to be able to access it from C as if it were memory.
You must use some platform-specific trickery to get the proper I/O instructions. This is a pain, and might be one reason why modern hardware seems to favor memory-mapped I/O.
C compilers for platforms with port-mapped I/O had to include this, see for instance the inp() function in the old Turbo C++ for DOS.
Two steps:
Install the correct toolchain for your target hardware.
Use provided header files with all the necessary properly dome definitions
For example if you want to program ARM STM uC bare metal gcc compiler - use arm-none-eabi-gcc and tools. Use header CMSIS header files provided by STM & ARM (usually they are already included if you choose a "ready made" toolchain)
Example declarations and definitions (only for ADC1):
#define FLASH_BASE ((uint32_t)0x08000000U) /*!< FLASH base address in the alias region */
#define CCMDATARAM_BASE ((uint32_t)0x10000000U) /*!< CCM(core coupled memory) data RAM base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000U) /*!< SRAM base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000U) /*!< Peripheral base address in the alias region */
#define SRAM_BB_BASE ((uint32_t)0x22000000U) /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000U) /*!< Peripheral base address in the bit-band region */
#define AHB3PERIPH_BASE (PERIPH_BASE + 0x10000000U)
#define ADC1_BASE (AHB3PERIPH_BASE + 0x00000000U)
#define ADC1 ((ADC_TypeDef *) ADC1_BASE)
#ifdef __cplusplus
#define __I volatile /*!< Defines 'read only' permissions */
#else
#define __I volatile const /*!< Defines 'read only' permissions */
#endif
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
/* following defines should be used for structure members */
#define __IM volatile const /*! Defines 'read only' structure member permissions */
#define __OM volatile /*! Defines 'write only' structure member permissions */
#define __IOM volatile /*! Defines 'read / write' structure member permissions */
typedef struct
{
__IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */
__IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */
__IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */
__IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */
uint32_t RESERVED0; /*!< Reserved, 0x010 */
__IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */
__IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */
uint32_t RESERVED1; /*!< Reserved, 0x01C */
__IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */
__IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */
__IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */
uint32_t RESERVED2; /*!< Reserved, 0x02C */
__IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */
__IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */
__IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */
__IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */
__IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */
uint32_t RESERVED3; /*!< Reserved, 0x044 */
uint32_t RESERVED4; /*!< Reserved, 0x048 */
__IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */
uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */
__IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */
__IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */
__IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */
__IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */
uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */
__IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */
__IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */
__IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */
__IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */
uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */
__IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */
__IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */
uint32_t RESERVED8; /*!< Reserved, 0x0A8 */
uint32_t RESERVED9; /*!< Reserved, 0x0AC */
__IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */
__IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */
} ADC_TypeDef;
and it expands to: ((ADC_TypeDef *) ((((uint32_t)0x40000000U) + 0x10000000U) + 0x00000000U))
It is not worth to do it manually.

How to access struct fields pointers inside another struct and write correctly?

I have the following structs (from a library I'm using) with some fields and I would like to assign with OR operation a new value. But I'm debugging and I can't see how anything is writing there in stm32l4xx_hal_tim.c file:
typedef struct
{
TIM_TypeDef *Instance; /*!< Register base address */
TIM_Base_InitTypeDef Init; /*!< TIM Time Base required parameters */
HAL_TIM_ActiveChannel Channel; /*!< Active channel */
DMA_HandleTypeDef *hdma[7]; /*!< DMA Handlers array This array is accessed by a #ref DMA_Handle_index */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_TIM_StateTypeDef State; /*!< TIM operation state */
}TIM_HandleTypeDef;
typedef struct
{
__IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */
__IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */
__IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */
__IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */
__IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */
__IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */
__IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */
__IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */
__IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */
__IO uint32_t CNT; /*!< TIM counter register,
} TIM_TypeDef;
I have a part of code where I defined: TIM_HandleTypeDef TIMER_Struct;
And I would like to access the field "CR1" of TIM_TypeDef struct that is "*Instance" field of TIM_HandleTypeDef. So I have done it by this way in a function DRV_TIMER_init():
#include "main.h"
#include "stm32l4xx_hal_tim.h"
uint32_t uwPrescalerValue = 0;
TIM_HandleTypeDef TIMER_Struct;
void DRV_TIMER_init(void);
int main(void)
{
DRV_TIMER_init();
while(1)
{
}
}
//where uint32_t SystemCoreClock = 4000000; in other system source file.
void DRV_TIMER_init(void)
{
uwPrescalerValue = (uint32_t)(SystemCoreClock / 1000000) - 1;
TIMER_Struct.Init.Period = 100 - 1;
TIMER_Struct.Init.Prescaler = uwPrescalerValue;
TIMER_Struct.Init.ClockDivision = 0; // these accesses work
TIMER_Struct.Instance -> CR1 |= 0x01 << 3; // this no works
}
Even if I write directly:
TIMER_Struct.Instance -> CR1 = 0xFFFFFFFF;
It stills without having effect.
I think It could be a fact that I'm not controlling appropiately the pointer access or similar. But I don't see how can I access and modify the content of the commented field. Since I can see (debug mode) how the rest of struct fields updates are writen correctly.
Any correction suggested here?
TIMER_Struct.Instance -> CR1 = 0xFFFFFFFF;
I try different ways to get it with no success. I need new ideas.
TIM_TypeDef *Instance;
is just a pointer which is not pointing to anywhere so you can't dereference it
you have defined somewhere in the lib a macro like this:
#define TIM (TIM_TypeDef*)0xDEADBEEF;
this is how you map the registers to the memory
try modifying your code like this:
TIMER_Struct.Instance = (TIM_Typedef*)0xDEADBEEF;
or just
TIMER_Struct.Instance = TIM;
than
TIMER_Struct.Instance -> CR1 |= 0x01 << 3;
should work
I think you forgot to define TIMER_Struct.Instance
TIM_HandleTypeDef TIMER_Struct;
TIMER_Struct.Instance = TIM1;
//Now you can access TIMER_Struct.Instance
TIMER_Struct.Instance->CR1 = (uint32_t)0xFFFFFFFF;
But I prefer to use CMSIS for writing to registers. No need for the HAL. With CMSIS writing to a register could look like:
TIM1->CR1=(uint32_t)0xFFFFFFFF
I have found the solution. First you must enable the peripheral clock. If not, there won't be any effect over timer registers, because the pointer structs fields were pointing directly to the hardware registers allocation. This was the cause why I can't see any update when watching my struct var at debugging.
I was enabling clk (with hal_xxx_init()) after writing to the allocation. That was my mistake.
Here in this part of code is the correction:
//where uint32_t SystemCoreClock = 4000000; in other system source file.
void DRV_TIMER_init(void)
{
uwPrescalerValue = (uint32_t)(SystemCoreClock / 1000000) - 1;
TIMER_Struct.Init.Period = 100 - 1;
TIMER_Struct.Init.Prescaler = uwPrescalerValue;
TIMER_Struct.Init.ClockDivision = 0; // these accesses work
if (HAL_TIM_Base_Init(&TIMER_Struct) != HAL_OK)
{
/* Initialization Error */
TIM_Error_Handler();
}
TIMER_Struct.Instance -> CR1 |= 0x01 << 3; // this works now
}
If you try to put the last sentence before the:
HAL_TIM_Base_Init(&TIMER_Struct);
no
TIMER_Struct.Instance -> CR1 = xxx; //write mode register acces
will have effect.

Struct pointer to access register on microcontroller?

I'm trying to understand the cmsis header file included with the STM-32 Cortex-M4 microcontroller.
They have a struct that is
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
Does it make sense, for example, to write:
((GPIO_TypeDef *) 0x08000) -> MODER = 0x12;
I don't get what this line is doing.
It would make more sense to me if you did
GPIO_TypeDef * td = 0x08000;
td -> MODER = 0x12;
Are these the same? Why?
The GPIO_TypeDef struct is a clever mechanism for encoding address offsets. So if we are given a pointer to the base GPIOD address and we cast that pointer to a GPIO_TypeDef struct pointer, we can use the standard C dereference operator (->) to access an address at some offset from the base GPIOD address.
So in your example, GPIOD_BASE evaluates to (AHB1PERIPH_BASE + 0x0C00) and MODER has an address offset of 0x0 from a GPIO_TypeDef struct pointer. This means that GPIOD_BASE->MODER evaluates to (AHB1PERIPH_BASE + 0x0C00) + 0x00. This is just the address of the port mode register for GPIOD.
This works for all the field defined in the GPIO_TypeDef struct. For example, GPIOD_BASE->PUPDR evaluates to(AHB1PERIPH_BASE + 0x0C00) + 0x0C. This is just the address of the pull-up/pull-down register for GPIOD.

Why pointer of type CAN_TypeDef is being used in following code?

I am trying to understand the code, my worry is why in below code "CAN_TypeDef" is being used, can I simply use pointer of type integer,what will be the adverse consequences of integer pointer ?
The below code belongs to CAN(controller area network) library code of stm32f discovery board
in Filename can.c
void CAN_setup (uint32_t ctrl) {
CAN_TypeDef *pCAN = (ctrl == 1) ? CAN1 : CAN2;
uint32_t brp;
When I went to definition of CAN_Typedef it throws me here.
typedef struct
{
__IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */
__IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */
__IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */
__IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */
__IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */
__IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */
__IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */
__IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */
uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */
CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */
CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */
uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */
__IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */
__IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */
uint32_t RESERVED2; /*!< Reserved, 0x208 */
__IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */
uint32_t RESERVED3; /*!< Reserved, 0x210 */
__IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */
uint32_t RESERVED4; /*!< Reserved, 0x218 */
__IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */
uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */
CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */
} CAN_TypeDef;
I don't understand why typedef .. I have base address of CAN and offset of Different registers, I didn't find any relevance of above code for my purpose.
I am trying to apply this way
/*=========================CAN BASE ADDRESS=============== */
#define CAN1_BASE 0x40006800
#define CAN2_BASE 0x40006400
/==========================================================/
#define CAN1_MCR (CAN1_BASE+ 0x00)
#define CAN2_MCR (CAN2_BASE+ 0x00) // 0x00 is offset for MCR
#define DEMO(X) (*(unsigned int*)(X)) // will use this to type cast and deference ,accessing the register.
DEMO(CAN1_MCR) = (CAN_MCR_INRQ | CAN_MCR_NART ); // CAN_MCR_INRQ and CAN_MCR_NART has hexadecimal vale pointing to specific bit in MCR register
Yes... You can use an integer pointer and go ahead with the method you use.
But you will miss on "Code Readability" and "Structured Code".
With the typedef struct, it is pretty easy to access the registers and code is easy to read/understand.
Nothing else!!
The key word is reuse. Saying
#define CAN1_MCR (CAN1_BASE+ 0x00)
#define CAN2_MCR (CAN2_BASE+ 0x00)
means that you'd have to define the layout twice (with all the double maintenance problems). The structure approach allows to define it once, and use the same definition for each bus.

Resources