cancel
Showing results for 
Search instead for 
Did you mean: 

Low Level API function to register map for STM32G4 family

LGard.1
Associate II

Hi there,

in the ADC FIrmware driver API Description for the STM32G0 family a table exists that shows which LL function accesses what register. In the UM2570 (which is the same doc but for STM32G4) this table is missing. This is giving me a hard time figuring out which function to use to achieve what I want to do as the TRM is just talking about registers.

Another thing I'd love to have is a bunch of predefined structs that address the various registers so that I could write something like this:

ADC1.ADC_CR.bit.ADCCAL = ADC_CAL_ENABLE;

Is there a header file that implements something like this or is the LL and HAL API the usual way to program the registers?

Thanks in advance!

Lenn

1 ACCEPTED SOLUTION

Accepted Solutions
MasterT
Lead

I know, the file is huge, but have you seen this:

/********************  Bit definition for ADC_CR register  ********************/
#define ADC_CR_ADEN_Pos                (0U)
#define ADC_CR_ADEN_Msk                (0x1UL << ADC_CR_ADEN_Pos)              /*!< 0x00000001 */
#define ADC_CR_ADEN                    ADC_CR_ADEN_Msk                         /*!< ADC enable */
#define ADC_CR_ADDIS_Pos               (1U)
#define ADC_CR_ADDIS_Msk               (0x1UL << ADC_CR_ADDIS_Pos)             /*!< 0x00000002 */
#define ADC_CR_ADDIS                   ADC_CR_ADDIS_Msk                        /*!< ADC disable */
#define ADC_CR_ADSTART_Pos             (2U)
#define ADC_CR_ADSTART_Msk             (0x1UL << ADC_CR_ADSTART_Pos)           /*!< 0x00000004 */
#define ADC_CR_ADSTART                 ADC_CR_ADSTART_Msk                      /*!< ADC group regular conversion start */

Registers bits fields definition follows naming accepted in Reference Manual for specific uCPU part number. The style is different, not like

you mention in OP used by TI - union & bits fields .

View solution in original post

6 REPLIES 6
ChahinezC
Lead

Hello @LGard.1​,

For the first part of your question, same as UM2319, the UM2570 contains a table describing the diverse functions of the LL ADC Firmware driver API, please refer to 65.2.1 section. Did I miss something else?

For the second one, I advise you to refer to the structures’ definitions in the stm32g4xx_ll_adc header file.

Chahinez.

LGard.1
Associate II

Hi Chahinez,

thanks for the quick reply! I'm not sure but there seams to be a missmatch of our document versions. In my UM2570 Rev. 2 section 65.2.1 is the Detailed description of functions for the ADC Firmware. But it doesn't say anything about which register is affected by each function.

The mentioned LL API lib is a good start but it solves the issue with struct typedefs and a whole lot of functions. In my opinion this results in quite unreadable code because I can never tell which registers will be affected and how they will be affected. Instead if ST would provide a header file that does not just have the typedefs but would actually implement those structs and refince their level of detail I could write code that makes absolutely clear what value is written to what register and a quick look in the TRM would tell me what behaviour to expect from it. Instead I use some rather badly documented function and hope that the result is what I really want.

Here's an example from TI that does it exactly the way I find it most readable:

// defining the struct for ADC B and matching it to the proper register address
#ifdef __cplusplus
#pragma DATA_SECTION("AdcbRegsFile")
#else
#pragma DATA_SECTION(AdcbRegs,"AdcbRegsFile");
#endif
volatile struct ADC_REGS AdcbRegs;
 
 
struct ADCCTL1_BITS {                   // bits description
    Uint16 rsvd1:2;                           // 1:0 Reserved
    Uint16 INTPULSEPOS:1;           // 2 ADC Interrupt Pulse Position
....
};
 
union ADCCTL1_REG {
    Uint16  all;
    struct  ADCCTL1_BITS  bit;
};
 
 
struct ADC_REGS {
    union   ADCCTL1_REG  ADCCTL1;                      // ADC Control 1 Register
    union   ADCCTL2_REG  ADCCTL2;                      // ADC Control 2 Register
...
};

With this I can clearly address every register individually and can quickly check in the TRM what the specific register does as the naming of the struct members matches the register names in the TRM.

I don't know how satisfied ST customers are with the LL lib. But in my department I can hear people already swearing about the HAL and even though LL has better performance it does not improve development speed (at least that's my opinion). If there is any other recommended way to setup the registers I'd love to hear about it.

Thanks for your time!

Lenn

MasterT
Lead

It's defined in:

~/~/system/Drivers/CMSIS/Device/ST/STM32G4xx/Include

Path may vary, do your search.

"/**

 * @brief Analog to Digital Converter

 */

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 1,                 Address offset: 0x0C */

 __IO uint32_t CFGR2;       /*!< ADC configuration register 2,                 Address offset: 0x10 */

 __IO uint32_t SMPR1;       /*!< ADC sampling time register 1,                 Address offset: 0x14 */

 __IO uint32_t SMPR2;       /*!< ADC sampling time register 2,                 Address offset: 0x18 */

      uint32_t RESERVED1;   /*!< Reserved,                                                     0x1C */"

The question seems like address bits manipulation in stm32 . Yes, it's possible in multiple ways.

TIM5->ARR  = 0xFFFFFFFF;

TIM5->CCR1 = 0b0101010101010;

Just an example with timer, the same way it works for all devices, ADC, DAC etc.

I'd suggest to study this macroses:

 #define SET_BIT(REG, BIT)    ((REG) |= (BIT))

 #define CLEAR_BIT(REG, BIT)  ((REG) &= ~(BIT))

 #define READ_BIT(REG, BIT)   ((REG) & (BIT))

 #define CLEAR_REG(REG)       ((REG) = (0x0))

 #define WRITE_REG(REG, VAL)  ((REG) = (VAL))

 #define READ_REG(REG)        ((REG))

 #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

 #define POSITION_VAL(VAL)    (__CLZ(__RBIT(VAL)))

You can also directly write content of the register:

 HAL_I2S_DMAPause( &hi2s2);

 Serial.print(F("\n\tchange clock to 250 ksps..."));

 delay(100);

 (*(uint32_t*)0x40003820) = 0x0000000C; // 192.0 / (32 * div * 2)

 HAL_I2S_DMAResume( &hi2s2);

But it's strongly not advisable, hardware is not like memory cells and there are some bits interconnection. Some bits may require special conditions /rules to be observed before you set/ reset them, otherwise instability or permanent hardware failure may happened.

HAL drivers usually well cautions on this matter. Look up how they do pre-test before access /modification.

LGard.1
Associate II

Thanks for pointing out that file. I actually tumbled over that before and thought this is what I'm looking for but it just almost is. I know I'm wining on a high level but I'd really like to have the struct from the stm32g4xxxx.h files to not be just typedefs but to be pre defined to point to the right address and have a bunch of unions and further structs, that brake the various 32bit registers down in the register fields representing one functionality. I sure could do that myself. But I think I'm not the only one who would like to see ST doing that work once so everybody else could just use it.

Off course HAL and LL have the huge advantage that they at least partially consider the relevant sequence of setting the registers for you. But how can I tell which functions I need to call and which ones I don't? Again the TRM is talkin in Registers. Not in HAL or LL functions and without a mapping between registers and HAL and LL functions I can probably take an example file, start from there and pray that everything will work fine. Because if it doesn't I will be having a very hard time to figure out which register isn't configured correctly.

Thanks again for taking the time to help. Really appreciate it!

MasterT
Lead

I know, the file is huge, but have you seen this:

/********************  Bit definition for ADC_CR register  ********************/
#define ADC_CR_ADEN_Pos                (0U)
#define ADC_CR_ADEN_Msk                (0x1UL << ADC_CR_ADEN_Pos)              /*!< 0x00000001 */
#define ADC_CR_ADEN                    ADC_CR_ADEN_Msk                         /*!< ADC enable */
#define ADC_CR_ADDIS_Pos               (1U)
#define ADC_CR_ADDIS_Msk               (0x1UL << ADC_CR_ADDIS_Pos)             /*!< 0x00000002 */
#define ADC_CR_ADDIS                   ADC_CR_ADDIS_Msk                        /*!< ADC disable */
#define ADC_CR_ADSTART_Pos             (2U)
#define ADC_CR_ADSTART_Msk             (0x1UL << ADC_CR_ADSTART_Pos)           /*!< 0x00000004 */
#define ADC_CR_ADSTART                 ADC_CR_ADSTART_Msk                      /*!< ADC group regular conversion start */

Registers bits fields definition follows naming accepted in Reference Manual for specific uCPU part number. The style is different, not like

you mention in OP used by TI - union & bits fields .

LGard.1
Associate II

Hmmm I might actually have overlooked this as I was looking for structs and didn't take a closer look on the defines. But sure that is a legit way of doing what I want.

Thanks for pointing that out to me! Really appreciate it!