Author Topic: A more generic approach to port operations  (Read 18823 times)

0 Members and 1 Guest are viewing this topic.

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
A more generic approach to port operations
« on: March 29, 2014, 11:36:32 pm »
I talked about a set of macros that help to simplify port operations on a STM32F3 chip:

Code: [Select]
#define PIN_SET(port, pins) port->ODR |= (pins) //set pins on port
#define PIN_CLR(port, pins) port->ODR &=~(pins) //clear pins on port
#define PIN_FLP(port, pins) port->ODR ^= (pins) //flip pins on port
#define PIN_GET(port, pins) ((port->IDR) & (pins)) //get pins

//user code
#define GPIO_LED  GPIOA
#define LED0          (1<<1) //led0 on gpioa.1
#define LED1          (1<<9) //led1 on gpioa.9

  PIN_SET(GPIO_LED, LED0); //set led0
  PIN_FLP(GPIO_LED, LED1); //flip led1
  ...

The advantage with this approach is that once your code has been built on this set of routines, you can easily retarget the code if you simply replace the macros with the ones appropriate on the new target.

Obviously, this approach is possible because most of the CMx chips (the only exception I know of is the TI / Luminary chips) come with the port registers nicely defined via a struct in the device header file.

So the question is: is it possible to do the same with other chips, like PIC24F, or AVR, or the lowly PIC?
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #1 on: March 29, 2014, 11:51:07 pm »
The obvious answer is "maybe".

For the PIC24 (and PIC32), you will notice that the register map for the ports starts with TRISx, then PORTx, LATx, and ODCx. For example, for dsPIC33FJ128GP804, TRISA is at 0x02c0, PORTA at 0x02c2, LATA at 0x02c4, and ODCA at 0x02c6.


So we can define our own structure like this:

Code: [Select]
//gpio definitions
typedef struct {
volatile unsigned short TRIS; //direction register - offset 0x0000
volatile unsigned short PORT; //input data register
volatile unsigned short LAT; //output data register
volatile unsigned short ODC; //open drain register
} GPIO_TypeDef; //gpio type definitions

The easy and hard way would be to define a struct pointer that points to TRISx:

Code: [Select]
#define GPIOA  ((GPIO_TypeDef *) 0x02c0) //gpioA pointer

In your user code, you would do this
Code: [Select]
#define PIN_SET(port, pins) port->LAT |= (pins) //set pins on port
#define PIN_CLR(port, pins) port->LAT &=~(pins) //clear pins on port
#define PIN_FLP(port, pins) port->LAT ^= (pins) //flip pins on port
#define PIN_GET(port, pins) ((port->PORT) & (pins)) //get pins
#define PIN_OUT(port, pins) port->TRIS &=~(pins) //pins as output
#define PIN_FLP(port, pins) port->TRIS |= (pins) //pins as input


//user code
#define GPIO_LED  GPIOA
#define LED0          (1<<1) //led0 on gpioa.1
#define LED1          (1<<9) //led1 on gpioa.9

  PIN_OUT(GPIO_LED, LED0 | LED1); //led0 and led1 as output
  PIN_SET(GPIO_LED, LED0); //set led0
  PIN_FLP(GPIO_LED, LED1); //flip led1
  ...

So the user code remains unchanged, even though the inner works of the port are vastly different between STM32F3 that the code originally ran on and PIC24 that it runs on now.

« Last Edit: March 29, 2014, 11:56:17 pm by dannyf »
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #2 on: March 29, 2014, 11:55:32 pm »
That is great.

However, each time we move the code to a new chip (within the same family), we have to check the datasheet and make sure that we define GPIOx to whatever the appropriate addresses are.

That's a major pain in the you-know-where.

One way to get around that would be to take advantage of the predefined macros:

Code: [Select]
//#define GPIOA  ((GPIO_TypeDef *) 0x02c0) //gpioA pointer - old way of doing business

#define GPIOA  ((GPIO_TypeDef *) &TRISA)

...
#if defined(TRISH)                                          //for PORTH, if it exists on the target
#define GPIOH  ((GPIO_TypeDef *) &TRISH)
#endif

Something like this would be infinitely portable.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #3 on: March 30, 2014, 12:08:16 am »
How to pull everything together?

You can always copy and paste the macros into your code. But that's too much work.

Instead, we will create a header file and put all the gpio-related code into it.

Code: [Select]
//mygpio.h
//my own gpio definitions for PIC24/32

#ifndef _MYGPIO_H
#define _MYGPIO_H

//gpio definitions
typedef struct {
volatile unsigned short TRIS; //direction register - offset 0x0000
volatile unsigned short PORT; //input data register
volatile unsigned short LAT; //output data register
volatile unsigned short ODC; //open drain register
} GPIO_TypeDef; //gpio type definitions

//gpio macros
#define GPIOA  ((GPIO_TypeDef *) &TRISA)
#define GPIOB  ((GPIO_TypeDef *) &TRISB)

#if defined(TRISC)                                          //for PORTx, if it exists on the target
#define GPIOC  ((GPIO_TypeDef *) &TRISC)
#endif

#if defined(TRISD)                                          //for PORTx, if it exists on the target
#define GPIOD  ((GPIO_TypeDef *) &TRISD)
#endif

#if defined(TRISE)                                          //for PORTx, if it exists on the target
#define GPIOE  ((GPIO_TypeDef *) &TRISE)
#endif

#if defined(TRISF)                                          //for PORTF, if it exists on the target
#define GPIOF  ((GPIO_TypeDef *) &TRISF)
#endif

...

#if defined(TRISH)                                          //for PORTx, if it exists on the target
#define GPIOH  ((GPIO_TypeDef *) &TRISH)
#endif

//port / pin macros
#define PIN_SET(port, pins) port->LAT |= (pins) //set pins on port
#define PIN_CLR(port, pins) port->LAT &=~(pins) //clear pins on port
#define PIN_FLP(port, pins) port->LAT ^= (pins) //flip pins on port
#define PIN_GET(port, pins) ((port->PORT) & (pins)) //get pins
#define PIN_OUT(port, pins) port->TRIS &=~(pins) //pins as output
#define PIN_FLP(port, pins) port->TRIS |= (pins) //pins as input

#endif  //_MYGPIO_H

Save that to a file ("mygpio.h" for example), and your user code would look like this:

Code: [Select]
#include <_yourdevice.h> //device header file
#include "mygpio.h" //my gpio macros

#define LED_PORT  GPIOC
#define LED            (1<<4) //led on PORTC.4
#define SW_PORT   ~GPIOA //switch active low
#define SW             (1<<6) //switch on PORTA.6

  PIN_OUT(LED_PORT, LED); //led as output
  PIN_IN(SW_PORT, LED); //switch as input
 
  ...
  if (PIN_GET(SW_PORT, SW)) PIN_SET(LED_PORT, LED); //set port.led if switch is pressed (low)

This approach works on C30/XC16 for as many pic24 chips as I have checked.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #4 on: March 30, 2014, 12:13:27 am »
Limitations?

1) As you may have figured out, this approach is endian-dependent. If the target compiler has a different endianess, you just have to reverse the order in the GPIO_TypeDef struct.

2) It is dependent on your chips' memory mapping - or the consistency of its memory mapping. It presumes that the order of port registers is always the same - the registers don't have to be consecutively packed (PIC32), as long as the order is consistent: if there are gaps among the registers, the gaps have to be the same.

For example, you can port this approach to AVR, but you cannot port it to PIC(10/12/16): on those PICs, the port registers are all over the place. Most modern chips have consistent register mapping so this approach is portable to them all.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #5 on: March 30, 2014, 12:23:03 am »
Here is an example of porting this to AVR.

AVRs have a slightly different register mapping: PINx followed by DDRx and then PORTx. They are 8-bit registers too.

So the "mygpio.h" file is slightly different:

Code: [Select]
//mygpio.h
//my own gpio definitions for PIC24/32

#ifndef _MYGPIO_H
#define _MYGPIO_H

//gpio definitions
typedef struct {
volatile unsigned char PIN; //input data register - offset 0x00
volatile unsigned char DDR; //direction register
volatile unsigned char PORT; //output data register
} GPIO_TypeDef; //gpio type definitions

//gpio macros
#define GPIOA  ((GPIO_TypeDef *) &PINA)
#define GPIOB  ((GPIO_TypeDef *) &PINB)

#if defined(PINC)                                          //for PORTx, if it exists on the target
#define GPIOC  ((GPIO_TypeDef *) &PINC)
#endif

#if defined(PIND)                                          //for PORTx, if it exists on the target
#define GPIOD  ((GPIO_TypeDef *) &PIND)
#endif

#if defined(PINE)                                          //for PORTx, if it exists on the target
#define GPIOE  ((GPIO_TypeDef *) &PINE)
#endif

#if defined(PINF)                                          //for PORTF, if it exists on the target
#define GPIOF  ((GPIO_TypeDef *) &PINF)
#endif

...

#if defined(PINH)                                          //for PORTx, if it exists on the target
#define GPIOH  ((GPIO_TypeDef *) &PINH)
#endif

//port / pin macros
#define PIN_SET(port, pins) port->PORT |= (pins) //set pins on port
#define PIN_CLR(port, pins) port->PORT &=~(pins) //clear pins on port
#define PIN_FLP(port, pins) port->PORT ^= (pins) //flip pins on port
#define PIN_GET(port, pins) ((port->PIN) & (pins)) //get pins
#define PIN_OUT(port, pins) port->DDR |= (pins) //pins as output
#define PIN_FLP(port, pins) port->DDR &=~(pins) //pins as input

#endif  //_MYGPIO_H

Your user code would remain identical, as expected:

Code: [Select]
#include <_yourdevice.h> //device header file
#include "mygpio.h" //my gpio macros

#define LED_PORT  GPIOC
#define LED            (1<<4) //led on PORTC.4
#define SW_PORT   ~GPIOA //switch active low
#define SW             (1<<6) //switch on PORTA.6

  PIN_OUT(LED_PORT, LED); //led as output
  PIN_IN(SW_PORT, LED); //switch as input
 
  ...
  if (PIN_GET(SW_PORT, SW)) PIN_SET(LED_PORT, LED); //set port.led if switch is pressed (low)

So in this case, our code went from a 32-bit chip to a 8-bit chip, with no change, aside from dropping in the "mygpio.h" for the new target and recompiling on the target: the code is fully insulated from the inner-working of the port registers.

Something that tedious and that lowly is best handled by the compiler, :)

edit: corrected the PIN_IN() and PIN_OUT() macros - they were flipped.
« Last Edit: March 30, 2014, 12:37:53 am by dannyf »
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #6 on: March 30, 2014, 12:26:51 am »
Disclaimer:

1) use this approach at your own risk.
2) I did not check all pic24/32/avr datasheets to make sure that their register mappings are consistent. To the extent that they are not, this approach will yield incorrect results.
3) be aware of your compiler's endianess, as mentioned earlier. Make necessary adjustments if needed;
4) be aware of your target's register mappings, if you are to port this approach to a new chip. Make necessary adjustments if needed.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #7 on: March 30, 2014, 12:29:19 am »
Extensions:

1) I prefer to use masks vs. bit fields. To the extent that you want to use bit fields, you can implement a set of structs / unions within GPIO_TypeDef. This is particularly attractive under gcc.
2) You can take the same approach to other peripherals. However, given the diversity there, it is not nearly as neat and useful.
...
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #8 on: March 30, 2014, 12:40:31 am »
For many of the modern chips, there are many input and output modes. So setting a pin to those modes is quite more involved than to set / clear bits in the direction registers.

For those cases, you are likely to write up PIN_OUT() and PIN_IN() functions. Thus, you may consider a mygpio.c file as well.
================================
https://dannyelectronics.wordpress.com/
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4219
  • Country: us
Re: A more generic approach to port operations
« Reply #9 on: March 30, 2014, 02:27:30 am »
Quote
is it possible to do the same with other chips
I'm not quite sure what you mean.
Many microcontrollers allow you to group io registers into a struct.  The registers need not be contiguous to do this.  You may be dependent on the quality of the C compiler to produce reasonable code from bit-logical operation on structure members :-(

It's CERTAINLY possible to create PIN_SET/etc macros for any processor, perhaps with some limitations on functionality.  Having structure-like definitions of the IO port is largely irrelevant.  It's nice for RUN-TIME non-constant values for "port", but not needed for compile-time constant values, if only because C macros are smarter than you think.

You mentioned TI not having the structure like defs (both Keil and IAR DO have such structures, apparently derived from CMSIS "SVD" data (which is xml provided by the vendor, I think.)  But suppose we're stuck with the #defines from "Tivaware" that look like:
Code: [Select]
#define GPIO_PORTA_DATA_R       (*((volatile unsigned long *)0x400043FC))
#define GPIO_PORTB_DATA_R       (*((volatile unsigned long *)0x400053FC))
#define GPIO_PORTC_DATA_R       (*((volatile unsigned long *)0x400063FC))

then you can just make your macros like:
Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins
and invoke it  like:

Code: [Select]
   PIN_SET(PORTC, 0x11);
You may run into one of those "implementation dependent" issues with your C compiler if you try to use another macro for the port name (which happens first?  Macro expansion or symbol concatenation, hmm?)  But I've got recent working code that contains stuff like:
Code: [Select]
#define LED_PORT DATA_BITS_R(E)[LED_BITMASK]
    :
LED_PORT |= LED_BITMASK;
(The first line is TI's (well, luminary's) solution to atomic bit operations; it uses bits from the address to control which bits are actually written.)

As another example, you should note that the Arduino function digitalWrite(pin, val), where "pin" is a completely abstracted number having nothing to do with actual ports, bitmasks, or pin numbers, has be implemented for several different architectures in several different ways, that yield optimal (eg 1-instruction) object code for the case where the parameters are compile-time constants.  (given "reasonable" C compilers and optimization levels, of course.)
« Last Edit: March 30, 2014, 03:52:32 pm by westfw »
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #10 on: March 30, 2014, 03:04:59 am »
Quote
then you can just make your macros like:
Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins;
and invoke it  like:

Code: [Select]
#define PIN_SET(PORTC, 0x11);

Two suggestions:

1) learn C;
2) read up on the datasheet.

There is zero chance your code will ever work.
« Last Edit: March 30, 2014, 03:07:42 am by dannyf »
================================
https://dannyelectronics.wordpress.com/
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: A more generic approach to port operations
« Reply #11 on: March 30, 2014, 03:57:12 am »
Quote
then you can just make your macros like:
Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins;
and invoke it  like:

Code: [Select]
#define PIN_SET(PORTC, 0x11);

Two suggestions:

1) learn C;
2) read up on the datasheet.

There is zero chance your code will ever work.

westfw has been on this forum as well as others for some time and I can vouch for his knowledge of C and C++.

I'm sure this is what he meant to say:

Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins /* No semi-colon here */
.
.
.
int foo(void)
{
    PIN_SET(PORTC, 0x11);

    ...
}

Which is completely valid C.  (See this link as to why you don't want your macros to end with a semi-colon.)

As to whether or not that will work for some particular MCU, I can't say.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4219
  • Country: us
Re: A more generic approach to port operations
« Reply #12 on: March 30, 2014, 05:08:14 am »
What TerminalJack said.  Sigh.  Long day today!
Here's some actual code (for TI), rather than badly edited cut&paste:
Code: [Select]
#define AMSEL_R(port) GPIO_PORT ## port ## _AMSEL_R
#define DIR_R(port) GPIO_PORT ## port ## _DIR_R
#define DEN_R(port) GPIO_PORT ## port ## _DEN_R
#define PCTL_R(port) GPIO_PORT ## port ## _PCTL_R
#define AFSEL_R(port) GPIO_PORT ## port ## _AFSEL_R
#define DATA_R(port) GPIO_PORT ## port ## _DATA_R
#define DATA_BITS_R(port) GPIO_PORT ## port ## _DATA_BITS_R
#define PUR_R(port) GPIO_PORT ## port ## _PUR_R

#define GPIO_CLOCK_M(port) SYSCTL_RCGC2_GPIO ##port
#define PCTL_M(port, bit) GPIO_PCTL_P##port##bit##_M

 :

AMSEL_R(E) &= ~(LED_BITMASK | SW_BITMASK);  // disable analog
AFSEL_R(E) &= ~(LED_BITMASK | SW_BITMASK); // disable other alt funcs.
PCTL_R(E) &= ~(PCTL_M(E,0) |PCTL_M(E,1));  // Configure as GPIO
DIR_R(E) &= ~SW_BITMASK; // SW1 is an input
DIR_R(E) |= LED_BITMASK;  // LED is an output
DEN_R(E) |= LED_BITMASK | SW_BITMASK;  // Enable digital pins
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: A more generic approach to port operations
« Reply #13 on: March 30, 2014, 05:21:04 am »
Two suggestions:

1) learn C;
2) read up on the datasheet.

There is zero chance your code will ever work.
Maybe dial down the attitude?

:palm:
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #14 on: March 30, 2014, 12:16:40 pm »
We talked earlier about how this approach could be used to mcus whose port register mapping is discontinuous, but consistently discontinuous. We mentioned PIC32 being such a case. I was asked how this approach could be implemented on PIC32.

If you look at the datasheet for say PIC32MX1xx/2xx, here is the mapping of port registers:

offset 0x6010, TRISA
offset 0x6020, PORTA
offset  0x6030, LATA
offset 0x6040, ODCA
...

offset 0x6110, TRISB
offset 0x6120, PORTB
offset  0x6130, LATB
offset 0x6140, ODCB
...

So the gap between each (named) register is 12 bytes, or ***3*** uint32_t types.

The number '3' should immediately ring a bell for those of us who know the chip well. Yes, they are the xCLR, xSET and xINV registers.

So we have an option in defining the GPIO_TypeDef struct: we can name those registers or just pad up the struct to fill the void between the named registers.

Here is what GPIO_TypeDef for a PIC32 may look like:

Code: [Select]
//gpio definitions
typedef struct {
    volatile uint32_t TRIS;     //output direction register
    //volatile uint32_t reserved1[3];          //padding
    volatile uint32_t TRISCLR;
    volatile uint32_t TRISSET;
    volatile uint32_t TRISINV;
    volatile uint32_t PORT;     //input data register
    volatile uint32_t reserved2[3];          //padding
    volatile uint32_t LAT;      //output data register
    //volatile uint32_t reserved3[3];          //padding
    volatile uint32_t LATCLR;
    volatile uint32_t LATSET;
    volatile uint32_t LATINV;
    volatile uint32_t ODC;      //opendrain register
} GPIO_TypeDef;

#define GPIOA       ((GPIO_TypeDef *) &TRISA)
#define GPIOB       ((GPIO_TypeDef *) &TRISB)
...

As you can see, I implemented TRIS, PORT, LAT and ODC for a port, as well as the corresponding xCLR, xSET and xINV for TRIS and LAT registers - if you wish, you could implement those for PORT and ODC as well.

The PIN_SET(), PIN_CLR() and other macros would remain the same.

However, the introduction of xCLR, xSET and xINV registers introduce a new way of doing things. The PIN_SET() and etc. macros are read-modify-write operations. With xCLR, xSET and xINV registers, you can make them into simple write operations, by introducing a set of fast PIN_xxx() macros:

Code: [Select]
//fast port operations
#define FPIN_OUT(port, pins)     port->TRISCLR = (pins)
#define FPIN_IN(port, pins)      port->TRISSET = (pins)
#define FPIN_SET(port, pins)     port->LATSET = (pins)
#define FPIN_CLR(port, pins)     port->LATCLR = (pins)
#define FPIN_FLP(port, pins)     port->LATINV = (pins)
#define FPIN_GET(port, pins)     IO_GET(port, pins)  //no fast operations for PORT as we did not implement xCLR/xSET/xINV for PORT

By swapping out PIN_xxx() with FPIN_xxx() macros, you would have significantly increased the speed of port operations.

Disclaimer:
1) the xCLR/xSET/xINV registers' offsets are not documented in any PIC32 documents that I have seen so you are running the risk that Microchip may not have implemented them consistently from chip to chip;
2) I don't have a real PIC32 chip to test but the above simulates as expected in C32/XC32.

A side note: the concept of FPIN_xxx() can be implemented on other chips, like STM32F via BSRR and BRR registers. Its performance gain will depend on both the compiler and hardware - BME for example offers better performance than FPIN_xxx() macros.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #15 on: March 30, 2014, 12:20:30 pm »
RMW: PIN_xxx() macros are done via read-modify-write operations. I was asked if that could be an issue.

Two aspects:
1) RMW operations: those macros are certainly RMW operations and offer lower speed than FPIN_xxx() macros, everything else being equal;
2) RMW issues: however, none of those macros have the RMW issues that you often see on older PICs or 8051, or other mcus where output and input data registers are combined (some LPC ARM chips for example). That's because write operations (PIN_SET(), PIN_CLR() and PIN_FLP()) are always done via output data registers and read operations (PIN_GET()) via input data registers.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #16 on: March 30, 2014, 12:31:19 pm »
Quote
I can vouch for his knowledge of C and C++.

I am sure he does a good job talking about C. The unfortunate truth is that there are many people who can talk and talk but cannot walk the walk.

There are quite a few mistakes in that one post of his that are so basic and fundamental to even an entry-level C programmer that raise doubts in my mind about to what extent he can walk the walk.

Quote
Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins /* No semi-colon here */
.
.
.
int foo(void)
{
    PIN_SET(PORTC, 0x11);

    ...
}

(See this link as to why you don't want your macros to end with a semi-colon.)

That particularly mistake unfortunately is so basic that if a student in C101 made it, you should fail him; if a gainfully employed C programmer made it, he should be canned on the spot.

I also don't buy this "I was too tired" argument. Not adding a semi-colon to your macros is like riding a bike: it is one of those basic skills that once you have learned it, you cannot unlearn it.

He made the same mistake more than once, in different spots. If copy-and-paste did it, he should ask copy-and-paste by a lottery ticket for him, :)

Worse yet, his code:

Code: [Select]
#define PIN_SET(port, pins) GPIO_ ## port ## _DATA_R |= pins;
will never work on that particular chip, even without the semi-colon at the end.

Writing something like that shows his profound lack of understanding of that particular chip and an important mechanism for the arm family of chips, and his willingness to shoot from the you-know-where, :)

He can find out why by running the code on a real chip or reading up on the datasheet.
================================
https://dannyelectronics.wordpress.com/
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: A more generic approach to port operations
« Reply #17 on: March 30, 2014, 01:14:04 pm »
Well, I can tell you one thing, I'd much rather work with a westfw than a dannyf!

Knowledge only counts for so much in a workplace.  If you exhibit the same attitude at your workplace that you do here in this forum then I can guarantee you that your fellow employees can't stand you.

I don't know if you're suffering from an inferiority complex, a superiority complex, or perhaps Asperger syndrome but the fact is that practically none of you posts on this forum are helpful.  All you do is post crap that boils down to "you're an idiot and I'm better you."  All your posts seem to be self-serving and never actually helpful.

If you worked for me, you'd be the one "canned on the spot."  People like you just poison the work environment.  You might know better than others but that doesn't mean anything since you always have to shove that fact in everyone else's face.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: A more generic approach to port operations
« Reply #18 on: March 30, 2014, 01:14:32 pm »
Not adding a semi-colon to your macros is like riding a bike: it is one of those basic skills that once you have learned it, you cannot unlearn it.
This speaks to the view that preprocessor macros are quite a bit different than functions, and one should surround macro trickery with yellow caution tape. I'm not saying your stuff counts as trickery, but if it looks like a macro library with function names in ALL CAPS, some people will assume that. If you can design your API to appear as normal C functions, that might be preferable.

Case in point: the Chibios/RT PAL driver. Chibios includes a standard API for I/O pins that runs on half a dozen different chip families (no PIC, as far as I can tell). The API appears to the programmer as if they're C functions. Much of the PAL driver is actually implemented as macros that "disappear" at compile time, but as an application programmer, you don't have to concern yourself with the difference, which is nice.

Implementors need to understand macro expansion in detail. Users should be able to get their work done with a simpler view.
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: A more generic approach to port operations
« Reply #19 on: March 30, 2014, 02:35:56 pm »
Quote
I can tell you one thing

Thanks for that great lecture on life.

Unfortunately, the fact remains that that buggy piece of "thing" that he wrote will never work on the target he wrote it for.

So unless you can show it otherwise, the discussion with you is over.
================================
https://dannyelectronics.wordpress.com/
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: A more generic approach to port operations
« Reply #20 on: March 30, 2014, 03:09:37 pm »
Quote
I can tell you one thing

Thanks for that great lecture on life.

Unfortunately, the fact remains that that buggy piece of "thing" that he wrote will never work on the target he wrote it for.

So unless you can show it otherwise, the discussion with you is over.

My beef is that your response "There is zero chance your code will ever work." doesn't bring anything to the table.  If it's wrong then state why that is so.  Use it as a teaching opportunity instead of belittling the guy.

The nice thing about this particular business is that you can fairly easily prove most claims.  You never seem to be willing to do that for some reason.  Your response is pretty much always just "you're wrong, educate yourself."  There no value-add to that.  A response like that is just noise.  I guess it might boost your ego but it doesn't bring anything to the conversation.
 

Offline mazurov

  • Frequent Contributor
  • **
  • Posts: 524
  • Country: us
Re: A more generic approach to port operations
« Reply #21 on: March 30, 2014, 03:37:31 pm »
For C++, there is a better way to abstract pin operations -> https://github.com/KonstantinChizhov/Mcucpp/tree/master/mcucpp using templates. It works very well, produces tight assembly code and allows to do wonderful things, like driving a character LCD either from individual pins or 595 latch with the same piece of code. I used this approach on several occasions; the biggest issue is that C++ is not available for every MCU.
With sufficient thrust, pigs fly just fine - RFC1925
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: A more generic approach to port operations
« Reply #22 on: March 30, 2014, 03:58:23 pm »
For C++, there is a better way to abstract pin operations -> https://github.com/KonstantinChizhov/Mcucpp/tree/master/mcucpp using templates. It works very well, produces tight assembly code and allows to do wonderful things, like driving a character LCD either from individual pins or 595 latch with the same piece of code. I used this approach on several occasions; the biggest issue is that C++ is not available for every MCU.
Is there an example of this in use? I'm not really getting it from looking at the implementation.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4219
  • Country: us
Re: A more generic approach to port operations
« Reply #23 on: March 30, 2014, 04:12:36 pm »
Quote
Not adding a semi-colon to your macros is like riding a bike: it is one of those basic skills that once you have learned it, you cannot unlearn it.
Ah, but I wasn't writing code at the time.  I was copying code from a working program into a forum message, trying to make it more generic and understandable, while pointing out how fundamentally different macros based on argument concatenation are from macros based on structures.

I'm sorry that the resulting errors in the code-like portions of my message distracted you from seeing my point, or answering my implied questions about what you are actually trying to accomplish.  Hopefully some people other than Jack "got it."
I have gone back and fixed the message (maybe.  It's still a forum message, and not a finished program that has actually been run through a compiler, much less a debug cycle.)

What world do you live in where employees are judged and/or "canned on the spot" based on errors in rough first drafts and casual conversations?  I'm glad I live elsewhere!  (You know, I've heard that it's become an interview technique to see if an applicant can write perfect code on a whiteboard.  That's SO 70s, in it's way: "be sure to have your code perfect before you submit it to the keypunch operators, because any error will cost big bucks when it has to be re-done."  I'm a big believer in letting the computer tools (the compiler) find typos and simple syntax errors...)
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: A more generic approach to port operations
« Reply #24 on: March 30, 2014, 04:22:48 pm »
As long as we're on the topic, one thing that's tripped me up on multiple occasions is not properly initializing GPIO pins. Sometimes it's a matter of simply forgetting to do the initialization at all (dumb). Other times, it's not seeing a mistake in the init code. There are often multiple steps involved in setting up a single pin and if you change your board layout a little, that will ripple back to initialization.

Anway, to keep myself sane, I've started using the "X Macro" technique to generate GPIO initialization. Here's a snippet from a board.h file in a project I'm working on:

Code: [Select]
/*
 * This file generates a enum definition for each pin, the name of which is
 * the concatenation of the port and the name of the pin. E.g., pin 7 on
 * port 'A' can be read using the ChibiOS API as follows:
 *
 *  ... palReadPad(GPIOA, GPIOA_PIN7) ...
 *
 * The ALL_PINS macro, defined below, contains the I/O pin architecture-
 * specific GPIO configuration for the application. The format used here is
 * different from the usual ChibiOS method, and hopefully easier to maintain.
 *
 *  port: GPIOA .. GPIOI
 *   pin: 0 .. 15
 *  name: any identifier
 *  mode: INPUT | OUTPUT | ANALOG | ALTERNATE
 *  type: PP "push pull" | OD "open drain"
 * pupdr: NOPULL | PULLUP | PULLDN
 *   odr: 0 | 1
 *    af: 0 .. 15
 */

//  port, pin, name,         mode,      type, speed,  pupdr,  odr, af
#define ALL_PINS \
PIN(GPIOA,  0, CHRG_PG,      INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOA,  1, CHRG_STAT2,   INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOA,  2, CHRG_STAT1,   INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOA,  3, CHRG_PROG2,   OUTPUT,    PP,   100,    NOPULL, 1,  0) \
PIN(GPIOA,  4, DISP_CS,      OUTPUT,    PP,   100,    NOPULL, 0,  0) \
PIN(GPIOA,  5, DISP_SCK,     ALTERNATE, PP,   100,    NOPULL, 0,  5) \
PIN(GPIOA,  6, DISP_MISO,    ALTERNATE, PP,   100,    NOPULL, 0,  5) \
PIN(GPIOA,  7, DISP_MOSI,    ALTERNATE, PP,   100,    NOPULL, 0,  5) \
                                                                     \
PIN(GPIOA,  8, CARD_PWR,     OUTPUT,    PP,   100,    NOPULL, 0,  0) \
PIN(GPIOA,  9, USB_VBUS,     INPUT,     PP,   100,    NOPULL, 0,  0) \
PIN(GPIOA, 10, USB_ID,       ALTERNATE, PP,   100,    NOPULL, 0, 10) \
PIN(GPIOA, 11, USB_DM,       ALTERNATE, PP,   100,    NOPULL, 0, 10) \
PIN(GPIOA, 12, USB_DP,       ALTERNATE, PP,   100,    NOPULL, 0, 10) \
PIN(GPIOA, 13, SWDIO,        ALTERNATE, PP,   100,    NOPULL, 0,  0) \
PIN(GPIOA, 14, SWCLK,        ALTERNATE, PP,   100,    NOPULL, 0,  0) \
PIN(GPIOA, 15, FSYNC,        OUTPUT,    PP,   100,    NOPULL, 0,  0) \
                                                                     \
PIN(GPIOB,  0, CARD_DETECT,  INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOB,  1, PIN1,         INPUT,     PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB,  2, BOOT1,        ALTERNATE, PP,   100,    NOPULL, 1,  7) \
PIN(GPIOB,  3, INT_9250,     INPUT,     PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB,  4, INT1_3115,    INPUT,     PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB,  5, INT2_3115,    INPUT,     PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB,  6, I2C1_SCL,     ALTERNATE, OD,   2,      NOPULL, 0,  4) \
PIN(GPIOB,  7, I2C1_SDA,     ALTERNATE, OD,   2,      NOPULL, 0,  4) \
                                                                     \
PIN(GPIOB,  8, BUTTON_1,     INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOB,  9, BUTTON_0,     INPUT,     PP,   100,    PULLUP, 0,  0) \
PIN(GPIOB, 10, UART_TX,      ALTERNATE, PP,   100,    NOPULL, 0,  7) \
PIN(GPIOB, 11, UART_RX,      ALTERNATE, PP,   100,    NOPULL, 0,  7) \
PIN(GPIOB, 12, DISP_IO0,     OUTPUT,    PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB, 13, DISP_IO1,     OUTPUT,    PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB, 14, DISP_IO2,     OUTPUT,    PP,   100,    NOPULL, 0,  0) \
PIN(GPIOB, 15, DISP_IO3,     OUTPUT,    PP,   100,    NOPULL, 0,  0) \

...

enum all_pins_defs {
#define PIN(port,pin,name,mode,ppod,speed,pull,value,altfn) port ## _ ## name = pin,
ALL_PINS
#undef PIN
};

The basic idea is to define a single macro (ALL_PINS) that carries all the information about every GPIO pin. That macro gets replayed multiple times to generate pin enums (e.g., GPIOA_DISP_MOSI) that are used by application code, as well as the initialization code that runs after reset.
The ALL_PINS macro is formatted to look like a nice table because it doubles as concise documentation about which pins are being used and how.

PS. Yes, I realize that defaulting slew rate control to 100MHz isn't optimal.
PPS. This definitely counts as macro trickery. ;-)
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf