creating a function but not always using all the arguments - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 11:20:27 am
content: So if i write a function that has a number of input arguments but I don't use them all what happens? will they just be assumed as 0? i am sure i have seen this in Arduino library functions.

author: oPossum
author_rank: Super Contributor
timestamp: August 25, 2019, 11:26:34 am
content: In C++ you indicate optional arguments by specifying default values.For example... int foo(int a, int b = 0, int c = 5, char * s = NULL);The first argument has no default, so it is required. The others are optional. You can not skip an optional argument.foo(1, , 3, "test"); <-- skip not allowedfoo(); <-- required argument missingfoo(1, 2); <-- OK

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 11:29:05 am
content: So does this work in C?

author: MosherIV
author_rank: Super Contributor
timestamp: August 25, 2019, 11:33:25 am
content: Arguments are passed by value, that is a copy is taken and put on the stack. The function then takes the value from the stack.Nothing will happen if you do not use the value.The compiler may warn you that you have not used a variable.Since variables are passed as copies, you cannot change input parameters from inside functions.Instead, you have to pass in by reference. In other words, pointers.Edit: no, C does not support default parameter values.Both C and C++ support parameter specifiers 'const' which tells the compiler that the copy of variable on the stack cannot be changed.

author: oPossum
author_rank: Super Contributor
timestamp: August 25, 2019, 11:45:41 am
content: C does supports varargs, but you get no compile time sanity checking, and no defaults without some extra work.printf() is an example of a function that uses varargshttps://en.wikipedia.org/wiki/Variadic_function

author: RoGeorge
author_rank: Super Contributor
timestamp: August 25, 2019, 11:51:52 am
content: Optional arguments may or may not be possible, depending on the language and the function definition. Same for arguments sent by value or by reference. In some languages the default is to send arguments by value, in other languages the default is to send them by reference.For C++ it is possible to have optional arguments, not so much for C.https://stackoverflow.com/questions/27795767/optional-arguments-in-c-functionArduino is a mixture of C and C++ libraries.Note: statistics show OP beginning with the conclusive word "so" have a 69% less chance to be read.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 12:06:26 pm
content: Quote from: RoGeorge on August 25, 2019, 11:51:52 amAnd 50% of statistics are made up"So" I am confused now as i seem to interpret people saying yes and no to the same question. The context is that i am wrining for the ARM GCC compiler which i beleive is C?As i have the posibilitfy of setting up several pins on one register write and thus save a lot of repetition of code (that may or may not be optimised out) i aim to write a function that has as arguments the setup parameters followed by the pins that it has to affect. So I can just put the string in of bits that will go straight into the 16 bits of the register or i can specify the pins one at a time. But i will hardly ever setup all 16 so many of the arguments would remain blank.

author: donotdespisethesnake
author_rank: Super Contributor
timestamp: August 25, 2019, 12:11:42 pm
content: Quote from: Simon on August 25, 2019, 12:06:26 pmSome people misinterpreted the question. I think you asked "what if I don't pass all the parameters?" (Answer: a compiler error).Of course, if you pass parameters that are not used, the answer is largely irrelevant, so I'm not sure why people answered that.You may also have seen overloaded functions in C++, which are not available in C.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 12:15:53 pm
content: Quote from: donotdespisethesnake on August 25, 2019, 12:11:42 pmThankyou, i will have to look at how i acheive what i want then. I suppose I can build up my mask as a seperate operation and then pass it to the function.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 12:18:31 pm
content: On the other hand when I create a new project it asks if i want o create a new C/C++ project for AVR/ARM. but then i am still learning C never mind C++

author: oPossum
author_rank: Super Contributor
timestamp: August 25, 2019, 12:26:42 pm
content: Quote from: donotdespisethesnake on August 25, 2019, 12:11:42 pmThere will not be an error if the function has default arguments. (C++ only)There will not be an error if it is a variadic function (C and C++)

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 12:28:09 pm
content: variadic ?

author: rhodges
author_rank: Frequent Contributor
timestamp: August 25, 2019, 12:31:10 pm
content: Quote from: Simon on August 25, 2019, 12:15:53 pmThat is probably a good option. Consider something like this:Code:#define OPTION_A 0x01 /* one bit */#define OPTION_B 0x02#define OPTION_C 0x04#define OPTION_X 0x10 /* two bits */#define OPTION_Y 0x20 /* two bits */#define OPTION_Z 0x30 /* two bits */something_init(OPTION_A | OPTION_C | OPTION_Y);

author: nctnico
author_rank: Super Contributor
timestamp: August 25, 2019, 12:37:55 pm
content: Quote from: Simon on August 25, 2019, 11:29:05 amNo. Only in C++.

author: oPossum
author_rank: Super Contributor
timestamp: August 25, 2019, 12:41:40 pm
content: Quote from: Simon on August 25, 2019, 12:28:09 pmReply #4 ^^^

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2019, 12:43:50 pm
content: >As i have the posibilitfy of setting up several pins on one register write and thus save a lot of repetition of codeYour solution may be worse- in size or usage, than just dealing with one pin at a time. Let's say you have 20 pins to deal with- in the big picture, this will result in code you may still have to get the magnifying glass out to find. If you can work out a way to 'combine' the register writes, you gain very little but now you have to figure out which pins belong to which ports, what their port settings will be such as pullup, input, output, etc. Keep it simple, deal with 1 pin at a time.simple C example to setup in/out for a pin-https://godbolt.org/z/EPZcADnow do 20 of them, not a big deal.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 01:01:08 pm
content: Not really. If i write code to set up each pin i will be writing the same code ove and over with some change in the numbers. but with one of the registers i can send the same configuration to 16 pins at a time if needed. Granted I will end up using it over and over as i can only send one configuration at a time.using bit shifts and andding multiple ones together can all be done in passing the argument and will be optimized out in the compiler as the final mask..So now i have the fun of working out what they called the registers and pondering again the wisdom of just addressing the memory space directly, I note for example that SAMc and SAMd have the same addresses for same registers so it would still be portable.

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2019, 01:25:03 pm
content: here is a very simple incomplete version of C++ templates-https://godbolt.org/z/I-JA_0>If i write code to set up each pin i will be writing the same code ove and over with some change in the numbers. but with one of the registers i can send the same configuration to 16 pins at a time if neededHow do you imagine your function call will look like? Start with what you want your code to look like, and work back from there.this is what it appears you want to do (multiple optional arguments, which will end up being a macro of some kind)-pin_outputs( PA02, PA05, PA15, PA20);pin_inputs( PA03, PA07, PA11, PA25);You are not using a pic10 with 256 bytes of flash. There are bigger battles to fight, and worrying about combining register writes is not worth your time, but you can certainly give it a go.

author: MosherIV
author_rank: Super Contributor
timestamp: August 25, 2019, 01:39:25 pm
content: HiOne trick which I did not understand when I first started, is to create a struct with the regsiter set that you are trying to control.Create 1 instance of the struct for each set of registers that you want to control.You have to somehow point the struct to the address of the registers.Then, all you have to do is to change the register in the struct of the instance that you are interrested in.Your function then just takes the struct and writes the whole lot.I first saw this for initialising UARTs.So you could initialise multiple UARTs with the same struct but pointing to UARTs at different register adresses.Hope that makes sense.I did not like it when I first saw this but now I understand and appreciate this technique.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 01:57:15 pm
content: Quote from: MosherIV on August 25, 2019, 01:39:25 pmi need to remind myself what structs are/how they work. The problem i have with the supplied headers is that they are geared up for their own ASF or IAR systems. I am now at a paint where I don't see the point inconforming to their "standards" if tht does not work for me.The other way of doing it is to pass the part name as a parameter/argumunt to the function, that "text" will simply be a define that is replaced by the base address ofthe port, so i can define each offset once and easily add them together in other defines so that I am not writing out every address and i have some chance of porting to another SAM device by just changing a fex offset definitions.

author: radiolistener
author_rank: Super Contributor
timestamp: August 25, 2019, 02:07:49 pm
content: Quote from: Simon on August 25, 2019, 12:06:26 pmI don't recommend to use a lot of arguments in the function, it's better to use struct and pass pointer to this struct.Regarding to pins and bit manipulation, it's better to define clear named constants with masks and values and combine it with | and & operations instead of passing each bit separately. It will be much easier to support such code. Because when you will use a lot of variables for bits and pins it will turns into nightmare

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 02:12:44 pm
content: Yes which is why i would pass one argument that is the final bit mask. That bit mask can be made up ad hoc in the function call. The compiler will work out the mask and put it into the program leaving out all the visible calculations. But with them being visible I can easily see what i wrote. I don't like just sending hex numbers to registers as it is not clear what is happening. You can use binary but at 8 digits that is hard work to easily identify which bit is which. Unfortunately you can't put spaces into long binary numbers. at 32 bits it just becomes silly.

author: SiliconWizard
author_rank: Super Contributor
timestamp: August 25, 2019, 02:21:20 pm
content: For C, the struct "trick" would be the way to go indeed IMO. (It's not a trick!)I don't recommend using variadic functions for that. Would be cumbersome and error-prone, especially if you have several parameters of different types.You can either reuse the same struct variable for subsequent calls, just modifying the fields you want modified (if the other fields are to retain their previous value), or if you want all the fields that are not explicitly set to be zeroed-out, you can use the C99 syntax (compound literals). Short example:Code:#include <stdio.h>#include <stdint.h>typedef struct{uint32_t n;uint8_t m;double x;char s;}MyParams_t;static void MyFunction(MyParams_t *pParams){if (pParams == NULL)return;printf("n = %u, m = %u, x = %g, s = '%s'\n", pParams->n, pParams->m, pParams->x, pParams->s);}int main(void){MyParams_t Parameters;// Initial values.Parameters = (MyParams_t){ .n = 1, .x = 0.5 };MyFunction(&Parameters);// Only change selected fields.Parameters.m = 2;Parameters.x = -0.5;MyFunction(&Parameters);// Only set selected fields, the others will be zeroed-out.Parameters = (MyParams_t){ .s = "Test" };MyFunction(&Parameters);return 0;}

author: sokoloff
author_rank: Super Contributor
timestamp: August 25, 2019, 02:30:30 pm
content: Quote from: Simon on August 25, 2019, 02:12:44 pmYou could use a preprocessor macro if you wanted to break up a long binary number for the author and recombine it later.If you're using gcc (in order to get support for 0bxxxxxxxx style binary literals), you can do something like this to break up your literals for reading:Code:#include <stdio.h>#define BINARY_CONST(A, B) (0b ## A ## B)int main(void) {int foo = BINARY_CONST(0101, 1111);printf("%s = 0x%x", "BINARY_CONST(0101, 1111)", foo);return 0;}You can also have a look at theBOOST_BINARY macro package.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 03:03:58 pm
content: Code:#ifndef PORTS_H_#define PORTS_H_// architecture offsets#define PORTS_OS 0x41000000UL#define PORTS_OS_STEP 0x80//port offsets#define PORTA_OS_STEP (0*PORTS_OS_STEP)#define PORTB_OS_STEP (1*PORTS_OS_STEP)#define PORTC_OS_STEP (2*PORTS_OS_STEP)#define PORTA_OS (PORTA_OS_STEP*PORTS_OS_STEP)#define PORTB_OS (PORTB_OS_STEP*PORTS_OS_STEP)#define PORTC_OS (PORTC_OS_STEP*PORTS_OS_STEP)// ports base address#define PORTA (PORTS_OS+PORTA_OS)#define PORTB (PORTS_OS+PORTA_OS)#define PORTC (PORTS_OS+PORTA_OS)// port register offset definitions#define DIR 0#define DIRCLR 0x04#define DIRSET 0x08#define DIRTGL 0x0C#define OUT 0x10#define OUTCLR 0x14#define OUTSET 0x18#define OUTTGL 0x1C#define IN 0x20#define CTRL 0x24#define WRCONFIG 0x28#define EVCTRL 0x2C#define PMUX_OS 0x30#define PMUX_OS_STEP 0x1#define PINCFG_OS 0x40#define PINCFG_OS_STEP 0x1#define REGISTER(A, B) (A + B)/* PORTS_H_ */

author: sokoloff
author_rank: Super Contributor
timestamp: August 25, 2019, 03:12:16 pm
content: I think you want something like:Code:#ifndef PORTS_H_#define PORTS_H_// architecture offsets#define PORTS_OS 0x41000000UL#define PORTS_OS_STEP 0x80//port offsets#define PORTA_OS_STEP (0*PORTS_OS_STEP)#define PORTB_OS_STEP (1*PORTS_OS_STEP)#define PORTC_OS_STEP (2*PORTS_OS_STEP)// ports base address#define PORTA (PORTS_OS+PORTA_OS_STEP)#define PORTB (PORTS_OS+PORTB_OS_STEP)#define PORTC (PORTS_OS+PORTC_OS_STEP)// port register offset definitions#define DIR 0#define DIRCLR 0x04#define DIRSET 0x08#define DIRTGL 0x0C#define OUT 0x10#define OUTCLR 0x14#define OUTSET 0x18#define OUTTGL 0x1C#define IN 0x20#define CTRL 0x24#define WRCONFIG 0x28#define EVCTRL 0x2C#define PMUX_OS 0x30#define PMUX_OS_STEP 0x1#define PINCFG_OS 0x40#define PINCFG_OS_STEP 0x1#define REGISTER(A, B) (A + B)/* PORTS_H_ */It seems unlikely that the ports are spaced by the square of the offset.It seems like you had copy/paste errors in the final port address lines.

author: nctnico
author_rank: Super Contributor
timestamp: August 25, 2019, 03:55:19 pm
content: Quote from: Simon on August 25, 2019, 02:12:44 pmThe usual way is to use defines for each bit in a register. Like#define UART_TX_ENABLE (1<<4)#define UART_RX_ENABLE (1<<6)and later on:#define UART_CONTROL (*(volatile unsigned char *) (some_address_goes_here))So you can write:UART_CONTROL = UART_TX_ENABLE | UART_RX_ENABLE;I think the libraries which come from IAR and ASF will be setup in a similar way (or use structs mapped onto the register map). I strongly suggest to go that way instead of trying to re-invent the wheel. Using binary numbers instead of defines will make code hard to understand and maintain. From the defines for the various bits it is immediately clear which bit is set.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 04:32:57 pm
content: Quote from: sokoloff on August 25, 2019, 03:12:16 pmI don't get you, i multiply the por number by the ofset the ports are apart. PORTS_OS * PORTx+ the start address for the ports

author: sokoloff
author_rank: Super Contributor
timestamp: August 25, 2019, 04:46:48 pm
content: Quote from: Simon on August 25, 2019, 04:32:57 pmYour original code was: (with my comments added as to what I believe the values are)Code:#define PORTS_OS 0x41000000UL#define PORTS_OS_STEP 0x80 // 128//port offsets#define PORTA_OS_STEP (0*PORTS_OS_STEP) // 0#define PORTB_OS_STEP (1*PORTS_OS_STEP) // 128#define PORTC_OS_STEP (2*PORTS_OS_STEP) // 256#define PORTA_OS (PORTA_OS_STEP*PORTS_OS_STEP) // 0 * 128 = 0#define PORTB_OS (PORTB_OS_STEP*PORTS_OS_STEP) // 128 * 128 = 16384#define PORTC_OS (PORTC_OS_STEP*PORTS_OS_STEP) // 256 * 128 = 32768// ports base address#define PORTA (PORTS_OS+PORTA_OS) // 0x41000000UL#define PORTB (PORTS_OS+PORTA_OS) // 0x41000000UL or 0x41040000UL if you fix copy/paste error#define PORTC (PORTS_OS+PORTA_OS) // 0x41000000UL or 0x41080000UL if you fix copy/paste errorIt boils down to whether the address are 128 (PORTS_OS_STEP) bytes apart or 16384 (PORTS_OS_STEP * PORTS_OS_STEP) bytes apart.I don't have the datasheet (or even know what part you're working on), but based on the way you wrote the macros, I was assuming that the ports are 128 bytes apart.

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2019, 04:52:00 pm
content: You can use the manufacturer includes, they are nothing special and just a way for them to do the same thing over and over for each chip. I don't particularly like them, but if you plan to move from chip to chip, you may as well learn and use them to save time.Simple C version, using what they already provide-Code:#include <stdint.h>#include <stdbool.h>#include "sam.h"static const bool INPUT = 0;static const bool OUTPUT = 1;static const uint32_t LED = PIN_PA02;static const uint32_t SW = PIN_PA07;static void pindir(const uint32_t pin, const bool io){if( io ) PORT->Group[pin/32].DIRSET.reg = 1<<(pin%32);else PORT->Group[pin/32].DIRCLR.reg = 1<<(pin%32);}static void pinout(const uint32_t pin, const bool v){if( v ) PORT->Group[pin/32].OUTSET.reg = 1<<(pin%32);else PORT->Group[pin/32].OUTCLR.reg = 1<<(pin%32);}static bool pinval(const uint32_t pin){return PORT->Group[pin/32].IN.reg & (1<<(pin%32));}int main(){pindir( LED, OUTPUT );pindir( SW, INPUT );for(;;){if( pinval( SW ) == 0 ) pinout( LED, 1 );}}(notice all the defines)If the mcu has more than one port, its already taken care of and functions can be used for various mcu's if the headers were done right (I think so).Or bypass the whole function thing and just use directly, which is what they do I'm sure.Each pin is treated separately, as each pin is a separate thing, so it makes sense. Trying to group them in some way so you can save a few instructions on a setup write will be more trouble than its worth.There are a thousand ways to go about this and they are all probably ok, except trying to use a pre-filled struct in this case.

author: ogden
author_rank: Super Contributor
timestamp: August 25, 2019, 05:03:59 pm
content: Quote from: oPossum on August 25, 2019, 11:45:41 amRight. Such kind of functions are main source of C software bugs and #1 attack vector for hacks. Usage of C functions with varargs shall be discouraged, especially in embedded applications that may set something on fire. As already said in this thread - you can easily avoid need for vararg functions in C.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 05:50:54 pm
content: Quote from: cv007 on August 25, 2019, 04:52:00 pmIt's actually the chip maker that encourages multiple pin setup. There is a register that will in one access do what it takes to do ot two register accesses if i was to go the other way around that is available to one pin at a time anyway. but yes no problem using the register that can set up more than one pin at once to setup one pin at a time, that is not the question now.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 05:51:50 pm
content: I'm working on SAMC

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 06:02:57 pm
content: Quote from: sokoloff on August 25, 2019, 04:46:48 pmPORTC_OS_STEP these are 0, 1, 2.0*0x80 = 01*0x80 = 0x802*0x08 = 0x100

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 06:27:13 pm
content: OK I see what i did, I repeated the same calculationCode:#ifndef PORTS_H_#define PORTS_H_// architecture offsets#define PORTS_OS 0x41000000UL#define PORTS_OS_STEP 0x80// port offsets#define PORTA_OS_CNT 0#define PORTB_OS_CNT 1#define PORTC_OS_CNT 2#define PORTA_OS (PORTA_OS_CNT*PORTS_OS_STEP)#define PORTB_OS (PORTB_OS_CNT*PORTS_OS_STEP)#define PORTC_OS (PORTC_OS_CNT*PORTS_OS_STEP)// ports base address#define PORTA (PORTS_OS+PORTA_OS)#define PORTB (PORTS_OS+PORTA_OS)#define PORTC (PORTS_OS+PORTA_OS)// port register offset definitions#define DIR 0x00#define DIRCLR 0x04#define DIRSET 0x08#define DIRTGL 0x0C#define OUT 0x10#define OUTCLR 0x14#define OUTSET 0x18#define OUTTGL 0x1C#define IN 0x20#define CTRL 0x24#define WRCONFIG 0x28#define EVCTRL 0x2C#define PMUX_OS 0x30#define PMUX_OS_STEP 0x01#define PINCFG_OS 0x40#define PINCFG_OS_STEP 0x01#define REGISTER(A, B) (A + B)/* PORTS_H_ */

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2019, 06:45:35 pm
content: >It's actually the chip maker that encourages multiple pin setupNot in Atmel Start-Code:#define LED GPIO(GPIO_PORTA, 0)#define SW GPIO(GPIO_PORTA, 6)#define OTHER GPIO(GPIO_PORTA, 25)// GPIO on PA00gpio_set_pin_level(LED,// <y> Initial level// <id> pad_initial_level// <false"> Low// <true"> Highfalse);// Set pin direction to outputgpio_set_pin_direction(LED, GPIO_DIRECTION_OUT);gpio_set_pin_function(LED, GPIO_PIN_FUNCTION_OFF);// GPIO on PA06// Set pin direction to inputgpio_set_pin_direction(SW, GPIO_DIRECTION_IN);gpio_set_pin_pull_mode(SW,// <y> Pull configuration// <id> pad_pull_config// <GPIO_PULL_OFF"> Off// <GPIO_PULL_UP"> Pull-up// <GPIO_PULL_DOWN"> Pull-downGPIO_PULL_OFF);gpio_set_pin_function(SW, GPIO_PIN_FUNCTION_OFF);// GPIO on PA25gpio_set_pin_direction(OTHER,// <y> Pin direction// <id> pad_direction// <GPIO_DIRECTION_OFF"> Off// <GPIO_DIRECTION_IN"> In// <GPIO_DIRECTION_OUT"> OutGPIO_DIRECTION_IN);gpio_set_pin_level(OTHER,// <y> Initial level// <id> pad_initial_level// <false"> Low// <true"> Highfalse);gpio_set_pin_pull_mode(OTHER,// <y> Pull configuration// <id> pad_pull_config// <GPIO_PULL_OFF"> Off// <GPIO_PULL_UP"> Pull-up// <GPIO_PULL_DOWN"> Pull-downGPIO_PULL_UP);gpio_set_pin_function(OTHER,// <y> Pin function// <id> pad_function// <i> Auto : use driver pinmux if signal is imported by driver, else turn off function// <GPIO_PIN_FUNCTION_OFF"> Auto// <GPIO_PIN_FUNCTION_OFF"> Off// <GPIO_PIN_FUNCTION_A"> A// <GPIO_PIN_FUNCTION_B"> B// <GPIO_PIN_FUNCTION_C"> C// <GPIO_PIN_FUNCTION_D"> D// <GPIO_PIN_FUNCTION_E"> E// <GPIO_PIN_FUNCTION_F"> F// <GPIO_PIN_FUNCTION_G"> G// <GPIO_PIN_FUNCTION_H"> H// <GPIO_PIN_FUNCTION_I"> IGPIO_PIN_FUNCTION_OFF);You can dig around for those functions somewhere in their produced code/headers but I'm just showing 'just enough' to prove they do not do 'multiple pin setup'. I'm sure they have examples somewhere that bitor a number of pins in bitmask form to set a group of pins, but I would not take what they do in Atmel Start, or any other example and turn it into 'the way its done'. Clearly they don't subscribe to a single method, and I'm not sure what method they 'encourage'.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 06:56:32 pm
content: No they would not advocate one way or the other. The available registers duplicate. On the one hand there are individual pin registers that configure the pin, but on the other hand there is a "bulk setup" register that will access several pin registers at a time. So the chips hardware is designed for both ways.

author: nctnico
author_rank: Super Contributor
timestamp: August 25, 2019, 09:02:03 pm
content: Quote from: Simon on August 25, 2019, 06:56:32 pmI've seen that on some chips as well. I'm not quite sure what the advantage is of having individual pin registers except for making sure that an operation doesn't touch a different pin. From a programming perspective I would either use the full register or the individual pin registers. Certainly not both because then you'll have two ways a peripheral can be setup. Sometimes you want to see where exactly a register gets set in the software (using a search) to hunt a bug. It is not helpful if you need to search for two different registers.

author: Simon
author_rank: Unknown
timestamp: August 25, 2019, 09:51:02 pm
content: Well I think there is the 8 bit setup register per pin and the 8 bit mux register per pin. The register that can set the whole shebang up in one go is a single 32 bit register that cannot be read. I think it is just a proxy to set the other 2 up and to do it in a batch. A bit like the set, clr and tcl registers.

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2019, 10:40:36 pm
content: >It's actually the chip maker that encourages multiple pin setup>No they would not advocate one way or the other.I thought they encouraged it, so I show they don't. Now they don't advocate one way of the other. I'm confused.>So the chips hardware is designed for both ways.It allows a way to write a group of pins in a single write (lcd 8 bit data, etc.), but that doesn't mean you have to round up all your pins in use and set them up in a single write to one or more of the port registers, which would only be used one time in an app. Once the initial pin setup is done, you are back to dealing with separate pins for reads/writes/etc. Just skip trying to corral all your pins into a group, and deal with individual pins.This is what it looks like in C++ to get a group of pins together for a single write-https://godbolt.org/z/3mbwsi(and you will still have to group the input pins and output pins separately)it will look a lot worse in C if/when you can get your macros to handle splitting out individual var args (I suspect you will need 32 levels/macros if you want to handle settings all 32 bits at once- quite a mess). Not worth it.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 09:05:08 am
content: Yes we are past all of that now and have been discussing how to address registers. At the end of the day I would only be setting up a few pins at a time with the same configuration. By configuration we are not talking: is it an input or output? that is done in a 32 bit DIR register. The configurations are about things like multiplexing pins to different functions so the scope to set them up all at once is near nil.

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 26, 2019, 10:11:20 am
content: (Have I mentioned how much I hate the #hashtags Dave added to the forums, yet? A lot.Just to un-b0rk the preprocessor directives in code snippets, I have to prepend the first line with a non-breaking space, U+00A0.Not to say the stupid "suggestions" the editor pops up, when you type a #. This is just evil.)I recently looked at SAM4S4/SAM4S2 in 48-pin LQFP, which has two GPIO ports (with max. 32 pins each), A and B. Port A offset is 0x400E0E00, and port B 0x400E1000. Other SAM4S have port C at 0x400E1200 and port D at 0x400E1400.GPIO input/output control:Each bit in PIO_OSR (offset 0x0018) corresponds to a pin. If zero, the pin is an input; otherwise, the pin is an output.Writing PIN_OER (0x0010), each set bit sets the pin to an output (setting the corresponding bit in PIO_OSR).Writing PIN_ODR (0x0014), each set bit sets the pin to an input (clearing the corresponding bit in PIO_OSR).Each bit in PIO_PSR (0x0008) determines whether a pin is a GPIO pin (set), or used for a peripheral (clear).Each bit pair in PIO_ABCDSR1 (0x0070) and PIO_ABCDSR2 (0x0074) for a pair of bits, that select between one of the peripherals for that pin, if PIO_PSR is zero for that pin.GPIO output state control:Each bit set in PIO_ODSR (0x0038) reflects the set output pin states. You can write to this register to set all (max 32) output pins to specific states.Each bit set in PIO_SODR (0x0030) sets the corresponding output pin high.Each bit set in PIO_CODR (0x0034) sets the corresponding output pin low.PIO_OWSR (0x00A8) is a read-only mask of output pins affected by writes to PIO_ODSR. It defaults to all zeros.If a bit is clear/zero, that bit in PIO_ODSR does affect the output pin state. If a bit is one, the output pin state is unchanged by writes to PIO_OWSR.You can set bits (disable PIO_ODSR from affecting the corresponding pins) by writing to PIO_OWER (0x00A0).You can clear bits (enable PIO_ODSR from affecting the corresponding pins) by writing to PIO_OWDR (0x00A4).Let's say you are using PA00..PA8 (first nine pins of port A) for some parallel communication task, and want a function that sets them, in parallel, to a specific pattern. However, some of the other pins in port A are also GPIO outputs, and you don't want to affect those.Code:#include <stdint.h>#define PIOA_ODSR_ADDR 0x400E0EA0 /* RW */#define PIOA_OWSR_ADDR 0x400E0EA8 /* R */#define PIOA_OWDR_ADDR 0x400E0EA4 /* W */#define PIOA_OWER_ADDR 0x400E0EA0 /* W */static inline void set_a00_a08(unsigned int value){uint32_t old_owsr;/* Save old mask. */old_owsr = *(volatile uint32_t *)PIOA_OWSR_ADDR;/* Enable writes to pins 0..8, disable to 9..15. */*(volatile uint16_t *)PIOA_OWDR_ADDR = 0xFE00;*(volatile uint16_t *)PIOA_OWER_ADDR = 0x01FF;/* Update output pins */*(volatile uint16_t *)PIOA_ODSR_ADDR = value;/* Restore old mask. */*(volatile uint32_t *)PIOA_OWDR_ADDR = old_owsr;*(volatile uint32_t *)PIOA_OWER_ADDR = ~old_owsr;}Note that if you have room for a look-up table of 2+2Nwords in ROM/flash, 32 bits each, you can use any set of N pins in any order of significance within a single port (A, B, C, or D). For 9 pins, that is just 2056-byte look-up table.(Why 9? Because it is one option for many LCD displays using a parallel data interface. I looked at SAM4S as a possible low-cost "blitter" for 320x240 or 480x320 18-bit color LCDs.)A variant ofdigitalWriteFast()implemented by an Arduino library and Teensyduino, that takes a pin number (using a mapping of your choice, can mix across all ports in any order), and the state it should be set to, on SAM4S can be written as:Code:#include <stdint.h>#define PIOA_CODR_ADDR 0x400E0E34 /* W */#define PIOB_CODR_ADDR 0x40010E34 /* W */#define PIOC_CODR_ADDR 0x40012E34 /* W */#define PIOD_CODR_ADDR 0x40014E34 /* W */#define PIOA_SODR_ADDR 0x400E0E30 /* W */#define PIOB_SODR_ADDR 0x400E1030 /* W */#define PIOC_SODR_ADDR 0x400E1230 /* W */#define PIOD_SODR_ADDR 0x400E1430 /* W *//* Pin port mapping, CODR and SODR addresses for the port, and the bit mask. */static const uint32_t _pin_odr = {/* { PIOx_CODR_ADDR, PIOx_SODR_ADDR, 1<<0 }, */};void digitalWriteFast(unsigned int pin_number, unsigned int state){#ifdef CHECK_ARGSif (pin_number < PIN_COUNT)*(volatile uint32_t *)(_pin_odr[!!state]) = _pin_odr;#else*(volatile uint32_t *)(_pin_odr) = _pin_odr;#endif}depending on whether you know your arguments are always safe or not. Note that this does not interfere with parallel writes through ODSR.If you wanted a function that sets a number of output pins to different states as simultaneously as possible (i.e., port A first at once, then port B pins, and so on), you can use gcc vector extensions:Code:#include <stdint.h>/* Pin number mapping. Each row has exactly one bit set. */static const uint32_t _pin_bits = {/* { port_A_bitmask, port_B_bitmask, port_C_bitmask, port_D_bitmask }, */};typedef uint32_t uint32x8 __attribute__((__vector_size__ (64)));#define SET_PIN(number) ((uint32x8){ _pin_bits, _pin_bits, _pin_bits, _pin_bits, _pin_bits, _pin_bits, _pin_bits, _pin_bits })#define CLEAR_PIN(number) ((uint32x8){ _pin_bits, _pin_bits, _pin_bits, _pin_bits, 0, 0, 0, 0 })#define PIN(number, state) ((uint32x8){ _pin_bits, _pin_bits, _pin_bits, _pin_bits, \(!!state) ? __pin_bits : 0, \(!!state) ? __pin_bits : 0, \(!!state) ? __pin_bits : 0, \(!!state) ? __pin_bits : 0 })static inline void digitalWriteFastMany(const uint32x8 config){uint32_t olda_owsr, oldb_owsr, oldc_owsr, oldd_owsr ;/* Save old masks. */olda_owsr = *(volatile uint32_t *)PIOA_OWSR_ADDR;oldb_owsr = *(volatile uint32_t *)PIOB_OWSR_ADDR;oldc_owsr = *(volatile uint32_t *)PIOC_OWSR_ADDR;oldd_owsr = *(volatile uint32_t *)PIOD_OWSR_ADDR;/* Disable writes to all pins; enable our mask pins. */*(volatile uint32_t *)PIOA_OWDR_ADDR = ~(uint32_t)0;*(volatile uint32_t *)PIOA_OWDR_ADDR = config;*(volatile uint32_t *)PIOB_OWDR_ADDR = ~(uint32_t)0;*(volatile uint32_t *)PIOB_OWDR_ADDR = config;*(volatile uint32_t *)PIOC_OWDR_ADDR = ~(uint32_t)0;*(volatile uint32_t *)PIOC_OWDR_ADDR = config;*(volatile uint32_t *)PIOD_OWDR_ADDR = ~(uint32_t)0;*(volatile uint32_t *)PIOD_OWDR_ADDR = config;/* Update output pins */*(volatile uint32_t *)PIOA_ODSR_ADDR = config;*(volatile uint32_t *)PIOB_ODSR_ADDR = config;*(volatile uint32_t *)PIOC_ODSR_ADDR = config;*(volatile uint32_t *)PIOD_ODSR_ADDR = config;/* Restore old masks. */*(volatile uint32_t *)PIOA_OWDR_ADDR = olda_owsr;*(volatile uint32_t *)PIOA_OWER_ADDR = ~olda_owsr;*(volatile uint32_t *)PIOB_OWDR_ADDR = oldb_owsr;*(volatile uint32_t *)PIOB_OWER_ADDR = ~oldb_owsr;*(volatile uint32_t *)PIOC_OWDR_ADDR = oldc_owsr;*(volatile uint32_t *)PIOC_OWER_ADDR = ~oldc_owsr;*(volatile uint32_t *)PIOD_OWDR_ADDR = oldd_owsr;*(volatile uint32_t *)PIOD_OWER_ADDR = ~oldd_owsr;}In this case, you could clear pin 15 and set pin 11 usingdigitalWriteFastMany(PIN(15,0) | PIN(11,1))ordigitalWriteFastMany(CLEAR_PIN(15) | SET_PIN(11)). This works, because of the gcc support for the vector attribute and trivial arithmetic operations on them.While the digitalWriteFastMany() code is not optimal, it does what it says on the tin (except a possible latency, loading theconfig[4..7]value between updates to port B, C, and D). To avoid that, one could easily write it in gcc inline assembly for each particular instruction set, minimizing the latency between port changes.The above shows that you usuallydo not wanta variable number of arguments, and can instead use alternate ways to define multiple items at once.Let's say you want a function that modifies output pins sequentially, and these sequences can be arbitrarily long. C strings come to mind here:Code:/* Maximum number of pins = 127.HH = PIN_RESERVED ^ ((bit & 31) + 32*(port) + 128*(!state))where PIN_RESERVED = ((bit & 31) + 32*(port) + 128*(!state)) of an unused pin,and bit = 0..31, state = 0 or 1, and port = 0..3.*/#define PIN_RESERVED 0xB7 /* Port B pin 23 cannot be cleared. */#define PIN_1_SET "\xB7" /* Port A pin 0 */#define PIN_2_SET "\xB6" /* Port A pin 1 */#define PIN_3_SET "\xF2" /* Port C pin 5 *//* : */#define PIN_1_CLEAR "\x37" /* Port A pin 0 */#define PIN_2_CLEAR "\x36" /* Port A pin 1 */#define PIN_3_CLEAR "\x72" /* Port C pin 5 */static inline void pin_sequence(const unsigned char *pins){while (*pins) {const uint32_t p = (*pins++) ^ PIN_RESERVED;/* Note: ((p>>5)&3)*32 == p & 0x60, and ((p >> 7) & 1)*4 == (p & 0x80) >> 5. */*(volatile uint32_t *)(0x400E0E30 + (p & 0x60) + ((p & 0x80) >> 5)) = ((uint32_t)1) << (p & 31);}}As you can see, there are many interface approaches to choose from. Picking one depends on exactly what you want to do; the above are just examples for SAM4S.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 10:40:22 am
content: I think todays job is to thoroughly understand pointers.

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 26, 2019, 11:23:23 am
content: Looking at the SAMC20/C21 datasheet, it looks very similar to another Cortex-M0+ I know, Teensy LC.For each port,OUT (0x10) reflects the intended output state of the pins.writing a value to OUTCLR (0x14) clears the pins corresponding to set bits.writing a value to OUTSET (0x18) sets the pins corresponding to set bits.writing a value to OUTTGL (0x1C) toggles the pins corresponding to set bits.For these, one might wish to use an interface similar to sayCode:#define PORTA_OUT_ADDR 0x41000010#define PORTA_OUTCLR_ADDR 0x41000014#define PORTA_OUTSET_ADDR 0x41000018#define PORTA_OUTTGL_ADDR 0x4100001C#define PORTA_OUT_ADDR 0x41000090#define PORTA_OUTCLR_ADDR 0x41000094#define PORTA_OUTSET_ADDR 0x41000098#define PORTA_OUTTGL_ADDR 0x4100009Ctypedef uint32_t uint32x2 __attribute__((__vector_size__ (8)));#define PA0 ((uint32x2){ ((uint32_t)1)<<0, 0 })#define PA1 ((uint32x2){ ((uint32_t)1)<<1, 0 })#define PA2 ((uint32x2){ ((uint32_t)1)<<2, 0 })#define PA30 ((uint32x2){ ((uint32_t)1)<<30, 0 })#define PA31 ((uint32x2){ ((uint32_t)1)<<31, 0 })#define PB0 ((uint32x2){ 0, ((uint32_t)1)<<0 })#define PB1 ((uint32x2){ 0, ((uint32_t)1)<<1 })#define PB2 ((uint32x2){ 0, ((uint32_t)1)<<2 })#define PB30 ((uint32x2){ 0, ((uint32_t)1)<<30 })#define PB31 ((uint32x2){ 0, ((uint32_t)1)<<31 })/* Numeric pin mapping for digitalWriteFast() */static const uint32x2 pin_mask = {PA0, PA1, PA4, PB6,};static void clear_pins(const uint32x2 pins){*(volatile uint32_t *)PORTA_OUTCLR = pins;*(volatile uint32_t *)PORTB_OUTCLR = pins;}static void set_pins(const uint32x2 pins){*(volatile uint32_t *)PORTA_OUTSET = pins;*(volatile uint32_t *)PORTB_OUTSET = pins;}static void toggle_pins(const uint32x2 pins){*(volatile uint32_t *)PORTA_OUTTGL = pins;*(volatile uint32_t *)PORTB_OUTTGL = pins;}static void digitalWriteFast(const unsigned int pin, const unsigned int state){if (pin < PIN_COUNT) {if (state)set_pins(pin_mask);elseclear_pins(pin_mask);}}static void digitalToggleFast(const unsigned int pin){if (pin < PIN_COUNT)toggle_pins(pin_mask);}Above,digitalWriteFast(pin_number, state)provides the expected interface.However, you can set/clear/toggle any set of pins, say PA1 and PB3, usingset_pins(PA1 | PB3). (For just two ports, as on SAMC20/C21, you could useuint64_tinstead ofuint32x2vector, if you wish to support non-gcc compilers; just use the upper 32 bits for port B, and lower 32 for port A.)While there is no register to set a set of pins at once, you can usetoggle_pins()to update any set of output pins in a sequence. If you have the current state of those output pins incurr, and the next state innext, all you need to do istoggle_pins(curr ^ next); curr = next;.(This may feel a bit magical at first, but it does work.)Again, if you have an N-bit parallel bus in one of the ports, you only need 4+22+Nbytes (1+N uint32_t's) in a lookup table in ROM or Flash, and you can use whichever pins in that port in whatever order you want; you just donext=lookup(next_value);to map the value to the bit pattern. Oh, and you do need to set the very first bit pattern in the sequence using OUTSET and OUTCLR.

author: sokoloff
author_rank: Super Contributor
timestamp: August 26, 2019, 11:37:25 am
content: Quote from: Simon on August 26, 2019, 10:40:22 am"Today" . BWHAHAHAAHAHAHA.Thoroughly understanding pointers is a months' long, coding while learning, minimum endeavor. (Once you do, you'll probably wonder what was so hard about it, but until you do, it's not a "long afternoon" kind of learning project...)

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 26, 2019, 11:38:44 am
content: Quote from: Simon on August 26, 2019, 10:40:22 amRead pointer specifications from right to left, tokenized by*, reading each * as"a pointer to".This means if you seestatic volatile uint32_t *ptr;you read it as"ptr is a pointer to static and volatile uint32_t".In a variable declaration,staticmeans the variable is only visible in the current compilation unitvolatilemeans the value can change without explicit access, so the compiler may not cache itconstmeans immutable, not modified by the C codeIn other words, the above declaration can also be read as"ptr is a pointer to an unsigned 32-bit integer. The pointer may change at any point in time, so the compiler must not cache its value. ptr is only visible in the current compilation unit".Current compilation unit means the main source file and any files it includes. If you flatten the source (include the include files, so you get one stream of source code), each declaration is visible only forwards. A declaration does not define a value or body, it just specifies what that name is; a definition is when you attach a function body or set a value to that name.The most complicated examples you might see are stuff likeint *volatile *const ptr = (int *volatile *const)0xDEADBEEF;which reads as"ptr is an immutable pointer (to 0xDEADBEEF) to a volatile pointer to an int".This means that the value ofptris immutable (will not be changed by the C code). If points to a pointer at memory address0xDEADBEEF. The contents of that pointer (at that memory address) can change without the compiler seeing any reason for it, so it isvolatile. That pointer points to a (signed) integer. The value of that integer is**ptr; in machine code, this involves loading the pointer value from 0xDEADBEEF, and then loading the int from wherever that pointer points to.Note thatconst volatileis valid, too. It means the C code won't change it, but its value may change by some other magic, so the compiler must not cache its value.Pointer arithmetic will take a second afternoon, though.Hope this helps

author: obiwanjacobi
author_rank: Super Contributor
timestamp: August 26, 2019, 11:43:50 am
content: I would seriously consider making distinct dedicated functions for each use case of the parameter combinations.This would be overloading in C++ and can be done in C - you just have to make each function name unique. Internally (privately) you can route to reusable function, but it makes you API a lot more revealing.PS: You could exercise you power as a global admin and move this thread to programming... (hint)

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 11:45:02 am
content: Quote from: sokoloff on August 26, 2019, 11:37:25 amWell thanks for reassuring me that this indeed a complex subject and that I am not as thick as I thought. The main problem I have is that every book is written from a PC standpoint not an embedded standpoint so the concepts are not always explained from an embedded point of view. I guess pointers may be the slight exception as they are about dealing with memory space directly which is a universal concept. but I still am yet to see a book give examples of pointing like those i see in some embedded code starting with the header files of the chip.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 11:51:11 am
content: Quote from: obiwanjacobi on August 26, 2019, 11:43:50 amWe have a section for that ? lost track with the forums that Dave created and then had to remove because the idiots complained about not having this mess in "microcontrollers and FPGA's" where every architecture and programming language under the sun gets discussed.

author: obiwanjacobi
author_rank: Super Contributor
timestamp: August 26, 2019, 12:07:32 pm
content: Quote from: Simon on August 26, 2019, 11:51:11 amhttps://www.eevblog.com/forum/programming/

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 04:39:53 pm
content: So I cannot directly address memory (registers). i cannot write a "memory address = value" to write to a register.

author: oPossum
author_rank: Super Contributor
timestamp: August 26, 2019, 04:48:22 pm
content: So is that a question.*(uint32_t *)0x1234 = 0x5678;

author: sokoloff
author_rank: Super Contributor
timestamp: August 26, 2019, 04:49:00 pm
content: You are the master of incompletely specified questions/comments...If you want to write a byte "value" to the memory address PORTC_OS, you could do something like:Code:unsigned char value = 0x80; // Or whatever value you wantunsigned char *port_C_OS = (unsigned char *) PORTC_OS;*port_C_OS = value;

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:02:15 pm
content: OK, i am confused here. My defines are clashing with the ones already loaded and if i don't load them nothing works. But how the hell do i use the existing definitions?If I type "PORT" I get that as a suggestion frommicrochips defines and it's equivalent to the address that PORTA starts at. Great, that's no use to me. What is the chip maker actually giving me? other than no instructions as to what they are they expect me to use their own code generator. Looking at the header files that I can find no register is explicitly pointed to with a variable. So what the hell am I doing? This is so stupid. If the manufacturer wants to supply existing headers why not document them for the slighly less giften in unravelling files and files linked together.Unless I rewrite everything I can't even program the thing.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:12:33 pm
content: So for example:Code:#define REG_PORT_DIR0 (*(RwReg *)0x41000000UL) /**< \brief (PORT) Data Direction 0 */#define REG_PORT_DIRCLR0 (*(RwReg *)0x41000004UL) /**< \brief (PORT) Data Direction Clear 0 */#define REG_PORT_DIRSET0 (*(RwReg *)0x41000008UL) /**< \brief (PORT) Data Direction Set 0 */#define REG_PORT_DIRTGL0 (*(RwReg *)0x4100000CUL) /**< \brief (PORT) Data Direction Toggle 0 */#define REG_PORT_OUT0 (*(RwReg *)0x41000010UL) /**< \brief (PORT) Data Output Value 0 */#define REG_PORT_OUTCLR0 (*(RwReg *)0x41000014UL) /**< \brief (PORT) Data Output Value Clear 0 */#define REG_PORT_OUTSET0 (*(RwReg *)0x41000018UL) /**< \brief (PORT) Data Output Value Set 0 */#define REG_PORT_OUTTGL0 (*(RwReg *)0x4100001CUL) /**< \brief (PORT) Data Output Value Toggle 0 */#define REG_PORT_IN0 (*(RoReg *)0x41000020UL) /**< \brief (PORT) Data Input Value 0 */#define REG_PORT_CTRL0 (*(RwReg *)0x41000024UL) /**< \brief (PORT) Control 0 */#define REG_PORT_WRCONFIG0 (*(WoReg *)0x41000028UL) /**< \brief (PORT) Write Configuration 0 */#define REG_PORT_EVCTRL0 (*(RwReg *)0x4100002CUL) /**< \brief (PORT) Event Input Control 0 */#define REG_PORT_PMUX0 (*(RwReg *)0x41000030UL) /**< \brief (PORT) Peripheral Multiplexing 0 */#define REG_PORT_PINCFG0 (*(RwReg *)0x41000040UL) /**< \brief (PORT) Pin Configuration 0 */#define REG_PORT_DIR1 (*(RwReg *)0x41000080UL) /**< \brief (PORT) Data Direction 1 */#define REG_PORT_DIRCLR1 (*(RwReg *)0x41000084UL) /**< \brief (PORT) Data Direction Clear 1 */#define REG_PORT_DIRSET1 (*(RwReg *)0x41000088UL) /**< \brief (PORT) Data Direction Set 1 */#define REG_PORT_DIRTGL1 (*(RwReg *)0x4100008CUL) /**< \brief (PORT) Data Direction Toggle 1 */#define REG_PORT_OUT1 (*(RwReg *)0x41000090UL) /**< \brief (PORT) Data Output Value 1 */#define REG_PORT_OUTCLR1 (*(RwReg *)0x41000094UL) /**< \brief (PORT) Data Output Value Clear 1 */#define REG_PORT_OUTSET1 (*(RwReg *)0x41000098UL) /**< \brief (PORT) Data Output Value Set 1 */#define REG_PORT_OUTTGL1 (*(RwReg *)0x4100009CUL) /**< \brief (PORT) Data Output Value Toggle 1 */#define REG_PORT_IN1 (*(RoReg *)0x410000A0UL) /**< \brief (PORT) Data Input Value 1 */#define REG_PORT_CTRL1 (*(RwReg *)0x410000A4UL) /**< \brief (PORT) Control 1 */#define REG_PORT_WRCONFIG1 (*(WoReg *)0x410000A8UL) /**< \brief (PORT) Write Configuration 1 */#define REG_PORT_EVCTRL1 (*(RwReg *)0x410000ACUL) /**< \brief (PORT) Event Input Control 1 */#define REG_PORT_PMUX1 (*(RwReg *)0x410000B0UL) /**< \brief (PORT) Peripheral Multiplexing 1 */#define REG_PORT_PINCFG1 (*(RwReg *)0x410000C0UL) /**< \brief (PORT) Pin Configuration 1 */So each port register is defined with a name and this seems to be the closest thing to what microchip provides for me to use out of the box.Ports have now been renamed 1 and 2 instead of A and B, why not just do that, write all of the documentation in terms of port numbers instead of letters

author: cv007
author_rank: Frequent Contributor
timestamp: August 26, 2019, 05:14:23 pm
content: This thread is hard to nail down, in more ways than one.>But how the hell do i use the existing definitions?You didn't like my little example of using the headers?https://www.eevblog.com/forum/programming/creating-a-function-but-not-always-using-all-the-arguments/msg2642181/#msg2642181You should just explain how you want your code to look- not how to implement. Once you decide what you want it to look like in use, then implementation becomes easier.int main(){//I have 2 leds as outputs, 2 switches as input with pullups, this is how I would do it//forget that its not implemented, just how its going to be used when its all done//fill in what you want pin setup code to look like here...}

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:20:35 pm
content: Quote from: cv007 on August 26, 2019, 05:14:23 pmIt's my usual brain storm when i try to use a new chip and the toss up between trying to do it my own way that i understand or work out how to drive the manufacturers stuff around without it getting in the way. Yes the topic has shifted somewhat. At the moment i can't even care what my code will look like as i am wrestling with how the manufacturer has defined things ani can i throw out anything that clashes as I'm a little worried about how much of the predefined stuff is for manual coding and how much is for the atmel start bull.The Mega 0 series was hard work at the start until I realized how they had done the headers and it all made sense:peripery instance followed by instance number . register, plain and simple. But what i see here does not look like it was made for human consumption but atmel start. If i try to redefine things i could just make matters worse.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:24:07 pm
content: Quote from: oPossum on August 26, 2019, 04:48:22 pmRight, so that is the basis of naming a register and a value to put into it? Great. OK, I can see it uses pointers which i now gather is the only way C will let me access memory addresses. But that looks nothing like what several books on C for PC have to say about pointers. i can of course just take that as gospel and use it to address anf memory location (register) i need to but in makes no sense to me and sooner or later I will clash with a vendor definition. If i remove the vendor headers everything falls apart. I seem to be stuck in the middle.

author: cv007
author_rank: Frequent Contributor
timestamp: August 26, 2019, 05:28:13 pm
content: you need to ignore stuff inside-#if (defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))(but there could be an #else buried in the middle, so watch for it)which is what you posted a few posts backin include/yourdevice.h, the port address is defined as a pointer to a Port struct#define PORT ((Port *)0x41004400UL) /**< \brief (PORT) APB Base Address */#define PORT_IOBUS ((Port *)0x60000000UL) /**< \brief (PORT) IOBUS Base Address */in include/component/port.h-you will find all the structs, starting at the bottom of the file will be the outermost structin include/pio/yourdevice.hwill be pin number defines

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:31:36 pm
content: Quote from: cv007 on August 26, 2019, 05:28:13 pmYes so it says if using assembly use this lot otherwise use: what I posted which I assume is being used for C and sets up a define for each register. The naming is a bit weired but it's the best i have so far.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:33:32 pm
content: Port.h only has defines, no structs. I'll have a look for them. This is half my concern, this stuff is all over the place. What happens if we have to move to MAPLABX will everything still work?

author: sokoloff
author_rank: Super Contributor
timestamp: August 26, 2019, 05:36:53 pm
content: Quote from: Simon on August 26, 2019, 05:12:33 pmThat macro definition is designed to make REG_PORT_DIR0 "look like" a variable. It contains the definition of the pointer:Code:(RwReg *)0x41000000ULand the dereferencing of the pointer:Code:(* <the pointer as above> )so to assign an unsigned 32 bit value to it (the RwReg is defined as uint32_t), you just write:Code:REG_PORT_DIR0 = <the value you want to assign>

author: sokoloff
author_rank: Super Contributor
timestamp: August 26, 2019, 05:38:23 pm
content: which ends up functioning exactly like the code @oPossum wrote above. (I'm saying this just to help you understand pointers thoroughly "this afternoon")Code:(*(RwReg *)0x41000000UL) = <the value>;

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 05:43:51 pm
content: so why does this asterisk keep moving around, the books put it before the variable that will be replaced with the address of what it points to yet all of this register stuff puts it after something follewed by a parenthisis. Is this declaration and assicnment all in one?

author: sokoloff
author_rank: Super Contributor
timestamp: August 26, 2019, 05:50:10 pm
content: I'll take a stab at it, but it's not going to be second-nature for a while.In the expression:(*(RwReg *)0x41000000UL) =<the value>;I think of it this way:Take anunsignedlongnumber 0x41000000Treat is as apointer to an RwReg.Then, take thatpointerand assignthe location to which it pointsthe value.

author: cv007
author_rank: Frequent Contributor
timestamp: August 26, 2019, 05:53:43 pm
content: an attempt at a simple explanation-//include/yourdevice.h#define PORT ((Port *)0x41004400UL)//include/pio/yourdevice.h#define PIN_PA05 5#define PORT_PA05 (_UL_(1) << 5)//include/component/port.htypedef struct {PortGroup Group; //this has only one port (port A)} Port;typedef struct {__IO PORT_DIR_Type DIR;__IO PORT_DIRCLR_Type DIRCLR;__IO PORT_DIRSET_Type DIRSET;__IO PORT_DIRTGL_Type DIRTGL;__IO PORT_OUT_Type OUT;__IO PORT_OUTCLR_Type OUTCLR;__IO PORT_OUTSET_Type OUTSET;__IO PORT_OUTTGL_Type OUTTGL;__I PORT_IN_Type IN;__IO PORT_CTRL_Type CTRL;__O PORT_WRCONFIG_Type WRCONFIG;RoReg8 Reserved1;__IO PORT_PMUX_Type PMUX;__IO PORT_PINCFG_Type PINCFG;RoReg8 Reserved2;} PortGroup;//etc, other structs defined above this onePORT->Group will be the struct of the first PORT (port A)PORT->Group will be the struct of the second PORT (port B) if you had oneto set a pin PA05 as output-without ->(*PORT).Group.DIRSET.reg = 1<<PIN_PA05; //pin numberor with -> as its intended to make it simpler to usePORT->Group.DIRSET.reg = 1<<PIN_PA05; //pin numberorPORT->Group.DIRSET.reg = PORT_PA05; //this is a bitmask, but gives no info on what port it belongs toor if you have more than port A, use the bit to figure out the portas PIN_PxXX will be consecutive (PIN_PB02 is defined as 34)PORT-Group[PIN_PB05/32].DIRSET.reg = 1<<(PIN_PB05%32);

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 06:00:41 pm
content: Right so (RwReg *)0x41000000UL is making the memory location appear to be a pointed to variable(*(RwReg *)0x41000000UL) = <the value>; puts the value in the address pointed to. But the confusing bit is that there is no variable name. i think that is what makes it look so different from standard pointers that don't explicitly control the address they only point to whatever address the compiler gave the variable.

author: Nominal Animal
author_rank: Super Contributor
timestamp: August 26, 2019, 07:05:58 pm
content: Let's examine the macro definition:#define REG_PORT_DIR0 (*(RwReg *)0x41000000uL)If may be easier to understand if we split it in three parts:#define REG_PORT_DIR0_ADDR 0x41000000uL#define REG_PORT_DIR0_PTR ((RwReg *)REG_PORT_DIR0_ADDR)#define REG_PORT_DIR0 (*REG_PORT_DIR0_PTR)So, the key here is that the two asterisks have different meanings.In C, expressions of form(type)value, or equivalently(type)(value), arecasts. They convert the type of thevaluetotype. Above,0x41000000uLisvalue, andRwReg *istype, a pointer to RwReg.TheREG_PORT_DIR0macro dereferences the RwReg pointer to 0x41000000uL. So, in the original macro, the first asterisk dereferences a pointer, whereas the second asterisk is part of a pointer type specification, as part of a cast from integer address to an RwReg pointer.Most likely, somewhere in the header files you'll see a typedef similar totypedef volatile unsigned long RwReg;which means thatREG_PORT_DIR0 = 5;is effectively the same as*((volatile unsigned long *)0x41000000uL) = 5;which is effectively the same as((volatile unsigned long *)0x41000000uL) = 5;i.e, the part in parentheses defines a pointer to a volatile unsigned long, and that pointer is immediately dereferenced without being assigned to any variable; it is how you use "pointer literals" in C.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 07:37:08 pm
content: Thank you, that makes more sense.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 08:13:42 pm
content: Well i have a square wave on the screen of my scope from toggling every pin of both ports. Just a bit mystifyed as to why all of port A does not seem to work.So I am back to square one, writing directly to register memory locations is easy, but anf definitions i write are bound to clash with those already present. If i try to remove the monster of header files that are automatically attched to the project nothing will work any more.

author: Simon
author_rank: Unknown
timestamp: August 26, 2019, 08:50:25 pm
content: aha, removed their setup code and my defines seem to work now and no errors. Porta still does not work. i am getting a wavefom through but it's like when you set an output to toggle but forget to set the pin direction up and the high impedance of the scope just manages to see the waveform anyway.