Accessing a structure with a Macro

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I didnt know where to post this thread as its a programming question

 

I am working with a TI processor evaluation board the ezdsp 28335

 

Its got a lot of stuff on there and theres about 95 pins

 

I am happy enough, I can setup the pins and make it do things  but I would like to make it a bit easier to keep track of for example

 

To setup pins there are six mux registers and three direction registers

 

	 //GPAMUX1 for functionality GPIO0-GPIO15
         //GPAMUX2 for functionality GPIO16-GPIO31
	 //GPBMUX1 for functionality GPIO32-GPIO47
	 //GPBMUX2 for functionality GPIO48-GPIO63
	 //GPCMUX1 for functionality GPIO64-GPIO79
         //GPCMUX2 for functionality GPIO80-GPIO95


	 //GPADIR for GPIO00-GPIO31
	 //GPBDIR for GPIO32-GPIO63
	 //GPCDIR for GPIO64-GPIO95

 

 

 

   

       // Each GPIO pin can be:
       // a) a GPIO input/output
       // b) peripheral function 1
       // c) peripheral function 2
       // d) peripheral function 3
       // By default, all are GPIO Inputs

      //  Mux bits : 00 for GPIO mode : 01 for function 1: 10 for function 2: 11 for function 3 // see table 52, 53, 54 and 55 in SPRUFB0D located here http://www.ti.com.cn/cn/lit/ug/sprufb0d/sprufb0d.pdf
      //  Dir bits :0 for input :1 for output

 

There are structures to setup the pins so if I type

 

           GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 0;
           GpioCtrlRegs.GPBDIR.bit.GPIO33 = 1;

Then GPIO33 is set in GPIO mode as output

 

and

 

GpioDataRegs.GPBTOGGLE.bit.GPIO33 = 1

Toggles the pin

 

GpioDataRegs.GPBSET.bit.GPIO33 = 1

Sets the pin

 

GpioDataRegs.GPBCLEAR.bit.GPIO33 = 1

Clears the pin

 

All is well and good

 

Now what I want is a nice way to be able to swap connections around and setup pins how I want them but with all these structures I just simply don't know how to do it

 

Now lets imagine I am using an LCD display I can easily use a macro to set the pin high or low so thats not a problem

 

#define LCD_RS_HIGH      (GpioDataRegs.GPBSET.bit.GPIO33 = 1)     

But how can I use a function to setup the outputs?

 

On an AVR I like this

 

/******************************  HD44780 Port Connections ********************************/

  #define LCD_RS 0    // pin for LCD R/S    PB0
  #define LCD_E  7    // pin for LCD enable PD7
  #define DAT4   6    // pin for d4         PB6
  #define DAT5   7    // pin for d5         PB7
  #define DAT6   5    // pin for d6         PD5
  #define DAT7   6    // pin for d7         PD6

  #define LCD_RS_PORT    PORTB
  #define LCD_E_PORT     PORTD
  #define DAT4_PORT      PORTB
  #define DAT5_PORT      PORTB
  #define DAT6_PORT      PORTD
  #define DAT7_PORT      PORTD

  #define LCD_RS_HIGH    LCD_RS_PORT |= (1<<LCD_RS)
  #define LCD_RS_LOW     LCD_RS_PORT &=~(1<<LCD_RS)
  #define LCD_E_HIGH     LCD_E_PORT  |= (1<<LCD_E)
  #define LCD_E_LOW      LCD_E_PORT  &=~(1<<LCD_E)
  #define SET_DAT4       DAT4_PORT   |= (1<<DAT4)
  #define SET_DAT5       DAT5_PORT   |= (1<<DAT5)
  #define SET_DAT6       DAT6_PORT   |= (1<<DAT6)
  #define SET_DAT7       DAT7_PORT   |= (1<<DAT7)
  /******************************************************************************************/

I define the pin and the port and the macro takes care of the rest

 

What I was wanting to do was write a function, I pass the pin and the mode but I am running into a massive lack of knowledge

 

Let me show you the nonesense I am thinking!

#define RS          GPIO33

#define OUTPUT 1

#define INPUT    0

#define IO          0

#define FCN_1   1

#define FCN_2   2

#define FCN_3   4



Setup_GPIO(Uint16 gpio_pin, Uint16 mode, Uint16 direction)

{

           GpioCtrlRegs.GPBMUX1.bit.gpio_pin = mode;
           GpioCtrlRegs.GPBDIR.bit.gpio_pin = direction;

}

Obviously its not going to work not least because GPIO33 isnt a Uint16 but it demonstrates what I am trying to do and this has got me really wondering how to do this

 

There are a lot of declarations and stuff that blows my mind this is way over my head and I would appreciate any input

 

 

Last Edited: Mon. Jan 5, 2015 - 05:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use the concatenation operator ("##"), however you would do it before calling the function since you would need to pass the macro a compile time string, not a variable. What you are ending up doing with your function is to decide at run time what port to use which is something very rarely needed.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't read the post but I can tell something is wrong from the thread title. A "macro" is an entity within the C preprocessor. It knows nothing about the syntax of C. It's effectively it's own independent (and very simple) string processing language. At the end of the day it just replaces one piece of text with another. As such it knows nothing about a "struct" or any other C concept.

 

A lot of people seem to have a problem seeing this division between the preprocessor and the C language compiler itself leading to a lot of confusion.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't read the post but I can tell something is wrong from the thread title.

 Something is wrong from the post (IMO)...

I am working with a TI processor evaluation board the ezdsp 28335...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use the concatenation operator ("##"), however you would do it before calling the function since you would need to pass the macro a compile time string, not a variable. What you are ending up doing with your function is to decide at run time what port to use which is something very rarely needed.

 Its funny because I am a sucker for wasting time and this is the exact type of thing that gets me every single time, I have a way of initialising everything the same way everyone else does but IMO its not very nice to keep track of lots of pins and spotting stupid mistakes becomes hard for me

 

I was looking at that ## operator today and I tried a lot before posting, I learned a bit too but I was unsuccessful

 

There will be a great way to do it I am sure but its way above my current level of knowledge

 

I haven't read the post

I am wounded!, Cliff you are the best C programmer I know of and I really wanted to see how you especially would do this, like how could you write an Arduino style function for noobs like me to use!

 

I do know the preprocessor is a separate thing, obviously its me here so I do not fully understand all of the implications of that but that is something I do know and I think its amazing the neat little tricks that can be achieved giving great flexibility so its high on the list of things to gain experience in

 

 Something is wrong from the post (IMO)...

 Lol

 

Its university issue and if this was a choice I made or if I was a quitter than I would of binned it ages ago as it has been the worst experience ever just getting it to work on Windoze 8

 

Do you know this board costs about £360, which is the cost of a laptop and it just isn't value for money by any measure

 

But my university use it a lot and there is some very impressive hardware on offer that interfaces with the board as well as lots of software and support so its all a very good area for me to learn things, the hardware I have been given would take me a lot of time to develop and it would never be as good so here I am!!!

 

I think to do what I want to do and for me to be able to easily follow it just isn't going to happen so I should move on to something more productive

 

 

Regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But my university use it a lot and there is some very impressive hardware on offer that interfaces with the board as well as lots of software and support so its all a very good area for me to learn things, the hardware I have been given would take me a lot of time to develop and it would never be as good so here I am!!!

 

Then explore on a TI forum.  Moderator?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Then explore on a TI forum.  Moderator?

 

?

 

TI sites suck, there simply isn't a site like AVR freaks for the full all round experience

 

The question is purely a C programming question (is it not?) I seek no knowledge about TI

 

IMO this site has a general electronics section thats very good I think a general programming section would be equally as good

Last Edited: Mon. Jan 5, 2015 - 05:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am wounded!, Cliff you are the best C programmer I know of and I really wanted to see how you especially would do this, like how could you write an Arduino style function for noobs like me to use!

Do it the same as AVR then;-)

 

On an AVR I can do something like this (just typing from memory - no easy way to check this even compiles right now!):
 

typedef struct {
 uint8_t gpio0:1;
 uint8_t gpio1:1;
 uint8_t gpio2:1;
 uint8_t gpio3:1;
 uint8_t gpio4:1;
 uint8_t gpio5:1;
 uint8_t gpio6:1;
 uint8_t gpio7:1;
} bits_t;

#define GPBDIR (*(bits_t *)0x1234)

...
    GPDIR.gpio3 = 1;

I think that when you explore the headers for the chip you are using it's going to be something like that (clearly they have two levels - one to define "GPIO" then one to define "GPIODIR" within that - so they nest the structs).

 

In fact if you explore the Xmega headers for AVR you will find something almost identical but in the Atmel case they defined everything "both ways" so as well as casting bitfields onto registers they'd also have the equvalent of just GPIO_GPIODIR as well as GPIO.GPIODIR where the former is just a definition of that 1234 as a pointer to volatile uint8_t.

 

So you should be able to work this backwards for the TI. Obviously thy'll be uint32_t not uint8_t registers but the idea holds. Just define something like:

#defined GpioDir (*(volatile uint32_t *)0x1234)

And then you can use:

GpioDir |= (1 << 3);

in the "old fashioned way" as an altrernative to the struct bit access method of:

    GPDIR.gpio3 = 1;

But as I say I have no easy easy way to test this and so I'm sure someone will be along in a minute to point out my typing/syntax errors. However the idea is sound.

 

BTW can't this £360 EZDSP thing run Linux? If so you wouldn't usually be bothering with direct register access (that's in the Linux BSP) but would just be writing to something like /sys/gpio/33 or perhaps an ioctl() on /dev/gpio of similar which abstracts all this away to a higher level. Of course if it ran Linux there'd probably be a /dev/lcd or perhaps even just /dev/fb0 mapped to an LCD and you wouldn't need to write a driver anyway!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a lot to be said for specific structures and named members.

 

The C compiler can detect improper access or typos.

 

The 'traditional' AVR method of SFR |= (1<<BIT_NAME); will quite happily permit UBBR0L=(1<<ADSC)

and we often see TCCR1A being initialised with BITs that belong to TCCR1B.

 

All the same,    macros are excellent if they make code clearer.    OTOH,   complex macros can just obfuscate things.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the problem here is that you are trying to set up something very generic, then use it for something specific with the expectation that that will simplify things. It won't. Creating the structs is fine, but assigning a function to a pin would be something like this:

#define RS_BIT_DIR GpioCtrlRegs.GPBDIR.bit.gpio33
...
RS_BIT_DIR = direction

 

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a lot to be said for specific structures and named members.

Yeah but (to coin a phrase) ,OP is looking for an easy wiring mechanism as in:

  #define LCD_RS 0    // pin for LCD R/S    PB0
  #define LCD_E  7    // pin for LCD enable PD7
  #define DAT4   6    // pin for d4         PB6
  #define DAT5   7    // pin for d5         PB7
  #define DAT6   5    // pin for d6         PD5
  #define DAT7   6    // pin for d7         PD6

  #define LCD_RS_PORT    PORTB
  #define LCD_E_PORT     PORTD
  #define DAT4_PORT      PORTB
  #define DAT5_PORT      PORTB
  #define DAT6_PORT      PORTD
  #define DAT7_PORT      PORTD

(that's got two signals on pin 6 by the way!!)

 

When the pin numbers are embedded in foo.bar.pin33 then it's quite tricky to do the "#define RW 33" kind of thing. While (1 << 33) is fairly easy to achieve. So this kind of argues against named pin numbers in a bitfield struct. But "(uint32_t *)&foo.bar |= (1<<33)" kind of thing should be possible in some way? I guess I'm saying that "#define Foo.Bar ((uint32_t *)&foo.bar)" and then "Foo.Bar |= (1 << 33)" which can then have a "#define LCD_RW (1 << 33)" added on may be an approach to save some convoluted syntax/typing.

 

To be honest though, I often wonder about the merit of all this - exactly how often do you change LCD_RW to pin 37 from pin 33 anyway? I am always tempted to just write stuff the "easy way" then, later, if I think there's a possibility of 33 moving to 37 to then macroize out the 33's in it. As Steve said in the first reply ## (also # to stringify) can help a lot.

Last Edited: Mon. Jan 5, 2015 - 05:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree 100%.   

It is often the setup() of a Peripheral that is complex and fiddly e.g. lcd_init(A, B, C, D, ....).   

The subsequent calls inside loop() are trivial e.g. lcd_putchar(c).

 

Incidentally,  the C++ constructor() generally does all your initialisation all in one statement.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think that when you explore the headers for the chip you are using it's going to be something like that

They are almost exactly like that

 

BTW can't this £360 EZDSP thing run Linux?

No I don't think so, you tell me!

http://uk.farnell.com/spectrum-d...

 

Its down to £349 so snap them up guys!

 

like I said it isn't value for money by any measure, but it does have some very good features and it is very good there is no doubt about it

 

assigning a function to a pin would be something like this:

Its going to be better as it is I think so I will just leave this

 

that's got two signals on pin 6 by the way!!

Its PD6 and PB6, the same for PD7 and PD7, it does look dodgy looking at it though but it was just an example

 

kind of thing should be possible in some way?

Cliff I really appreciate you effort here but being totally 100% honest you lost me at

 

#defined GpioDir (*(volatile uint32_t *)0x1234)

That line does not make any sense to me, (pointer to a pointer!?)  I have a long way to go and I will get there for now I will stick with what I know and build on that as long as I can follow my code easily and its solid is the main criteria

 

To be honest though, I often wonder about the merit of all this - exactly how often do you change LCD_RW to pin 37 from pin 33 anyway?

I wont be rerouting something like an LCD's connections but it was just an example, I will eventually be using Labview so there won't even be an LCD but there will be lots of pins to setup

 

The merits of this endeavour though are great, you programmers make me laugh =), there always has to be a definite well defined bounds and a concrete spec in place, have you seen the joke about the programmer who never stops washing his hair...

 

The whole reason was to settle my curiosity and perhaps learn something (which I have) and become a little tiny bit better than yesterday, that is the top and bottom of this

 

I thank you all

 

Regards

 

Edit

 

to stringify

Why does that make me smile?, its word of the day for sure!

Last Edited: Mon. Jan 5, 2015 - 05:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Its PD6 and PB6, the same for PD7 and PD7, it does look dodgy looking at it though but it was just an example

And that, in a nutshell is why I'm never a major fan of this kind of thing! It confused the hell out of me frown

 

Now if you had been able to say:

  #define LCD_RS combine(B,0)    // pin for LCD R/S    PB0
  #define LCD_E  combine(D,7)    // pin for LCD enable PD7
  #define DAT4   combine(B,6)    // pin for d4         PB6
  #define DAT5   combine(B,7)    // pin for d5         PB7
  #define DAT6   combine(D,5)    // pin for d6         PD5
  #define DAT7   combine(D,6)    // pin for d7         PD6

I would have more interest - obviously the macro would be named something more sensible than combine() but it conveys my intent. You then just move DAT6 to C2 with a change to:

  #define DAT6   combine(C,2)    // pin for c2         PC2

or whatever. But having these things so you define the bit number in one place and the port in the other is just fraught with danger isn't it? It also doesn't really help the maintainer to read the code either!

 

I'm sure we've seen the equivalent of my mysterious "combine()" macro offered here before now.

Last Edited: Mon. Jan 5, 2015 - 06:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

#defined GpioDir (*(volatile uint32_t *)0x1234)

That line does not make any sense to me, (pointer to a pointer!?)

Nope.  It is dereferencing a pointer.  This is just C.

 

Been discussed oodles of times.  Here's one:

https://www.avrfreaks.net/forum/volatile-unsigned-char-0xfoo

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]