[gcc C] concatenating text to make up code - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: October 06, 2018, 07:24:31 pm
content: So I want to create functions to set up various things. for example a function may need to be tol which input to use, but there are up to 48 inputs. Writing a switchcase every time just seems very long as it will have 48 case's.Now for example I may use a function argument "PA5" and i may want to set it up as an input with a pullup and using my little function PA5_input_w_pullup();Surely there is a way to add the "PA5" and "_input_w_pullup(); together and vastly sreamline the code? some sort of concatenation? but I want to do it on code not string variables.

author: nctnico
author_rank: Super Contributor
timestamp: October 06, 2018, 07:29:37 pm
content: Use an enumeration and a look-up table.

author: langwadt
author_rank: Super Contributor
timestamp: October 06, 2018, 07:45:41 pm
content: Quote from: Simon on October 06, 2018, 07:24:31 pmwhat wrong with input_w_pullup(PA5); ?

author: Simon
author_rank: Unknown
timestamp: October 06, 2018, 07:50:13 pm
content: Quote from: langwadt on October 06, 2018, 07:45:41 pmIt puts me back to square one, I have to look at the argument and decide what to do, this means running through all possible pins and the code to set each pin up.

author: Simon
author_rank: Unknown
timestamp: October 06, 2018, 08:02:58 pm
content: Quote from: nctnico on October 06, 2018, 07:29:37 pmare enumeration and enum the same thing? enum is about variables, the problem i have is converting a variable text into code and concatenating it with other text to make the code required.

author: tsman
author_rank: Frequent Contributor
timestamp: October 06, 2018, 08:15:06 pm
content: Use ##http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

author: DC1MC
author_rank: Super Contributor
timestamp: October 06, 2018, 08:15:59 pm
content: First of all this may be a case of "I want to X by doing Y, even if I don't know for sure if Y it's the best way to do it or if it's even possible to do X via Y"There is some information lacking of what are you trying to accomplish but if the function code it's really so much different in between setting the different pins with/without pullups, the fastest way is to make an array of function pointers for each function and use some macro trickery to select them depending on the designators, this will work at compile time only.If actually there are just some(how) consistent bits twidilng in some registers, the macro system of C/C++ will help you produce values for each of them in an array of whatever size the registers are.More details are needed for a complete solution with examples.Cheers,DC1MC

author: Simon
author_rank: Unknown
timestamp: October 06, 2018, 08:26:51 pm
content: Quote from: DC1MC on October 06, 2018, 08:15:59 pmCode:static inline void PA0_input(){ PORTA.DIRCLR = 0x01; }static inline void PA1_input(){ PORTA.DIRCLR = 0x02; }static inline void PA2_input(){ PORTA.DIRCLR = 0x04; }static inline void PA3_input(){ PORTA.DIRCLR = 0x08; }static inline void PA4_input(){ PORTA.DIRCLR = 0x10; }static inline void PA5_input(){ PORTA.DIRCLR = 0x20; }static inline void PA6_input(){ PORTA.DIRCLR = 0x40; }static inline void PA7_input(){ PORTA.DIRCLR = 0x80; }so say i want to write a function like set_pin_as_input(); with the argument being PA0, PA1.....At the moment I would write a switch case system calling out the appropriate code. but surely there is a way to join together the variable part: PA0" etc and "_input();"

author: ajb
author_rank: Super Contributor
timestamp: October 06, 2018, 08:30:39 pm
content: Quote from: Simon on October 06, 2018, 08:26:51 pmYes, that's what ## is for. But you can do this much more simply. For AVR style parts you might do something like this:Code:#define IO_OUTSET(...) IO_OUTSET_SUB(__VA_ARGS__)#define IO_OUTSET_SUB(port, pin) PORT##port |= (1<<pin)Which you'd then use like:Code:IO_OUTSET(A,1);The two levels of macros is helpful because the first macro substitution expands macros in the arguments, which allows things like this to work:Code:#define LED_PIN B,3IO_OUTSET(LED_PIN);If you tried to do that without the extra layer of indirection, the preprocessor would complain about not having enough arguments for the macro.You can make similar macros for enabling pullups, and then do more complicated macros that do multiple things like this:Code:#define IO_INPUT_PULLUP(...) IO_INPUT_PULLUP_SUB(__VA_ARGS__)#define IO_INPUT_PULLUP_SUB(port, pin) do { \IO_DIR_IN(port, pin); \IO_PULLUP_EN(port, pin); \} while(0);

author: Simon
author_rank: Unknown
timestamp: October 06, 2018, 08:35:40 pm
content: sure but i am looking at writing a function that takes multiple input arguments. so say which input pin do i want to use and which event system channel will i use. I could even throw in which counter do I want to use.

author: langwadt
author_rank: Super Contributor
timestamp: October 06, 2018, 08:36:12 pm
content: Quote from: Simon on October 06, 2018, 07:50:13 pmisn't PA5 defined a something sensible? like #define PA5 GPIOA,GPIO_Pin_5so a function like input_w_pullup((GPIO_TypeDef* port, uint32_t pin) just needs to access the right registers using the supplied pointer

author: ajb
author_rank: Super Contributor
timestamp: October 06, 2018, 08:38:53 pm
content: Quote from: Simon on October 06, 2018, 08:35:40 pmMostly pin numbers and event channels are either numeric subfields within registers or bits at a specific offset, so you would have to look at how the relevant registers are structured and figure out the best way to do that. Worst case, you might need a const array to use as a lookup table or something.

author: IanB
author_rank: Super Contributor
timestamp: October 06, 2018, 08:49:26 pm
content: Quote from: Simon on October 06, 2018, 08:26:51 pmEvery one of those functions is exactly the same except for the value "0x01" etc. So it is very wasteful to write eight functions instead of one function with a parameter. You could do it more compactly like this:Code:static inline void PA_input(unsigned int pin) { PORTA.DIRCLR = pin; }And you would call it like this:Code:PA_input(PA3);In some header file you would have a definition for PA3 and others like this:Code:#define PA3 0x08

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2018, 08:51:49 pm
content: Thishttps://github.com/ataradov/mcu-starter-projects/blob/master/samd21/hal_gpio.hsounds exactly what you want. Obviously port this to AVR.

author: martinjaymckee
author_rank: Contributor
timestamp: October 06, 2018, 08:53:22 pm
content: Macros can be made to work with more arguments if you wish. They are rarely a good solution, however. The question has been asked before but I will repeat it. Why do you need the code to work this way? It is clear (and certainly reasonable!) that you want to get away from using a massive switch statement to handle this. What isn't clear, however, is why that would be a problem. When at all possible, I prefer to avoid macros because they happen only on text before the compiler sees the program. That can lead to annoying bugs and inconsistency in how the code works. Still, they would work here -- either as ,Code:input_w_pullup(PA5);or as something like,Code:input_w_pullup(PORTA, 5);Actually, these could be easily implemented as static inline functions and have the same cost as a macro.I usually code in C++ and handle the same thing by wrapping the port/pin in a class. That way I can pass them around as one piece of data. This works just fine in pure C too (as it may look for an AVR). It takes a bit of work to get the compiler to see that it can remove all the abstraction cost though. So just immediate functions might be preferable.I would look very closely at the comments by IanB and ajb. They are pointing you in a good direction as they are showing how you can make a function handle multiple situations without a massive switch/case statement. Even if it takes some math to calculate offsets or something, trust the compiler. They are exceptionally good these days. You'd be surprised what it can figure out it doesn't need.Cheers,Martin Jay McKee

author: hamster_nz
author_rank: Super Contributor
timestamp: October 06, 2018, 09:53:31 pm
content: C is next to useless for meta-programming (code that generates code). If you really want to do this, the best way might be to write a program or script to generate the C source you need, and incorporate running that into your Makefile or build scripts.I would question the motivation for wanting to do this. It seems like a square peg for a round hole, and in a few months you will be wondering why you did this.The pnly use case I can see would be if you needed to log or breakpoont when the register gets changed, or it you wanted to have a 'shadow register' that can be read when the underlying hardware register is write-only, or a read causes undesirable side effects.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 06, 2018, 10:51:06 pm
content: There's an important distinction based on when the "PA5" becomes known.If it is known at compile time, you create a macro or a set of macros and use one of them. Or you just write code directly.If it only becomes known at run time, you think of a data type or structure which would best hold "PA5" then you write a single function which accepts the data of such type and produces the desired action(s).Either way, you don't need 50 different functions.

author: TK
author_rank: Super Contributor
timestamp: October 06, 2018, 11:03:23 pm
content: Quote from: Simon on October 06, 2018, 08:35:40 pmWhat you can do is to assign each argument a weight in binary (1, 2, 4, 8, 16, and so on) and use AND / OR operators to separate them inside the function, so you can pass only 1 argument.Something like:Code:#define PA0 b00000001#define PA1 b00000010#define PA2 b00000100#define PA3 b00001000#define PA4 b00010000#define PA5 b00100000#define PA6 b01000000#define PA7 b10000000void function(arg) {if (arg & PA1) {do something}void main() {argument = PA1 | PA5;function(argument);}

author: sleemanj
author_rank: Super Contributor
timestamp: October 06, 2018, 11:05:27 pm
content: Maybe something like (note using typeof GCC extention since I don't know what the type of PORTA is, maybe you'd have to jiggle about with that to work out what works)...Code:// Note that you must declare it first, at least in my experiment, otherwise GCC will complain about type mismatchvoid inputMode( typeof (PORTA) &thePort, uint8_t thePin );void inputMode( typeof (PORTA) &thePort, uint8_t thePin ){thePort.DIRCLR = 1<<thePin;}...inputMode(PORTA, 1);

author: donotdespisethesnake
author_rank: Super Contributor
timestamp: October 06, 2018, 11:05:56 pm
content: It's funny how people hate on Arduino's inefficient digitalWrite() then get stuck trying to write a better one.Micros are fast, it doesn't usually matter if you need a switch statement, unless you need to bitbang serial comms at high speed. You can go to great lengths with preprocessor trickery, code generators, are macros which generate C++ templates but is it really worth it.However, if you want an efficient preprocessor solution, have a look at the way Marlin does it.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 07:22:09 am
content: Quote from: donotdespisethesnake on October 06, 2018, 11:05:56 pmYes funny enough I am back there. I want to write a function where I set up a counter to measure PWM, so i need at the very least to determine which pin I am using as an input and which event channel i am using (ATmega 0-series)

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 07:38:35 am
content: I take it I can use string variables to put a bit of text in? As the compiler is efficient this will all come out anyway in optimisation. The idea is to simplify program writing.

author: hamster_nz
author_rank: Super Contributor
timestamp: October 07, 2018, 08:03:35 am
content: Quote from: Simon on October 07, 2018, 07:38:35 amUnless a variable is guaranteed to never be written to - which means it is declared as static (so it has no external linkage) & const (so it can never be changed) the compiler will not be able to optimize anything away. When it comes to string constants I'm pretty sure it will not be optimized away - for example the checksum for a constant string can be calculated at compile time, but I doubt that a compiler will optimize "cksum = checksum(my_constant_string);" away and replace it with a constant.Sometimes you can jump though hoops and use "inline" functions to widen the scope of constant propagation, but it really bad form to plan on this working.A C program's executable code is 'frozen' when it is compiled and linked - it usually ends up in a read-only segment of memory, or written to a controller's flash in the case of microcontrollers like most Arduino variants. So anything that makes new code on the fly is out.An aside: This makes me recall, that In the '80s my standard 'speed test' for scientific pocket calculators was to calculate factorial(69) - they should have just used a 70 entry lookup table :-)

author: IanB
author_rank: Super Contributor
timestamp: October 07, 2018, 09:08:58 am
content: Quote from: Simon on October 07, 2018, 07:38:35 amNot in the way you are thinking, not unless you use the C preprocessor as indicated higher up the thread.I do think you are somehow barking up the wrong tree here. "Concatenating text to make up code" is not the solution to problem you are trying to solve.Instead of this, you should be thinking of function parameters and various operations in involving bit patterns like shifting, AND'ing and OR'ing them together. In the computer's world the "text" that needs concatenating is groups of bits, not human letters and numbers.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 09:46:44 am
content: So I am plunging into the switch statements. Some will be hummungus, this in itself does not concern me as I know the compiler will strip out the unused stuff. But it will take a while to write, is there a clever program out there that manipulates text that I can use to generate the mundane stuff?

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 09:52:02 am
content: Quote from: IanB on October 07, 2018, 09:08:58 amI am using defines but yes you are right, I just wrote the below but yes if i mathematically used the right hand digit I could have done a bit of automation there. i have a set of definitions that translate a text to the numbers like "#define PORT_A_PIN_0 10" Which i have made intentionally wordy to avoid accidentally using an existing manufacturer definition.Code:void set_input_w_pullup(uint8_t port_pin){switch (port_pin){case 10: PA0_input_w_pullup();break;case 11: PA1_input_w_pullup();break;case 12: PA2_input_w_pullup();break;case 13: PA3_input_w_pullup();break;case 14: PA4_input_w_pullup();break;case 15: PA5_input_w_pullup();break;case 16: PA6_input_w_pullup();break;case 17: PA7_input_w_pullup();break;case 20: PB0_input_w_pullup();break;case 21: PB1_input_w_pullup();break;case 22: PB2_input_w_pullup();break;case 23: PB3_input_w_pullup();break;case 24: PB4_input_w_pullup();break;case 25: PB5_input_w_pullup();break;case 26: PB6_input_w_pullup();break;case 27: PB7_input_w_pullup();break;case 30: PC0_input_w_pullup();break;case 31: PC1_input_w_pullup();break;case 32: PC2_input_w_pullup();break;case 33: PC3_input_w_pullup();break;case 34: PC4_input_w_pullup();break;case 35: PC5_input_w_pullup();break;case 36: PC6_input_w_pullup();break;.case 37: PC7_input_w_pullup();break;case 40: PD0_input_w_pullup();break;case 41: PD1_input_w_pullup();break;case 42: PD2_input_w_pullup();break;case 43: PD3_input_w_pullup();break;case 44: PD4_input_w_pullup();break;case 45: PD5_input_w_pullup();break;case 46: PD6_input_w_pullup();break;case 47: PD7_input_w_pullup();break;case 50: PE0_input_w_pullup();break;case 51: PE1_input_w_pullup();break;case 52: PE2_input_w_pullup();break;case 53: PE3_input_w_pullup();break;case 54: PE4_input_w_pullup();break;case 55: PE5_input_w_pullup();break;case 56: PE6_input_w_pullup();break;case 57: PE7_input_w_pullup();break;case 60: PF0_input_w_pullup();break;case 61: PF1_input_w_pullup();break;case 62: PF2_input_w_pullup();break;case 63: PF3_input_w_pullup();break;case 64: PF4_input_w_pullup();break;case 65: PF5_input_w_pullup();break;case 66: PF6_input_w_pullup();break;case 67: PF7_input_w_pullup();break;}}

author: hamster_nz
author_rank: Super Contributor
timestamp: October 07, 2018, 10:06:51 am
content: why not...Code:void set_input_w_pullup(uint8_t port_pin){uint8_t port = port_pin/8;uint8_t pin = port_pin%8;uint8_t mask = 1<<pin;switch( (port) {case 0:PORTA.DIRCLR = mask;break;case 1:PORTB.DIRCLR = mask;break;case 2:PORTB.DIRCLR = mask;break;... and so on ...default:/* Error */break;}}Then just have #defines for port a pins as 0 through 7, port b pins as 8 through 15, port c pins as 16 through 23 and so on.You can do the '/8' and '%8' using ">>3" and '&7' to make it more efficient.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 10:28:35 am
content: Yes I should have done something like that.

author: TK
author_rank: Super Contributor
timestamp: October 07, 2018, 01:13:04 pm
content: Quote from: hamster_nz on October 07, 2018, 10:06:51 amThis method can process only 1 pin at a time. If you want to process all pins in one function call, then you can set in one byte all the pin settings and then loop bit shifting and processing the pins that are set and ignore the ones that are not set.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 07, 2018, 03:06:10 pm
content: Quote from: Simon on October 07, 2018, 10:28:35 amIt is ok if you really need to select the pin at run time.If you just want to set/clear a specific pin, there's no reason for doing the parsing at run-time.Of course, if you inline the function and call it with a constant as a parameter, the compiler may indeed be able to optimize everything out. But even if it can do such optimizations, the compiler is very likely not to honor your inlining when you call the same function 20 or 30 times from different places.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 03:22:46 pm
content: Quote from: TK on October 07, 2018, 01:13:04 pmi would only be using one pin at a time anyway as it will be used with a periphery setup.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 03:59:28 pm
content: Well I have been able to replace the massive switch with the following, but this does not include the pull-ups which are a register per pin so it would be another massive switch to choose the register unless i can play with raw memory address numbers, um safe in the knowledge that the addresses will not change.so for example: "static inline void PF3_pullup_on(){ BIT1(PORTF.PIN3CTRL, 3); }"Code:void set_input(uint8_t port_pin){uint8_t port = (port_pin / 8); // Calculate the part numberuint8_t pin = (port_pin % 8); // Calculate the pin numberswitch (port){case 0: (PORTA.DIRCLR = 0x01 << pin);break;case 1: (PORTB.DIRCLR = 0x01 << pin);break;case 2: (PORTC.DIRCLR = 0x01 << pin);break;case 3: (PORTD.DIRCLR = 0x01 << pin);break;case 4: (PORTE.DIRCLR = 0x01 << pin);break;case 5: (PORTF.DIRCLR = 0x01 << pin);break;}}

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 04:28:37 pm
content: Its much more fun when you can get away from the preprocessor-Code:#include <stdint.h>typedef enum {A0=0x400, A1, A2, A3, A4, A5, A6, A7,B0=0x420, B1, B2, B3, B4, B5, B6, B7,C0=0x440, C1, C2, C3, C4, C5, C6, C7,D0=0x460, D1, D2, D3, D4, D5, D6, D7,E0=0x480, E1, E2, E3, E4, E5, E6, E7,F0=0x4F0, F1, F2, F3, F4, F5, F6, F7,} PIN_t;typedef enum { OUT=1, IN } DIR_t;typedef enum { PULLUP_OFF, PULLUP_ON } PULLUP_t;static inline void pin_dir(PIN_t pin, DIR_t io){((volatile uint8_t*)(pin & 0XFF0)) = 1<<(pin&7);}static inline void pin_pullup(PIN_t pin, PULLUP_t pu){//with pullup wanted, also assume is an inputif(pu){pin_dir(pin, IN);((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] |= 1<<3;} else {((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] &= ~(1<<3);}}static inline void pin_set(PIN_t pin, uint8_t hl){if(hl)((volatile uint8_t*)(pin & 0XFF0)) = 1<<(pin&7);else ((volatile uint8_t*)(pin & 0XFF0)) = 1<<(pin&7);}static inline void pin_toggle(PIN_t pin){((volatile uint8_t*)(pin & 0XFF0)) = 1<<(pin&7);}static inline uint8_t pin_get(PIN_t pin){return ((volatile uint8_t*)(pin & 0XFF0)) & (1<<(pin&7));}#define sw1 A2#define led1 B3int main(){pin_pullup(sw1, PULLUP_ON);pin_dir(led1, OUT);//led1 lights when sw1 pressedfor(;;){pin_set(led1, !pin_get(sw1));}}/*Disassembly of section .text:00000000 <main>://sw1 input0: 84 e0 ldi r24, 0x04 ; 42: 80 93 02 04 sts 0x0402, r24 ; 0x800402 <_edata+0x3a2>//sw1 pullup on6: 80 91 12 04 lds r24, 0x0412 ; 0x800412 <_edata+0x3b2>a: 88 60 ori r24, 0x08 ; 8c: 80 93 12 04 sts 0x0412, r24 ; 0x800412 <_edata+0x3b2>//led1 output10: 88 e0 ldi r24, 0x08 ; 812: 80 93 21 04 sts 0x0421, r24 ; 0x800421 <_edata+0x3c1>//get sw1 (loop starts here)16: 90 91 08 04 lds r25, 0x0408 ; 0x800408 <_edata+0x3a8>1a: 92 fd sbrc r25, 21c: 03 c0 rjmp .+6 ; 0x24 <__zero_reg__+0x23>//sw1=0, so set led1=11e: 80 93 25 04 sts 0x0425, r24 ; 0x800425 <_edata+0x3c5>22: f9 cf rjmp .-14 ; 0x16 <__zero_reg__+0x15>//else sw1=1, so led1=024: 80 93 26 04 sts 0x0426, r24 ; 0x800426 <_edata+0x3c6>28: f6 cf rjmp .-20 ; 0x16 <__zero_reg__+0x15>*/I'm no expert, but the more you let the compiler handle things, the better.The above may not be perfect, bu the only 2 defines above are to give friendly names for the pins. I don't need a thousand lines of defines, and each function- whether inline or not can be easily tested/tweaked.As can be seen above, the compiler does a pretty good job (I used -Os).

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 05:03:53 pm
content: so can I do something like.given the offset for pin control register is: 0x10 + n*0x01 [n=0..7]Code:uint8_t pin;#define PORTA_OFFSET 0x0400PORTA_OFFSET + 0x10 + pin*0x01 = to an expression ;so basically can I assign a value to a register address who's number is calculated. This would make for some very slick code as a single line would do all ports and pins.

author: TK
author_rank: Super Contributor
timestamp: October 07, 2018, 05:17:37 pm
content: Quote from: Simon on October 07, 2018, 05:03:53 pmpin * 0x01 is equal to pin.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 05:32:50 pm
content: I don't understand enums and even if i learned that bit now how does it help to make another variable that represents a memory address when i can just write to that memory address and calculate it as well, all i need to do is extract the part number and pin number.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 05:33:20 pm
content: Quote from: TK on October 07, 2018, 05:17:37 pmYes true, but does it work in principle?

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 05:33:34 pm
content: Did you even look at the code I posted? Compile it, look at the code produced, change it, whatever.Every pin is in the enum- A0 - F7the port address is 'encoded' in the pin enum- A2 = 0x0402, PORTA = (A2 & 0xFF0) = 0x400, pin = (A2 & 7) = 2static inline void pin_pullup(PIN_t pin, PULLUP_t pu){//with pullup wanted, also assume is an inputif(pu){pin_dir(pin, IN);((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] |= 1<<3;} else {((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] &= ~(1<<3);}}I added setting the pin_dir since it mostly makes no sense to have a pullup unless an input, I also added the ability to specify PULLUP_ON or PULLUP_OFF instead of having separate functions for ON and OFF.but it boils down to this to set the pullup bit-((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] |= 1<<3;if A2 (0x402) passed into function, result is-((volatile uint8_t*)(0x400))[0x12)] |= 1<<3;orPINCTRL2 |= 8;which sets the PULLUPEN bit for PORTA/PIN2With the pin enums, you have all the info needed to do about anything with a pin- you have the port base address and the pin number and the various port registers are at known offsets from the base register.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 07, 2018, 05:49:30 pm
content: Quote from: cv007 on October 07, 2018, 04:28:37 pmIf you used defines instead of static functions, it would be about the same number of lines, not thousands.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 06:00:40 pm
content: Quote from: cv007 on October 07, 2018, 05:33:34 pmNo sorry I did not understand it but that made more sense, essentially it does the same as what I proposed.All i need to make this work with defines is:Code:/* Memory location offsets for ATmega 0-series */#define PORTS_OFFSET 0x4000#define PORTS_OFFSET_GAP 0X20#define PORTA_OFFSET 0x0400#define PARTB_OFFSET 0x0420#define PORTC_OFFSET 0x0440#define PORTD_OFFSET 0x0460#define PORTE_OFFSET 0x0480#define PORTF_OFFSET 0x04A0

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 06:02:07 pm
content: QuoteThere is the internet, which can give lots of info.Basically the typdef enum turns a set of numbers into a specific type which the compiler can enforce usage like any other type..In my example, using the PIN_t for the parameter to these functions prevents one from misusing the function. Only a PIN_t value can be used, which means only the numbers in the typedef enum can be used. You can't pass arbitrary numbers, only specific ones which the compiler enforces. So the compiler does the checking for valid 'pins'- if we use a 'normal' number passed into these functions (by mistake) the compiler will complain and prevent you from doing it (of which the end result would be a write to who-knows-where).If one allowed any number to be used by the function, you would either have to check if its in a valid range or just 'hope' its not used with an invalid number.QuoteI was referring to his current system of a thousand defines.Yes, you could also turn these into defines, but how do you then enforce type?#define PULLUP_ON(pin) ((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] |= 1<<3I can use about any number for 'pin' and there will be nothing to prevent incorrect useWhy not let the compiler help out.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 06:14:28 pm
content: Quote from: cv007 on October 07, 2018, 06:02:07 pmI no longer use the defines, I use functions but then I can end up writing as many functions as defines. Doing it this way does cut down massively on either, but defines will still be key. because I can't have C take text as a variable and I human don't see the meaning of random numbers so i have to use defines to convert sensible text into numbers that are sensible for the compiler. I am not using any defines to paste code as i used to. Even the manufacturer uses defines to convert register names to numbers, i am simply proposing to bypass all of that and write my own stuff straight to memory numbers.So if I do have a wrong number what actually happens? does it come out in compile. the numbers are set in defines, if i write a wrong define that too flags an error.

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 06:53:34 pm
content: Maybe we should back up- what do you want your end result to look like when you finally write code and use these functions-using my example (for pins), I would want a simple way to get a friendly name for my pins, and also an easy way to change them-#define sw1 A2#define led1 B3ORI could simple add these to the end of my PIN_t typedef enum (probably a better idea, as all pin info will now be in one location)...F6, F7,sw1=A2, led1=B3} PIN_t;ORconst PIN_t sw1=A2;const PIN_t led1=B3;and to use them, have some set of functions with obvious names to do things to the pins-pin_pullup(sw1, PULLUP_ON); pin_pullup(sw1, PULLUP_OFF);pin_dir(led1, OUT); pin_dir(led1, IN);pin_set(led1, 1); pin_set(led1, 0);whatever you want it to look like, you now have a target and just have to figure out how to get there.Maybe show us what you want your end result to look like for pins- how would you get a friendly pin name and how would you use it to do something.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 07:00:37 pm
content: Pin_input(port, pin);So i either use one (defined) number for the port and then do /8 and %8 to or input them individually as above.So with a number for the port and pin I can carry out the math to calculate the memory address of the register in question, this is much easier that caring what the register names are.This but a simple example. I strarted trying to write a function to setup a TCB counter to read a PWM input:TCB_pwm_setup(port, pin, event_channel);I was going to make that a channel specific function and write it all 4 times over but this way it can be one set of code for any number of counters.So essentially i am bypassing the manufacturers defines and writing my own coding system like the arduino.

author: IanB
author_rank: Super Contributor
timestamp: October 07, 2018, 07:28:23 pm
content: Quote from: Simon on October 07, 2018, 06:14:28 pmWell you can, sort of, if you use the C preprocessor and the concatenation operators.Probably, what cv007 said makes sense. Show what you want your end result to look like, and maybe someone can show you how to make it happen. Also, be wary of reinventing wheels. Whatever you are trying to do, there must be many people before you trying to do the same thing. Can you find out what solution they used?

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 07:29:26 pm
content: still don't know how you get a friendly pin name, so-How about this- write some code to duplicate what I wrote earlier in whatever syntax/format/functions/defines you want so you end up with a main function that simply-1) sets a pin to an input with pullup on (pin I called sw1)2) set a pin as an output (pin I called led1)3) loops with the pin output set to the input pin inverted value (sw1 pressed=led1 on)(don't care about anything else but pins, and only needed functions- just enough to do 1,2,3)should be easy enough, then 'we' can see where you are heading and maybe help by pointing out improvements that can be madeif you want

author: NorthGuy
author_rank: Super Contributor
timestamp: October 07, 2018, 07:58:31 pm
content: Quote from: Simon on October 07, 2018, 07:00:37 pmPort is basically an array in memory, so you can simply pass a pointer to the first register. Then you can access other registers as elements of the array as cv007 did in his. However, this is, of course, architecture specific.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 07, 2018, 08:07:07 pm
content: Quote from: cv007 on October 07, 2018, 06:02:07 pmYou can still pass a wrong value of a correct type. This will still mess up your program.I'm not a big fan of strong typification - it often requires lots of typecasts which are time-consumingand clutter the code. Moreover, once you apply the typecast, there's no longer any checks for types.

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 08:24:49 pm
content: Code:#define PORTS__OFFSET 0x4000#define PORTS__OFFSET_GAP 0X20#define PORTA__OFFSET 0x0400#define PARTB__OFFSET 0x0420#define PORTC__OFFSET 0x0440#define PORTD__OFFSET 0x0460#define PORTE__OFFSET 0x0480#define PORTF__OFFSET 0x04A0#define DATA_DIRECTION 0x00#define DATA_DIR_SET 0x01#define DATA_DIR_CLEAR 0x02#define DATA_DIR_TOGGLE 0x03#define DATA_OUTPUT 0x04#define DATA_OUTPUT_SET 0x05#define DATA_OUTPUT_CLEAR 0x06#define DATA_OUTPUT_TOGGLE 0x07#define DATA_INPUT_PIN_VALUE 0x08#define PIN_INTERRUPT_FLAGS 0x09#define PORT_CONTROL 0x0A#define PIN_CONTROL 0x10#define PORT_A 0x00#define PORT_B 0x01#define PORT_C 0x02#define PORT_D 0x03#define PORT_E 0x04#define PORT_F 0x05#define PIN_0 0x00#define PIN_1 0x01#define PIN_2 0x02#define PIN_3 0x03#define PIN_4 0x04#define PIN_5 0x05#define PIN_6 0x06#define PIN_7 0x07#define PULLUP 0x03void setup_input_w_pullup(uint8_t port, uint8_t pin){PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET = 0x01 << pin;PORTS__OFFSET + PORTS__OFFSET_GAP * port + PIN_CONTROL + pin = 0x01 << PULLUP;}

author: Simon
author_rank: Unknown
timestamp: October 07, 2018, 08:28:41 pm
content: That one function replaces 48 + 48 inline functions, I guess the compiler will pre-calculate the addresses in optimisation.

author: TK
author_rank: Super Contributor
timestamp: October 07, 2018, 09:32:18 pm
content: Quote from: Simon on October 07, 2018, 08:24:49 pmI don't think this code compiles without errors as the left side expression is not assignable a value

author: cv007
author_rank: Frequent Contributor
timestamp: October 07, 2018, 09:32:39 pm
content: I guess you got close, but I'll help. You must not have tried to compile it.Code:static inline void setup_input_w_pullup(uint8_t port, uint8_t pin){*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET) = 0x01 << pin;*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + PIN_CONTROL + pin) = 0x01 << PULLUP;}//I assume this is close to what you want#define SW1_PORT PORT_A#define SW1_PIN PIN_2//OR#define SW1 PORT_A,PIN_2//#define SW1 PIN_2,PORTA //<- wrong order, but will get no complaintsint main(){setup_input_w_pullup(SW1_PORT, SW1_PIN); //oksetup_input_w_pullup(SW1_PIN, SW1_PORT); //<- wrong usage, but no errorsetup_input_w_pullup(PORT_A, PULLUP); //<- wrong usage, but no error//ORsetup_input_w_pullup(SW1); //<- cannot mix up order unless define uses mixed up order}I guess we are just going to do 1, and not 2 and 3 also.In my simple example, there is a reason I did not create defines for anything- they are only used in one place so its not needed. I did not comment the code, but that would take care of it-static inline void pin_dir(PIN_t pin, DIR_t io){//io- OUT=1 (DIRSET) IN=2 (DIRCLR)((volatile uint8_t*)(pin & 0XFF0)) = 1<<(pin&7);}You could also eliminate many defines-Code:#define PORT_BASE 0x400#define PORT_A 0x00#define PORT_B 0x20#define PORT_C 0x40#define PORT_D 0x60#define PORT_E 0x80#define PORT_F 0xA0static inline void setup_input_w_pullup(uint8_t port, uint8_t pin){//PORT_BASE = PORTA base address//=DIRSET((volatile uint8_t*)(PORT_BASE+port)) = 1 << pin;//0x10 = PINCTRL offset, PULLUPEN = bit3((volatile uint8_t*)(PORT_BASE+port))[0x10+pin] = 1 << 3;}#define SW1_PORT PORT_A#define SW1_PIN 2//OR#define SW1 PORT_A,2//#define SW1 2,PORTA //<- wrong order, but no complaintsThe comments would take care of filling in the user about what bits/registers are used. Either that or they have to hunt down the define.Or simply forget the defines altogether (I'm probably getting annoying).

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:20:51 am
content: Quote from: TK on October 07, 2018, 09:32:18 pmHow is the left expression not assigned a value?

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:23:10 am
content: Your code is parsed by the compiler like this: "PORTS__OFFSET + PORTS__OFFSET_GAP * port + (DATA_DIR_SET = 0x01 << pin)". And DATA_DIR_SET is a constant. This is a wrong code all around.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:23:52 am
content: Quote from: cv007 on October 07, 2018, 09:32:39 pmYes you are. While I understand that using defines to define code is a bad idea they are not bad in themselves and used by the manufacturers, without them any address location would be a number and then that code would not be portable across devices maybe in the same family. Just look at the manufacturers own chip header file.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:24:28 am
content: Quote from: ataradov on October 08, 2018, 06:23:10 amSo I need parenthesis?

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:25:33 am
content: Quote from: Simon on October 08, 2018, 06:24:28 amNo, you need to rewrite the code, so it makes sense. I believe somebody actually posted the correct version of this code. You've got to read what people write.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:30:23 am
content: So microchip are mad to write things like:#define CLKCTRL_MCLKCTRLA _SFR_MEM8(0x0060)which allows me to sayCLKCTRL_MCLKCTRLA = "some value"

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:32:54 am
content: I don't understand what do you mean.As far as I can see _SFR_MEM8 is a just a dummy, so the code is wrong. I'm not sure why you think you can do this.Actually no, _SFR_MEM8 for C is defined as "_MMIO_BYTE(mem_addr)". Look it up and see that it is exactly the same thing as fixed code proposes. For assembly it is just a number.So ultimately your code turns into (*(volatile uint8_t *)(0x0060)) = "some value". You need to do the same in your code.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:38:12 am
content: Quote from: ataradov on October 08, 2018, 06:32:54 amSo what is the difference there? that assigns a value to a calculated address location, or is it the asterisks that make it work (pointers?)

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:39:33 am
content: Yes, it makes a difference. This code casts the constant address to a pointer, then dereferences the pointer and assigns the value to that dereferenced value.You are assigning constant to a constant.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 06:44:25 am
content: I see, so *(volatile uint8_t*) is the bit i am missing and tells the compiler it is a memory location, it feels like this was explained backwards as my initial question was: can I simply assign a value to a calculated memory address location? the answer was no you need pointers.

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:45:48 am
content: You do need pointers. This expression is full of pointer operations.

author: Nusa
author_rank: Super Contributor
timestamp: October 08, 2018, 06:57:29 am
content: * in this context is called dereferencing, or indirection.https://en.wikipedia.org/wiki/Dereference_operatorSo *(0x0060) says we want to access the location pointed to by 0x0060.The (volatile uint8_t *) part is assigning a type override so the compiler knows that we want to do a byte operation rather than a word operation. And to keep the compiler from throwing errors at you since the typing is necessary when using raw constants like this. (edited to be more correct)

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 06:59:30 am
content: Not really. "*(0x0060)" is not a valid expression in C. Typecast is not just to prevent compiler from complaining, it is necessary there.

author: Nusa
author_rank: Super Contributor
timestamp: October 08, 2018, 07:08:08 am
content: Yes, I know...when it's a variable instead of a constant, the variable already has an associated type. But I was trying to show the dereference separate from the typing, since Simon is having trouble grasping the whole.

author: cv007
author_rank: Frequent Contributor
timestamp: October 08, 2018, 10:44:29 am
content: QuoteI know quite well how they do it, that's where I was getting my numbers in my example. I think I said in one of your other threads- either embrace them (manufacturers headers) or do your own thing. As long as you are doing your own thing, you are free to do as you wish and not restricted to the way everybody does it.I have a pic32mm which I wrote 'drivers' for every peripheral and usb- I have a total of 6 defines in all the code (2 for isr declarations, 4 in usb for generating parts of descriptor- when there was no other option). It will work for every pic32mm-gpm chip made (they only vary by pin count, memory size). I dislike their headers, their code generators, so I decided to do my own thing. So I'm not unaware what you want to do.QuoteI think I also showed in another one of your threads how this works-https://www.eevblog.com/forum/microcontrollers/(atmega-0)-tcat-(aka-union-tca-union)-has-no-member-named-ctrl/msg1771667/#msg1771667mini tutorial-((volatile uint8_t*)(0x400)) = 8;0x400 - just a number(uint8_t*)0x400 - an address that points to an 8bit number(volatile uint8_t*)0x400 - an address that points to an 8bit number which is volatile- tell compiler it has to read/write every time it is used as it can change without anyone's knowledge (hardware)*(volatile uint8_t*)0x400 - get/set the 8bit value in the location 0x400((volatile uint8_t*)0x400) - or get/set the 8bit value in the location 0x400+0 (0x400 now acts like a pointer to an array of 8bit numbers, and we want the first element)result-((volatile uint8_t*)(0x400)) = 8; //write the value of 8 to the 16th element in the array of 8bit numbers which are at address 0x400 (ultimately at address 0x410)

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 04:41:09 pm
content: so the  has the effect of +0x10 ? why not just +0x10 then?So essentially to address memory i need to multiply the address by (volatile uint8_t*) or is it *(volatile uint8_t*) ?

author: ataradov
author_rank: Super Contributor
timestamp: October 08, 2018, 04:44:29 pm
content: You really need to understand how this works, not just copy-paste. This stuff is critical in C. is a standard array operation. It is equivalent to incrementing the address by sizeof(uint8_t) * 0x10. This is important, since the element size is uint8_t, and increment happens in bytes. This will not be the case if the array element type is different."a[ b ]" in C is 100% the same as "*(a + b)". Either a or be may be a pointer and the other one must be an integer.

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 05:01:19 pm
content: Yes i have every intention of understanding pointers but I'm not there yet.

author: langwadt
author_rank: Super Contributor
timestamp: October 08, 2018, 06:46:50 pm
content: Quote from: Simon on October 08, 2018, 04:41:09 pmmultiply is the wrong word(volatile uint8_t*) tells the compiler that the following should be seen as a pointer to volatile uint8_tputting a * in front tell it use it as a pointer

author: Simon
author_rank: Unknown
timestamp: October 08, 2018, 07:38:45 pm
content: OK, I see, notes to self:Pointers in CVariables contain values and reside in a memory location that has an address. Pointers hold the address of another variable.The address value of the variable x is represented by &x.The data type of a pointer variable is the same as the data type of the variable who’s address it is to store. A pointer variable is declared by putting an asterisk before its name.int *pointer;pointer = &x;The pointer can be used to refer to the value of the data stored at the address of the variable pointed to. To do this put an asterisk in front of the pointer.Int x, y;int *pointer;int pointer = &x;y = *pointer;y is now the same value of x.

author: IanB
author_rank: Super Contributor
timestamp: October 08, 2018, 09:42:17 pm
content: Yes, and in addition pointers know the size of the thing they point to.For example:int *pointer1; /* pointer1 points to ints */int x;pointer1 = &x; /* OK, because x is an int and it matches the pointer type */int *pointer2 = pointer1 + 1; /* pointer2 points to the next integer in memory */Be very careful to understand that pointer2 doesnotpoint one memory address after pointer1. If an int is 4 bytes, then pointer2 points 4 bytes after pointer1. When you increment a pointer or do pointer arithmetic, the arithmetic always works with objects of the size the pointer references.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 06:20:00 am
content: OK but I still do not understand how all of this relates to:(*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET)) = 0x01 << pin;(*(volatile uint8_t*)?? and I am still not clear on if it is one asterisk or two.The first asterisk which seems to be the optional one would suggest that we are pointing to the data at the address of the variable that presumably is being made up on the spot and is the address we want to write to? The second one is a mystery or is it also denoting the number as an address and using the parenthesis to stop things getting misinterpreted by the compiler?(*(volatile uint8_t*) is something I have not found an explanation to anywhere.

author: tombi
author_rank: Regular Contributor
timestamp: October 09, 2018, 07:06:45 am
content: Dunno if this helpsSo the PORTS_OFFSET... expression calculates a pointer address(uint8_t *) casts this as a pointer to an 8 bit unsigned integer. Lets leave volatile for a bit but then using *(uint8_t *)(XXX) then de-references the pointer (i.e. gets the 8 bit unsigned integer the pointer points at).Volatile is a way of making the compiler not snip out references to data it thinks shouldn't have changed (for example because it thinks the program didn't change it). Without volatile it might replace this with a constant as it thinks the value hasn't changed.Tom

author: Nusa
author_rank: Super Contributor
timestamp: October 09, 2018, 07:29:52 am
content: volatile is a keyword that tells the compiler not to make any assumptions when optimizing this. It may not be strictly required, but it's a good idea for things like this.White space before the * is optional. I tend to add a space myself.If you were doing this with variables it would be something like so, in concept:volatile uint8_t * myAddress;myAddress = (your address calculation); // assigning an address to the pointer.*myAddress = (your pin value); // setting the memory location pointed to by myAddress.If you replace myAddress with (your address calculation):*(your address calculation) = (your pin value); // setting the memory location pointed to by (your address calculation).See why you need the leading *, at least?What I left out was some required casting of the calculation to make the compiler happy. The cast would be the same (volatile uint8_t *) used to declare myAddress. Parens are required when casting types.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 07:31:28 am
content: Yea I get volatile

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 07:32:49 am
content: Quote from: tombi on October 09, 2018, 07:06:45 amSo basically the name and address of the variable is the same?

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 07:34:27 am
content: Quote from: Nusa on October 09, 2018, 07:29:52 amThat is the confusing bit.

author: Nusa
author_rank: Super Contributor
timestamp: October 09, 2018, 07:49:14 am
content: Quote from: Simon on October 09, 2018, 07:34:27 amWhen you use:*myAddress = (your pin value);the compiler already knows it's of type (volatile uint8_t *), because that's how MyAddress was defined.When you use:*(your address calculation) = (your pin value);the compiler doesn't know what type the calculated value is, so you have to tell it with a cast! In particular it doesn't know the size of the operation. Should the assignment be to a byte, word, double_word, etc. It makes a difference!So you use this:*(volatile uint8_t *)(your address calculation) = (your pin value);

author: Siwastaja
author_rank: Super Contributor
timestamp: October 09, 2018, 08:35:43 am
content: It's syntactically a bit confusing in C to just write stuff into a memory address.First, you need to tell the compiler which kind of store instruction it needs to generate, because let's say on a typical 32-bit CPU, memory can be written in 8-bit, 16-bit or 32-bit operation. If you use a 32-bit operation to do the store of your 8-bit value, you would accidentally overwrite the neighbors!C is "high level" enough that it simply lacks operators to do this directly.So, even if the end-result isnotusing pointers, you need to use thesyntaxfor creating a pointer, then dereferencing it - neatly in one expression. This way, you can tell the compiler which write operation to use. For example, to write 8 bits into memory address 0x1234, you first make a pointer with your desired 8-bit type:(uint8_t*)0x1234Then you dereference it: meaning you write to the location pointer by this pointer:* ((uint8_t*)0x1234) = something;Now the pointer itself is meaningless, and it won't exist (or maybe it does, depending on the addressing modes and instructions available on the particular CPU, but this is irrelevant; the compiler knows how to do the write). But you temporarily use the pointer syntax to give the compiler all the information it needs. The only reason the pointer needs to have a certain type is so that the compiler knows whether to pick a 8-bit, 16-bit or 32-bit store instruction. Otherwise, it's just an address.Volatile is added to tell the compiler that yes, it really needs to perform the operation, right now.Doing very low-level things in higher level language such as C may actually be more difficult to grasp than writing in assembly directly. This is because the C syntax is not designed around doing assembly-level stuff all the time. So you need to do syntactical extras such as play with a "pointer" even when an actual pointer is not involved.Hope this helps.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 08:59:15 am
content: But I can write assembler in C can't I? so in assembler how do you write to a memory location?My other syntax question is reading from these memory locations, do I just put the variable wanting the value to the left of the = and the same address syntax to the right?variable = (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_DIR_SET)) & mask;

author: Nusa
author_rank: Super Contributor
timestamp: October 09, 2018, 09:33:27 am
content: Quote from: Simon on October 09, 2018, 08:59:15 amNo, you can write C in C. The compiler turns it into assembler. There is a way to insert actual assembly code in the middle of C code, but that's not C anymore. That's actually assembly. If you want to actually write your program in assembly, you don't need C at all!The exact details vary based on the instruction set your hardware uses. In general you load an address into a register, then you use a load or move instruction to do an indirect memory write based on that register of the appropriate size.QuoteRight! If the variable was declared as uint8_t, even better!

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 09:39:37 am
content: OK I meant the IDE's allow assembler to be used in a C program, I know it can be done I was just wondering if the code was simpler to write when dealing with memory.

author: hamster_nz
author_rank: Super Contributor
timestamp: October 09, 2018, 10:09:18 am
content: Quote from: Simon on October 09, 2018, 09:39:37 amMost C compilers allow you to include inline assembler, and it is a real pain. Avoid it at all costsIf you really have to, use only one or two instructions that don't need access to data. (e.g. Enabling or disabling interrupts)Why?First of, you have to be very familiar with the underlying instruction set and architecture of the target processorThen you need to know the C compiler's expected assembler syntax, that might not match the vendor's native assembler (looking at you GCC for Intel).Then you have to work out how you can pass values and addresses between C and assembler (e.g. so you can access C variables), which involves magic incantations.Inline ASM prevents the optimizer from doing it's job, you have added a little block of magic in the middle of C code.And you can't format your code nicely, and make it readable, because it is intermixed with C and needs to be quotedAnd perhaps worst of all, adding labels so you can do jumps is just ugly. You need to force the C compiler to generate labels and use its naming schemes, rather than sensible meaningful names.And finally your code isn't portable, so you will only ever use the result of all your blood, sweat and tears once.Seehttps://locklessinc.com/articles/gcc_asm/if interested in a brief guide.To see how bad it really is, here is the start of some code that I wrote to drive WS2812B LEDs. I wish I had just used the SPI peripheral...Code:void outputWS2812Bbytes(unsigned char (*leds), unsigned char length){delay(1);asm volatile(" cli \n\t" // Disable interrupts" mov 18,%1\n\t" // Copy length" add %1, 18\n\t" // Add it back" add %1, 18\n\t" // Add it back, so it is now x3 what it was"L_next%=:" "\n\t"// Bit 7" SBI 5, 4 \n\t" // Set port b bit 4 Arduino pin 12" NOP \n\t" // A pause" LD 18, Z \n\t" // Load and post increment - two cycles" ANDI 18, 128 \n\t" // Test the bit - one cycle" BRNE L_bit7%= \n\t" // Skip the clear if the bit is set" CBI 5, 4 \n\t" // clear port b bit 4"L_bit7%=:" "\n\t"" NOP \n\t" // A pause" NOP \n\t" // A pause" NOP \n\t" // A pause" NOP \n\t" // A pause.....It is usually better to do the hard yards and get it to work in C

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 09, 2018, 10:27:34 am
content: and i hope the code will not go into some life critical equipments without rigorous testing or peer review from experienced personnel.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 09, 2018, 10:30:04 am
content: Quote from: Simon on October 09, 2018, 08:59:15 amYou do it by RTFM'ing the exact CPU architecture you are working with. But there is no C pointer syntax involved! Just one clearly documented operation which directly does your memory access.... hopefully. Not all architectures have simple addressing. C quickly becomes convenient when there is no simple load/store with immediate address operand over the desired address spaceThe great thing in the C way:*(uint8_t*)0xacdc = 0xabba;is that this is portable and the compiler generates the correct 8-bit memory access instruction for practically any CPU.If you don't like the pointer syntax, you can create some macros, like#define MEM8(addr) (*(uint8_t*)(addr))#define MEM16(addr) (*(uint16_t*)(addr))and thenMEM8(0xacdc) = 0xabba;but I won't recommend creating such "helpers" on the language basics if you ever intend the code to be read by others. You should learn the basics, and expect that others have learned the basics, too; then you have a common language everyone understands. Create helper macros and add documentative comments when this significantly reduces the syntactic overhead, or when dealing with some difficult specifics with the language most "normal" people never encounter. This isn't such a case.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 11:48:06 am
content: Quote from: Mechatrommer on October 09, 2018, 10:27:34 amNo it won'tQuoteThat is why I am asking here. First application will be a fan speed controller, I am planning to use the new ATmega 0-series so all my code templates and basic functionality needs re-writing - with the benefit of hindsight.assembler does indeed sound like a bad idea in a C project, won't be doing any of that.

author: TK
author_rank: Super Contributor
timestamp: October 09, 2018, 12:11:07 pm
content: There is a nice tool that visually executes python, C, C++ code and it shows graphically variables and pointers.http://pythontutor.comSelect C or C++ as your language, paste your code and execute it.Try visually executing the following code, it shows variables, pointers, an array and then it assigns the address of the array to the pointer and loops by using the pointer and incrementing it to put a value into each array element.Code:int main() {int z;int x=1, y=2;int *pointer;pointer = &x;y = *pointer;z = 3;pointer = z;for (int i=0; i<10; i++)*pointer++ = i;return 0;}

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 09, 2018, 06:21:11 pm
content: Quote from: Simon on October 09, 2018, 11:48:06 ami think you asked the wrong question (asking how to practice bad?) thats why i hesitated to make an involvement. but since a simple question usually expanded into several pages so... your question is kind of novel to me afaik nobody needed 48 functions for a simple task of fan speed control, i will not worry much if its 48 cases which some/many coders did so, but i never heard people want to do 48 functions, you can device fancy naming macro workaround in C but in the end your assembled code will expand to 48 routines complete with memory/stack/return vector shuffling each time, which i think sub optimum.Quote from: Simon on October 09, 2018, 11:48:06 amthe sense of reusable codes needs practice (good practice), time and experience. you can get to it fast here in forum if you ask the right question, your kind of question will get you the other way around (worse and even farther from reusable), imho. and other thing is... copy paste might help, if you really have toQuote from: Simon on October 09, 2018, 11:48:06 amyou can if you can process everything in mind, with knowledge or sense on how a compiler generates compiled code (c -> assembly/machine code) esp on how compiler will automatically allocate/use register/memory location to do stuff like temporary variables or even the static one, and the implication that you will screw this auto mapping by compiler if you mix it with assembly code. but as other more experienced member here said, its not that easy, last time i looked at the example i gave up i'd rather do it in fully C or fully assembler, or maybe it can be done for a very tight and isolated assembly codes. for a moderate complexity program that full assembly is not feasible, i'll learn how to do a "tight code" not "fancy code" in C and hope compiler is optimum enough at generating optimum/tight/smallest assembly/machine code. one example is explicitly using "register" when declaring a variable thats the wonderfulness of C not available in any other modern and higher level language (well its not a troll).sorry i cannot help you with your specific question since as i said its a novel question for meso i'm not being helpful here.

author: Simon
author_rank: Unknown
timestamp: October 09, 2018, 06:28:24 pm
content: Quote from: Mechatrommer on October 09, 2018, 06:21:11 pmi am refering to the attached

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 09, 2018, 06:38:56 pm
content: Quote from: Simon on October 09, 2018, 06:28:24 pmif you give specific problem you might get specific answerCode:static inline void PA0_input(){ PORTA.DIRCLR = 0x01; }static inline void PA1_input(){ PORTA.DIRCLR = 0x02; }static inline void PA2_input(){ PORTA.DIRCLR = 0x04; }static inline void PA3_input(){ PORTA.DIRCLR = 0x08; }static inline void PA4_input(){ PORTA.DIRCLR = 0x10; }static inline void PA5_input(){ PORTA.DIRCLR = 0x20; }static inline void PA6_input(){ PORTA.DIRCLR = 0x40; }one way at least i usually see it can be coded as...Code:#define PA0c 0x01#define PA1c 0x02...continue...static inline void input(code){ PORTA.DIRCLR = code; }that inline function will just expand to a single (reusable) line of code, which also you can define as...Code:#define input(code) PORTA.DIRCLR = codeymmv...

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2018, 06:48:42 pm
content: Quote from: Nusa on October 09, 2018, 07:29:52 amYes. Actually people thinking that this qualifier is useless are either always compiling with zero optimization or are sadly mistaken.Any variable that the compiler sees as never modified may (and depending on the opt. level and compiler, WILL) be optimized and either pruned altogether or at least considered as a, and replaced by a constant anywhere it's used.In embedded dev, a good example of "variables" that *can't* be seen as modified in the source code (at least when you don't write to them explicitly) are declared MCU registers. That's why you can see "volatile" in front of all MCU registers' declarations in most vendors' C headers. The compiler has no way of knowing their value can change.Another example are variables that are only modified in interrupt routines. In this case, the variables may be explicitly modifed in the source code, but the compiler has no way of knowing the interrupt routines that modify them will actually ever be called. ISRs are never called explicitly. So it may assume they won't, and thus assume as a consequence that the variables in question are never modified. That's why it's also suggested to use volatile as a qualifier for global variables that are written in interrupt routines.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 09, 2018, 06:54:56 pm
content: Quote from: SiliconWizard on October 09, 2018, 06:48:42 pm+1 i never have a need for "volatile" until i entered embedded interrupt/ISR world. learnt the hardway and took a lot of head scratching there while debugging. imho stating it as optimization purpose may not give its full picture.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 09, 2018, 08:25:05 pm
content: Quote from: SiliconWizard on October 09, 2018, 06:48:42 pmIt is also possible to error in other direction. I've seen people use "volatile" in such situations and think that this solves all the problems. This is not always the case. If your variable is longer than the processor can load in a single cycle (say 16-bit variable on 8-bit processor), the interrupt may happen right in the middle of the loading. This screws up the loading of the variable regardless of whether it was declared volatile or not.

author: hamster_nz
author_rank: Super Contributor
timestamp: October 09, 2018, 08:27:09 pm
content: Quote from: NorthGuy on October 09, 2018, 08:25:05 pm... and that is where you need your single line of inline assembler for "disable interrupts" & "enable interrupts" either side...

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2018, 08:35:25 pm
content: Quote from: NorthGuy on October 09, 2018, 08:25:05 pmSure. That's required, but not sufficient.You also need to care about the atomicity of operations indeed. Yet another possible pitfall. For this, you need to guard the read or write operation to make sure it can't get interrupted. Some processors have specific instructions for this. Most MCUs don't and you need to selectively disable and re-enable interrupts around the operation.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 09, 2018, 08:39:55 pm
content: Quote from: SiliconWizard on October 09, 2018, 08:35:25 pmI personally don't like disabling interrupts because it increases interrupt latency. Usually I can figure a way to avoid disabling interrupts, such as reading the variable twice and making sure it's the same both times.

author: Simon
author_rank: Unknown
timestamp: October 10, 2018, 06:28:10 am
content: I take it that:void pin_state(uint8_t port, uint8_t pin){return (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_INPUT_PIN_VALUE)) & 0x01 << pin; // return state of pin.} // Use PORT_x and PIN_n as arguments.would work? so I'd end up with something like:if ( pin_state( PORT_B, PIN_5 )) {some code;}And presumably !pin_state(PORT_B, PIN_5) to negate the result.

author: Nusa
author_rank: Super Contributor
timestamp: October 10, 2018, 06:46:49 am
content: Quote from: Simon on October 10, 2018, 06:28:10 amNo, because a void function doesn't return anything. But otherwise, it looks workable.I'd probably write it more like this, just for clarity.bool pin_state(...) {if (condition){return true;}return false;}

author: Simon
author_rank: Unknown
timestamp: October 10, 2018, 07:19:13 am
content: yea, duh of course, my excuse is copy and pasting.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 10, 2018, 07:23:45 am
content: Quote from: NorthGuy on October 09, 2018, 08:39:55 pmIt helps if the architecture allows you to selectively disable only certain priority of interrupts (still with a single instruction), for example, on ARM with the BASEPRI. Having all the super timing critical, non-jittery interrupts on the highest priority class that won't get disabled solves the problem. This priority class, of course, cannot communicate using such shared non-atomic variables, but they often won't need to; you tend to need to perform some simple, quick operation first. Then, at the end of the timing critical ISR, if you need, you can trigger a software interrupt to effectively lower the interrupt priority to go on with the post-processing - if no other timing-critical interrupts are pending, it tail-chains with no extra penalty like it would be a longer interrupt to begin with, but itcanget disabled and postponed for atomic operations.For example, in my current project, I use 16 interrupt priorities, out of which the two highest are never masked off. The highest level is safety shutdown locking in while(1) after doing the necessary shutdown stuff (power MOSFET gate driver disables, or correct power-down sequencing order while the caps still hold some charge) quickly, and the second-highest level may be used to quickly turn signals on or off in what's considered "normal operation".Although, if you find out that you need to communicate between your threads using such big datatypes, I would recommend trying to rethink it if you could just live the 8-bit/16-bit/32-bit (depending on your CPU) naturally atomic variable.

author: TK
author_rank: Super Contributor
timestamp: October 10, 2018, 10:53:39 am
content: Quote from: Simon on October 10, 2018, 06:28:10 amYou can define it as a macro to avoid calling a functionCode:#define pin_state(port, pin) (*(volatile uint8_t*)(PORTS__OFFSET + PORTS__OFFSET_GAP * port + DATA_INPUT_PIN_VALUE)) & 0x01 << pinif ( pin_state( PORT_B, PIN_5 )) {some code;}

author: Simon
author_rank: Unknown
timestamp: October 10, 2018, 11:29:02 am
content: Quote from: TK on October 10, 2018, 10:53:39 amWhat and invoke the wrath of the define haters? but yes that would guarantee code efficiency as there would be no argument about how it compiles.

author: cv007
author_rank: Frequent Contributor
timestamp: October 10, 2018, 11:44:32 am
content: QuoteYou have a compiler, right? Why are you not testing your code by trying to compile it?I don't have anything avr installed except arduino, so I find the bin folder, create a test file, and compile it then disassemble it. If it compiles, that's a good start. But a happy compiler does not mean the result is what I want, so I disassemble and see what happened. I haven't been slinging any code here that I have not tested. If I can do it, you can too.Here is a simple example I just put together just to show how easy it is to test things out. This is a simple example (and no pin dir code included, so will not set the led), but simple means you can see what is happening. I also used -Os, since its both much easier to read and most likely that is what will eventually be used.Code://test.c#include <stdint.h>#include <stdbool.h>typedef enum {A0=0x00, A1, A2, A3, A4, A5, A6, A7,B0=0x20, B1, B2, B3, B4, B5, B6, B7,C0=0x40, C1, C2, C3, C4, C5, C6, C7,D0=0x60, D1, D2, D3, D4, D5, D6, D7,E0=0x80, E1, E2, E3, E4, E5, E6, E7,F0=0xA0, F1, F2, F3, F4, F5, F6, F7,//all my pin info hereSW1=A2, LED1=B3} PIN_t;enum { PORTBASE = 0x400 };static inline bool pin_get(PIN_t pin){//offset  is IN registerreturn ((volatile uint8_t*)(PORTBASE+(pin & 0XF0))) & (1<<(pin&7));}static inline void pin_set(PIN_t pin, bool tf){//offset  is OUTSET register,  is OUTCLRif(tf)((volatile uint8_t*)(PORTBASE+(pin & 0XF0))) = 1<<(pin&7);else ((volatile uint8_t*)(PORTBASE+(pin & 0XF0))) = 1<<(pin&7);}void main(){pin_set( LED1, pin_get(SW1) );for(;;);}/*(test.c is in bin directory just to make it easier)in avr bin directory, at command prompt (Linux in this case)compile and disassemble (use -Os so its easier to read)./avr-gcc -C -Os test.c && ./avr-objdump -d a.outor Windows-c:\pathtoavr\avr\bin>avr-gcc -C -Os test.c && avr-objdump -d a.outDisassembly of section .text:00000000 <main>:0: 80 91 08 04 lds r24, 0x0408 ; 0x800408 <_edata+0x3a8>4: 82 ff sbrs r24, 26: 04 c0 rjmp .+8 ; 0x10 <__zero_reg__+0xf>8: 88 e0 ldi r24, 0x08 ; 8a: 80 93 25 04 sts 0x0425, r24 ; 0x800425 <_edata+0x3c5>e: 03 c0 rjmp .+6 ; 0x16 <__zero_reg__+0x15>10: 88 e0 ldi r24, 0x08 ; 812: 80 93 26 04 sts 0x0426, r24 ; 0x800426 <_edata+0x3c6>16: ff cf rjmp .-2 ; 0x16 <__zero_reg__+0x15>*/If you can get in the habit of trying your code to see whether it compiles without error and what is produced by the compiler, you will get a better understanding of C, the compiler, and even assembly.

author: Simon
author_rank: Unknown
timestamp: October 10, 2018, 12:14:21 pm
content: It compiles fine, I will be testing at the earliest opportunity.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 10, 2018, 02:59:17 pm
content: Quote from: Siwastaja on October 10, 2018, 07:23:45 amYes, this is what I usually do when possible and needed.Another possible approach is using flags (that themselves are small enough to be guaranteed to be accessed atomically) for guarding access to the variables in question. A bit like critical sections. This is not 100% failsafe depending on the architecture though, and adds obvious overhead, but it's often adequate.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 10, 2018, 03:58:04 pm
content: Quote from: Siwastaja on October 10, 2018, 07:23:45 amUsually, there's only the highest priority interrupt you need to worry about. All others can be pre-empted by the highest priority ISR, and therefore they must be able to tolerate higher latencies. You cannot do two different things at the same time, so you can provide the best latency for only one interrupt. Therefore, disabling lower priority interrupts for a short time won't do any harm.Sometimes, such as on PIC32, the interrupt priorities are switched in software, so even the higher priority interrupts may be postponed if they happen exactly when the lower priority ISR is starting. In this case, disabling lower priority interrupts is actually decreases the latency for the higher ones.

author: westfw
author_rank: Super Contributor
timestamp: October 10, 2018, 11:42:20 pm
content: QuoteIf writing assembler inside a C program, the main problem becomes "how do I get data from my C program to where my assembler code can see it..." The gcc syntax for inline assembler is ... arcane. The simpler process of writing a C-compatible subroutine in assembler means having to learn the interface (ABI) that your particular compiler uses for subroutines (and - not all assembler produce object code that is linkable with C programs. The Atmel assemblers don't, for example (not either one of the two that they have.) (Fortunately for gcc, they have their OWN assembler for Atmel that is "almost the same" as the Atmel assemblers.)

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 06:31:37 pm
content: This does not work:void setup_output(uint8_t port, uint8_t pin){(*(volatile uint8_t*)(PORTS_OFFSET + (PORTS_OFFSET_GAP * port) + DATA_DIR_CLR)) = 0x01 << pin; // set pin as output} // Use PORT_x and PIN_n as arguments.How does the compiler know the difference between multiplication and a pointer?

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 06:35:16 pm
content: How exactly it does not work? And what are the values of of the constants?Compiler knows about operators based on context. Multiplication can only occur after one expression was parsed. Pointer dereference can only happen as part of the expression.I strongly recommend to look at the formal C grammar. It may take some time to understand, but it may clarify a lot of things. Here is one for example:https://www.lysator.liu.se/c/ANSI-C-grammar-y.htmlThere are way too many parenthesis here, but the expression overall looks correct.Code:*(volatile uint8_t *)(PORTS_OFFSET + PORTS_OFFSET_GAP * port + DATA_DIR_CLR) = 0x01 << pin;

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 06:41:53 pm
content: It's ok my fault, I am trying to access the virtual ports but am using the wrong starting address as i am using the parts address not the base addresses of the special registers i can access pin at a time.

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:20:30 pm
content: OK setting as an output works but setting and clearing the pin does not.// Port and pin numbers for use in programs.#define PORT_A 0x00#define PORT_B 0x01#define PORT_C 0x02#define PORT_D 0x03#define PORT_E 0x04#define PORT_F 0x05#define PIN_0 0x00#define PIN_1 0x01#define PIN_2 0x02#define PIN_3 0x03#define PIN_4 0x04#define PIN_5 0x05#define PIN_6 0x06#define PIN_7 0x07// Port register addresses.// Virtual ports#define VIRTUAL_PORTS_START 0x0000#define VIRTUAL_PORT_OFFSET 0x0004#define VP_DATA_DIR_SET 0x01#define VP_DATA_DIR_CLR 0x02#define VP_DATA_DIR_TOGGLE 0x03#define VP_DATA_OUTSET 0x05#define VP_DATA_OUTCLR 0x06#define VP_DATA_OUTPUT_TOGGLE 0x07void clr_pin(uint8_t port, uint8_t pin){(*(volatile uint8_t*)(VIRTUAL_PORTS_START + (VIRTUAL_PORT_OFFSET * port) + VP_DATA_OUTCLR)) = 0x01 << pin; // clear pin low} // Use PORT_x and PIN_n as arguments.

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:22:50 pm
content: I have not looked at the datasheet, but VIRTUAL_PORT_OFFSET can't be 4. Assuming VP_xxx are correct, it must be at least 8. Otherwise your addresses will overlap.

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:25:45 pm
content: Virtual ports only have the following registers:Code:0x00 DIR0x01 OUT0x02 IN0x03 INTFLAGSThere is no OUTSET/OUTCLR.

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:28:23 pm
content: 0x0000 VPORTA Virtual Port A0x0004 VPORTB Virtual Port B0x0008 VPORTC Virtual Port C0x000C VPORTD Virtual Port D0x0010 VPORTE Virtual Port E0x0014 VPORTF Virtual Port FFrom page 43 of the atmega 0 series manual, if the offset was wrong then the setup_output(); would not work either.

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:29:12 pm
content: Quote from: Simon on October 11, 2018, 08:28:23 pmOffset is correct, registers are not.

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:29:54 pm
content: Page 148, the following page is output clearOutput Value SetName: OUTSETOffset: 0x05Reset: 0x00Property: -Bit 7 6 5 4 3 2 1 0OUTSET[7:0]Access R/W R/W R/W R/W R/W R/W R/W R/WReset 0 0 0 0 0 0 0 0Bits 7:0 – OUTSET[7:0] Output Value SetThis bit field can be used instead of a read-modify-write to set the output value of individual pins to '1'.Writing a '1' to OUTSET will set the corresponding bit in PORTx.OUT.Reading this bit field will alway

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:30:35 pm
content: This is for the real port. Virtual Ports have different layout. I'm looking at Series 0 manual. There it is described in the section "15.6 Register Summary - VPORTx".

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:32:15 pm
content: The offsets are the same for normal or virtual ports for in/out data but there are two sets of general offsets for normal rregisters and bit accessible ones

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:33:14 pm
content: Quote from: ataradov on October 11, 2018, 08:30:35 pmno it is the virtual, that is why they talk about it replacing the other (normal) one that requires read/modify/write

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:35:10 pm
content: Quote from: Simon on October 11, 2018, 08:33:14 pmI'm looking at the Series 0 manual and the section "15.6 Register Summary - VPORTx" clearly describes what registers are available for the VPORTs.You can't have 8 registers located with intervals of 4 bytes.

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:40:45 pm
content: This is very confusing, i need to have another look, but yes, so which ports have the set/clr registers ? like you say an address gap of 4 does not allow for all of that.

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:42:56 pm
content: Regular PORT peripherals have all registers. VPORTs only have the listed 4.Look at "Register Summary" section of the peripheral description to know exactly what registers are available for the peripheral.

author: Simon
author_rank: Unknown
timestamp: October 11, 2018, 08:46:40 pm
content: so what is a virtual port? it has no use at all other than regular registers, the non virtual registers are the more flexible.

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2018, 08:48:47 pm
content: Here virtual ports are just mappings into bit accessible region. So you have an alternative for the regular registers, which you can access using bit set/clear instructions. You don't need OUTSET/OUTCLR, since you have dedicated instructions to do so. In C this is virtually useless.

author: cv007
author_rank: Frequent Contributor
timestamp: October 12, 2018, 12:08:22 am
content: On my last post where I posted an example, I also tacked on the comment at the end that the PORTBASE could be changed to the vport address. I then realized the spacing of those addresses (4) did not make sense and saw that these vport registers are just a minimal set. I deleted the comment, but you may have seen it before I deleted it. Didn't mean to send you on the wrong path with vport, although now you know.I probably shouldn't cause more confusion, but...Here's what (I think) can be done using vport addresses. This is the exact same code as I posted in my previous post, except now using vport addresses. The calculation of the vport port offset (a,b,c...) is different (>>3) and the register offsets are different (=OUT, =IN), but otherwise the its mostly the same except now the IN/OUT/CBI/SBI instructions are available.I think this is correct, but not 100% certain.Code://test.c#include <stdint.h>#include <stdbool.h>typedef enum {A0=0x00, A1, A2, A3, A4, A5, A6, A7,B0=0x20, B1, B2, B3, B4, B5, B6, B7,C0=0x40, C1, C2, C3, C4, C5, C6, C7,D0=0x60, D1, D2, D3, D4, D5, D6, D7,E0=0x80, E1, E2, E3, E4, E5, E6, E7,F0=0xA0, F1, F2, F3, F4, F5, F6, F7,//all my pin info hereSW1=A2, LED1=B3} PIN_t;//added VPORTBASE- also added 0x20 offset as//vport addresses are IO addresses (in iom4809.h), seems we//need to get vport address into a 'mem' address range//so the compiler will see its in the IO address range (confusing, yes)//go read srf_defs.h for a good timeenum { PORTBASE= 0x400, VPORTBASE = 0x20 };static inline bool pin_get(PIN_t pin){//VPORTBASE offset  is IN registerreturn ( (volatile uint8_t*)(VPORTBASE+(pin>>3)) ) & (1<<(pin&7));}static inline void pin_set(PIN_t pin, bool tf){//VPORTBASE offset  is OUT registerif(tf)( (volatile uint8_t*)(VPORTBASE+(pin>>3)) ) |= 1<<(pin&7);else ( (volatile uint8_t*)(VPORTBASE+(pin>>3)) ) &= ~(1<<(pin&7));}void main(){pin_set( LED1, pin_get(SW1) );for(;;);}/*./avr-gcc -C -Os test.c && ./avr-objdump -d a.outDisassembly of section .text:00000000 <main>:0: 12 9b sbis 0x02, 2 ; 22: 02 c0 rjmp .+4 ; 0x8 <__zero_reg__+0x7>4: 2b 9a sbi 0x05, 3 ; 56: 01 c0 rjmp .+2 ; 0xa <__zero_reg__+0x9>8: 2b 98 cbi 0x05, 3 ; 5a: ff cf rjmp .-2 ; 0xa <__zero_reg__+0x9>*/edit-and to make it complete by setting the pin dirs, one could addCode:typedef enum { DIR_IN, DIR_OUT } DIR_t;static inline void pin_dir(PIN_t pin, DIR_t dir){//VPORTBASE offset  is DIR registerif(dir)( (volatile uint8_t*)(VPORTBASE+(pin>>3)) ) |= 1<<(pin&7);else ( (volatile uint8_t*)(VPORTBASE+(pin>>3)) ) &= ~(1<<(pin&7));}with this added to mainCode:pin_dir( LED1, DIR_OUT );pin_dir( SW1, DIR_IN );resulting in this additional codeCode:0: 23 9a sbi 0x04, 3 ; 42: 02 98 cbi 0x00, 2 ; 0

author: Simon
author_rank: Unknown
timestamp: October 12, 2018, 06:42:21 am
content: It's working, my dyslexic eyes did not see the difference between 0x0400 and 0x04000

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:57:44 am
content: So I am now looking into the counter registers and of course now we have 16 bit registers. so does uint16_t simply make this work? or have I bypassed that much of the compiler that I need to do the arithmetic to add together the two 8 bit memory locations?

author: cv007
author_rank: Frequent Contributor
timestamp: October 13, 2018, 12:06:27 pm
content: Compilers know how to work with 8/16/32/64 bit numbers, you just need to tell it.You can now also figure out why array type access can go wrong when dealing in array types larger than a byte. Read the comments in code.So in this case the normal 'pointer method' is used (*) with the 'normal' byte addition taking place before the compiler knows it's going to be a uint16_t pointer.The code below shows the compiler can access the 16bit register (actually 2 8bit registers since this is an 8bit micro), and it also does it in the correct order (because they know all about these things- read avr mega0 manual 7.5.6).Code://test.c#include <stdint.h>enum { TCB_BASE = 0x0A80 };typedef enum { TCB_0, TCB_1, TCB_2, TCB_3 } TCBn_t;static inline uint16_t tcb_count(TCBn_t n){return *(volatile uint16_t*)(TCB_BASE+(n<<4)+0x0A);//now we can no longer use 'array' type access, since 0x0A//is a byte offset and the array element is a 'uint16_t'//so the the actual offset value in bytes would be 0x0A*(sizeof(uint16_t)//try it so see the register values accessed- will be wrong//return ( (volatile uint16_t*)(TCB_BASE+(n<<4)) );}uint16_t c;void main(){c = tcb_count(TCB_0);for(;;);}//2 bytes loaded from CNT, stored in global variable//notice the compiler reads the low byte first, which is correct//(they know all about these type of things)/*00000010 <main>:10: 80 91 8a 0a lds r24, 0x0A8A ; 0x800a8a <__bss_end+0xa28>14: 90 91 8b 0a lds r25, 0x0A8B ; 0x800a8b <__bss_end+0xa29>18: 90 93 61 00 sts 0x0061, r25 ; 0x800061 <_edata+0x1>1c: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <_edata>20: ff cf rjmp .-2 ; 0x20 <main+0x10>*/

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 01:31:10 pm
content: I am sorry but you have confused me.From what you say I can do as i proposed: simply use a 16 bit type pointer and the compiler deals with the rest.I ask as i know that if i use the manufacturers definitions i can just read/write a 16bit register name as the IDE have built in tools that deal with it as it would any 16 bit+ variable. I am though not using those defines i am writing to a memory location, so when I point to one 8 bit address with a 16bit pointer does the compiler simply put the extra 8 bits in the next address location?Alternatively i manually do the 16bit read/write procedure described in the manual (which i have done in the past).

author: cv007
author_rank: Frequent Contributor
timestamp: October 13, 2018, 02:31:39 pm
content: QuoteThe built in tool is the compiler. The compiler doesn't care where the defines come from, and actually the compiler doesn't really deal with defines at all- by the time the compiler gets to work the pre-processor has done a big search and replace on all your code. You are dealing with the search and replace, I prefer to talk directly to the compiler when possible. The end result can be the same, but one of us has to figure out what is happening in the search and replace.My examples use no defines, and the compiler does what its told. We are all talking to the same compiler, you just have to tell it what you want in whatever method you want.edit- here is a 'define' version-Code://test.c#include <stdint.h>#define TCB0_BASE 0x0A80#define TCB0_COUNT_OFFSET 0x0A#define TCB0_COUNT() *(volatile uint16_t*)(TCB0_BASE+TCB0_COUNT_OFFSET)uint16_t c;void main(){c = TCB0_COUNT();for(;;);}/*//compiled with --save-temps//in test.i you can see what the pre-processor did//and the compiler uses this file as its source//and looks like this (for the part we are interested in)-//(you can see the result of simple search and replace)uint16_t c;void main(){c = *(volatile uint16_t*)(0x0A80+0x0A);for(;;);}//and the same result00000010 <main>:10: 80 91 8a 0a lds r24, 0x0A8A ; 0x800a8a <__bss_end+0xa28>14: 90 91 8b 0a lds r25, 0x0A8B ; 0x800a8b <__bss_end+0xa29>18: 90 93 61 00 sts 0x0061, r25 ; 0x800061 <_edata+0x1>1c: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <_edata>20: ff cf rjmp .-2 ; 0x20 <main+0x10>*/

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 02:50:19 pm
content: The only defines I am using is to allocate names to memory addresses to make the code more readable and so that if they come out with a similar chip (ATmega 1-series) that puts things in different memory locations I can still use the same functions if i rewrite the memory allocation definitions again. This is not dissimilar to how it already works.If they make a new chip with the same TCBn peripheral but stick all of it's registers as are at a different offset all i need to do is change one definition and all of the current code will work.

author: cv007
author_rank: Frequent Contributor
timestamp: October 13, 2018, 03:02:12 pm
content: QuoteMakes no differenceI change this lineenum { TCB_BASE = 0x0A80 };you change this line#define TCB_BASE 0x0A80same resultIf they make new parts in the same series, they are not going to start moving peripheral addresses around. If they come out with another series, kiss most of your code goodbye anyway.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 03:16:35 pm
content: Well I gather that they have new tiny series out already which likely use the same peripheries. I am confused why the names ever happened and why they are sticking as it it clear that all AVR's will now go up to 64KB of address space as they use a 16bit address bus and all use the AVR cpu and I expect all of the peripheries are the same so I could use my code on tiny's. To make the new mega's they have taken the xmega architecture and knocked the address space down and moved it to 5V silicon. I suspect the 1-series will perhaps come back with more memory space (16MB address space anyone?)using defines or variables is amounting to the same thing but I am using pure numbers for the compiler not variables that will either be optimised out or stick around in overhead.

author: MarkF
author_rank: Super Contributor
timestamp: October 13, 2018, 04:01:19 pm
content: Quote from: cv007 on October 13, 2018, 03:02:12 pmExcept that there is a subtle difference between the two.In the "enum" case, the compiler creates an actual variable that's used at run time.In the "#define" case, separate constants are created for each instance that it's used at compile time. The pre-processor replaces each instance of TCB_BASE with 0x0A80 and then the code is compiled.I haven't been following this thread, so you would need to determine which is appropriate.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 04:09:35 pm
content: Quote from: MarkF on October 13, 2018, 04:01:19 pmThe difference is that with enum the value is checked which i can see no use for. I am using this stuff to write arduino style functions so once written and verified they will never be edited again, only invoked

author: NorthGuy
author_rank: Super Contributor
timestamp: October 13, 2018, 04:17:09 pm
content: Quote from: cv007 on October 13, 2018, 03:02:12 pmIt is, but even in this rudimentary case defines are more flexible. For example, you can change the define later:Code:#define TCB_BASE get_tcb_base()and it'll change this automatically through all your files.Or this:Code:#ifndef TCB_BASE#define TCB_BASE 0x0A80#endifThis way if you define TCB_BASE in the upstream .h file then it will use your define, otherwise it'll fall back to the default value.Or this:Code:#ifdef USE_BOOTLOADER // this will come from the command line#define TCB_BASE get_tcb_base()#else#define TCB_BASE 0x0A80#endifThere are countless things you can do with macros which cannot be done without them.

author: Nusa
author_rank: Super Contributor
timestamp: October 13, 2018, 04:40:58 pm
content: Quote from: MarkF on October 13, 2018, 04:01:19 pmIn this case there is no variable used at run time since we're dealing with static inline functions. It's all compiler magic. The resulting assembly code was even produced as proof of identical results by either method.But as you said, you haven't been following the thread.

author: MarkF
author_rank: Super Contributor
timestamp: October 13, 2018, 05:19:51 pm
content: I guess you are right. An enum would be evaluated at compile time.I don't know why I was thinking the compiler would typically create an array of values in memory.I should know better...

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 08:08:08 pm
content: If I remove iom4809.h will it hurt me? trying to find alternate names for definitions is getting problematic

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 08:12:41 pm
content: Quote from: MarkF on October 13, 2018, 05:19:51 pmI am working on the assumption that the compiler will decide that actually putting my actual functions into the code is a waste of time and will calculate the address values and stuff the value I supply into it, even done repeatedly it will be more efficient than carrying out the calculations in the resulting code.

author: cv007
author_rank: Frequent Contributor
timestamp: October 13, 2018, 08:49:09 pm
content: Quoteeventually, as you have already seen, you will want to slim down the defines and will end up with defines that takes an argument or more (macros)once you start taking arguments, you have the possibility of using 'incorrect' argumentstype checking can help eliminate those type of user errorsCode:#include <stdint.h>//example- this is defined 'somewhere' else, included by one of my include files//just placing it here as an example#define TCB0 (*(volatile uint8_t *) 0x0A80)#define TCB_BASE 0x0A80#define TCB_COUNT_OFFSET 0x0A#define TCB_0 0x00#define TCB_1 0x10#define TCB_2 0x20#define TCB_3 0x30#define TCB_COUNT(n) *(volatile uint16_t*)(TCB_BASE+n+TCB_COUNT_OFFSET)uint16_t c;void main(){c = TCB_COUNT(TCB_0); //ok//if I misuse the name, we may or may not get an error//in this case, not, and the result will be incorrect//and you will never know, except you will have to eventually//figure it out (why is my tcb count always wrong?)//with typed arguments, the compiler will tell you when you use a wrong typec = TCB_COUNT(TCB0);for(;;);}/*QuoteI would agree some things can only be done with defines, but I would not characterize it as countless.The problem is when people think its countless, and seek to prove it- where the result will certainly look countless as it becomes very difficult to decipher- jumping from one file to another, eventually getting to what you are looking for. I seek every opportunity to get out of the define world, although not always possible. I have drivers for every peripheral + usb for pic32mm and I have a total of 6 defines- 2 for isr declarations and 4 for usb descriptor generation- I could not come up with alternatives in those cases.QuoteYes if you use something inside of it. No if you do not.If you read the top of that file, you will see that they want you to use io.h, which then includes the iomxxxx.h file for you- determined by what you have set as the target micro in the ide (typically, and propagates via an ide generated command line define).I guess this shows you have not compiled any code with that include. If you had, you would have seen an error "Include <avr/io.h> instead of this file."If you are not compiling any code, why not?If you are not compiling any code, do you realize you have to at some point?If you are not compiling any code, tell me now- so I can quit posting examples.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 09:01:13 pm
content: I am compiling which is why I ask about iom4809.h, I have defined TCB0, TCB1, TCB2 & TCB3 as 0, 1, 2, 3, but I get a warning that these defines already exist in iom4809.h

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 09:04:05 pm
content: You can remove it, but it is a really bad idea to not use manufacturer header files. Your code will be instantly incompatible with all other code. So someone (including you) can't just borrow a snippet of the code to be included in another project that already uses vendor header files. This practice looks like late 90s when vendor supplied files were horrible, and thank god we are over that BS now.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 09:19:39 pm
content: The current header files are designed for the "atmel start" bullshit and very little use to me. unless i can calculate on memory addresses as discussed earlier then nothing the manufacturer has to offer is much use.I'm not sure how AVR code is going to run on anything else anyway, I will of course have to write a new header file of my own for any new chip with a different memory layout. I am already writing code for the largest chips in the series and they are cheap enough to not not bother with a tiny. If I have to deal with anything else it will likely be an ARM

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 09:26:42 pm
content: Quote from: Simon on October 13, 2018, 09:19:39 pmNot really. Those files are the same as they were before Atmel Start. They are more of the XMega style, not the classical AVR, but still.Quote from: Simon on October 13, 2018, 09:19:39 pmYou are just doing thing that re not necessary in real life. But that's fine.Quote from: Simon on October 13, 2018, 09:19:39 pmThis is the worst idea ever. This will also make you unhireable in the industry. Or you will have to re-learn all the things for a real job. Nobody in their right mind will write custom header files. It is a huge headache.Quote from: Simon on October 13, 2018, 09:19:39 pmARM uses similar style of header files.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 09:40:10 pm
content: so can i write set_output(PORT_A, PIN_1); with the iom4809.h file? once everything has been converted to text by that file how do I manipulate any input I give to match the variables assigned to the registers in text form without using a lot of C code like switch cases, in other words:GOTO The start of this discussion!

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 09:45:40 pm
content: Quote from: Simon on October 13, 2018, 09:40:10 pmYou can, but you should reuse as many defines as possible from that file. So you should keep it included are make sure that your own definitions don't conflict wit the standard ones. Right now it is probably not important, once you have to integrate someone else's code in your project and you run into the conflicts, you will see why it was important to not have those conflicts.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 09:49:23 pm
content: How? At the moment I cannot make much sense of that file as it appears that to reference anything I have to pull together different sections that are likely to be many lines apart.

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 09:52:42 pm
content: Quote from: Simon on October 13, 2018, 09:49:23 pmI don't understand what is your problem with that file. You don't need to look at it most of the time once you get used to naming conventions. You can read the datasheet and write code and expect that things you need will be defined.ARM files are much better, of course, since this carries some legacy AVR compatibility, so it could not be too far off the old tradition. But this is still plenty usable.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 09:57:57 pm
content: If i want to write to porta.pin1 sure but if I want to put "a" and "1" in as arguments in a function then what? this puts me back to writing 48 lines all the same but for the ports and pins

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 10:04:45 pm
content: If your arguments are constant, then do it through a compiler macro that unrolls into the standard defines. That's easy:Code:#define PORT_SET(port, pin) PORT##port->OUTSET = (1 << pin)UseCode:PORT_SET(A, 5);If you want parameters to be taken from the variables (a really strange and rare requirement in real life scenarios), then the code you write is fine. Just don't reuse the same name.This problem really only exists for ports, and even then it is not as big of a problem as you make it out to be.One big thing this file is missing is direct (non-dereferenced) versions of defines like this:Code:#define PORTA (*(PORT_t *)0x0400) /* I/O Ports */...#define AC0 (*(AC_t *)0x0680) /* Analog Comparator */...And may be that's the only thing worth defining in addition to the stock file.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:10:49 pm
content: What like:#define PORTA (*(PORT_t *) 0x0400) /* I/O Ports */#define PORTB (*(PORT_t *) 0x0420) /* I/O Ports */#define PORTC (*(PORT_t *) 0x0440) /* I/O Ports */#define PORTD (*(PORT_t *) 0x0460) /* I/O Ports */#define PORTE (*(PORT_t *) 0x0480) /* I/O Ports */#define PORTF (*(PORT_t *) 0x04A0) /* I/O Ports */

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 10:13:42 pm
content: Quote from: Simon on October 13, 2018, 10:10:49 pmWhat like? Those are dereferenced versions. What you want is this:Code:#define PORTA (PORT_t *)0x0400This way you can write functions like this:Code:void out_set(PORT_t *port, int pin){port->OUTSET = (1<<pin);}And use them like this:Code:out_set(PORTA, 3);

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:13:52 pm
content: I should be able to avoid same names, I am trying to avoid writing every piece of code over by giving arguments to a function rather than having to write the same code for every instance of the same thing.All the manufacturers header file seems to do in do roundabout defines of every address.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 13, 2018, 10:15:54 pm
content: Quote from: Simon on October 13, 2018, 09:57:57 pmIf you know what are you going to pass to the function, then doing my_function(a,1) or MY_MACRO(a,1) it's about the same as assigning to PORTA, except you're doing a lot of extra work defining your own entities.If you do not know what are you going to pass (say you save the pin number in a variable which may change at run-time, and then you pass the variable to the function), then what has been discussed in this thread is mostly not applicable. In this case, the compiler will not be able to reduce your functions to almost nothing, but will literally execute the code your wrote.

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 10:17:50 pm
content: Quote from: Simon on October 13, 2018, 10:13:52 pmThe other way around. What you are doing is a roundabout way to not use manufacturer files.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:18:19 pm
content: This is out of their header:Code:==========================================================================IO Module Instances. Mapped to memory.==========================================================================*/#define VPORTA (*(VPORT_t *) 0x0000) /* Virtual Ports */#define VPORTB (*(VPORT_t *) 0x0004) /* Virtual Ports */#define VPORTC (*(VPORT_t *) 0x0008) /* Virtual Ports */#define VPORTD (*(VPORT_t *) 0x000C) /* Virtual Ports */#define VPORTE (*(VPORT_t *) 0x0010) /* Virtual Ports */#define VPORTF (*(VPORT_t *) 0x0014) /* Virtual Ports */#define RSTCTRL (*(RSTCTRL_t *) 0x0040) /* Reset controller */#define SLPCTRL (*(SLPCTRL_t *) 0x0050) /* Sleep Controller */#define CLKCTRL (*(CLKCTRL_t *) 0x0060) /* Clock controller */#define BOD (*(BOD_t *) 0x0080) /* Bod interface */#define VREF (*(VREF_t *) 0x00A0) /* Voltage reference */#define NVMBIST (*(NVMBIST_t *) 0x00C0) /* BIST in the NVMCTRL module */#define WDT (*(WDT_t *) 0x0100) /* Watch-Dog Timer */#define CPUINT (*(CPUINT_t *) 0x0110) /* Interrupt Controller */#define CRCSCAN (*(CRCSCAN_t *) 0x0120) /* CRCSCAN */#define RTC (*(RTC_t *) 0x0140) /* Real-Time Counter */#define EVSYS (*(EVSYS_t *) 0x0180) /* Event System */#define CCL (*(CCL_t *) 0x01C0) /* Configurable Custom Logic */#define PORTA (*(PORT_t *) 0x0400) /* I/O Ports */#define PORTB (*(PORT_t *) 0x0420) /* I/O Ports */#define PORTC (*(PORT_t *) 0x0440) /* I/O Ports */#define PORTD (*(PORT_t *) 0x0460) /* I/O Ports */#define PORTE (*(PORT_t *) 0x0480) /* I/O Ports */#define PORTF (*(PORT_t *) 0x04A0) /* I/O Ports */#define PORTMUX (*(PORTMUX_t *) 0x05E0) /* Port Multiplexer */#define ADC0 (*(ADC_t *) 0x0600) /* Analog to Digital Converter */#define AC0 (*(AC_t *) 0x0680) /* Analog Comparator */#define USART0 (*(USART_t *) 0x0800) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART1 (*(USART_t *) 0x0820) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART2 (*(USART_t *) 0x0840) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART3 (*(USART_t *) 0x0860) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define TWI0 (*(TWI_t *) 0x08A0) /* Two-Wire Interface */#define SPI0 (*(SPI_t *) 0x08C0) /* Serial Peripheral Interface */#define TCA0 (*(TCA_t *) 0x0A00) /* 16-bit Timer/Counter Type A */#define TCB0 (*(TCB_t *) 0x0A80) /* 16-bit Timer Type B */#define TCB1 (*(TCB_t *) 0x0A90) /* 16-bit Timer Type B */#define TCB2 (*(TCB_t *) 0x0AA0) /* 16-bit Timer Type B */#define TCB3 (*(TCB_t *) 0x0AB0) /* 16-bit Timer Type B */#define SYSCFG (*(SYSCFG_t *) 0x0F00) /* System Configuration Registers */#define NVMCTRL (*(NVMCTRL_t *) 0x1000) /* Non-volatile Memory Controller */#define SIGROW (*(SIGROW_t *) 0x1100) /* Signature row */#define FUSE (*(FUSE_t *) 0x1280) /* Fuses */#define LOCKBIT (*(LOCKBIT_t *) 0x128A) /* Lockbit */#define USERROW (*(USERROW_t *) 0x1300) /* User Row */#endif /* !defined (__ASSEMBLER__) */but they never set a gap, all i have done is rewritten that for the first instance of any periphery category and then defined the offset between instances.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:20:38 pm
content: Quote from: ataradov on October 13, 2018, 10:17:50 pmI'd love to but i don't know where to start, the header file is for both assembler and C and it makes little sense. They did not produce this file to make anything easy for anyone as it looks like their idea was that we would use the code generator.

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 10:22:01 pm
content: Quote from: Simon on October 13, 2018, 10:18:19 pmI know. Do you see the difference betweenCode:#define PORTA (PORT_t *)0x0400andCode:#define PORTA (*(PORT_t *) 0x0400)?All you really need is the former. And that's what ARM files have.If you define that, for example like this:Code:#define MY_PORTA (PORT_t *)0x0400Then you will be able to create universal functions like this:Code:void out_set(PORT_t *port, int pin){port->OUTSET = (1<<pin);}What else do you need?

author: ataradov
author_rank: Super Contributor
timestamp: October 13, 2018, 10:24:28 pm
content: Quote from: Simon on October 13, 2018, 10:20:38 pmIf you work with assembler, then look at the assembler parts, if you work with C, then look at C parts.This file is almost perfect for everything you want to do in real life. Don't focus on writing universal libraries, it is a waste of time. Work on real projects and you will see that in reality you have all you need in that file.ARM header files are perfect.Work on learning how to use them even if you don't get it now. Avoiding them is much worse.

author: Simon
author_rank: Unknown
timestamp: October 13, 2018, 10:27:05 pm
content: Well I'm off to bed, I'll do more headbanging tomorrow.

author: cv007
author_rank: Frequent Contributor
timestamp: October 13, 2018, 10:45:48 pm
content: Code://test.c#define _AVR_IO_H_ //I need to prevent error as I'm not using io.h#include "sfr_defs.h"#include "iom4809.h"#include <stdint.h>#define set_output(pt,pn) pt.DIR |= (1<<pn)#define set_voutput(pt,pn) _SFR_IO8(&V##pt.DIR) |= (1<<pn)void main(){set_output(PORTA, 2);set_voutput(PORTB, 3);for(;;);}/*Disassembly of section .text:00000000 <main>:0: 80 91 00 04 lds r24, 0x0400 ; 0x800400 <_edata+0x3a0>4: 84 60 ori r24, 0x04 ; 46: 80 93 00 04 sts 0x0400, r24 ; 0x800400 <_edata+0x3a0>a: 23 9a sbi 0x04, 3 ; 4c: ff cf rjmp .-2 ; 0xc <__zero_reg__+0xb>*/Either PORT or VPORT can be used, but for some reason they don't seem to take advantage of the VPORT feature (all addresses in the header are mem addresses). I wrapped up the vport address into an io macro to make it work like its supposed to (using sbi).

author: Simon
author_rank: Unknown
timestamp: October 14, 2018, 08:02:07 am
content: Right so here is my quandary:Code:/* I/O Ports */typedef struct PORT_struct{register8_t DIR; /* Data Direction */register8_t DIRSET; /* Data Direction Set */register8_t DIRCLR; /* Data Direction Clear */register8_t DIRTGL; /* Data Direction Toggle */register8_t OUT; /* Output Value */register8_t OUTSET; /* Output Value Set */register8_t OUTCLR; /* Output Value Clear */register8_t OUTTGL; /* Output Value Toggle */register8_t IN; /* Input Value */register8_t INTFLAGS; /* Interrupt Flags */register8_t PORTCTRL; /* Port Control */register8_t reserved_0x0B;register8_t reserved_0x0C;register8_t reserved_0x0D;register8_t reserved_0x0E;register8_t reserved_0x0F;register8_t PIN0CTRL; /* Pin 0 Control */register8_t PIN1CTRL; /* Pin 1 Control */register8_t PIN2CTRL; /* Pin 2 Control */register8_t PIN3CTRL; /* Pin 3 Control */register8_t PIN4CTRL; /* Pin 4 Control */register8_t PIN5CTRL; /* Pin 5 Control */register8_t PIN6CTRL; /* Pin 6 Control */register8_t PIN7CTRL; /* Pin 7 Control */register8_t reserved_0x18;register8_t reserved_0x19;register8_t reserved_0x1A;register8_t reserved_0x1B;register8_t reserved_0x1C;register8_t reserved_0x1D;register8_t reserved_0x1E;register8_t reserved_0x1F;} PORT_t;So this makes sense sort of, they create a variable type that is a structure containing all of the generic port registers and create the variable PORT_tThe next appearance of PORT_t is here:Code:==========================================================================IO Module Instances. Mapped to memory.==========================================================================*/#define VPORTA (*(VPORT_t *) 0x0000) /* Virtual Ports */#define VPORTB (*(VPORT_t *) 0x0004) /* Virtual Ports */#define VPORTC (*(VPORT_t *) 0x0008) /* Virtual Ports */#define VPORTD (*(VPORT_t *) 0x000C) /* Virtual Ports */#define VPORTE (*(VPORT_t *) 0x0010) /* Virtual Ports */#define VPORTF (*(VPORT_t *) 0x0014) /* Virtual Ports */#define RSTCTRL (*(RSTCTRL_t *) 0x0040) /* Reset controller */#define SLPCTRL (*(SLPCTRL_t *) 0x0050) /* Sleep Controller */#define CLKCTRL (*(CLKCTRL_t *) 0x0060) /* Clock controller */#define BOD (*(BOD_t *) 0x0080) /* Bod interface */#define VREF (*(VREF_t *) 0x00A0) /* Voltage reference */#define NVMBIST (*(NVMBIST_t *) 0x00C0) /* BIST in the NVMCTRL module */#define WDT (*(WDT_t *) 0x0100) /* Watch-Dog Timer */#define CPUINT (*(CPUINT_t *) 0x0110) /* Interrupt Controller */#define CRCSCAN (*(CRCSCAN_t *) 0x0120) /* CRCSCAN */#define RTC (*(RTC_t *) 0x0140) /* Real-Time Counter */#define EVSYS (*(EVSYS_t *) 0x0180) /* Event System */#define CCL (*(CCL_t *) 0x01C0) /* Configurable Custom Logic */#define PORTA (*(PORT_t *) 0x0400) /* I/O Ports */#define PORTB (*(PORT_t *) 0x0420) /* I/O Ports */#define PORTC (*(PORT_t *) 0x0440) /* I/O Ports */#define PORTD (*(PORT_t *) 0x0460) /* I/O Ports */#define PORTE (*(PORT_t *) 0x0480) /* I/O Ports */#define PORTF (*(PORT_t *) 0x04A0) /* I/O Ports */#define PORTMUX (*(PORTMUX_t *) 0x05E0) /* Port Multiplexer */#define ADC0 (*(ADC_t *) 0x0600) /* Analog to Digital Converter */#define AC0 (*(AC_t *) 0x0680) /* Analog Comparator */#define USART0 (*(USART_t *) 0x0800) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART1 (*(USART_t *) 0x0820) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART2 (*(USART_t *) 0x0840) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define USART3 (*(USART_t *) 0x0860) /* Universal Synchronous and Asynchronous Receiver and Transmitter */#define TWI0 (*(TWI_t *) 0x08A0) /* Two-Wire Interface */#define SPI0 (*(SPI_t *) 0x08C0) /* Serial Peripheral Interface */#define TCA0 (*(TCA_t *) 0x0A00) /* 16-bit Timer/Counter Type A */#define TCB0 (*(TCB_t *) 0x0A80) /* 16-bit Timer Type B */#define TCB1 (*(TCB_t *) 0x0A90) /* 16-bit Timer Type B */#define TCB2 (*(TCB_t *) 0x0AA0) /* 16-bit Timer Type B */#define TCB3 (*(TCB_t *) 0x0AB0) /* 16-bit Timer Type B */#define SYSCFG (*(SYSCFG_t *) 0x0F00) /* System Configuration Registers */#define NVMCTRL (*(NVMCTRL_t *) 0x1000) /* Non-volatile Memory Controller */#define SIGROW (*(SIGROW_t *) 0x1100) /* Signature row */#define FUSE (*(FUSE_t *) 0x1280) /* Fuses */#define LOCKBIT (*(LOCKBIT_t *) 0x128A) /* Lockbit */#define USERROW (*(USERROW_t *) 0x1300) /* User Row */So I do not understand how it understands PORTA.DIR, when was PORTA declared as a port struct type variable ?can I "build my variables as you have appeared to do with "something.something_else" being two variables with a "." between them read by the compiler and understood?

author: IanB
author_rank: Super Contributor
timestamp: October 14, 2018, 08:39:01 am
content: Quote from: Simon on October 14, 2018, 08:02:07 amFrom here:Code:#define PORTA (*(PORT_t *) 0x0400) /* I/O Ports */0x0400is a raw memory address.(PORT_t*)0x0400is a pointer to a PORT_t structure (casting the raw memory address to a pointer of the right type).*(PORT_t*)0x0400is an actual PORT_t structure (dereferencing the pointer).Therefore PORTA is an instance of a PORT_t type.

author: Simon
author_rank: Unknown
timestamp: October 14, 2018, 08:48:34 am
content: OK that makes sense, so they are using the struct to automatically do what i do when i calculate the register offset from the start of the port because they use a structure that is declared to start at the address pointed to and goes up from there in order. This is why they have included all of the unused register addresses to make sure the numbers stay in sync.So how flexible can I be with this? can I have the right text appear before the dot and the right text after and it correctly interprets things? I gather this does mean going to the dark side and using defines again for functions as I will be doing text replacements.i don't need assignments to change at run time so the defines make sense as it will be clearer for the compiler to optimise.

author: cv007
author_rank: Frequent Contributor
timestamp: October 14, 2018, 11:08:08 am
content: we have been here before-https://www.eevblog.com/forum/microcontrollers/(atmega-0)-tcat-(aka-union-tca-union)-has-no-member-named-ctrl/msg1771667/#msg1771667I think we are going in circles.#define set_output(pt,pn) pt.DIR |= (1<<pn)set_output(PORTA, 2);the pre-processor treats macro arguments as plain text at the first level (which is why we can use the already defined 'PORTA' as an argument, since its not 'expanded' yet)so PORTA and 2 become pt and pn- as-isnow substitution takes place and you end up with PORTA.DIR = (1<<2), which then replaces 'set_output(PORTA, 2)' in your code source(you can figure out what PORTA.DIR expands to)so the compiler only sees '(*(volatile uint8_t *)(0x0400)) |= (1<<2);'(also notice string concatenation is not required here with the struct . access)#define set_voutput(pt,pn) _SFR_IO8(&V##pt.DIR) |= (1<<pn)set_voutput(PORTB, 3);the pre-processor treats macro arguments as plain text at the first level, so PORTB and 3 become pt and pn- as-isnow substitution takes place and you end up with _SFR_IO8(&VPORTA.DIR) |= (1<<3), but still not done (notice that V##pt becomes VPORTA)now the pre-processor has to look for _SFR_IO8, and if exists passes '&VPORTA.DIR' to it (or maybe 0x400 as it now expanded)I'm not going to go through the rest, because its another ~4 levels of macros in sfr_defs.hbut you end up with- the compiler only sees (*(volatile uint8_t *)((&(*(volatile uint8_t *)(0x0004))) + 0x20)) |= (1<<3);you will have to learn about string concatenation in macros- which is not that easy to learn, and you end up in trial/error method to figure it outhere is another method, which uses the old style defines instead of struct types-#define set_output(pt,pn) pt##_DIR |= (1<<pn)#define set_voutput(pt,pn) _SFR_IO8(&V##pt##_DIR) |= (1<<pn)same result, pick your poison

author: Simon
author_rank: Unknown
timestamp: October 14, 2018, 11:28:36 am
content: Yes we have discussed concatenation in the past. But what i was asking for sure clarity on is that the struct type variables can be named by adding together two pieces of text rather than the actual concatenation operation. This does seem to be a better method as it means that i can do what i require without having to rewrite the headers and with other AVR chips having the same style of header the code will be portable. I think it will also be easier for the compiler to optimise the code which i am sure it would have done a good job of anyway.Oh and sorry macro haters but macro's 1, inline functions 0,

author: cv007
author_rank: Frequent Contributor
timestamp: October 14, 2018, 12:44:09 pm
content: QuoteThen why do you think everyone uses string concatenation if there were better alternatives? There is no better magic available.QuoteNo, it couldn't possibly be done without macros-Code://test.c#define _AVR_IO_H_ //I need to prevent error as I'm not using io.h#include "iom4809.h"#include <stdint.h>#include <stdbool.h>typedef enum {A0=0x00, A1, A2, A3, A4, A5, A6, A7,B0=0x20, B1, B2, B3, B4, B5, B6, B7,C0=0x40, C1, C2, C3, C4, C5, C6, C7,D0=0x60, D1, D2, D3, D4, D5, D6, D7,E0=0x80, E1, E2, E3, E4, E5, E6, E7,F0=0xA0, F1, F2, F3, F4, F5, F6, F7,//all my pin info hereSW1=A2, LED1=B3} PIN_t;typedef enum { DIR_IN, DIR_OUT } DIR_t;//I am adding 0x20 'manually' since I don't want to do the _SFR_IO8 incantation//the result is the compiler will use the cbi/sbi/in/out instructions for these vportsstatic inline void pin_dir(PIN_t p, DIR_t d){if(d) ((volatile uint8_t*)&VPORTA.DIR+0x20)[p>>3] |= 1<<(p&7);else ((volatile uint8_t*)&VPORTA.DIR+0x20)[p>>3] &= ~(1<<(p&7));}static inline void pin_set(PIN_t p, bool tf){if(tf) ((volatile uint8_t*)&VPORTA.OUT+0x20)[p>>3] |= 1<<(p&7);else ((volatile uint8_t*)&VPORTA.OUT+0x20)[p>>3] &= ~(1<<(p&7));}static inline bool pin_get(PIN_t p){return ((volatile uint8_t*)&VPORTA.IN+0x20)[p>>3] & (1<<(p&7));}void main(){pin_dir(LED1, DIR_OUT);pin_dir(SW1, DIR_IN );for(;;){pin_set( LED1, pin_get(SW1) );}}/*./avr-gcc -C -Os test.c && ./avr-objdump -d a.out00000000 <main>:0: 23 9a sbi 0x04, 3 ; 42: 02 98 cbi 0x00, 2 ; 04: 12 9b sbis 0x02, 2 ; 26: 02 c0 rjmp .+4 ; 0xc <__zero_reg__+0xb>8: 2b 9a sbi 0x05, 3 ; 5a: fc cf rjmp .-8 ; 0x4 <__zero_reg__+0x3>c: 2b 98 cbi 0x05, 3 ; 5e: fa cf rjmp .-12 ; 0x4 <__zero_reg__+0x3>*/exact same result using their headers, so I'm not sure what you are talking aboutedit-you can also use _SFR_IO8, and change the enums to simply increment from 0-Code:typedef enum {A0, A1, A2, A3, A4, A5, A6, A7,B0, B1, B2, B3, B4, B5, B6, B7,C0, C1, C2, C3, C4, C5, C6, C7,D0, D1, D2, D3, D4, D5, D6, D7,E0, E1, E2, E3, E4, E5, E6, E7,F0, F1, F2, F3, F4, F5, F6, F7,//all my pin info hereSW1=A2, LED1=B3} PIN_t;typedef enum { DIR_IN, DIR_OUT } DIR_t;static inline void pin_dir(PIN_t p, DIR_t d){if(d) (&_SFR_IO8(&VPORTA.DIR))[p>>3<<2] |= 1<<(p&7);else (&_SFR_IO8(&VPORTA.DIR))[p>>3<<2] &= ~(1<<(p&7));}static inline void pin_set(PIN_t p, bool tf){if(tf) (&_SFR_IO8(&VPORTA.OUT))[p>>3<<2] |= 1<<(p&7);else (&_SFR_IO8(&VPORTA.OUT))[p>>3<<2] &= ~(1<<(p&7));}static inline bool pin_get(PIN_t p){return (&_SFR_IO8(&VPORTA.IN))[p>>3<<2] & (1<<(p&7));}There are many ways to say the same thing.

author: Simon
author_rank: Unknown
timestamp: October 14, 2018, 01:13:38 pm
content: Now I understand their headers better I have nothing to talk about

author: Siwastaja
author_rank: Super Contributor
timestamp: October 15, 2018, 09:17:59 am
content: Quote from: ataradov on October 13, 2018, 10:24:28 pmThis.As a beginner, what you need most is exposure to real-world projects; that's the only way you learn how everything works, and especially, how wouldyoulike everything to work. As you gain experience, you find out the actual pain points, and you also find out ways to make them better without too much head banging. It may be something as simple as twenty lines of helper macros around the existing manufacturer #defines.

author: Simon
author_rank: Unknown
timestamp: October 15, 2018, 11:07:01 am
content: Indeed, I have given up and will start work on my first project with this chip. i am writing myself a word document instead to make notes of procedures and code snippets.

author: Mattjd
author_rank: Regular Contributor
timestamp: October 17, 2018, 11:41:40 pm
content: Im very late to the party, but OP, this is what you want I suppose?Code:#ifndef _GPIOMacros_#define _GPIOMacros_#include <msp430.h>//Digital I/O registers// P1IV - Port 1 Interrupt Vector// P1IES - Port 1 Interrupt Edge Select#define PORT1_INTERRUPT_PIN_ON_POS_EDGE(pin) P1IES &= ~BIT ## pin#define PORT1_INTERRUPT_PIN_ON_NEG_EDGE(pin) P1IES |= BIT ## pin// P1IE - Port 1 Interrupt Enable#define ENABLE_PORT1_INTERRUPTS_PIN(pin) P1IE |= BIT ## pin#define DISABLE_PORT1_INTERRUPTS_PIN(pin) P1IE &= ~BIT ## pin// P1IFG - Port 1 Interrupt Flag#define CLEAR_PORT1_FLAG_PIN(pin) P1IFG &= ~BIT ## pin#define SET_PORT1_FLAG_PIN(pin) P1IFG |= BIT ## pin// P2IES - Port 2 Interrupt Edge Select#define PORT2_INTERRUPT_PIN_ON_POS_EDGE(pin) P2IES &= ~BIT ## pin#define PORT2_INTERRUPT_PIN_ON_NEG_EDGE(pin) P2IES |= BIT ## pin// P2IE - Port 2 Interrupt Enable#define ENABLE_PORT2_INTERRUPTS_PIN(pin) P2IE |= BIT ## pin#define DISABLE_PORT2_INTERRUPTS_PIN(pin) P2IE &= ~BIT ## pin// P2IFG - Port 2 Interrupt Flag#define CLEAR_PORT2_FLAG_PIN(pin) P2IFG &= ~BIT ## pin#define SET_PORT2_FLAG_PIN(pin) P2IFG |= BIT ## pin// PxIN - Port x Input#define READ_GPIO_INPUT(port,pin) P ## port ## IN & BIT ## pin // Note to Self: Assume P1IN = 8'b10000000 then P1IN & BIT7 = 128// PxOUT - Port x Output#define GPIO_OUT(port) P ## port ## OUT// PxDIR - Port x Direction#define GPIO_DIR(port) P ## port ## DIR// PxREN - Port x Resistor#define GPIO_REN(port) P ## port ## REN// PxDS - Port x Drive Strength#define GPIO_DS(port) P ## port ## DS// PxSEL - Port x Peripheral Selection#define GPIO_SEL(port) P ## port ## SEL// Pin Selections - (Input OR Output) & (Peripheral OR NO_Peripheral)#define SET_GPIO_AS_OUTPUT(port,pin) GPIO_SEL(port) &= ~BIT ## pin; \GPIO_DIR(port) |= BIT ## pin; \GPIO_OUT(port) &= ~BIT ## pin;#define SET_GPIO_AS_INPUT(port,pin) GPIO_SEL(port) &= ~BIT ## pin; \GPIO_DIR(port) &= ~BIT ## pin;#define USE_GPIO_PERIPHERAL_AS_OUTPUT(port,pin) GPIO_SEL(port) |= BIT ## pin; \GPIO_DIR(port) |= BIT ## pin;#define USE_GPIO_PERIPHERAL_AS_INPUT(port,pin) GPIO_SEL(port) |= BIT ## pin; \GPIO_DIR(port) &= ~BIT ## pin;#define ENABLE_PIN_RESISTOR(port,pin) GPIO_REN(port) |= BIT ## pin;#define DISABLE_PIN_RESISTOR(port,pin) GPIO_REN(port) &= ~BIT ## pin;#define SET_GPIO_OUTPUT_HIGH(port,pin) GPIO_OUT(port) |= BIT ## pin;#define SET_GPIO_OUTPUT_LOW(port,pin) GPIO_OUT(port) &= ~BIT ## pin;#define TOGGLE_GPIO_OUTPUT(port,pin) GPIO_OUT(port) ^= BIT ## pin;// Resistor Selection - Pullup OR Pulldown// Note to Self: Pull Up means Active Low (MCU receives a 1 until button is pressed), Pull Down Means Active High (MCU receives a 0 untill button is pressed)#define SET_GPIO_RESISTOR_AS_PULLUP(port,pin) SET_GPIO_AS_INPUT(port,pin); \ENABLE_PIN_RESISTOR(port,pin); \SET_GPIO_OUTPUT_HIGH(port,pin);#define SET_GPIO_RESISTOR_AS_PULLDOWN(port,pin) SET_GPIO_AS_INPUT(port,pin); \ENABLE_PIN_RESISTOR(port,pin); \SET_GPIO_OUTPUT_LOW(port,pin);#endif // _GPIOMacros_which I then use to build this?Code:#ifndef _DevelopmentBoardMacros_#define _DevelopmentBoardMacros_#include <msp430.h>#include "GPIOMacros.h"// Development Board Specific Macros// BUTTONS - Switch1 is pin 2.1 and Switch2 is pin 1.1// Note to Self: Button Interrupts appear to not work when using Active High (pull down configuration), use only Active Low (pull up)#define CONFIGURE_INTERRUPT_SWITCH1_ON_PRESS SET_GPIO_RESISTOR_AS_PULLUP(2,1); \PORT2_INTERRUPT_PIN_ON_NEG_EDGE(1); \ENABLE_PORT2_INTERRUPTS_PIN(1); \CLEAR_PORT2_FLAG_PIN(1);#define CONFIGURE_INTERRUPT_SWITCH1_ON_RELEASE SET_GPIO_RESISTOR_AS_PULLUP(2,1); \PORT2_INTERRUPT_PIN_ON_POS_EDGE(1); \ENABLE_PORT2_INTERRUPTS_PIN(1); \CLEAR_PORT2_FLAG_PIN(1);#define CONFIGURE_INTERRUPT_SWITCH2_ON_PRESS SET_GPIO_RESISTOR_AS_PULLUP(1,1); \PORT1_INTERRUPT_PIN_ON_NEG_EDGE(1); \ENABLE_PORT1_INTERRUPTS_PIN(1);\CLEAR_PORT1_FLAG_PIN(1);#define CONFIGURE_INTERRUPT_SWITCH2_ON_RELEASE SET_GPIO_RESISTOR_AS_PULLUP(1,1); \PORT1_INTERRUPT_PIN_ON_POS_EDGE(1); \ENABLE_PORT1_INTERRUPTS_PIN(1); \CLEAR_PORT1_FLAG_PIN(1);// LEDS - left LED1 (red) pin 1.0 and right LED2 (green) is pin 4.7#define CONFIGURE_LED1 SET_GPIO_AS_OUTPUT(1,0); \SET_GPIO_OUTPUT_LOW(1,0);#define CONFIGURE_LED2 SET_GPIO_AS_OUTPUT(4,7); \SET_GPIO_OUTPUT_LOW(4,7);#define TURN_LED1_ON SET_GPIO_OUTPUT_HIGH(1,0);#define TURN_LED1_OFF SET_GPIO_OUTPUT_LOW(1,0);#define TOGGLE_LED1 TOGGLE_GPIO_OUTPUT(1,0);#define TURN_LED2_ON SET_GPIO_OUTPUT_HIGH(4,7);#define TURN_LED2_OFF SET_GPIO_OUTPUT_LOW(4,7);#define TOGGLE_LED2 TOGGLE_GPIO_OUTPUT(4,7);// GPIO - SET AS INPUTS// Left Set of Pins// Left Column#define SET_PORT6_PIN5_GPIO_INPUT SET_GPIO_AS_INPUT(6,5);#define SET_PORT3_PIN4_GPIO_INPUT SET_GPIO_AS_INPUT(3,4);#define SET_PORT3_PIN3_GPIO_INPUT SET_GPIO_AS_INPUT(3,3);#define SET_PORT1_PIN6_GPIO_INPUT SET_GPIO_AS_INPUT(1,6);#define SET_PORT6_PIN6_GPIO_INPUT SET_GPIO_AS_INPUT(6,6);#define SET_PORT3_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(3,2);#define SET_PORT2_PIN7_GPIO_INPUT SET_GPIO_AS_INPUT(2,7);#define SET_PORT4_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(4,2);#define SET_PORT4_PIN1_GPIO_INPUT SET_GPIO_AS_INPUT(4,1);// Right Column#define SET_PORT6_PIN0_GPIO_INPUT SET_GPIO_AS_INPUT(6,0);#define SET_PORT6_PIN1_GPIO_INPUT SET_GPIO_AS_INPUT(6,1);#define SET_PORT6_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(6,2);#define SET_PORT6_PIN3_GPIO_INPUT SET_GPIO_AS_INPUT(6,3);#define SET_PORT6_PIN4_GPIO_INPUT SET_GPIO_AS_INPUT(6,4);#define SET_PORT7_PIN0_GPIO_INPUT SET_GPIO_AS_INPUT(7,0);#define SET_PORT3_PIN6_GPIO_INPUT SET_GPIO_AS_INPUT(3,6);#define SET_PORT3_PIN5_GPIO_INPUT SET_GPIO_AS_INPUT(3,5);// Right Set of Pins// Left Column#define SET_PORT2_PIN5_GPIO_INPUT SET_GPIO_AS_INPUT(2,5);#define SET_PORT2_PIN4_GPIO_INPUT SET_GPIO_AS_INPUT(2,4);#define SET_PORT1_PIN5_GPIO_INPUT SET_GPIO_AS_INPUT(1,5);#define SET_PORT1_PIN4_GPIO_INPUT SET_GPIO_AS_INPUT(1,4);#define SET_PORT1_PIN3_GPIO_INPUT SET_GPIO_AS_INPUT(1,3);#define SET_PORT1_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(1,2);#define SET_PORT4_PIN3_GPIO_INPUT SET_GPIO_AS_INPUT(4,3);#define SET_PORT4_PIN0_GPIO_INPUT SET_GPIO_AS_INPUT(4,0);#define SET_PORT3_PIN7_GPIO_INPUT SET_GPIO_AS_INPUT(3,7);#define SET_PORT8_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(8,2);// Right Column#define SET_PORT2_PIN0_GPIO_INPUT SET_GPIO_AS_INPUT(2,0);#define SET_PORT2_PIN2_GPIO_INPUT SET_GPIO_AS_INPUT(2,2);#define SET_PORT7_PIN4_GPIO_INPUT SET_GPIO_AS_INPUT(7,4);//RST PIN?#define SET_PORT3_PIN0_GPIO_INPUT SET_GPIO_AS_INPUT(3,0);#define SET_PORT3_PIN1_GPIO_INPUT SET_GPIO_AS_INPUT(3,1);#define SET_PORT2_PIN6_GPIO_INPUT SET_GPIO_AS_INPUT(2,6);#define SET_PORT2_PIN3_GPIO_INPUT SET_GPIO_AS_INPUT(2,3);#define SET_PORT8_PIN1_GPIO_INPUT SET_GPIO_AS_INPUT(8,1);// GPIO - SET AS OUTPUTS// Left Set of Pins// Left Column#define SET_PORT6_PIN5_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,5);#define SET_PORT3_PIN4_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,4);#define SET_PORT3_PIN3_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,3);#define SET_PORT1_PIN6_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(1,6);#define SET_PORT6_PIN6_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,6);#define SET_PORT3_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,2);#define SET_PORT2_PIN7_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,7);#define SET_PORT4_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(4,2);#define SET_PORT4_PIN1_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(4,1);// Right Column#define SET_PORT6_PIN0_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,0);#define SET_PORT6_PIN1_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,1);#define SET_PORT6_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,2);#define SET_PORT6_PIN3_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,3);#define SET_PORT6_PIN4_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(6,4);#define SET_PORT7_PIN0_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(7,0);#define SET_PORT3_PIN6_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,6);#define SET_PORT3_PIN5_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,5);// Right Set of Pins// Left Column#define SET_PORT2_PIN5_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,5);#define SET_PORT2_PIN4_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,4);#define SET_PORT1_PIN5_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(1,5);#define SET_PORT1_PIN4_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(1,4);#define SET_PORT1_PIN3_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(1,3);#define SET_PORT1_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(1,2);#define SET_PORT4_PIN3_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(4,3);#define SET_PORT4_PIN0_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(4,0);#define SET_PORT3_PIN7_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,7);#define SET_PORT8_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(8,2);// Right Column#define SET_PORT2_PIN0_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,0);#define SET_PORT2_PIN2_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,2);#define SET_PORT7_PIN4_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(7,4);//RST PIN?#define SET_PORT3_PIN0_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,0);#define SET_PORT3_PIN1_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(3,1);#define SET_PORT2_PIN6_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,6);#define SET_PORT2_PIN3_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(2,3);#define SET_PORT8_PIN1_GPIO_OUTPUT SET_GPIO_AS_OUTPUT(8,1);// GPIO - Enable Interrupts// Left Set of Pins// Left Column#define ENABLE_GPIO_INTERRUPTS_PORT1_PIN6 ENABLE_PORT1_INTERRUPTS_PIN(6); \CLEAR_PORT1_FLAG_PIN(6);#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN7 ENABLE_PORT2_INTERRUPTS_PIN(7); \CLEAR_PORT2_FLAG_PIN(7);// Right Column// Right Set of Pins// Left Column#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN5 ENABLE_PORT2_INTERRUPTS_PIN(5); \CLEAR_PORT2_FLAG_PIN(5);#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN4 ENABLE_PORT2_INTERRUPTS_PIN(4); \CLEAR_PORT2_FLAG_PIN(4);#define ENABLE_GPIO_INTERRUPTS_PORT1_PIN5 ENABLE_PORT1_INTERRUPTS_PIN(5); \CLEAR_PORT1_FLAG_PIN(5);#define ENABLE_GPIO_INTERRUPTS_PORT1_PIN4 ENABLE_PORT1_INTERRUPTS_PIN(4); \CLEAR_PORT1_FLAG_PIN(4);#define ENABLE_GPIO_INTERRUPTS_PORT1_PIN3 ENABLE_PORT1_INTERRUPTS_PIN(3); \CLEAR_PORT1_FLAG_PIN(3);#define ENABLE_GPIO_INTERRUPTS_PORT1_PIN2 ENABLE_PORT1_INTERRUPTS_PIN(2); \CLEAR_PORT1_FLAG_PIN(2);// Right Column#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN0 ENABLE_PORT2_INTERRUPTS_PIN(0); \CLEAR_PORT2_FLAG_PIN(0);#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN2 ENABLE_PORT2_INTERRUPTS_PIN(2); \CLEAR_PORT2_FLAG_PIN(2);#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN6 ENABLE_PORT2_INTERRUPTS_PIN(6); \CLEAR_PORT2_FLAG_PIN(6);#define ENABLE_GPIO_INTERRUPTS_PORT2_PIN3 ENABLE_PORT2_INTERRUPTS_PIN(3); \CLEAR_PORT2_FLAG_PIN(3);// GPIO - Disable Interrupts// Left Set of Pins// Left Column#define DISABLE_GPIO_INTERRUPTS_PORT1_PIN6 DISABLE_PORT1_INTERRUPTS_PIN(6); \CLEAR_PORT1_FLAG_PIN(6);#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN7 DISABLE_PORT2_INTERRUPTS_PIN(7); \CLEAR_PORT2_FLAG_PIN(7);// Right Column// Right Set of Pins// Left Column#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN5 DISABLE_PORT2_INTERRUPTS_PIN(5); \CLEAR_PORT2_FLAG_PIN(5);#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN4 DISABLE_PORT2_INTERRUPTS_PIN(4); \CLEAR_PORT2_FLAG_PIN(4);#define DISABLE_GPIO_INTERRUPTS_PORT1_PIN5 DISABLE_PORT1_INTERRUPTS_PIN(5); \CLEAR_PORT1_FLAG_PIN(5);#define DISABLE_GPIO_INTERRUPTS_PORT1_PIN4 DISABLE_PORT1_INTERRUPTS_PIN(4); \CLEAR_PORT1_FLAG_PIN(4);#define DISABLE_GPIO_INTERRUPTS_PORT1_PIN3 DISABLE_PORT1_INTERRUPTS_PIN(3); \CLEAR_PORT1_FLAG_PIN(3);#define DISABLE_GPIO_INTERRUPTS_PORT1_PIN2 DISABLE_PORT1_INTERRUPTS_PIN(2); \CLEAR_PORT1_FLAG_PIN(2);// Right Column#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN0 DISABLE_PORT2_INTERRUPTS_PIN(0); \CLEAR_PORT2_FLAG_PIN(0);#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN2 DISABLE_PORT2_INTERRUPTS_PIN(2); \CLEAR_PORT2_FLAG_PIN(2);#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN6 DISABLE_PORT2_INTERRUPTS_PIN(6); \CLEAR_PORT2_FLAG_PIN(6);#define DISABLE_GPIO_INTERRUPTS_PORT2_PIN3 DISABLE_PORT2_INTERRUPTS_PIN(3); \CLEAR_PORT2_FLAG_PIN(3);// GPIO - Interrupt On Positive Edge// Left Set of Pins// Left Column#define INTERRUPT_PORT1_PIN6_GPIO_ON_POS_EDGE PORT1_INTERRUPT_PIN_ON_POS_EDGE(6);#define INTERRUPT_PORT2_PIN7_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(7);// Right Column// Right Set of Pins// Left Column#define INTERRUPT_PORT2_PIN5_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(5);#define INTERRUPT_PORT2_PIN4_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(4);#define INTERRUPT_PORT1_PIN5_GPIO_ON_POS_EDGE PORT1_INTERRUPT_PIN_ON_POS_EDGE(5);#define INTERRUPT_PORT1_PIN4_GPIO_ON_POS_EDGE PORT1_INTERRUPT_PIN_ON_POS_EDGE(4);#define INTERRUPT_PORT1_PIN3_GPIO_ON_POS_EDGE PORT1_INTERRUPT_PIN_ON_POS_EDGE(3);#define INTERRUPT_PORT1_PIN2_GPIO_ON_POS_EDGE PORT1_INTERRUPT_PIN_ON_POS_EDGE(2);// Right Column#define INTERRUPT_PORT2_PIN0_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(0);#define INTERRUPT_PORT2_PIN2_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(2);#define INTERRUPT_PORT2_PIN6_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(6);#define INTERRUPT_PORT2_PIN3_GPIO_ON_POS_EDGE PORT2_INTERRUPT_PIN_ON_POS_EDGE(3);// GPIO - Interrupt On Negative Edge// Left Set of Pins// Left Column#define INTERRUPT_PORT1_PIN6_GPIO_ON_NEG_EDGE PORT1_INTERRUPT_PIN_ON_NEG_EDGE(6);#define INTERRUPT_PORT2_PIN7_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(7);// Right Column// Right Set of Pins// Left Column#define INTERRUPT_PORT2_PIN5_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(5);#define INTERRUPT_PORT2_PIN4_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(4);#define INTERRUPT_PORT1_PIN5_GPIO_ON_NEG_EDGE PORT1_INTERRUPT_PIN_ON_NEG_EDGE(5);#define INTERRUPT_PORT1_PIN4_GPIO_ON_NEG_EDGE PORT1_INTERRUPT_PIN_ON_NEG_EDGE(4);#define INTERRUPT_PORT1_PIN3_GPIO_ON_NEG_EDGE PORT1_INTERRUPT_PIN_ON_NEG_EDGE(3);#define INTERRUPT_PORT1_PIN2_GPIO_ON_NEG_EDGE PORT1_INTERRUPT_PIN_ON_NEG_EDGE(2);// Right Column#define INTERRUPT_PORT2_PIN0_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(0);#define INTERRUPT_PORT2_PIN2_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(2);#define INTERRUPT_PORT2_PIN6_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(6);#define INTERRUPT_PORT2_PIN3_GPIO_ON_NEG_EDGE PORT2_INTERRUPT_PIN_ON_NEG_EDGE(3);// GPIO - SET OUTPUT HIGH// Left Set of Pins// Left Column#define SET_PORT6_PIN5_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,5);#define SET_PORT3_PIN4_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,4);#define SET_PORT3_PIN3_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,3);#define SET_PORT1_PIN6_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(1,6);#define SET_PORT6_PIN6_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,6);#define SET_PORT3_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,2);#define SET_PORT2_PIN7_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,7);#define SET_PORT4_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(4,2);#define SET_PORT4_PIN1_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(4,1);// Right Column#define SET_PORT6_PIN0_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,0);#define SET_PORT6_PIN1_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,1);#define SET_PORT6_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,2);#define SET_PORT6_PIN3_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,3);#define SET_PORT6_PIN4_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(6,4);#define SET_PORT7_PIN0_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(7,0);#define SET_PORT3_PIN6_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,6);#define SET_PORT3_PIN5_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,5);// Right Set of Pins// Left Column#define SET_PORT2_PIN5_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,5);#define SET_PORT2_PIN4_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,4);#define SET_PORT1_PIN5_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(1,5);#define SET_PORT1_PIN4_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(1,4);#define SET_PORT1_PIN3_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(1,3);#define SET_PORT1_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(1,2);#define SET_PORT4_PIN3_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(4,3);#define SET_PORT4_PIN0_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(4,0);#define SET_PORT3_PIN7_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,7);#define SET_PORT8_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(8,2);// Right Column#define SET_PORT2_PIN0_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,0);#define SET_PORT2_PIN2_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,2);#define SET_PORT7_PIN4_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(7,4);//RST PIN?#define SET_PORT3_PIN0_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,0);#define SET_PORT3_PIN1_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(3,1);#define SET_PORT2_PIN6_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,6);#define SET_PORT2_PIN3_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(2,3);#define SET_PORT8_PIN1_GPIO_OUTPUT_HIGH SET_GPIO_OUTPUT_HIGH(8,1);// GPIO - SET OUTPUT LOW// Left Set of Pins// Left Column#define SET_PORT6_PIN5_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,5);#define SET_PORT3_PIN4_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,4);#define SET_PORT3_PIN3_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,3);#define SET_PORT1_PIN6_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(1,6);#define SET_PORT6_PIN6_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,6);#define SET_PORT3_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,2);#define SET_PORT2_PIN7_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,7);#define SET_PORT4_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(4,2);#define SET_PORT4_PIN1_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(4,1);// Right Column#define SET_PORT6_PIN0_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,0);#define SET_PORT6_PIN1_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,1);#define SET_PORT6_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,2);#define SET_PORT6_PIN3_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,3);#define SET_PORT6_PIN4_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(6,4);#define SET_PORT7_PIN0_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(7,0);#define SET_PORT3_PIN6_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,6);#define SET_PORT3_PIN5_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,5);// Right Set of Pins// Left Column#define SET_PORT2_PIN5_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,5);#define SET_PORT2_PIN4_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,4);#define SET_PORT1_PIN5_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(1,5);#define SET_PORT1_PIN4_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(1,4);#define SET_PORT1_PIN3_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(1,3);#define SET_PORT1_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(1,2);#define SET_PORT4_PIN3_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(4,3);#define SET_PORT4_PIN0_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(4,0);#define SET_PORT3_PIN7_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,7);#define SET_PORT8_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(8,2);// Right Column#define SET_PORT2_PIN0_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,0);#define SET_PORT2_PIN2_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,2);#define SET_PORT7_PIN4_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(7,4);//RST PIN?#define SET_PORT3_PIN0_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,0);#define SET_PORT3_PIN1_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(3,1);#define SET_PORT2_PIN6_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,6);#define SET_PORT2_PIN3_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(2,3);#define SET_PORT8_PIN1_GPIO_OUTPUT_LOW SET_GPIO_OUTPUT_LOW(8,1);// GPIO - TOGGLE OUTPUT// Left Set of Pins// Left Column#define TOGGLE_PORT6_PIN5_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,5);#define TOGGLE_PORT3_PIN4_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,4);#define TOGGLE_PORT3_PIN3_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,3);#define TOGGLE_PORT1_PIN6_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(1,6);#define TOGGLE_PORT6_PIN6_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,6);#define TOGGLE_PORT3_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,2);#define TOGGLE_PORT2_PIN7_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,7);#define TOGGLE_PORT4_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(4,2);#define TOGGLE_PORT4_PIN1_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(4,1);// Right Column#define TOGGLE_PORT6_PIN0_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,0);#define TOGGLE_PORT6_PIN1_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,1);#define TOGGLE_PORT6_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,2);#define TOGGLE_PORT6_PIN3_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,3);#define TOGGLE_PORT6_PIN4_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(6,4);#define TOGGLE_PORT7_PIN0_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(7,0);#define TOGGLE_PORT3_PIN6_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,6);#define TOGGLE_PORT3_PIN5_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,5);// Right Set of Pins// Left Column#define TOGGLE_PORT2_PIN5_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,5);#define TOGGLE_PORT2_PIN4_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,4);#define TOGGLE_PORT1_PIN5_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(1,5);#define TOGGLE_PORT1_PIN4_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(1,4);#define TOGGLE_PORT1_PIN3_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(1,3);#define TOGGLE_PORT1_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(1,2);#define TOGGLE_PORT4_PIN3_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(4,3);#define TOGGLE_PORT4_PIN0_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(4,0);#define TOGGLE_PORT3_PIN7_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,7);#define TOGGLE_PORT8_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(8,2);// Right Column#define TOGGLE_PORT2_PIN0_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,0);#define TOGGLE_PORT2_PIN2_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,2);#define TOGGLE_PORT7_PIN4_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(7,4);//RST PIN?#define TOGGLE_PORT3_PIN0_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,0);#define TOGGLE_PORT3_PIN1_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(3,1);#define TOGGLE_PORT2_PIN6_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,6);#define TOGGLE_PORT2_PIN3_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(2,3);#define TOGGLE_PORT8_PIN1_GPIO_OUTPUT TOGGLE_GPIO_OUTPUT(8,1);// ADC 12#define SET_PORT6_PIN5_ADC_A5 USE_GPIO_PERIPHERAL_AS_INPUT(6,5);#define SET_PORT6_PIN6_ADC_A6 USE_GPIO_PERIPHERAL_AS_INPUT(6,6);#define SET_PORT6_PIN0_ADC_A0 USE_GPIO_PERIPHERAL_AS_INPUT(6,0);#define SET_PORT6_PIN1_ADC_A1 USE_GPIO_PERIPHERAL_AS_INPUT(6,1);#define SET_PORT6_PIN2_ADC_A2 USE_GPIO_PERIPHERAL_AS_INPUT(6,2);#define SET_PORT6_PIN3_ADC_A3 USE_GPIO_PERIPHERAL_AS_INPUT(6,3);#define SET_PORT6_PIN4_ADC_A4 USE_GPIO_PERIPHERAL_AS_INPUT(6,4);#define SET_PORT7_PIN0_ADC_A12 USE_GPIO_PERIPHERAL_AS_INPUT(7,0);// Timers// PWMs#define SET_PORT3_PIN6_TIMERB0_6_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(3,6);#define SET_PORT3_PIN5_TIMERB0_5_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(3,5);#define SET_PORT2_PIN0_TIMERA1_1_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(2,0);#define SET_PORT7_PIN4_TIMERB0_2_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(7,4);#define SET_PORT2_PIN3_TIMERA2_0_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(2,3);#define SET_PORT2_PIN5_TIMERA2_2_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(2,5);#define SET_PORT2_PIN3_TIMERA2_1_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(2,3);#define SET_PORT1_PIN3_TIMERA0_2_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(1,3);#define SET_PORT1_PIN2_TIMERA0_1_PWM_OUTPUT USE_GPIO_PERIPHERAL_AS_OUTPUT(1,2);// Timer Captures#define SET_PORT3_PIN6_TIMERB0_6_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(3,6);#define SET_PORT3_PIN5_TIMERB0_5_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(3,5);#define SET_PORT2_PIN0_TIMERA1_1_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(2,0);#define SET_PORT7_PIN4_TIMERB0_2_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(7,4);#define SET_PORT2_PIN3_TIMERA2_0_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(2,3);#define SET_PORT2_PIN5_TIMERA2_2_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(2,5);#define SET_PORT2_PIN3_TIMERA2_1_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(2,3);#define SET_PORT1_PIN3_TIMERA0_2_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(1,3);#define SET_PORT1_PIN2_TIMERA0_1_CAPTURE_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(1,2);// Timer Clock Inputs#define SET_PORT2_PIN2_TA2CLK_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(2,2);#define SET_PORT1_PIN6_TA1CLK_INPUT USE_GPIO_PERIPHERAL_AS_INPUT(1,6);#endif // _DevelopmentBoardMacros_Which I then use to build some basic timer set up macrosCode:#ifndef _TimerMacros_#define _TimerMacros_#include <msp430.h>// Timer A (Consists of Timers A0,A1,A2. So x can be 0 OR 1 OR 2)// TAxCTL: Timer_Ax Control Register(x can be: 0 OR 1 OR 2)// source can be: TACLK OR ACLK OR SMCLK OR INCLK#define SET_TIMER_Ax_CLOCK_SOURCE(x,source) TA ## x ## CTL |= TASSEL__ ## source;// divider can be: 1 OR 2 OR 3 OR 4 OR 8#define SET_TIMER_Ax_INPUT_DIVIDER(x,divider) TA ## x ## CTL |= ID__ ## divider;// mode can be: STOP OR UP OR CONTINUOUS OR UPDOWN#define SET_TIMER_Ax_MODE_CONTROLER(x,mode) TA ## x ## CTL |= MC__ ## mode;#define CLEAR_TIMER_Ax(x) TA ## x ## CTL |= TACLR;#define ENABLE_INTERRUPTS_TIMER_Ax(x) TA ## x ## CTL |= TAIE;#define DISABLE_INTERRUPTS_TIMER_Ax(x) TA ## x ## CTL &= ~TAIE;#define CHECK_PENDING_INTERRUPT_TIMER_Ax(x) TA ## x ## CTL & TAIFG;#define CLEAR_INTERRUPT_TIMER_Ax(x) TA ## x ## CTL &= ~TAIFG;// TAxR: Timer_Ax Counter Register#define READ_TIMER_Ax_VALUE(x) TA ## x ## R;#define OVERWRITE_TIMER_Ax_VALUE(x,overwrite) TA ## x ## R = overwrite;#define CLEAR_TIMER_Ax_VALUE(x) TA ## x ## R = 0;#define ADD_TO_TIMER_Ax_VALUE(x,addend) TA ## x ## R += addend;#define SUBTRACT_FROM_TIMER_Ax_VALUE(x,subtrahend) TA ## x ## R -= subtrahend;#define DIVIDE_TIMER_Ax_VALUE(x,divisor) TA ## x ## R /= divisor;#define MULTIPLY_TIMER_Ax_VALUE(x,multiplicand) TA ## x ## R *= multiplicand;// TAxCCTLn: Timer_Ax Capture/Compare Control n Register#define SELECT_TIMER_Ax_CCRn_CAPTURE_MODE(x,n,mode) TA ## x CCTL ## n |= CM_ ## mode;#define SELECT_TIMER_Ax_CCRn_CC_INPUT(x,n,select) TA ## x CCTL ## n |= CCIS_ ## select;#define SYNCHRONOUS_TIMER_Ax_CCRn_CAPTURE(x,n) TA ## x CCTL ## n |= SCS;#define ASYNCHRONOUS_TIMER_Ax_CCRn_CAPTURE(x,n) TA ## x CCTL ## n &= ~SCS;#define SET_TIMER_Ax_CCRn_CAPTURE_MODE(x,n) TA ## x CCTL ## n |= CAP;#define SET_TIMER_Ax_CCRn_COMPARE_MODE(x,n) TA ## x CCTL ## n &= ~CAP;#define SELECT_TIMER_Ax_CCRn_OUTMOD(x,n,outputmode) TA ## x CCTL ## n |= OUTMOD_ ## outputmode;#define ENABLE_TIMER_Ax_CCRn_INTERRUPT(x,n) TA ## x CCTL ## n |= CCIE;#define DISABLE_TIMER_Ax_CCRn_INTERRUPT(x,n) TA ## x CCTL ## n &= ~CCIE;#define READ_TIMER_Ax_CCRn_INPUT(x,n) TA ## x CCTL ## n & CCI;#define SET_TIMER_Ax_CCRn_OUTPUT_HIGH(x,n) TA ## x CCTL ## n |= OUT;#define SET_TIMER_Ax_CCRn_OUTPUT_LOW(x,n) TA ## x CCTL ## n &= ~OUT;#define CHECK_TIMER_Ax_CCRn_CAPTURE_OVERFLOW(x,n) TA ## x CCTL ## n & COV;#define CLEAR_TIMER_Ax_CCRn_CAPTURE_OVERFLOW(x,n) TA ## x CCTL ## n &= ~COV;#define CHECK_TIMER_Ax_CCRn_INTERRUPT_FLAG(x,n) TA ## x CCTL ## n & CCIFG;#define CLEAR_TIMER_Ax_CCRn_CCIFG(x,n) TA ## x CCTL ## n &= ~CCIFG;// TAxCCRn: Timer_A Capture/Compare n Register#define READ_TIMER_Ax_CCRn_VALUE(x,n) TA ## x ## CCR ## n ## ;#define OVER_TIMER_Ax_CCRn_VALUE(x,n,overwrite) TA ## x ## CCR ## n = overwrite;#define CLEAR_TIMER_Ax_CCRn_VALUE(x,n) TA ## x ## CCR ## n = 0;#define ADD_TO_TIMER_Ax_CCRn_VALUE(x,n,addend) TA ## x ## CCR ## n += addend;#define SUBTRACT_FROM_TIMER_Ax_CCRn_VALUE(x,n,subtrahend) TA ## x ## CCR ## n -= subtrahend;#define MULTIPLY_TIMER_Ax_CCRn_VALUE(x,n,multiplicand) TA ## x ## CCR ## n *= multiplicand;#define DIVIDE_TIMER_Ax_CCRn_VALUE(x,n,divisor) TA ## x ## CCR ## n /= divisor;// TAxIV: Timer_Ax Interrupt Vector Register#define// TAxEX0: Timer_Ax Expansion 0 Register#endifof course the tabs are messed up

author: brucehoult
author_rank: Super Contributor
timestamp: October 18, 2018, 02:35:49 am
content: Quote from: ataradov on October 13, 2018, 10:22:01 pmYou can use that function perfectly well even with the pre-dereferenced #defines like "#define PORTA (*(PORT_t *) 0x0400)"Code:void do_it(){out_set(&PORTA, 3);}If you're using C++ then it's a little bit tidier to make the function take a reference instead:Code:void out_set(PORT_t &port, int pin){port->OUTSET = (1<<pin);}void do_it(){out_set(PORTA, 3);}