element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Embedded Forum OO Digital Pin class: single class or separate in and out classes
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Embedded and Microcontrollers requires membership for participation - click to join
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 37 replies
  • Subscribers 457 subscribers
  • Views 3793 views
  • Users 0 members are here
Related

OO Digital Pin class: single class or separate in and out classes

Jan Cumps
Jan Cumps over 1 year ago

I'm experimenting with light-weight C++ for microcontrollers. Looking for your opinion for class(es) that represent GPIO pin behaviour

An output pin can

  • accept a value and set it
  • return the value it is set to
  • maybe go high-z

An input can:

  • return the value at its input

I may represent this

  • very simple as a single class.
    Getting the value would perform the "return" action above. The class would decide if it returns output state or input state internally
    Setting would only succeed if the pin is an output pin, else either throw an exception or silently ignore.
    Switching between the two would be simple
  • as separate classes
    out type pin would set the output as indicated, and return the output state if asked
    in type pin would return the value at its input. It would not provide setters.
    Switching between two modes would mean discarding the original object and creating one of the other kind.
    (should both classes inherit from a common parent that defines the actual pin?)

How would you solve this, as OO designer / abstractor that loves simplicity?

  • Sign in to reply
  • Cancel

Top Replies

  • shabaz
    shabaz over 1 year ago in reply to michaelkellett +3
    The compiler was way better than I thought... I gave it a shot with the GNU compiler for ARM, using the Pi Pico SDK. I created a simple class: Then that class is used in the main() function. The…
  • shabaz
    shabaz over 1 year ago +2
    Single class : ) Also it makes it easier to configure up loads of GPIO from an array etc. I would prefer it instead of DigitalOut and DigitalIn style. Usually it's very clear from the object name whether…
  • ggabe
    ggabe over 1 year ago +2
    Take a look at WiringPi, as an example how the single class API can be structured: http://wiringpi.com/reference/ I like how it can abstract MCU GPIO and IO expanders under the same API. if you need…
Parents
  • michaelkellett
    michaelkellett over 1 year ago

    I'd like to know what actual machine code is produced by these different approaches.

    I don't use C++ so I set/clear/read pins with C macros - the code produced is the most compact and fastest possible for the processor, in some cases10x faster than the more abstract approaches in the manufacturers support files.

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Cancel
  • shabaz
    shabaz over 1 year ago in reply to michaelkellett

    The compiler was way better than I thought... 

    I gave it a shot with the GNU compiler for ARM, using the Pi Pico SDK. I created a simple class:

    image

    Then that class is used in the main() function. The red shows the use of a couple of functions in the class:

    image

    This was the listing:

    image

    The compiler managed to reduce the entire operation against the objects, to a single instruction (granted earlier up in the code, it set up the R7 and R4 registers, but that was just a few instructions too). This is for the Pico RP2040, which admittedly has simpler GPIO registers than some microcontrollers (although the older microcontrollers are even simpler), but shows that the compiler doesn't do too bad of a job.

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • Cancel
  • michaelkellett
    michaelkellett over 1 year ago in reply to shabaz

    Thanks for that.

    I'm not that sure how good it really is yet, can you get the GCC to interleave source code and assembler ?

    I can't (easily) work out how much is going on inside the for loop and if it's relying on setting some registers outside the loop which wouldn't work in a a more demanding example.

    I'll check out a similar example in C with macros for reference tomorrow.

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • michaelkellett
    michaelkellett over 1 year ago in reply to shabaz

    Thanks for that.

    I'm not that sure how good it really is yet, can you get the GCC to interleave source code and assembler ?

    I can't (easily) work out how much is going on inside the for loop and if it's relying on setting some registers outside the loop which wouldn't work in a a more demanding example.

    I'll check out a similar example in C with macros for reference tomorrow.

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Cancel
Children
  • shabaz
    shabaz over 1 year ago in reply to michaelkellett

    Hi Michael,

    I've placed the interspersed listing (main.cpp.s file) along with the source and CMakeLists.txt file here: https://1drv.ms/f/s!AlgR3ZgXGd0MtmGKdTt4dFwrGfLG?e=gsAB6T

    The interspersed file is a bit hard to follow, a snippet is in the screenshot below. There are lots of .loc lines but they are all labels that don't translate to instructions.

    image

    The green item is what the compiler translated the led.clear() and led2.set() functions to, i.e. it figured out that the code in the functions was calling a Pico SDK function that happened to write to a single register (with different content depending on the object instance's private member variable content). Since I had two instances, the compiler previously set the R6 and R7 registers to the bit mask (the GPIO pins were GPIO25 and GPIO26), by shifting 128 left by 18 and 19 respectively. It also set R4 to a register address (shown as 3489660928B in the screenshot above, which is 0xD0000000 which is called SIO_BASE), so that adding 20 or 24 to it would reach the gpio_set or gpio_clr offset in the register map.

    image

    That setup took 6 instructions (shown in blue below), and then whenever it then needed to set or clear an LED, it then just needed a single instruction (red boxes).

    image

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Cancel
  • michaelkellett
    michaelkellett over 1 year ago in reply to shabaz

    Thanks very much - I'll see what the Keil C compiler can manage and post a result.

    MK

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
  • michaelkellett
    michaelkellett over 1 year ago in reply to michaelkellett

    I tweaked some code that was running on a current project using a Giga Devices M4 core processor.

    Compiler is Keil V5.06 (build 960)

    I'm using my normal macros for pin definition (the Giga Devices GPIO is almost identical to the earlier ST Cortex parts)

    #define PIN_OPA_EN      GPIOA,3
    #define PIN_LED_OPA_EN  GPIOB,8

    #define PIN(g, p) p
    #define GPIO(g, p) g

    #define PIN_SET(p) (GPIO_BOP(GPIO(p)) = (1UL << PIN(p)))
    #define PIN_CLR(p) (GPIO_BC(GPIO(p)) = (1UL << PIN(p)))

    First I tried some single event (not in a loop) pin control:

    Lowest level of optimisation

    511: PIN_SET(PIN_LED_OPA_EN);
    0x08005E4C F44F7080 MOV r0,#0x100
    0x08005E50 495C LDR r1,[pc,#368] ; @0x08005FC4
    0x08005E52 6008 STR r0,[r1,#0x00]


    Highest level of optimisation


    511: PIN_SET(PIN_LED_OPA_EN);
    0x080056AC 4634 MOV r4,r6
    0x080056AE 6008 STR r0,[r1,#0x00]
    0x080056B0 49A4 LDR r1,[pc,#656] ; @0x08005944

    Somehow the optimisation manages to change a register load with constant to a register to register transfer but either way it takes three instructions per toggle.

    I wrote a little loop function:

    691 static void test_pin_toggle(void)
    692 {
    693     uint32_t counter;
    694
    695     for(counter = 0u; counter < 100u; counter++)
    696     {
    697         PIN_SET(PIN_LED_OPA_EN);
    698         PIN_SET(PIN_OPA_EN);
    699         PIN_CLR(PIN_LED_OPA_EN);
    700         PIN_CLR(PIN_OPA_EN);
    701     }
    702 }

    Compiled with max optimisation:

          698: PIN_SET(PIN_OPA_EN);
    0x08005690 4BAF     LDR r3,[pc,#700] ; @0x08005950
    0x08005692 FA1FFA80 UXTH r10,r0
          697: PIN_SET(PIN_LED_OPA_EN);
    0x08005696 1589     ASRS r1,r1,#22
          698: PIN_SET(PIN_OPA_EN);
    0x08005698 2208     MOVS r2,#0x08
    0x0800569A F8DF92B8 LDR.W r9,[pc,#696] ; @0x08005956
          507: uint16_t pulse_base = 0u;
    0x0800569E 462C     MOV r4,r5
          695: for(counter = 0u; counter < 100u; counter++)
          696: {
          697: PIN_SET(PIN_LED_OPA_EN);
    0x080056A0 4628     MOV r0,r5
    0x080056A2 1D1F     ADDS r7,r3,#4
          697: PIN_SET(PIN_LED_OPA_EN);
    0x080056A4 F8CC1000 STR r1,[r12,#0x00]     single instruction pin operation
          698: PIN_SET(PIN_OPA_EN);
    0x080056A8 601A     STR r2,[r3,#0x00]     single instruction pin operation
          699: PIN_CLR(PIN_LED_OPA_EN);
    0x080056AA F8C91000 STR r1,[r9,#0x00]     single instruction pin operation
          700: PIN_CLR(PIN_OPA_EN);
    0x080056AE 603A     STR r2,[r7,#0x00]     single instruction pin operation
    0x080056B0 1C40     ADDS r0,r0,#1
          695: for(counter = 0u; counter < 100u; counter++)
    0x080056B2 2864     CMP r0,#0x64
    0x080056B4 D3F6     BCC 0x080056A4
    0x080056B6 49A2     LDR r1,[pc,#648] ; @0x08005940

    The compiler inserts a logically correct (but irrelevant for this example ) initialisation of an unrelated variable at line 507, ignore that.

    Otherwise it finds exactly the same single instruction optimisation per bit operation as Shabaz's C++ example.

    So from all this we can say that C macros and C++ OOPery end up with much the same code and performance in this case.

    Which means you can choose whichever you like best.

    (But I've seen manufacturers code in C which makes a complete mess of things so you still need to take a peek at the assembler output from the compiler if the performance isn't what you had expected.)

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Cancel
  • shabaz
    shabaz over 1 year ago in reply to michaelkellett

    Hi Michael,

    Thanks for that! Very interesting to see how the two compilers both arrived at the same result. I get the feeling people don't examine performance until someone complains sadly. I wish there was a function in IDEs that would (like a kind of extreme debug build) insert instrumentation, so people could see on a graph which function calls were consuming most time. This was quite feasible on C/C++ code built on x86/x64 Linux (still real-time to an extent, and although it does affect execution speed, it wasn't that far removed from the very high speeds that modern microcontrollers can run at), there were products to do this on Linux, although I think some of them have disappeared over the years. Android development IDE has very good capability for this, granted there are a lot more resources in the processors in phones!

    image

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube