BSRR in STM32F4xx.h - Page 1
Forum section: electronics / general

author: lollandster
author_rank: Contributor
timestamp: April 08, 2019, 11:41:07 am
content: Hi,I'm having some trouble understanding the BSRR register in the STM32F4xx.h file.The BSRR register is defined as two 16 bit registers (H and L) in the struct, but the BSRR bit definitions are 32 bit. What am I missing?Doing GPIOA->BSRRL = GPIO_BSRR_BR_4 will put a 32bit value into a 16 bit space. That may work, but doesn't feel like the right thing to do. For now I'll just do GPIOA->BSRRH = ((uint16_t)0x0010), but I would like to know why the STM32F4xx.h is like it is.In the STM32F30x.h the BSRR is 32 bit and makes sense.Code: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; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */} GPIO_TypeDef;Code:/****************** Bits definition for GPIO_BSRR register ******************/#define GPIO_BSRR_BS_0 ((uint32_t)0x00000001)#define GPIO_BSRR_BS_1 ((uint32_t)0x00000002)#define GPIO_BSRR_BS_2 ((uint32_t)0x00000004)#define GPIO_BSRR_BS_3 ((uint32_t)0x00000008)#define GPIO_BSRR_BS_4 ((uint32_t)0x00000010)#define GPIO_BSRR_BS_5 ((uint32_t)0x00000020)#define GPIO_BSRR_BS_6 ((uint32_t)0x00000040)#define GPIO_BSRR_BS_7 ((uint32_t)0x00000080)#define GPIO_BSRR_BS_8 ((uint32_t)0x00000100)#define GPIO_BSRR_BS_9 ((uint32_t)0x00000200)#define GPIO_BSRR_BS_10 ((uint32_t)0x00000400)#define GPIO_BSRR_BS_11 ((uint32_t)0x00000800)#define GPIO_BSRR_BS_12 ((uint32_t)0x00001000)#define GPIO_BSRR_BS_13 ((uint32_t)0x00002000)#define GPIO_BSRR_BS_14 ((uint32_t)0x00004000)#define GPIO_BSRR_BS_15 ((uint32_t)0x00008000)#define GPIO_BSRR_BR_0 ((uint32_t)0x00010000)#define GPIO_BSRR_BR_1 ((uint32_t)0x00020000)#define GPIO_BSRR_BR_2 ((uint32_t)0x00040000)#define GPIO_BSRR_BR_3 ((uint32_t)0x00080000)#define GPIO_BSRR_BR_4 ((uint32_t)0x00100000)#define GPIO_BSRR_BR_5 ((uint32_t)0x00200000)#define GPIO_BSRR_BR_6 ((uint32_t)0x00400000)#define GPIO_BSRR_BR_7 ((uint32_t)0x00800000)#define GPIO_BSRR_BR_8 ((uint32_t)0x01000000)#define GPIO_BSRR_BR_9 ((uint32_t)0x02000000)#define GPIO_BSRR_BR_10 ((uint32_t)0x04000000)#define GPIO_BSRR_BR_11 ((uint32_t)0x08000000)#define GPIO_BSRR_BR_12 ((uint32_t)0x10000000)#define GPIO_BSRR_BR_13 ((uint32_t)0x20000000)#define GPIO_BSRR_BR_14 ((uint32_t)0x40000000)#define GPIO_BSRR_BR_15 ((uint32_t)0x80000000)

author: newbrain
author_rank: Super Contributor
timestamp: April 08, 2019, 12:36:03 pm
content: Quote from: lollandster on April 08, 2019, 11:41:07 amIt definitely won't, I'll refrain from quoting the standard as I usually do...BUT:Which version of the file are you using?I'm asking as in the F4 Cube version I have (1.21) BSRR is a 32 bit register, the files where it's defined (which are processor specific and included by STM32F4xx.h) are dated 2017.EtA: found it in the release notes:QuoteSo, you might need to update your Cube...

author: iMo
author_rank: Super Contributor
timestamp: April 08, 2019, 01:59:10 pm
content: If you look carefully at below defs the address offset is 2 in this case (otherwise it will be 4 for 32bit):Code:__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 */

author: lollandster
author_rank: Contributor
timestamp: April 08, 2019, 02:20:59 pm
content: Quote from: newbrain on April 08, 2019, 12:36:03 pmMy STM32F4xx.h file is marked V1.8.0 / 09-November-2016.The CubeMX software has stopped working for some reason (Http access forbidden), probably some work related firewall issues. I downloaded the standard peripheral library using System Workbench.Is there a better way to get the libraries?

author: newbrain
author_rank: Super Contributor
timestamp: April 08, 2019, 03:08:12 pm
content: You can directly download the last Cube (not CubeMX), including HAL, CMSIS, examples etc. from st.com website.AFAIU, the standard peripheral library is no longer supported, "replaced" by the LL.E.g. this is thelink for the F4 Cube.Quote from: imo on April 08, 2019, 01:59:10 pmYes, the register has not changed with the SW update. I don't know if correct #defines for a split BSRR were provided in some version, but the ones shown by lollandster will not work.

author: Yansi
author_rank: Super Contributor
timestamp: April 08, 2019, 03:33:41 pm
content: This is a well known shithole.Just live with it. Either write 32bit to the address of BSRRL, or write 16bit BRR content to BSRRH register.Code:/* Write to BSRR */*(uint32_t*)&GPIOx->BSRRL =VALUEfor32bitBSRRreg;//Or just modify the header file and forget it.

author: iMo
author_rank: Super Contributor
timestamp: April 08, 2019, 06:05:22 pm
content: Quote from: lollandster on April 08, 2019, 11:41:07 amWell, you normally do not write GPIO_BSRR_BR_4 into BSRRL.The GPIO_BSRR_BR_4 is only the "bit name" of the BSRR register. Therefore 32 bit names.Simply forget the GPIO_BSRR_BR_4 and a like bit names.And do not modify header files unless you understand well what you are doing.You write an uint16 "mask" with bits you want to set or reset atomically to BSRRL (set) or BSRRH (reset).GPIOA->BSRRL = 0b0000000001101000;GPIOA->BSRRH = 0b0000000001101000; // toggles A6,A5,A3

author: ajb
author_rank: Super Contributor
timestamp: April 08, 2019, 06:12:20 pm
content: I'm all for using named bitmask macros for situations where each bit has a different function, but for stuff like this where the bits reflect an array of the same function on different pins, it's easy enough just to do a bitshift based onCode:PORTx->BSRRL = (1<<pinNum)Personally I just hide all that shit behind macros I can write once for each platform and then not have to think about ever again:Code:#define IO_OUTSET(...)/* set (port, pin) output */IO_OUTSET_SUB(__VA_ARGS__)#define IO_OUTCLR(...)/* clear (port, pin) output */IO_OUTCLR_SUB(__VA_ARGS__)#define IO_OUTSET_SUB(gpio, pin) GPIO##gpio->BSRRL = (1<<pin)#define IO_OUTCLR_SUB(gpio, pin) GPIO##gpio->BSRRH = (1<<pin)The double defines allow things like this to work:Code:#define LED_PIN B,4IO_OUTSET(LED_PIN)

author: iMo
author_rank: Super Contributor
timestamp: April 08, 2019, 06:27:41 pm
content: The beauty of the BSRR is you can set/reset atomically (no read/modify/write) a "set of bits".Something like:#define LEDS_PINS B,0,1,4,12,15IO_OUTSET(LEDS_PINS)IO_OUTCLR(LEDS_PINS)should end as:GPIOB->BSRRL = 0b1001000000010011;GPIOB->BSRRH = 0b1001000000010011;

author: Yansi
author_rank: Super Contributor
timestamp: April 08, 2019, 06:32:38 pm
content: Please note that using POSITIONs insted of MASKs makes rather mess of a code, use the pre-defined GPIO_Pin_0..15 macros instead.Using binary constants (and magic numbers anyway) shall be even punished or made illegalSo instead ofGPIOA->BSRRL = 0x01101000;GPIOA->BSRRH = 0x01101000; // toggles A6,A5,A3use pleaseGPIOA->BSRRL = GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_3; /* sets A6, A5 and A3 */GPIOA->BSRRH = GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_3; /* clears A6, A5 and A3 */

author: iMo
author_rank: Super Contributor
timestamp: April 08, 2019, 06:46:01 pm
content: @Yansi: your example above is a good example why it should be punished

author: lucazader
author_rank: Regular Contributor
timestamp: April 08, 2019, 07:31:57 pm
content: Quote from: Yansi on April 08, 2019, 06:32:38 pmThis is the best solution so far in this thread IMO.Clearly readable which pins are being set and reset here.Just turn this into two inline functions that takes the gpio port and then the pin bitmask and hey presto: super readable and maintainable code with no overhead

author: ajb
author_rank: Super Contributor
timestamp: April 08, 2019, 07:35:38 pm
content: Quote from: Yansi on April 08, 2019, 06:32:38 pmThe second is hardly any better than the first in practice. It still doesn't tell me what the logical function of the pin(s) you're toggling are, just which physical pins they're mapped to, which I don't really care about when I'm writing software. So I have to mentally know that pin A3 is the first red LED or whatever. And what happens when you revise the board layout and the function that was on A3 is now on B13?Better to define, in one place, how logical signals map to your IO pins, and then use those logical names throughout your code. I tend to do the same thing with peripherals, where I have multiples on a chip. That way the code explicitly says which logical function I'm interacting with, which makes it easier to spot errors, and if I revise the circuitry I only have to edit one file to correct the pin mappings. It doesn't really matter what you use in that definition, as long as it's clear to the future reader.Code:#define DEBUG_RX_IE,0#define DEBUG_TX_OE,1#define DEBUG_RX_AF8#define DEBUG_TX_AF8#define DEBUG_USARTUART8#define DEBUG_USART_IRQNUART8_IRQn#define DEBUG_USART_IRQHANDLERUART8_IRQHandler// Panel IO#define BTN_DOWN_IB,2#define BTN_MENU_IF,11#define BTN_LEFT_IF,12#define BTN_ENTER_IF,13#define BTN_RIGHT_IF,14Obviously you could also do this abstraction through functions and variables as opposed to macros, which would give you runtime flexibility at the expense of more complex code that may or may not optimize out depending on how you implement them.

author: Yansi
author_rank: Super Contributor
timestamp: April 08, 2019, 07:59:47 pm
content: It is not about the logical function, but READABILITY of the code. Nobody is likely interested in solving cryptic 16 or eve 32bit magical constants throughout the code.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 08, 2019, 08:53:26 pm
content: The whole point of the BSRR register is that you can atomically and efficiently both set and reset any arbitrary bit combination at the same time; separating them into two 16-bit accessors is a mistake. I haven't seen it on STM32 headers before, even on older versions, pretty weird. I suggest you use the newer version or fix it manually to your header file, also for compatibility reasons.About the readability. People tend to like rectifying unreadable mess of the others (with good intentions, of course), but sadly, I feel like while doing so, we lack some ambition, I mean, the "better" way tends to be OK...ish, but not much more. We could certainly do better than just always blindly use the "good old" microcontroller header way of:Code:SOME_REGISTER = SOME_REGISTER_SOMETHING_1 | SOME_REGISTER_MOAR_THINGS2 | SOME_REGISTER_HEY_WE_LIKE_DUPLICATION | SOME_REGISTER_ASDFG.This is a strange mixture of "abstraction" and total non-abstraction (only duplication) and I don't like it. So sometimes, I just feel very... hacky, and go with:Code:SUPER_PERIPHERAL = 0b10100010;On the surface, it looks unwritable and unreadable, the intent doesn't show, but the official alternative often offered doesn't show the intent any better:Code:SUPER_PERIPHERAL = SUPER_PERIPHERAL_ASDFG | SUPER_PERIPHERAL_QWEHJB | SUPER_PERIPHERAL_KLMAMF;The "library way" is even worse:Code:struct Super_Peripheral_Init_Struct letsGiveAsLongNameAsPossibleInitStruct;memset(letsGiveAsLongNameAsPossibleInitStruct, 0, sizeof(struct Super_Peripheral_Init_Struct));letsGiveAsLongNameAsPossibleInitStruct.configureTheASDFGFeature = SUPER_PERIPHERAL_INIT_STRUCT_CONFIGURE_THE_ASDFG_FEATURE_ON; // Don't abstract anythingletsGiveAsLongNameAsPossibleInitStruct.configureTheQWEHJBFeature = SUPER_PERIPHERAL_INIT_STRUCT_CONFIGURE_THE_QWEHJB_FEATURE_ON; // Expose everythingletsGiveAsLongNameAsPossibleInitStruct.configureTheKLMAMFFeature = SUPER_PERIPHERAL_INIT_STRUCT_CONFIGURE_THE_KLMAMF_FEATURE_ON; // Copy paste a lotInitTheSuperPeripheral(Super_Peripheral1, &letsGiveAsLongNameAsPossibleInitStruct); // Just writes the registers. No real abstraction done.Oh my god, let's forget about that.In reality, inmost caseswhen accessing MCU peripherals, you absolutely need to have the register description datasheet page open for full descriptions both when writing your code, and when reading through it (to get the actual full meaning), so it doesn't make a difference in the end whether you use bit numbers or the totally gibberish (unless you have the datasheet open!) defined names. So the readability is zero anyway, and writability is worse when you need to duplicate and worse, lookup the header files to get all the masks you need.So I started doing this instead: I don't use the names since they are not descriptive enough; but I addcommentsto the bit indeces to tell the reader what'sactuallygoing on:Code:SUPER_PERIPHERAL =1<<7 /*Bit called ASDFG, actually turns on the output register carburetor*/ |1<<5 /*Enable transmitter (called QWEHJB in the datasheet for unknown reasons)*/ |1<<1 /*STM32 trap: the mysterious, almost unspecified KLMAMF bit MUST BE TURNED ON, otherwise the multiplexer cam shaft breaks randomly*/;If someone still has problems with this, their loss. I have done my best, and it's definitely better than the status quo. (I also comment in the document number for the reference manual, and often refer to the actual pages.)For the GPIO, I personally use these macros:Code:#define HI(port, idx) do{(port)->BSRR = 1UL<<(idx);}while(0)#define LO(port, idx) do{(port)->BSRR = 1UL<<(16+idx);}while(0)(Of course, when I need the atomicity feature, I bypass these macros and write to the BSRR manually.)I like both the writability and the readability ofCode:HI(PORTB, 12);Which doesn't include much extra that isn't in your head, and in the documentation (everything says PB12).Then abstracting the actual pin meanings doesn't involve replicating stupid, mixed-case, long header/vendor specific names like "GPIO_Pin_3" (or was it GPIO_pin_3, or GPIO_Pin3?). Instead, I define:Code:#define IMU024_G_NCS_PORT GPIOA#define IMU024_G_NCS_PIN 14(Some more preprocessor trickery could help getting rid of duplication of the functionality name; the optimal case would be, with no duplication, and to match with schematics and device pinout documentation:Code:#define IMU024_G_NCS PA14...LO(IMU024_G_NCS);But I'm not there yet. (ajb is very close in his post above.)Sometimes, if I feel like it, I abstact the complete registeroperationto a macro:Code:#define en_gate_pha() do{ HI(GPIOG, 1); }while(0)#define dis_gate_pha() do{ LO(GPIOG, 1); }while(0)Using "GPIO_Pin_3" here would do nothing else than introduce extra typing, and introduce another level of arbitrary per-product line header dependency, for no reason.I agree that "direct" magic numbers are not that neat, which is why I went for some helper macros. Some of these look ugly, but are write-once, use for years, and make the application code look nice, for example:Code:#define IO_SET_ALTFUNC(port, pin, af) do{ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wshift-count-negative\"") _Pragma("GCC diagnostic ignored \"-Wshift-count-overflow\"") \if((pin)<8) {uint32_t _tmp_ = (port)->AFR; _tmp_ &= ~(0b1111UL<<((pin)*4)); _tmp_ |= af<<((pin)*4); (port)->AFR = _tmp_;} \else {uint32_t _tmp_ = (port)->AFR; _tmp_ &= ~(0b1111UL<<(((pin)-8)*4)); _tmp_ |= af<<(((pin)-8)*4); (port)->AFR = _tmp_;} _Pragma("GCC diagnostic pop") }while(0)Or,Code:#define DMA_CLEAR_INTFLAGS(_dma_, _stream_) do{ \if ((_stream_) == 0) (_dma_)->LIFCR = 0b111101UL<<0; \else if((_stream_) == 1) (_dma_)->LIFCR = 0b111101UL<<6; \else if((_stream_) == 2) (_dma_)->LIFCR = 0b111101UL<<16; \else if((_stream_) == 3) (_dma_)->LIFCR = 0b111101UL<<22; \else if((_stream_) == 4) (_dma_)->HIFCR = 0b111101UL<<0; \else if((_stream_) == 5) (_dma_)->HIFCR = 0b111101UL<<6; \else if((_stream_) == 6) (_dma_)->HIFCR = 0b111101UL<<16; \else if((_stream_) == 7) (_dma_)->HIFCR = 0b111101UL<<22; \}while(0)Hope you get the point. Sorry for the incoherent message, hope it makes any sense.But I do admit that there is a lot of "matter of taste" going on here.

author: Yansi
author_rank: Super Contributor
timestamp: April 08, 2019, 09:27:24 pm
content: You seem to be a tad annoyed by longer (although very clear) names, don't you?Get a better IDE. Noone writes these names in full by hand anyway, so whats the point?

author: Siwastaja
author_rank: Super Contributor
timestamp: April 08, 2019, 09:55:50 pm
content: Quote from: Yansi on April 08, 2019, 09:27:24 pmMissed the point entirely. The problem is when the names are NOT clear, or are not providinganyextra information. Only if/when this is the case, being lengthy becomes really annoying. OTOH, you can accept such minor issuewhen it makes sense.QuoteI strongly disagree with the idea that the code should be obfuscated, and then this obfuscation process automated. Even if you automate the typing, the readability still suffers.Especially, the peripheral bit mask names tend to be really gibberish, often with one letter differences, like ASDFG and ADSFG. If you speed up your writing with an IDE autocomplete, you are bound to make a mistake at some point. During 20 years, I have learned that better just have the datasheet open and go through the register, very carefully, bit by bit, and comment what you are doing. Takes 5 minutes - saves days of debugging. (I used IDEs for microcontroller work for ca. 1998 - 2010. Now it's text editor, make + custom tools per project.)Of course I want to be practical instead of pedantic - sometimes this automation is inevitable (for example, I do write VHDL with assisting tools thanks to some braindeadness in the VHDL syntax), but in the case of embedded C, there isvery littlereason to use such amount of copypasting or autogeneration as seen today (the worst offenders are the now discontinued (thank god) STM32 peripheral libraries).My productivity increases every time I drop unnecessary tools and unnecessary layers of complexity (or obfuscation). Similarly, my productivity increases whenever I create my own tools,where actually needed.Yes, I know "obfuscation" is a bit strong here. It's not such a big deal, but in principle, it's still obfuscation. In any case:The IO port is called PA7 in the datasheet, in your schematics, through the whole process. It should be as close as PA7 as possible in the code. GPIO_Pin_7 is only obfuscation, nothing else. Yes, text editor automation makes it behave better, but I have very hard time understanding why would you want to do that, when you can easily solve the problem completely, instead of bodging around it?Similarly, for example, you may have a chapter about "enabling TX" in the manual, which may involve setting a bit called, say, TXE, in CTRL_REG.enable_tx();is a real abstraction.CTRL_REG |= CTRL_REG_TXE;is pure laziness by design. It's tradition dating back to at least 90's. I'm not saying it'sbad. It's just... not good either. It's status quo, it's acceptable, it's neutral. But:CTRL_REG |= 1;is equally good or bad, IMHO.Both absolutely require comments (the first one could look like "clearing some TX empty flag" as well!), and both require that you look at the actual datasheet every time this code line is relevant. I don't see much difference in readability. I think this is a fairly minor issue in any case; the real readability issues will be elsewhere (especially, how all the data and program flow is organized). And, copy-paste/autogenerationmentalityis a real showstopper there. Not saying youcouldn'tswitch mentalities as per required, but... There's always a risk, and it's good to understand it.

author: lucazader
author_rank: Regular Contributor
timestamp: April 08, 2019, 10:10:23 pm
content: Quote from: Siwastaja on April 08, 2019, 08:53:26 pmIs there any reason you don't use an inline function here instead of the macro?

author: Siwastaja
author_rank: Super Contributor
timestamp: April 08, 2019, 10:19:13 pm
content: Quote from: lucazader on April 08, 2019, 10:10:23 pmI have had some really bad experiences with inline functions, and I was just being careful. I'm almost 100% sure this one could be replaced no problem. In 99% of cases, they work as intended.The problem is, when GCC fails inlining an inline function, it's not called a compiler bug, so these are not high-priority issues. After all, the viewpoint they are coming from (PC, mainly userland code), is that this is a very slight performance/optimization issue. But on a microcontroller code, it can be a matter of functioningat all.IIRC, in this specific header, I had one function that failed to inline no matter what I did, so I made them all macros. Later, I learned about __attribute__((always_inline)), but quite frankly, using a macro is a more portable way to say the same.Always remember to use -Winline with GCC when you inline anything where inlining does matter.As a general note macro vs. inline function, they offer the opposite set of advantages / disadvantages: macro doesn't type check, but allows type genericity; function does type check, but doesn't allow type genericity (in C++, you can of course overload or use templates, which offers both advantages).

author: lucazader
author_rank: Regular Contributor
timestamp: April 08, 2019, 10:40:29 pm
content: Thanks for the clarification and succinct reasoning!(not being facetious here, your answer is great)

author: Yansi
author_rank: Super Contributor
timestamp: April 08, 2019, 10:54:31 pm
content: Quote from: Siwastaja on April 08, 2019, 09:55:50 pmAs reasonable as you may sound, I just can't agree with what you are presenting here.First abstract example - okay, nothing to say. Second example - you seem to be a typical programmer - you need to changes things and make them differently, just for the sake of it. Who cares if it is from 90's or not, it is a well readable piece of code, unlike your last example, where one would really need to open the datasheet to see, what freaking bits are 0x00040C00. No thanks!If I continue to take examples, so lets stay with TXE:(SPI1->SR & SPI_SR_TXE)It is more then self-explanatory even without additional comment and even without opening the datasheet. You even don't need to refer to the library manuals, as the only thing you remember, is that SPI has a SR reg with TXE (TX Empty) bit in it. Which is quite lovely as ST has their ducks quite in a row and the register and bits are very VERY similar if not same across multiple devices or even complete product lines.I don't understand why would you ever suggest to replace SPI_SR_TXE with some cryptic 32bit number! It is not equally bad, it is pure dumb!

author: lollandster
author_rank: Contributor
timestamp: April 09, 2019, 06:15:15 am
content: I downloaded Core F4. I find the ST webpage somewhat hard to understand and I could not find Cube anywhere under STM32F401RC, but knowing what I was looking for enabled google to help me, thanks Newbrain!Inside STM32Cube F4 i found STM32F401xc.h which replaced my STM32F4xx.h without breaking anything. And it uses 32 bit for BSRR, so it fixes my problem. I had to keep my old core_cm4.h file though as the one I found in CubeF4 gave me some errors.FYI Siwastaja, I do program the registers with the reference manual open and I'm using the defined names from STM32F401xc.h because it perfectly matches the names of the registers in the reference manual. So when I write RCC->CR |= RCC_CR_PLLON, I know that I can find the register under the RCC chapter and that the PLLON bit is in the CR register found in that chapter. It may not be the best way, but it is the way I'm doing it for now. I may become wiser in the future.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 09, 2019, 08:49:11 am
content: Quote from: Yansi on April 08, 2019, 10:54:31 pmNot suggesting that. I'm just saying, when working with peripheral registers, I always workvery closelywith the reference manual at hand, all the times I do anything with these registers (write or read the code), because I find you tend to need the full describtions instead of the acronym names, because they do have catches. It's better to do this work carefully and then abstract it away inside a simple "driver" function or a macro. This way, when reading the code, you are not trying toneitherdecipher the bare bit number,norSPI_SR_TXE like macros. You read the function names, and comments, and then finally, the manual. This is why I'm saying there is not much difference in readability.Now, generic bitmasks (like 1<<5, which says "bit number 5" to anyone experienced in embedded C) do match perfectly what I see in the refman: it lists bit number 5. It also says TXE, but it doesn't say SPI_SR_TXE. With these defines, I need to hold two sets of slightly different naming schemes in my head. Then, if I ever need to print out the actual register value in a trace or a debug system, then it's back to the raw bit indices again. So I'm avoiding "multiple names for the same thing" phenomenon here. Instead, I'm all for higher level abstraction.The idea that a more verbose, duplicated coding style is "easier to read" is a fallacy. Itfeelseasier to read, our brain is designed to process natural language text. Our brain also has a auto-correct system, did you spot all my typos? I guess not. It's a pattern matching, pick the closest good-looking match silently. So while a verbose copy-pasta language is fast to read out loud, your brain is not giving the correct weight to the things requiring it (SPI_SR_TXE). Thus I want to avoid duplication and unnecessary noise.SPI_SR_TXE is also not a good example, because it's one of the simplest peripherals there. Many others have easily over 200-300 configuration and status bits, with completely gibberish acronym names, even for those who do understand these peripherals, like HRTIM_BDMUPR_MDIER. I just looked it up randomly, and even having done several complex HRTIM designs (I like it), I have no idea whatsoever what this would be. So even if SPI_SR_TXE would be a descriptive, easy to understand name for some, this concept is not scalable: the next one isn't understandable, and you need a full manual lookup anyway.These bitmask defines should, but donotalways match the datasheet names. And when they do not match, it completely ruins the whole idea.For example, the datasheet says:HRTIM_TIMxDIER register has bit RSTx2DE, where x = A, B, C, D or E. Following this, this code should work:Code:HRTIM->TIMBDIER |= HRTIM_TIMBDIER_RSTB2DE;Or wait? Should it be:Code:HRTIM->TIMBDIER |= HRTIM_TIMxDIER_RSTx2DE; // because the timer units are similar and could share the bitmask defines?Or maybe it is:Code:HRTIM->TIMxDIER |= HRTIM_TIMxDIER_RSTx2DE;Or maybe:Code:HRTIM->TIM.DIER |= HRTIM_TIMxDIER_RSTx2DE;These would be logical: and it's none of these. In reality, it's:... drum roll ...Code:HRTIM1->sTimerxRegs.TIMxDIER |= HRTIM_TIMDIER_RST2IE;Complete illogical mess:1) name mismatch: in the complete documentation, there is no HRTIM1 instantiation, only HRTIM2) name mismatch: what the heck is sTimerxRegs?And finally, most relevant to this discussion:3) inner logic mismatch: in the regname define, it's TIMxDIER, then in the mask macro, it's just TIMDIER.Not saying this is a huge problem, or problem at all, and not saying you should change what you do, you are doing just fine. Just pointing out that your way is not superior, either, and isn't actually making the code any more readable, and is factually making it less writable, and then you need automation to compensate. It's still cryptic code that needs to be commented and referenced with the reference manual; exactly like the bare bit numbers you so strongly dismissed.Unlike you say, I see the variability in STM32 peripherals a big, time-consuming problem. Almost every freaking STM32 MCU I have used so far has had differences in almost all basic peripherals like USART, SPI, I2C or DMA. You could circumvent this with really good cross-compatible library abstractions, but ST hasn't been able to do that properly, and quite frankly, itisdifficult, I couldn't do it either; MCU users tend to be demanding in both flexibility and performance. To be fair, maybe I have been unlucky, because I have used devices very widely from different families (F0, F2, F3, F7, H7), and different eras (F224, for example, is quite old compared to the new H7).

author: MT
author_rank: Super Contributor
timestamp: April 09, 2019, 05:01:00 pm
content: Quote from: imo on April 08, 2019, 06:05:22 pmJust recently had to modify an old F334 and F301 project to discover loads of things didnt work due to missing register defs in svd file, wrongly and missing NVIC defs in .h file and even wrongly labeled NVIC IRQs in startup .s file! 3 independent ST files all garbled up!ST is famous for the inconsistently inconsequently language use of SET and RESET to SET and CLEAR register bits.Why shall i be punished to spend hours on debug ST arrogant crap?

author: Yansi
author_rank: Super Contributor
timestamp: April 09, 2019, 10:16:09 pm
content: Because you are arrogant as well?

author: MT
author_rank: Super Contributor
timestamp: April 10, 2019, 02:08:54 am
content: Quote from: Yansi on April 09, 2019, 10:16:09 pmFrench are arrogant, just look at the baguette! By birth they think philosophy is their invention!

author: westfw
author_rank: Super Contributor
timestamp: April 10, 2019, 02:13:54 am
content: QuoteBecause it's the sort of Crap that once you figure it out, isn't that bad.Because other vendors aren't much better (Atmel's SAMD has the PORT and PORTGROUP backwards, and is woefully inconsistent when it come to which peripherals are "arrays" of a stucture (PORT), and which are individually defined but numbers (Timers, Sercom...)Because it's the hardware features that matter.Because they're what you boss told you you were using.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 10, 2019, 11:50:54 am
content: Exactly. To get work done, you sometimes need to drop pedantism (or arrogance, whatever, but that's an... arrogant choice of words), be practical, and choose the most suitable solution, despite the flaws. STM32 tends to offer a large portfolio of MCUs with a flexible set of HW peripherals at a good price.As you work with them, the difficult annoying things get easier and easier to deal with. This is called experience.I sometimes fix header files if they are clearly wrong; I think it'spracticallybetter. Because the header versions are incompatibleanyway, if you want to have a robust project, you can't just let others open the code with whatever library versions they might have; the easiest way around is to make copies of the headers and include them in your project - in which case it isn't a problem to treat them like you do with any code: this is, fix errors, to make the application code easier to write and read.IIRC these header files don't contain proper formal #defined revision numbers anyway, so you can't automatically enforce compatibility.Don't worry, the headers between families, products and versions are all inconsistent, no one knows "by heart" how they work, so you are not going to make anyone's life harder by fixing things inside your project; only easier.It's still quicker to use the headers, even if you had to modify a few #defines, instead of doing your own from scratch.

author: MT
author_rank: Super Contributor
timestamp: April 10, 2019, 11:55:53 pm
content: Quote from: westfw on April 10, 2019, 02:13:54 amThe user of the device are not supposed to have to figure it out because the crap should not exist to begin with.QuoteMost likely, but thats not an valid excuse, just sloppy work from vendor.QuoteThere you go, welcome to sloppy vendor craftsmanship.QuoteDont seam to for lots of people, they gladly take a simpler device if it suits the project.QuoteThe bosses i have had often had no clue at all what to use for a particular project.Strange that you excuses deliberate implementation of CRAP in documents and files! But mileage varies i suppose.Quote from: Siwastaja on April 10, 2019, 11:50:54 amSince your post is directed to me via westfw post , thats what i describe, got the work done.QuoteOk, yet previously you spent a whole page on describingyour pedantry and arroganceover ST documentations e.g illogical mess!Thats kinda portraying your self as hypocrite! But then please chose whatever device that suits your project , the docs and files are all crap anyway according to westfw so why then all the pedantic windbaging Siwastaja?QuoteYou'r hilarious, why all this chose this chose nonsense got to do with faulty written files from vendors? besides being quite dumb in context to what i said in my first post.Since when is fixing a SVD file so that peripheral start working as barely described in device datasheet it should becomepedantry and arrogance as you say it is?QuoteYet again, you previously wrote a full page complaining about ST docs , not much of experience collecting there rather constant pedantic windbaging over nothing asper your say.QuoteIm not even debating that, irrelevant to whats i said, but anyway again ,when you fix faulty written vendor files its labeledfixing and collecting experiencewhen i and other do the same thing itslabeled pedantry and arrogance.Thats nice of you!QuoteNoone asked nor mentioned it yet you still use vendors def files for sure not writing them all by your self clearly. So which rev of a SVD, def, etc file do you use for your next project? Rev1 yet Rev4 with later date is there, both contains faults but on different positions. Thats quality of vendor craftsmanship you call i presume?QuoteThats some uber heavy excuse lifting for the device maker your doing there , so by your way of reasoning device manufacturers shouldn't bother write any define, startup, register definition etc files at all, just dump it on device user, i.e why bother , so then why do they bother writing faulty files then? For fun to waste users dev times?

author: westfw
author_rank: Super Contributor
timestamp: April 11, 2019, 01:29:18 am
content: QuoteOops, I left out one:Because that why you get paid the big bucks...

author: Siwastaja
author_rank: Super Contributor
timestamp: April 11, 2019, 07:37:27 am
content: Quote from: MT on April 10, 2019, 11:55:53 pmI don't feel that describing a problem, then deciding to balance between living with it, or proposing solutions (such as fixing the damn thing) is hypocrisy; quite the opposite. It's good to understand what you are dealing with.Otherwise than that, I have very hard time trying to understand your post, so let's leave it at that.