[ATmega 0] 'TCAt {aka union TCA union}' has no member named 'CTRLA' - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: August 24, 2018, 10:32:15 am
content: I am confused.This error is produced by: #define TCA0_CLKSEL_DIV64 TCA0.CTRLA = ((TCA0.CTRLA & 0b11110001) | 0b00001010)This is exactly the register name scheme used on TCB0 etc. All of the register names throw up the same error

author: andersm
author_rank: Super Contributor
timestamp: August 24, 2018, 10:41:23 am
content: What is the declaration of TCA0?

author: cv007
author_rank: Frequent Contributor
timestamp: August 24, 2018, 12:18:34 pm
content: iom4809.hsearch for TCA0#define TCA0 (*(TCA_t *) 0x0A00) /* 16-bit Timer/Counter Type A */search for TCA_t/* 16-bit Timer/Counter Type A */typedef union TCA_union{TCA_SINGLE_t SINGLE; /* 16-bit Timer/Counter Type A - Single Mode */TCA_SPLIT_t SPLIT; /* 16-bit Timer/Counter Type A - Split Mode */} TCA_t;search for TCA_SINGLE_t and TCA_SPLIT_t , etc.so you need#define TCA0_CLKSEL_DIV64 TCA0.SINGLE.CTRLA = ((TCA0.SINGLE.CTRLA & 0b11110001) | 0b00001010)(and the error message told you exactly what was wrong)

author: Simon
author_rank: Unknown
timestamp: August 24, 2018, 12:24:28 pm
content: I see, I did look at that file but did not really understand it.The problem here is not the defines, without the define I would have used the same raw cryptic code that would have still produced the error.I have been going by the datasheet that does not make clear that they have done some strange stuff in the IDE. They said that the registers are the very same in either single or split mode so this leads into the false assumption that that would be the case in the IDE as well.CTRLA register simply turns the periphery on and set the clock prescaler and does not even warrant different names as it is the same in both modes So I will be writing sets of defines that say the same thing....

author: rstofer
author_rank: Super Contributor
timestamp: August 24, 2018, 02:13:39 pm
content: It is fairly common for libraries to use different names than datasheets. You would think they could get together in a meeting or something.

author: Simon
author_rank: Unknown
timestamp: August 24, 2018, 03:22:32 pm
content: Quote from: rstofer on August 24, 2018, 02:13:39 pmYes well it is the first time I find this. Usually chip header files just define names as numerical addresses probably valid for any language. This chip is the first time to my knowledge that the register names are relating to some variable that is created and related to the register number by actual code in a particular language. and it is further complicated by the fact that it is clever enough to have one set of "definitions" for multiple instances of the periphery. I know i should have more knowledge of "C" but this really makes matters more complicated and they could not be arsed to define the register bit names.

author: Simon
author_rank: Unknown
timestamp: August 24, 2018, 05:25:23 pm
content: I'm still confused as to how TCA0_SPLIT_CTRLA translates into TCA0.SPLIT.CTRLA, is this what enum typdef does?

author: cv007
author_rank: Frequent Contributor
timestamp: August 24, 2018, 08:28:48 pm
content: QuoteIt doesn't translate. Its just the same thing.#define TCA0_SPLIT_CTRLA _SFR_MEM8(0x0A00)#define TCA0_SINGLE_CTRLA _SFR_MEM8(0x0A00)#define TCA0 (*(TCA_t *) 0x0A00)all refer to the same memory address//sfr_defs.h#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))result-TCA0_SPLIT_CTRLA -> (*(volatile uint8_t *)(0x0A00))TCA0_SINGLE_CTRLA -> (*(volatile uint8_t *)(0x0A00))TCA0 -> (*(TCA_t *) 0x0A00)the first two telling you that the memory location is a pointer to a volatile byteand when used you are saying you want the byte in that address (not the address)the last is pointing to the same address, but now you are saying that memory location is a pointer to a struct(or whatever TCA_t happens to be)and when used you are saying you want to access that address as a struct (however that struct is defined)so you end up with-TCA0.SINGLE.CTRLAwhich ultimately is (*(volatile uint8_t *)(0x0A00))there are a number of ways manufacturers go about creating their headers, in this case they decided to split the timer use into single/splitthey could also have used a combo of unions/structs so you don't need the additional single/splitbut I assume they wanted you see what mode you were using when used so you can see that at a glance, otherwise it would be more difficult to spot mix up register usage for single/splitits been a while since I used an avr, and I think I picked the right _SFR_MEM8 in sfr_defs.h, but check it out and see if you can decipher which one gets used

author: Simon
author_rank: Unknown
timestamp: August 24, 2018, 08:34:57 pm
content: So guess why I use my defines that everyone takes the piss out of.... Because in those I make it clear the mode or anything else I want to be clear to me or anyone else.

author: mikerj
author_rank: Super Contributor
timestamp: August 24, 2018, 09:59:24 pm
content: Quote from: Simon on August 24, 2018, 08:34:57 pmThe piss-taking is primarily not because you are using #define, it's the way you are using them. I suspect quite a few people on here have endured the pain of debugging similarly badly written code in the past.Code:#define TCA0_CLKSEL_DIV64 TCA0.SINGLE.CTRLA = ((TCA0.SINGLE.CTRLA & 0b11110001) | 0b00001010)What do 0b11110001 and 0b00001010 do? Outside of GCC they do nothing at all apart from raise a compiler error. Within GCC they are "magic numbers"; literals that a programmer might have pulled out of their arse for all they mean to anyone else. All the bit functions are defined as bitmasks for you in the header, so you don't even have that extra work to do. You might note that literals in the header files don't use binary notation either; it's decimal or hex.You have put an assignment into a macro with no protection against the macros use in other constructs. Sooner or later this will bite you, especially if you start putting multiple statements into a macro.You are presumably using the macro as function (with no arguments) yet the lack of parentheses means this isn't obvious in your code.

author: sokoloff
author_rank: Super Contributor
timestamp: August 24, 2018, 10:36:34 pm
content: Quote from: mikerj on August 24, 2018, 09:59:24 pmBinary literals are part of C++14 spec.Most compilers supportthem now.

author: Simon
author_rank: Unknown
timestamp: August 25, 2018, 07:02:39 am
content: Quote from: mikerj on August 24, 2018, 09:59:24 pmWell if you are trying to debug such code firstly the compiler does not flag the custom definition TCA0_CLKSEL_DIV64 but actually goes to the code in the header file and flags TCA0.SINGLE.CTRLA = ((TCA0.SINGLE.CTRLA & 0b11110001) | 0b00001010). Or you can find it's "implementation".In either case my definition makes very clear what is being done so if you know the chip which will be essential you can see very easily what is being done and find the register in question in the datasheet. TCA0.SINGLE.CTRLA is actually going to be impossible to find in the datasheet as microchip have made a pigs ear of things and do not make at all clear that the "single" bit does not exist, the only clue being that the same registers are used in split and single mode. Of course these are all details that i can put in comments in the header that the define will lead you too. I often put notes in my periphery header files where the manufacturer makes a pigs ear of explaining things or is so cryptic about it that it can slow work down. I work on the basis that a stranger may have to understand my code as that is how i feel returning to my own code some time later.If you look up any tutorial about writing to registers you find that AND and ORing numbers on the register is common place and my code there widely used with the numbers in various formats from bit shifts to hex values that do not make any sense at all unless you are fluent in all 256 combinations of hex numbers. My binary numbers that as have been pointed out are supported and now part of C make it very clear what is happening. The number represents the register digit for bit, making it clear what bits are being considered in the AND operation and what they are being set to in the OR operation. Yes ORing 0b00000000 is pointless but i do it just to keep a consistent format and it does no harm. It takes longer faffing about doing several statements with and one without.Bit names in registers in the atmega 0-series are not a thing anymore you have to refer to the bit numbers as microshit could not be bothered to put them in a header file like the legacy atmel chips have so that makes direct code further unreadable.No I no longer put more than one statement in a define, I will put one operation only such that a colon is not required in the define and will go where in belongs in the code. That is also why i now AND/OR the entire 8 bit register in one go to avoid multiple statements that then need colons or a function for every single simple thing. Where I want to do more complicated things that are referred to by one line of code i will create functions like an entire counter setup.

author: newbrain
author_rank: Super Contributor
timestamp: August 25, 2018, 04:25:44 pm
content: Simon,It still seems to me there you're trying to fight the language, rather than go with it.Please don't take this personally, but I would advise (think I already did) to actually study the language and learn from some good code.You've already got a decent grasp of the syntax, but some subtleties still make your code a bit "cranky", as the comments above show.Quote from: Simon on August 25, 2018, 07:02:39 amActually they are not, as Sokoloff pointed out, they are part ofC++14, not C (and AFAIK they are not coming in C 17, either).They are a GNU supported extension, but (Note: personal opinion following) I usually strive to avoid unnecessary divergence with the standard.Defining a macro with side effects in object-like form makes for hard to understand code and will cause problems.It should be a function-like macro, to make it clear that something is happening and it does not simply represents a value, even if it takes no parameters.As such, it should be possible to use wherever a function call is used; the usual way is to surround it with a do { ... } while(0), as you correctly say: no semicolon.Though, I would probably use a static inline function: it has the same* efficiency of the macro, and the syntax does not exhibit any peculiar behaviour, as macro often do.An example: what if your macro is part of a comma operator expression? The lack of semicolon makes it possible with no syntax error.* Usual caveat about "competent compiler" applies.

author: Simon
author_rank: Unknown
timestamp: August 25, 2018, 04:57:09 pm
content: Quote from: newbrain on August 25, 2018, 04:25:44 pmWell i am trying to make life simple while trying to not break things. If I just stick register manipulations in my code i will just be commenting the crap out of everything at which point I'll still have to write it all out in advance with comments ready to copy and paste.QuoteOK, begs the question as to why not. Clearly GNU added it because it is aimed at embedded systems where binary works well as we deal with some 8 or 16 bit numbers on a bit by bit basis. Again if i do not do this I will have to comment the crap out of my code as the hex values will be impossible to relate to the register. Ultimately i am using AS and all projects are C/C++ by default. Where do I find the current "C" standard?Quoteso what does object like form mean? I try to write up all possible code pieces I will use and test them out, this means that once written and validated i can reuse them with confidence. I either create significant amounts of comments to go by each piece of code or just replace it with a clear piece of meaningful text. I avoid any actual "decision making" in macro's. I simply use defines as an automated text replacement or to pre-calculate a constant value at compile time (because const is actually a pointless thing).Are you suggesting that instead of macro's/defines I use functions? these will need putting in a C file with accompanying header files and I was lead to believe that they are more resource hungry although I apparently can trust the compiler to work it out although most of what I am being told on here seems to revolve around writing code that is compiler independent and I don't know if another compiler is going to be as efficient or clever as AVR GCC although I see no reason the compiler should change, I think even microshit finally figured out no one is going to pay for a compiler they can get for free and and offer GCC as well, they should stop crippling theirs intentionally just to screw you out of $1000 and stick to selling chips!

author: Simon
author_rank: Unknown
timestamp: August 25, 2018, 04:59:56 pm
content: Code:#include <avr/io.h>#include <ports_mega0.h>#include <TCA_split_mega0.h>#include <TCB_mega0.h>#include <clock_atmega0.h>int main(void){PA2_OUTPUT;PA5_OUTPUT;TCA0_SPLIT_SPLIT_MODE_ENABLE;TCA0_SPLIT_L_TOP = 255;TCA0_SPLIT_H_TOP = 255;TCA0_SPLIT_L_COMP2 = 200;TCA0_SPLIT_H_COMP2 = 200;TCA0_SPLIT_H = 127;TCA0_SPLIT_L = 0;TCA0_SPLIT_H_WF2_ENABLE;TCA0_SPLIT_L_WF2_ENABLE;TCA0_SPLIT_CLKSEL_DIV64;TCA0_SPLIT_ENABLE;/* Replace with your application code */while (1){PD2_HI;PD2_LOW;PD2_HI;PD2_LOW;}}

author: newbrain
author_rank: Super Contributor
timestamp: August 25, 2018, 06:52:01 pm
content: Ah,many interesting point for discussion!One premise: I refer to C99, as it introduces a number of new functionalities that (IMO) lead to cleaner and more understandable code.To use C99 with gcc, just add the std=c99 to the compilation flags, or std=gnu99 if you want to use gcc specific extensions (binary constants, inline assembler etc.).ETA: I checked, the default is actually gnu11.Quote from: Simon on August 25, 2018, 04:57:09 pmYes, I see you are trying to build some kind of library, and replace bit-setting with meaningful names.That's quite an effort, probably a bit tedious, but not a bad idea if you are so inclined: after all that's what most vendor-provided libraries try to achieve (with varying degree of success...).Quote from: Simon on August 25, 2018, 04:57:09 pmgcc is very general, definitely not born for or specifically aimed at embedded code.During C99 standardization, the binary constant were proposed and rejected (Ch 6.4.1 in the C99 rationale):QuotePersonally, I prefer using hex, as I tend to very quickly lose track of the digit positions in long numbers.Here C++14 got it quite right: you have binary constants with expected0bprefix, and single quotes in numbers are ignored, so one can group the digits as they see fit.Quote from: Simon on August 25, 2018, 04:57:09 pmObject-like and function-like are the actual terms used in the standard (6.10.3, cl. 9&10), their meaning is pretty simple.An object-like macro is defined and appears in the code as an identifier (like the ones you defined!):it takes no parameters (what the replacement text does is completely free).A function-like macro is defined with a list of parameters, as a (guess what!) function, and as such is invoked in the code.In C you don't expect in general that simply naming an identifier will carry on some action, that's the reason for suggesting the use of a function-like macro. In your case, it's enough to add (), as there are no parameters:Code:#define TCA0_CLKSEL_DIV64() TCA0.CTRLA = ((TCA0.CTRLA & 0b11110001) | 0b00001010) // <= note the () !It makes it clear that something is changing, as a function can be expected to have side effects.I learnt C after Pascal, and the most common bug in my beginners programs was to call functions forgetting the ()...then wondering why nothing happened:As i said, I still find this kind of definition problematic: the do {...} while(0) bracketing helps, though it's normally used when one wants more than one statement inside a macro.Code:#define TCA0_CLKSEL_DIV64() do { TCA0.CTRLA = ((TCA0.CTRLA & 0b11110001) | 0b00001010); } while (0)In this way, we have now something safer, that can be used more or less as any "void function(void)".Quote from: Simon on August 25, 2018, 04:57:09 pmExactly! But not any old functionC99 introduced the concept of inline functions, one of the most useful characteristics is that they can replace function-like macros, with the advantage of retaining the same speed, having a clear and known syntax, and perform type checking of the parameters.An inline function, declared also static, is visible only in the translation unit where it's defined and declared, so its natural place is in an include file, exactly as your macro!Code:static inline void TCA0_CLKSEL_DIV64(void) { TCA0.CTRLA = ((TCA0.CTRLA & 0b11110001) | 0b00001010); }Now all ambiguities and syntax corner cases are solved.As for the speed, the compiler willinlinethe function, i.e. the the assignment will be baked in the code, with no call/return overhead.Gcc and most other modern compilers do that, for gcc optimization flag must not be -O0 (no optimization), any other will do.There are some things to be careful with when using inline, but if you just use static inline, it's easy.Quote from: Simon on August 25, 2018, 04:59:56 pmYep, looks like Pascal!

author: Simon
author_rank: Unknown
timestamp: August 25, 2018, 07:29:27 pm
content: So and inline function belongs in a *.c file but a static one can go in a *.h file ? that makes more sense if it has the same effect but is clearer and puts more into the compiler than the preprocessor.So is an inline function basically just using one statement to replace a section of code that will be replaced every time and not get treated as a function increasing code space but running faster while static versus an actual function would not potentially be carrying out some calculation with a return like a normal function.I guess this is where the compiler likely works all of this out for itself finding the best trade-off ?

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2018, 08:16:20 pm
content: One way it can look-Code://tcb.h#include "../avr/include/stdint.h"#define TCB_BASE ((volatile uint8_t*)0x0A80)typedef enum __attribute__((packed)){TCB_0, TCB_1 = 0x10, TCB_2 = 0x20, TCB_3 = 0x30} TCBn_t;typedef enum __attribute__((packed)){TCB_DIV1 = 0, TCB_DIV2 = 2, TCB_TCA = 4} TCB_CLK_t;static inlinevoid TCBclock_select (TCBn_t n, TCB_CLK_t clk){//CTRLA = TCB_BASE, CLKSEL = <2:1>TCB_BASE[n+0] = (TCB_BASE[n+0] & ~(3<<1)) | clk;}Code://main.c#include "tcb.h"int main(){TCBclock_select(TCB_1, TCB_DIV2);for(;;);}./avr-gcc -Os main.c./avr-objdump -D a.out...00000000 <main>:0: 80 91 90 0a lds r24, 0x0A90 ; 0x800a90 <_edata+0xa30>4: 89 7f andi r24, 0xF9 ; 2496: 82 60 ori r24, 0x02 ; 28: 80 93 90 0a sts 0x0A90, r24 ; 0x800a90 <_edata+0xa30>c: ff cf rjmp .-2 ; 0xc <__zero_reg__+0xb>...I would take one peripheral, try various methods (defines/inlines/whatever) to get what you ultimately want, compile them, use them, see what code is produced, see what it looks like to use it, and when settled go to town on all the rest of the peripherals.

author: newbrain
author_rank: Super Contributor
timestamp: August 25, 2018, 08:26:21 pm
content: Quote from: Simon on August 25, 2018, 07:29:27 pmOne can have static inline function wherever they make sense: if they are part of a set of definitions used multiple times, then a .h file is fine.Just treat them as you would function-like macro.(My English language parser is a slightly confused by your middle paragraph)Using inline functions can and often will increase code size, as the code is repeated every time it's used rather than being only in one place and called as a subroutine (I think this is also what you were saying).Yes, the compiler will apply some smart, depending on the optimization level, to get the best of both worlds.Optimizing for size will often defeat inlining, but nothing prevents it to do the same with macros, if the repeated code sections are detected.The static keyword in this case has the only effect to specify internal linkage (==the name is not visible outside this translation unit).A static and/or inline function can of course be as long as one likes, take parameters and return values, it's no different from other functions.A standard conforming compiler could implement only the grammar and visibility related effects, and never actually inline any code (J.3.The longer it is and the more it's called, the lower the probabilities that it will be actually inlined (due to diminishing returns).