C++ Libraries based on a pin object

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

I’ve been experimenting with C++ and have been working on a library based on a Pin class. The goal is to improve code reuse, readability, and development time. I have been on the C side of the fence till recently. C++ brings classes, overloading, and other useful features. I played a little with dynamic allocation, but fell its best to keep everything statically defined.

 

With the Pin class and a couple of libraries built on it. I can write code like this:

Uart uart;
RotaryEncoder enc1;
CLCD lcd;
Pins pins;

ISR(PCINT1_vect)
{
 enc1.Scan();
 _delay_ms(1);
}
void enc1Callback(int8_t direction, int16_t _val)
{
 lcd.Goto(1,10,5);
 lcd.WriteInt16(_val);
 uart.Write(0x07);  uart.Write(_val>>8);  uart.Write(_val);
 uart.Write(0x08);  uart.Write(direction);
}
int main(void)
{
 uart.Init(9600);

 lcd.Init(pin.B0, pin.B1, pin.D4, pin.D5, pin.D6, pin.D7);
 
 enc1.Init(pin.C0, pin.C1, &enc1Callback);
 enc1.PinA->PCIntOn(); enc1.PinB->PCIntOn();
 
 sei();
  lcd.Goto(0,0);
  lcd.WriteInt16(sizeof(pins));
 while (1);
}

Sure, the pin class eats up memory. But I got it down to 48Bytes for all of port B,C,D on a 328p.

The Pin Class:

// | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// |   Not Used   |      Bit#    | * |   PCMsk   |   PCIE    |   Port    |
//                           ( * bool Pin Change interrupt available) 
 volatile uint16_t pinData;

 Pin();

 uint8_t Read();
 void PullHigh();
 void PullLow();
 void SetOutput();
 void SetInput();
 void HighImpedance();
 void PCIntOn();
 void PCIntOff();

Available for download at:

https://github.com/finisj/LAF

 

What do you guys think about use of a pin class? I’m not looking to open another discussion on C vs C++. Rather, I’m interested in styles and approaches for C++ programming for microcontrollers.   

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

It is stuf like this that is unacceptable to me:

 lcd.Init(pin.B0, pin.B1, pin.D4, pin.D5, pin.D6, pin.D7);

The reason is that by making variables of pins all I/O gets bogged down (Just as in the "arduino" wold). Yuch.

It is acceptable for turning a relay on/off, but not for any CPU intensiv I/O.

 

A long time ago I had a look at an arduino lib for controlling a small TFT lcd and the performance was quite bad.

Poked in the code and I got an overall performance boost of 40% (Adafruit demo program) by just changing the strobe pin from the arduino garbage to macro's which compile into single SBI / CBI asm instructions. This is a very significant performance boost.

And then I turned to STM32F103D8T6 for stuff like this.

On my desk is a "DSO138" with an F103 and it happily does screen updates @10Hz (guesswork, but it feels pretty responsive).

(Note Unfortunately I can not reccomend this oscilloscope because the triggering is quite bad and other small stuff)

 

For me a very important part of low level embedded programming is Fast I/O (for the parts where it matters).

And once you have decided on how you want that fast I/O to look in C ( Some #define'd macro's) it is just plain silly to also add another way to access I/O pins.

It adds an unneeded layer of complexity.

 

But I do find portability etc important.

For years I've put all I/O macro's in a single file, which looks like this (Excerpt):

#pragma once
/* Copyright (C) 2010 - 2014 Paul van der Hoeven. http://www.hoevendesign.com
This is free software, licensed under the terms of the GNU General
Public License as published by the Free Software Foundation. */

/* Main.h contains te pin definitions for all port pins of this project.
It also has some settings for the libraries which may vary between project
such as the speed of the timer, maximum packet size for mumarnet,
lcd configuration etc.
*/

#include <avr/io.h>
#include <inttypes.h>

/*============================================================================
Global macro's
===========================================================================*/
#define F_CPU	3686400ul
#define nop( ) asm volatile ("nop" ::)

/*===========================================================================
 User definitions for: Different projects.
 ===========================================================================*/
// Behringer DCX2496 output oud mod with 3x pga2311 for 6 channels.
// Opto couplers for PGA's. Need slow SPI.
#define PROJECT_DCX				1
// Small audio project with single PGA3211 and LC89091 and TDA1543 as DAC.
#define PROJECT_SMALL			2

// Set the project number to one of the above.
//#define PROJECT_NUMBER			PROJECT_SMALL
#define PROJECT_NUMBER			PROJECT_DCX

/*============================================================================
 Timer lib.
===========================================================================*/
// TIMER_RESOLUTION in micro seconds: 500 1000 2000 2500 5000 10000 20000
#define TIMER_RESOLUTION 998

/*==========================================================================
 Mumarnet, global configuration.
===========================================================================*/
#if PROJECT_NUMBER == PROJECT_DCX
#define NODE_ADDRESS				NODE_ADDRESS_AUDIO_DCX

#elif PROJECT_NUMBER == PROJECT_SMALL
#define NODE_ADDRESS				NODE_ADDRESS_AUDIO_SMALL
#endif

// PORT and BIT for the rs485 Transmitter enable signal
#define NETWORK_RS485_DDR			DDRD
#define NETWORK_RS485_PORT			PORTD
#define	NETWORK_RS485_PIN			PIND
#define NETWORK_RS485_ENABLE_BIT	(1<<4)

// PORT and BITs for the UART
#define NETWORK_UART_DDR			DDRD
#define NETWORK_UART_PORT			PORTD
#define NETWORK_UART_PIN			PIND
#define NETWORK_UART_RXD_BIT		(1<<0)
#define NETWORK_UART_TXD_BIT		(1<<1)

// Valid packet size is < 240 bytes of data
#define MUMAR_MAX_DATA_SIZE			100

/*============================================================================
 I2C / TWI.
============================================================================*/
// 2013-01-30 At the moment Master or slave, not both yet.
#define TWI_MASTER
//#define TWI_SLAVE
#define TWI_SLAVE_ADRESS		'b'		// General Call enabled by setting lsb.
#define TWI_BAUDRATE			0x0C	// TWI Bit rate Register setting.
#define TWI_MASTER_BUF_SIZE		100		// Master Transceiver buffer size.
#define TWI_TX_BUFFER_SIZE		5		// Slave Transmitter buffer size.
#define TWI_RX_BUFFER_SIZE		10		// Slave Recevier buffer size.
//#define TWI_PRESCALER		0x00

/*===========================================================================
 User definitions for: main.h
===========================================================================*/
#if PROJECT_NUMBER == PROJECT_DCX

#define PGA2311_DDR		DDRB
#define PGA2311_PORT		PORTB
#define PGA2311_PIN		PINB

#define DCX_IS_OFF_PORT		PORTB
#define DCX_IS_OFF_DDR		DDRB
#define DCX_IS_OFF_PIN		PINB
#define DCX_IS_OFF_BIT		(1<<0)

#define PGA2311_CLK_BIT		(1<<5)	// Clock pin for optocoupler on PGA2311.
#define	PGA2311_DATA_BIT	(1<<3)	// Data pin.
#define	PGA2311_CS_BIT		(1<<2)	// Chip Select.

#elif PROJECT_NUMBER == PROJECT_SMALL
#define PGA2311_DDR		DDRC
#define PGA2311_PORT		PORTC
#define PGA2311_PIN		PINC

#define PGA2311_MUTE_BIT	(1<<0)
#define PGA2311_CLK_BIT		(1<<1)	// Clock pin for optocoupler on PGA2311.
#define	PGA2311_DATA_BIT	(1<<2)	// Data pin.
#define	PGA2311_CS_BIT		(1<<3)	// Chip Select.

#define AMP_POWER_DDR		DDRD
#define AMP_POWER_PORT		PORTD
#define AMP_POWER_PIN		PIND
#define AMP_POWER_BIT_N		(1<<7)	// Setting the bit turns the LM2596 off.

One of the nice thing of such a file is that you often do not have to draw a schematic (such as in KiCad) any more. All connections to external hardware are in this file (And therefore also easy to move around for different projects). Works quite good for me.

 

Now I'm moving on to ARM I'm sometimes thinking about a different set of I/O macro's because the method above does not fit any more.

ARM I/O has a quite different port structure and i'm looking for an implementation of macro's which will work well in both (and other future) CPU architecture's.

 

FYI: This really makes my eyes roll:

ISR(PCINT1_vect) {
 enc1.Scan();
 _delay_ms(1);
}

It almost convinced me to move to another post without even bothering to post a reply here.

Maybe do some more projects and get some general experience before trying to write the best nextgen I/O library.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Sat. Jul 29, 2017 - 12:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your input and sharing your macro example. You make a valuable point, that’s good food for thought, the point for the post.

 

However, I see this as an empty statement. I understand you don’t like it. But why?

Paulvdh wrote:
FYI: This really makes my eyes roll:

 ISR(PCINT1_vect) 
 { 
   enc1.Scan(); 
   _delay_ms(1);
}

 

Can you elaborate on why this makes your eyes roll, is it style or do you see a major performance overhead that I don’t?

 

Paulvdh wrote:
Maybe do some more projects and get some general experience before trying to write the best nextgen I/O library.
 

I’m humbled you had the idea I trying to write the best nextgen I/O library. Like I said, I’m experimenting. Not building a communications satellite. Some might even say that’s a way to gain general experience.

 

Thanks again for sharing your thoughts, I always appreciate knowledgeable insight and constructive criticism!

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

Last Edited: Sun. Jul 30, 2017 - 12:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Putting a delay in an ISR! This is not recommended practice. You want interrupts for microsecond response. If you've having to put delays in there, then that suggests that the wrong technique is being used. Say you had a usart interrupt as well with the baud rate of 19200. You'll lose characters as your pcint isr stalls for 1ms.

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

That must be what makes Paulvdh roll his eyes. Lol, yes a crude attempt at debounce. It works fine without it, after rewriting the scan function...    

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

Last Edited: Sun. Jul 30, 2017 - 01:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm experimenting with C++ too. Templates are very cool, for example. With them, you can avoid the preprocessor in many cases.

Here is a blinky program for Mega 328P:

 

#define F_CPU 16000000UL
#include <util/delay.h>

#include "pins.h"

// this program is supposed to run on a Mega 168P/328P with a LED on pin B5
// (i. e., an Arduino)
int main(){
    typedef Pin<B,5> LED_pin;   //instead of creating an object
    LED_pin::as_output();       // set LED pin as output
    //blink
    while (1) {
        LED_pin::toggle();      // toggle LED
        _delay_ms(500);
    }
}

You can also do:

    Pin<D, 0, 1, 5, 7>::as_output();

This will set all the indicated pins of port D as outputs at the same time.

 

I attached the header file to make this work. The templates generate a class for each pin combination that is used, but I never actually instantiate any object, these classes only have static members. Also, every member of these classes is "constexpr", so that it can be evaluated at compile time. The whole thing is like a super-preprocessor, so it will generate well optimized code.

Attachment(s): 

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

I’m not too familiar with constexpr or templates. That is cool. I could see doing some interesting things, especially in the desktop world. Makes me want to learn more…

 

As far as using a pin class. From a high level perspective it’s neat and even a little fun. But Pualvdh got me thinking about the impact. Okay, there might be, what, 2 or 3 more instructions using variables... So, I created a new project with two functions. Both to set an io bit. One with defines the other with variables. I built it and then examined the generated assembly. Even though I don’t really understand it. It looks like there maybe 10 or more instructions using variables. Wow 10. So I came to the conclusion it’s Totally Unacceptable!

 

Thanks for all the responses…

_Z16SetBitWithDefinev:
.LFB2:
 .loc 1 24 0
 .cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
 .loc 1 25 0
 sbi 0x5,2
 ret
 .cfi_endproc
 
 
 
_Z18SetBitWithVariablePhh:
.LFB1:
 .file 1 ".././main.cpp"
 .loc 1 16 0
 .cfi_startproc
.LVL0:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
 .loc 1 17 0
 lds r30,port
 lds r31,port+1
 ld r18,Z
 ldi r24,lo8(1)
 ldi r25,0
.LVL1:
 rjmp 2f
 1:
 lsl r24
 rol r25
 2:
 dec r22
 brpl 1b
 or r24,r18
 st Z,r24
 ret
 .cfi_endproc

 

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

finis wrote:

As far as using a pin class. From a high level perspective it’s neat and even a little fun. But Pualvdh got me thinking about the impact. Okay, there might be, what, 2 or 3 more instructions using variables... So, I created a new project with two functions. Both to set an io bit. One with defines the other with variables. I built it and then examined the generated assembly. Even though I don’t really understand it. It looks like there maybe 10 or more instructions using variables. Wow 10. So I came to the conclusion it’s Totally Unacceptable!

Is that irony? I think that if these instructions

sbi 0x5,2
ret

becomes

 lds r30,port
 lds r31,port+1
 ld r18,Z
 ldi r24,lo8(1)
 ldi r25,0
.LVL1:
 rjmp 2f
 1:
 lsl r24
 rol r25
 2:
 dec r22
 brpl 1b
 or r24,r18
 st Z,r24
 ret

then I hope you agree that you have a big impact on performance, right? The problem is not only that the code size grows from 2 to 13 instructions (which may be acceptable in some situations), but you also have a conditional loop ("brpl"). You will have a performance degradation of more than 12x in terms of speed.

 

Maybe you could make a more realistic test, e.g. try toggling one or two pins as fast as possible. That would be really interesting.

 

I agree with previous posts; statically defining I/O pins is simple, adds no overhead and generates the fastest possible I/O using sbi/cbi instructions, etc. Besides, it is probably the most common way of doing I/O anyway. If you need to be able to adapt pin assignments for different applications, then you can achieve that by moving the pin definitions to a separate *.h file that is defined in the application context. I use that method quite a lot - not only for pin defs but also lots of other build options.

 

But sure, your class holds some value as a method of learning C++ and understanding what run-time overhead is introduced. In that context, I think that the class template that El Tangas showed is very relevant - it shows how you can write code to effectively move evaluation from run-time to compile-time. I wouldn't be surprised if that class has much less run-time overhead.

/Jakob Selbing

Last Edited: Mon. Jul 31, 2017 - 05:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I have to say it...

I think making a pin blink as an example of object-oriented programming is a bit silly.   Because "blinking" is fundamentally verb-ish, rather than object-like.

A "pin" isn't a bad object, but it doesn't go far enough (or something...)

 

How about an "Indicator" object?

Indicator <PORTD, PD5> pin13led;

// in setup?
  pin13led.setBrightness(50);  // set PWM frequency (if capable?)
  pin13led.setPeriod(1100);    // set blink period. 0 for continuous on/
  pin13led.setOntime(100);     // make it a "short" blink
  pin13led.on();               // turn it on.
    :

void loop() {
    pin13led.run();   // drive stuff that needs polling/etc.
}

 

Note that you could add/replace/inherit most of this for a speaker rather than an LED (replace/add setBrightness() with setTone() ?)

 

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

Upvoted Karman #4

For polling I/O the usual flow is to use a 1ms (10ms, whatever) timer tick interrupt to poll I/O without extra waiting.

 

Or as I do:

My timer tick interrupt is very small ( = fast) and it just increments a ms counter.

(Can you see the code between the #define's ? )

//===========================================================================
#ifndef __linux__
// Timer Compare interrupt.
#if defined (TIMER2_COMPA_vect)		// Avr's with 2 compare registers.
ISR( TIMER2_COMPA_vect)
#elif defined (TIMER2_COMP_vect)	// ATmega16, AVR_ATmega8 (single compare)
ISR( TIMER2_COMP_vect)
#endif
{
	TimerTicks++;
#ifdef USE_TIMER_CALLBACK
	Timer_Callback();
#endif
}
#endif // __linux__

Then I wrote a timer lib which can be used as a stopwatch, periodic timer etc.

My main() is usually very small and just checks a bunch of timers:


void main (void) {
    ...
    for( ;;) {
        if (timer1.expired())
            {...}
        if (timer2.expired())
            {...}
        if (timer3.expired())
            {...}
        if (timer4.expired())
            {...}
    }
}

Routines such as polling I/O are then executed on a regular interval (with some jitter), but without wasting valuable interrupt time.

 

Just as a practice in interrupt madness I did a test with a 3.6864MHz AVR and a 2000ppr quadrature encoder.

The encoder interrupts (both channels) were triggered on each flank, so 8000 interrupts / second.

I could rotate the encoder 200rpm without loosing count.

And all the time the timer interrupt was also ticking, keyboard being polled 7-segment display being multiplexed etc.

Can you imagine what would have happened if I was delaying a single ms in another interrupt?

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

I do like the pin template. Id like to see something more generic to set the port addresses, as it its not all that portable.

Then again I'm not sure something like a pin is ever going to be portable to anything.

 

If I wanted, and I do, a blink method it would not be in the pin object, it would sit on top of that and call pin. Then pin could be swapped out to support different hardware.

 

 

Keith Vasilakes

Firmware engineer

Minnesota

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

I agree with the previous posters that opine that abstracting just to the pin level is not that much of an improvement.

 

I'd like to think one (or several) levels further up. The abstraction could be as simple as an LED.

 

It is my opinion that code like:

led2.on();

is much clearer than

#define LED2PORT PORTB
#define LED2PIN  3

.
.
.

LED2PORT &= ~(1<<LED2_PIN); // ON - LED2 is active low!

So what I'd like is some sort of class and template class "base" that would allow me to do e.g. (sketchy!)

DigitalPin led2<PORTB, 3, DIRECTION_OUT, ACTIVE_LOW>;

and then (from above)

led2.on();

 

For a non OO/C++ take on this, see https://github.com/ataradov/mcu-... . Alex uses C and (the venerable) macros, but the test program has very "pretty" code like

HAL_GPIO_PIN(LED,      A, 14)
HAL_GPIO_PIN(BUTTON,   A, 15)

   .
   .
   .
   
  HAL_GPIO_LED_out();
  HAL_GPIO_LED_clr();

  HAL_GPIO_BUTTON_in();
  HAL_GPIO_BUTTON_pullup();
  
  .
  .
  .
  
  
  if (HAL_GPIO_BUTTON_read()) ...
  
  .
  .
  .
  
  HAL_GPIO_LED_toggle();

Add a little C++ to that and it will be really pretty.

 

*For buttons you could of-course "hide the debouncing". For a UART there'd be other member functions. It might even inherit from a base class implementing e.g. circular buffers. Same base class could be used for other serial transmitter/receiver hardwre like I2C..

 

So for me, the HAL level is probably the first abstraction level. Written with care I'd expect the overhead to be small or none at all.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

For pins that are known at compile time, there are a number of ways of dealing with that efficiently in C or C++, I don't think it needs a lot of abstraction.

 

What I am more interested in is the most efficient way (code size, execution time) to handle variable pins. There are occasions where I want to have the same firmware running on a few different board variants, and it would be useful to configure at run time. I can encode port number and bit into a byte, but there doesn't seem to be a fast way to use that to access the bit. I am resigned to having some significant penalty for run-time config, if I need fast bit banging on different pins, I guess I would write a different function for each pin supported, at the cost of Flash size.

Bob. Engineer and trainee Rocket Scientist.

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

donotdespisethesnake wrote:
I can encode port number and bit into a byte, but there doesn't seem to be a fast way to use that to access the bit. I am resigned to having some significant penalty for run-time config,

Encode in two bytes. First byte holds the port address, second holds the bitmask for the pin. Depending on how you count that should give only a small overhead (in absolute number of flash bytes and CPU clocks), or possibly a huge overhead (if given as the fraction of the cost of the simple thePort |= (1<<thePinNumber);

 

donotdespisethesnake wrote:
I don't think it needs a lot of abstraction.

The point of my abstraction (the HAL) above is to clear the "main" code (application layer, or whatever you want to call it) of anything "low-level physical" like port names and pin numbers. For me it comes down to optimizing the code for readability and maintenance/change.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

It is my opinion that code like:

led2.on();

In plain C I can arrange:

int main(void) {
    RED_LED = ON;
}

and it turns on. So the rest of it is just "syntactic sugar" surely?

 

This is the thing about C++ for AVR. No one can ever really seem to provide a concrete angle where things are "noticeably better" because of the use of C++.

 

One of the best example I have seen I suppose is the inheritance hierarchy of the Arduino Serial class. It inherits from Stream and it inherits from Print and some of the print(unsigned char), print(float), etc etc could be just as usuable for an LCD as a UART and perhaps other "output devices". So this actually shows a concrete example where you engineer the core of print(float) and then for no work you can apply that to TextLcd or GLCD or Serial or ...

 

That really show the "power" of C++ as applied to AVR.  But turning

    RED_LED = ON;

into

ledRed.on();

and things like that are nothing more than syntax. Apart from the fact you are invoke a method of an object what makes this C++ implementation "better". How do you convince C "luddites" that this brave new world is worth the effort?

 

One day some will post an ADC or a Timer or some such class here that makes me think "hey, now that is why you should be using C++ on an AVR" but I have yet to see it and I fear GPIO pins are not going to cut it.

 

Perhaps "Timer" is one worth exploring. You generally have 3+ of the things and many common things you might want to do for all so that suggests a base class (virtual?) that holds the common stuff and then individual classes to separate out the unique bits of a particular timer (like 8 vs 16 bit) and they all inherit from the base? I suppose that's why UART/Serial is shown as an example because you often have 2+ of them?

 

On that basis maybe IOPin is a candidate for this but apart from setting the direction and pull-up etc what common thing would you have in a base class that could be useful to derived classes? As many have shown (and especially for the purposes of  getting the efficient SBIs an so on) it's more templates than anything that might be useful for IO lines but then you look back to Peter Fleury's:

/*
** constants/macros
*/
#define DDR(x) (*(&x - 1))      /* address of data direction register of port x */
#if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
    /* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
    #define PIN(x) ( &PORTF==&(x) ? _SFR_IO8(0x00) : (*(&x - 2)) )
#else
	#define PIN(x) (*(&x - 2))    /* address of input register of port x          */
#endif

which shows that nothing in this world is really new: C++ templates are just fancy pre-pro macros after all ;-)

Last Edited: Mon. Jul 31, 2017 - 05:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, the

led2.on();

is a "degenrated" example. But something as basic like

DigitalPin led2<PORTB, 3, DIRECTION_OUT, ACTIVE_LOW>;

still is an improvement of readability, and removal of "physical clutter" from the code.

 

For more complex classes, e.g. a buffering serial class, it makes even more sense to "hide" the implementation in a class. The first priniple of OO is implementation hiding. And when we put such things in a HAL, then it makes sense to put everything in a HAL so that syntax is uniform. I.e. I'd rather have

 

if (serial2.dataReady()) {
    led2.on();
}

than

if (serial2.dataReady()) {
    LED2 = ON;
}

In another thread I spoke about how need (indeed love) things to be "systematic.

 

There are several arguments for OO, and in my world one of them is "source code quality". A single

led2.on();

does not make for better code, but when all of the code follows the same principle for how it is written then that is a huge improvement in code quality IMO.

 

I have not written any of the classes you want to see. Yet.. (ATM I'm fully occupied with something else, as you know ;-) ). I appeal to your imagination of how e.g. these would make for neater code (and allow for code reuse):

  • Abstract serial I/O device
  • USART serial device, inherits from the above
  • I2C serial device, inherits...
  • ...

 

  • The Abstract serial IO above could use a circular buffer implemented in it's own class (composition, not inheritance)

 

  • The ADC you yourself mentioned
  • Debounced set of buttons
  • State machine

 

  • Anything complicated like USB or wireless (think LUFA or LwMesh) (lots of internal state!) And inside e.g. LwMesh there are different types of complex data, (think packets) that are manipulated and "inspected" using plain old C functions getting passed e.g. the packets as parameters.
  • Anything which have "state" and which you need to "have more than one of" (C++, or most any OO language, will relieve you from explicitly passing structs for the instances around).

 

 But this is the same-old-same-old we've had so many times before. I won't prevail. But I hope to come back with something useful and neat when I can return to spend some of my time with the SAM D20 again. ;-) 

 

BTW: As a side comment, one of the goodies of C++ is namespaces (and remember that every class is a separate namespace). The risk for naming collisions is reduced to a high degree.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

clawson wrote:
How do you convince C "luddites" that this brave new world is worth the effort?

I guess it’s kind of like cars. You don’t need a Cadillac to get to work, but you may prefer the ride, and it does a good job. A budget 15 year old Geo Metro will work and cost less to operate. Maybe you prefer a high end exotic sport car. Yep, is cost more. But if need the power to avoid an accident, it’s there. Be careful with that powerful car, especially on residential streets. It can get you in trouble as quick as it gets you out. With great power comes great responsibility. The driver is in control, not the car. Please, drive with caution…

 

After all isn't C++ just C with Classes(or at least it was in the late 70s). To me C++ AVR code looks a lot like C. I recommend a test drive. No pressure to buy.

...

 

 

It’s clear to me now, that using defines over variables offers a significant performance advantage. Declaring defines in a separate file is not difficult to implement or read. It works great until you need to declare more than one instance of the class. Such as a rotary encoder class.

 

One way I found to handle this situation. Is to subclass it and override key functions, for each encoder. There may be a more elegant solution. But this seems to be working well and there’s not much boilerplate editing, etc..

i.e.


class REnc2 : public RotaryEncoder
{
 public:
 #define _pina PORTC2
 #define _pinb PORTC3
 #define _ppin PINC
 #define _port PORTC
 #define _ddr  DDRC
 void Init()
 {
    _port |= ((1<<_pina)|(1<<_pinb));
    _ddr &= ~((1<<_pina)|(1<<_pinb));
 }

 void Scan()
 {
    gray = (gray<<2) | (((_ppin & (1<<_pina)) >> _pina)|((_ppin & (1<<_pinb)) >> _pinb-1));
    ProcessGray();
 }

};
REnc2 enc2;

 

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

As El Tangas got me interested in wanting to learn more about C++ templates. I just did a search on Amazon Books for 'C++ Templates' and I saw this relevant book by Christopher Kormanyos. Thought it may interest some of you. ​

 

https://www.amazon.com/Real-Time-Efficient-Object-Oriented-Microcontroller-Programming/dp/3662478099/ref=sr_1_5?s=books&ie=UTF8&qid=1501553622&sr=1-5&keywords=c%2B%2B+template

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

Last Edited: Tue. Aug 1, 2017 - 02:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So ... why is template metaprogramming. and templates in general, discouraged for embedded use?
I would think they would be just the thing for gaining some extra efficiency out of compile-time constructs, while being somewhat less ugly than trying to do similar things with normal C macros (which people do all the time, anyway.   Look at some of the "fastDigitalWrite()" implementations that have cropped up for Arduino, using __builtin_constant_p() and a long string of verbose math and/or logic, all "ok" because it will evaluate at compile time...)

 

 

 

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

finis wrote:

It’s clear to me now, that using defines over variables offers a significant performance advantage. Declaring defines in a separate file is not difficult to implement or read. It works great until you need to declare more than one instance of the class. Such as a rotary encoder class.

 

One way I found to handle this situation. Is to subclass it and override key functions, for each encoder. There may be a more elegant solution. But this seems to be working well and there’s not much boilerplate editing, etc..

i.e.


class REnc2 : public RotaryEncoder
{
 public:
 #define _pina PORTC2
 #define _pinb PORTC3
 #define _ppin PINC
 #define _port PORTC
 #define _ddr  DDRC
 void Init()
 {
    _port |= ((1<<_pina)|(1<<_pinb));
    _ddr &= ~((1<<_pina)|(1<<_pinb));
 }

 void Scan()
 {
    gray = (gray<<2) | (((_ppin & (1<<_pina)) >> _pina)|((_ppin & (1<<_pinb)) >> _pinb-1));
    ProcessGray();
 }

};
REnc2 enc2;

That is an interesting example, but I think you are missing some points. For example, the Init and Scan functions look like generic methods, right? If so it should go into the base class. To achieve that you would need to get the sub-class' port and pin defs into the base class. The proper way to do that if you need multiple instances of the class is to use the template method (like El Tangas' example). It is possible also to provide them as parameters to the base class constructor, but that will probably incur run-time overhead.

 

If you only need one instance, or possibly multiple instances but with same port and pin defs, then you could move the defs into an application-specific *.h file instead.

 

IMHO, we all have different opinions regarding code styles but what really matter is:

  • performance
  • footprint size
  • re-usability

 

westfw wrote:

So ... why is template metaprogramming. and templates in general, discouraged for embedded use?

Who told you that?

/Jakob Selbing

Last Edited: Tue. Aug 1, 2017 - 06:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

finis wrote:
It’s clear to me now, that using defines over variables offers a significant performance advantage.

Can we see a small example demonstrating this?

 

E.g. where your variables declared 'const', or even (since C++11) 'constexpr', actually making them compile time constants? 

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

This discussion is very interesting. But, has drifted from the topic of use of a pin class. I’ve started a new thread: A cup of AVR, would you like cream & C++ with that?

https://www.avrfreaks.net/forum/cup-avr-would-you-cream-c

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

finis wrote:

This discussion is very interesting. But, has drifted from the topic of use of a pin class. I’ve started a new thread: A cup of AVR, would you like cream & C++ with that?

https://www.avrfreaks.net/forum/cup-avr-would-you-cream-c

You still didn't try out what I suggested in my first post: compare the performance of your pin class with the direct I/O manipulation by toggling a pin as fast as possible in a loop, like this:

for (unsigned long i = 0; i < 1000000; i++)
PORTA ^= (1 << LED_PIN);

It will take you not more than 5 minutes, and it will objectively show how well your pin class performs, and it is very much on subject. Then study the generated assembly. Then maybe try creating a C++ class template that does the same thing but with same performance as the direct I/O manipulation (which is possible).

 

You will probably realize that the problem is not C++ itself, but the way you use it. You could write equivalent C code that has just as bad performance as your pin class.

/Jakob Selbing

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

It may even be faster to toggle a pin like this:

for (unsigned long i = 0; i < 1000000; i++)
PINA = (1 << LED_PIN);

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

ki0bk wrote:
It may even be faster to toggle a pin like this: [...]

...on selected AVRs.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:

 

So what I'd like is some sort of class and template class "base" that would allow me to do e.g. (sketchy!)

DigitalPin led2<PORTB, 3, DIRECTION_OUT, ACTIVE_LOW>;

and then (from above)

led2.on();

 

Turns out it is not possible to directly pass SFRs as template arguments. The best I could find was a trick: https://stackoverflow.com/questions/40774632/passing-an-io-register-as-template-parameter

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

El Tangas wrote:
not possible to directly pass SFRs as template arguments
You mean SFRs as they are defined in <avr/io.h> ? If they were defined in some other way I'm sure they could be passed ;-)

 

Actually I am 99.9% certain I have seen a post on  Freaks a long time ago that talked about C++ templates and it was generating really "tight" code by doing exactly that and passing the io.h defined SFRs as template arguments so I would question the statement anyway.

 

(bet search will never find the thing I'm thinking of - maybe symbolhound.com will allow a search involving "C++", "<", ">" and so on that might typically occur in such a post about templates?)

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

clawson wrote:
You mean SFRs as they are defined in <avr/io.h> ?

 

Yes, of course, because it would be much better if we could reuse all that code.

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

What I was actually thinking of was:

 

https://www.avrfreaks.net/forum/b...

 

cheeky

(thinks: must update that with latest XML from Atmel).

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

I use classes for the various I/O devices.  I don't know how that compares with using ASF.  Someday I might investigate more but the thought of swimming through ASF is a bit discouraging.

 

I wonder if avr-gcc couldn't add something to convert the C++ code to something more efficient whenever that's possible.  Something similar to the way it knows when ASF style code is referencing low memory where shorter instructions can be used.

 

Writing the most efficient code is not always better than writing code that is easier to understand and gets to market faster. 

 

 

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

How very curious, you actually use C++ bit want to perpetrate this myth that it is inefficient.
.
What makes a language inefficient is how it's used not the language itself. You can write hugely inefficient C or even Asm too.
.
If used correctly C++ should be capable of the same efficiency as C. (neither can quite match asm)

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

Does the compiler recognize low memory is being accessed when the access is via the this pointer?

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

steve17 wrote:

Does the compiler recognize low memory is being accessed when the access is via the this pointer?

 

Everything is fine with the this pointer, but I don't like the generated code.

 

#include <avr/io.h>

struct sfr {
    volatile uint8_t a;
    volatile uint8_t test (){
        return this->a;
    }
};

int main(){
    //sfr base = *(sfr *) &PORTB;
    sfr *base = (sfr *) &PORTB;
    //base.a = 3;
    base->a = 3;
    //return base.test();
    return base->test();
}

 

The generated code is well optimized, but what about volatile? It was ignored. I'm sure this is the intended behavior, since the probability of being a bug is insignificant, but I don't like it anyway.

 

0000007a <main>:
  7a:   83 e0           ldi     r24, 0x03       ; 3
  7c:   90 e0           ldi     r25, 0x00       ; 0
  7e:   08 95           ret

 

Edit: Never mind, it has to be done with pointers. I better just call it a night. Code above is corrected, now.

Generated code:

0000007a <main>:
  7a:   83 e0           ldi     r24, 0x03       ; 3
  7c:   85 b9           out     0x05, r24       ; 5
  7e:   85 b1           in      r24, 0x05       ; 5
  80:   90 e0           ldi     r25, 0x00       ; 0
  82:   08 95           ret

 

Last Edited: Wed. Aug 2, 2017 - 09:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see that use of the in and out instructions.  That surprised me.  I had just assumed the C++ compiler wouldn't recognize low memory when using a "this" pointer.  Maybe it didn't when I started using it many moons ago.

 

How many this pointers are in use in the world as I write this?  I'd guess billions.  How many have values that are known at compile time?  I'd guess somewhere around 0.000 %.  Avr-gcc is on the ball!

 

The Xmega doesn't have regular ports at low memory but it does have "virtual" ports.  I've never bothered with virtual ports, but I tried it for this occasion.

 

   (new Virtual_port_0_regs)->Set_out_data(3);        // virtual port 0 at address 0x10
   u_int8 in_data = (new Virtual_port_0_regs)->Get_in_data();

 

 


   void Set_out_data(u_int8 data)   {
      Out_data = data;
 236: 83 e0        ldi r24, 0x03 ; 3
 238: 81 bb        out 0x11, r24 ; 17
      }

   u_int8 Get_in_data()   {
      return In_data;
 23a: 82 b3        in r24, 0x12 ; 18

 

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

Perhaps I'm a tad slow but how do you get this to work in C:

 

   RED_LED = ON;

if you have more than 1 LED on same port ON needs to have different values or ?

 

you can define something like

RED_LED_ON

RED_LED_OFF 

 

ok I get it it's with a union 

Last Edited: Thu. Aug 3, 2017 - 12:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
ok I get it it's with a union
Not my solution - what I had in mind was a bit struct. You know the sort of thing...

#include <avr/io.h>

typedef struct {
	int b0:1;
	int b1:1;
	int b2:1;
	int b3:1;
	int b4:1;
	int b5:1;
	int b6:1;
	int b7:1;
} bits_t;

#define PortB (*(volatile bits_t *)&PORTB)

#define RED_LED PortB.b3

#define OFF 0
#define ON 1

int main(void) {
	while (1) {
		RED_LED = ON;
		RED_LED = OFF;
	}
}
0000006c <main>:
#define OFF 0
#define ON 1

int main(void) {
        while (1) {
                RED_LED = ON;
  6c:   c3 9a           sbi     0x18, 3 ; 24
                RED_LED = OFF;
  6e:   c3 98           cbi     0x18, 3 ; 24
  70:   fd cf           rjmp    .-6             ; 0x6c <main>

 

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

The other thing I wonder about is what happens when the this pointer has a value that is not in low memory.  In this case it seems the compiler uses the this pointer.  At least I guess that is what the Z register is for.

 

I don't know much about the instruction set and I don't know much about ASF.  The C++ compiler uses 4 instructions for the Set if the Z register is not already loaded.  I suppose it would use 3 instructions for the Get if the Z register hasn't been previously set.  How does ASF do it?

 

   (new Port_c_regs)->Set_out_data(7);        // address 0x640
   u_int8 in_data = (new Port_c_regs)->Get_in_data();

 

 


   void Set_out_data(u_int8 data)   {
      Out_data = data;
 236: e0 e4        ldi r30, 0x40 ; 64
 238: f6 e0        ldi r31, 0x06 ; 6
 23a: 87 e0        ldi r24, 0x07 ; 7
 23c: 84 83        std Z+4, r24 ; 0x04
         }
      }


   Register Get_in_data()   {
      return In_data;
 23e: 80 85        ldd r24, Z+8 ; 0x08

 

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

What are you talking about? this always points to an instance of some class' member variables in RAM. It will never point to "low memory"

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

It points to whatever I want it to point too.  In the case of I/O devices, it points to the I/O device's address.

 

Here is the Virtual_port_0_regs class, and the Port_c_regs class.

 


#pragma once

#include <stdlib.h>     // for size_t in new and delete operators
#include "virtual_port_regs.h"
#include <avr/io.h>


class Virtual_port_0_regs : public Virtual_port_regs   {
public:

   void* operator new(size_t objsize)   {
      return (void*)_SFR_ADDR(VPORT0);            // assign actual memory location of the registers
      }

   };

 

 


#pragma once

#include <stdlib.h>     // for size_t in new and delete operators
#include "port_regs.h"
#include <avr/io.h>


class Port_c_regs : public Port_regs   {
public:

   void* operator new(size_t objsize)   {
      return (void*)_SFR_ADDR(PORTC);            // assign actual memory location of the registers
      }

   };

 

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

Wow. Like crazy!!

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

Here is what my Virtual_port_regs class looks like.  It's quite simple.  I could also give you my Port_regs class.  It's a lot bigger because it has a whole lot of registers.  The Virtual ports only have a small subset.

 

EDIT:

Actually it is lacking some functions.  I just wrote the virtual stuff to give me something in low memory for testing.  I could add more functions, but because I have no plans to use virtual ports, I'll probably never write them.

 


#pragma once

#include "myTypes.h"

class Virtual_port_regs   {
public:

   void Set_out_data(u_int8 data)   {
      Out_data = data;
      }

   u_int8 Get_in_data()   {
      return In_data;
      }

   volatile Register Direction_out;                  // DIR            0
   volatile Register Out_data;                       // OUT            1
   volatile Register In_data;                        // IN             2
   volatile Register interrupt_request;              // INTFLAGS       3
   };

 

Last Edited: Thu. Aug 3, 2017 - 07:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's called C++, although Stroustrup called it C with classes.   If you don't got no classes you got C.

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

Is it better to use _SFR_ADDR() instead of the & operator?

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

El Tangas wrote:

Is it better to use _SFR_ADDR() instead of the & operator?

I have no freakin' idea.  I just tried stuff until I found something that worked.  wink  I forgot, if I ever knew, what _SFR_ADDR is.  And I don't know exactly what something like PORTC is.   The name doesn't explain much to me, but I gave up complaining about names other people use long ago.

 

If you can figure out something better, let me know. 

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

steve17 wrote:

  I just tried stuff until I found something that worked.  wink  I forgot, if I ever knew, what _SFR_ADDR is.

 

I refuse to believe it's possible to bang on a keyboard until a working C++ program emerges cheeky not enough time on a human lifespan. So I checked that indeed, at some time in the past, you knew the purpose of _SFR_ADDR.

 

And well, I really don't have anything better, just equivalent, I suppose. Replacing

return (void*)_SFR_ADDR(PORTC);

by

return (void*) &(PORTC);

 

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

That was a long time, he says something completely different today :-)

 

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

This is my attempt to avoid the preprocessor using C++. It's not very pretty, but it's possible. Fortunately, the auto type reduces the ugliness (the explicit type is DigitalPin<PB5>).

 

#include <avr/io.h>

template <int b>
struct DigitalPin  {
    volatile uint8_t reg;
    void operator= (bool n){
        reg = reg & ~(1<<b) | (n<<b);
    }
};

int main(){
    auto &LED = *(DigitalPin<PB5> *) &(PORTB);
    
    LED = true;
    LED = false;
}

 

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

El Tangas wrote:

So I checked that indeed, at some time in the past, you knew the purpose of _SFR_ADDR.

That must have been a different steve17, or it could have been me back before my brain wore out.

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

jaksel wrote:
Then maybe try creating a C++ class template that does the same thing but with same performance as the direct I/O manipulation (which is possible).

The time I have experimenting with this is limited. But I did manage to convert my rotary encoder class to a template version. It seems to be performing much better when spinning an encoder very fast. It keeps a tighter count. 

template <uint16_t port, uint8_t pinA, uint8_t pinB>
class RotaryEncoder
{ 
 uint8_t ticks;
 uint8_t upCnt;
 uint8_t dnCnt;
 uint8_t lastTick;
 uint8_t gray=0;

  public:
 int16_t val;
 int16_t valUpperLimit = 100;
 int16_t valLowerLimit = -100;
 int8_t lastDirection = -1;
 uint8_t ticksPerClick = 4;
 uint8_t callbackEnabled;
 typedef void (*encCallback)(int8_t direction, int16_t _val);
 encCallback callback;

 RotaryEncoder()
 {
  *reinterpret_cast<volatile uint16_t*>(port) |= (1<<pinA)|(1<<pinB);
  *reinterpret_cast<volatile uint16_t*>(port-1) &= ~(1<<pinA)|(1<<pinB);
 }

 void InitCallback(encCallback _cb)
 {
  callbackEnabled = true;
  callback = _cb;
 }
 
 void Scan()
 {
  gray <<= 2;
  if(*reinterpret_cast<volatile uint16_t*>(port-2) & (1<<pinA))  gray |= (1<<1);
  else gray &= ~(1<1);
  
  if(*reinterpret_cast<volatile uint16_t*>(port-2) & (1<<pinB))  gray |= (1<<0);
  else gray &= ~(1<0);

  gray &= 0X0F;

  switch(gray)
  {
   case 0x08:
   case 0x01:
   case 0x07:
   case 0x0E:
   dnCnt = 0;
   if(++upCnt >= 4)
   {
    if(val < valUpperLimit) val++;
    upCnt=0;
    Change(UP);
   }
   break;

   case 0x0D:
   case 0x04:
   case 0x02:
   case 0x0B:
   upCnt = 0;
   if(++dnCnt >= 4)
   {
    if(val > valLowerLimit) val--;
    dnCnt =0;
    Change(DOWN);
   }
   break;
  }

  lastTick = ticks;
 }

  void Change(int8_t direction)
 {
  if(callbackEnabled)
  {
   callback(direction,val);
  }
 }
};

Then I initialize multiple instances with different parameters...

RotaryEncoder<(uint16_t)&PORTC,0,1> enc1;
RotaryEncoder<(uint16_t)&PORTC,2,3> enc2;

 

jaksel wrote:
You could write equivalent C code that has just as bad performance as your pin class.

I believe a lot of my c code may fit the bill. I appreciate you guys... 

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

Rather than have a flag to say the callback is enabled, the usual technique is to test the function pointer for null. If not null, then it is assumed it is valid.

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

So simple and sweet. Why didn't I see that... ty

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

Kartman wrote:
If not null,
nullptr ;-)

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

 

 

 

 

El Tangas wrote:

 

And well, I really don't have anything better, just equivalent, I suppose. Replacing

return (void*)_SFR_ADDR(PORTC);

 

by

return (void*) &(PORTC);

 

 

I like it.  Simpler is better.  You can save a couple of parentheses too.  A byte saved is a byte earned, you know.  I think Ben Franklin said it.

      return (void*)&PORTC;            // assign actual memory location of the registers

 

 

There is an anomaly though with the Xmega iox.....h files that I find interesting.  The Xmegas have an I/O device called CPU.  I guess the Mega doesn't have it.  Atmel doesn't define CPU though.  It has dozens of defines for each individual CPU register, but CPU itself isn't defined.  I reported it to Atmel years ago but it's still not there.  As I recall, it did exist for some AVRs at one time.

 

This suggests to me that nobody else uses these addresses so they must not program their AVRs the way I do. 

 

Anyway when I specify the CPU address as 0x30, I need to use the _SFR_ADDR thing.  If I use the &, I get an error "lvalue required as unary '&' operand".

 

 

EDIT:  Now I get it.  I don't need anything there when I use a number.   

      return (void*)0x30;               //  Atmel doesn't define CPU.
 

Last Edited: Sat. Aug 5, 2017 - 11:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I took a look at your code and changed it around a bit:

 

#include <cstdint>

 

template <volatile uint8_t* reg, int bit>

struct RegitserBit {

static bool set(bool state){

*reg = *reg & ~(1<<bit) | (state<<bit);

}

};

 

volatile uint8_t sim_port_a = 0; // This is a dummy to illustrate the usage.

 

typedef RegitserBit<&sim_port_a, 5> LED;

 

int main(){

LED::set(true);

}

The LED::set only produces this code (on a intel cpu):

 

        movzbl  sim_port_a(%rip), %eax
        orl         $32, %eax
        movb    %al, sim_port_a(%rip)

 

Since it is only a type with a static method, no instance data is allocated, the above assembly code is the only that gets generated.

 

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

A even more complete template:

template <volatile uint8_t* reg, int bit>

struct RegitserBit{

static void set(bool state){

*reg = *reg & ~(1<<bit) | (state<<bit);

}

 

static bool get() {

return (*reg & (1<<bit)) != 0;

}

 

static void toggle() {

*reg ^= 1<<bit;

}

};

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

cap4096 wrote:
That was a long time, he says something completely different today

That was just a campaign promise, not intended to have any basis in fact.  When brought up now, it is easily decried as "fake news".

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:

That was just a campaign promise, not intended to have any basis in fact.  When brought up now, it is easily decried as "fake news".

I'm taking the fifth, as soon as I BleachBit my hard drive.

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

cap4096 wrote:

A even more complete template:

template <volatile uint8_t* reg, int bit>

struct RegitserBit{

static void set(bool state){

*reg = *reg & ~(1<<bit) | (state<<bit);

}

 

static bool get() {

return (*reg & (1<<bit)) != 0;

}

 

static void toggle() {

*reg ^= 1<<bit;

}

};

 

It seems that simple, but it isn't. Try to pass an actual memory mapped register to the template, instead of a test variable. In AVR gcc, it fails.

 

Edit: in fact, I tried to do it in several ways. You can either do like my example in #47, or you need to pass the address as an int, then cast to volatile uint8_t* inside the member functions. Problem is, some registers are 8bit, others are 16 bit.

Last Edited: Mon. Aug 7, 2017 - 12:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
There is an anomaly though with the Xmega iox.....h files that I find interesting. The Xmegas have an I/O device called CPU. I guess the Mega doesn't have it. Atmel doesn't define CPU though. It has dozens of defines for each individual CPU register, but CPU itself isn't defined.
Yeah and those are:

/* CPU - CPU registers */
#define CPU_CCP  _SFR_MEM8(0x0034)
#define CPU_RAMPD  _SFR_MEM8(0x0038)
#define CPU_RAMPX  _SFR_MEM8(0x0039)
#define CPU_RAMPY  _SFR_MEM8(0x003A)
#define CPU_RAMPZ  _SFR_MEM8(0x003B)
#define CPU_EIND  _SFR_MEM8(0x003C)
#define CPU_SPL  _SFR_MEM8(0x003D)
#define CPU_SPH  _SFR_MEM8(0x003E)
#define CPU_SREG  _SFR_MEM8(0x003F)

and things like:

/* CPU.SREG  bit masks and bit positions */
#define CPU_I_bm  0x80  /* Global Interrupt Enable Flag bit mask. */
#define CPU_I_bp  7  /* Global Interrupt Enable Flag bit position. */

#define CPU_T_bm  0x40  /* Transfer Bit bit mask. */
#define CPU_T_bp  6  /* Transfer Bit bit position. */

#define CPU_H_bm  0x20  /* Half Carry Flag bit mask. */
#define CPU_H_bp  5  /* Half Carry Flag bit position. */

#define CPU_S_bm  0x10  /* N Exclusive Or V Flag bit mask. */
#define CPU_S_bp  4  /* N Exclusive Or V Flag bit position. */

#define CPU_V_bm  0x08  /* Two's Complement Overflow Flag bit mask. */
#define CPU_V_bp  3  /* Two's Complement Overflow Flag bit position. */

#define CPU_N_bm  0x04  /* Negative Flag bit mask. */
#define CPU_N_bp  2  /* Negative Flag bit position. */

#define CPU_Z_bm  0x02  /* Zero Flag bit mask. */
#define CPU_Z_bp  1  /* Zero Flag bit position. */

#define CPU_C_bm  0x01  /* Carry Flag bit mask. */
#define CPU_C_bp  0  /* Carry Flag bit position. */

but for tiny/mega there is:

#include <avr/common.h>

which includes:

#if __AVR_ARCH__ >= 100
#  ifndef SPL
#    define SPL _SFR_MEM8(0x3D)
#  endif
#  ifndef SPH
#    define SPH _SFR_MEM8(0x3E)
#  endif
#  ifndef SP
#    define SP _SFR_MEM16(0x3D)
#  endif
#elif __AVR_ARCH__ != 1 
#  ifndef SPL
#    define SPL _SFR_IO8(0x3D)
#  endif
#  if XRAMEND < 0x100 && !defined(__COMPILING_AVR_LIBC__)
#    ifndef SP
#      define SP  _SFR_IO8(0x3D)
#    endif
#  else
#    ifndef SP
#      define SP  _SFR_IO16(0x3D)
#    endif
#    ifndef SPH
#      define SPH _SFR_IO8(0x3E)
#    endif
#  endif /* XRAMEND < 0x100 && !defined(__COMPILING_AVR_LIBC__) */
#endif /* __AVR_ARCH__ != 1 */


/* Status Register */
#ifndef SREG
#  if __AVR_ARCH__ >= 100
#    define SREG _SFR_MEM8(0x3F)
#  else
#    define SREG _SFR_IO8(0x3F)
#  endif
#endif

and

/* SREG bit definitions */
#ifndef SREG_C
#  define SREG_C  (0)
#endif
#ifndef SREG_Z
#  define SREG_Z  (1)
#endif
#ifndef SREG_N
#  define SREG_N  (2)
#endif
#ifndef SREG_V
#  define SREG_V  (3)
#endif
#ifndef SREG_S
#  define SREG_S  (4)
#endif
#ifndef SREG_H
#  define SREG_H  (5)
#endif
#ifndef SREG_T
#  define SREG_T  (6)
#endif
#ifndef SREG_I
#  define SREG_I  (7)
#endif

also:

/* AVR 6 Architecture */
#  if __AVR_ARCH__ == 6
#    ifndef EIND
#      define EIND  _SFR_IO8(0X3C)
#    endif
/* XMEGA Architectures */
#  elif __AVR_ARCH__ >= 100
#    ifndef EIND
#      define EIND  _SFR_MEM8(0x3C)
#    endif
#  endif

So some of the stuff is there but with different (original) names.

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

 

 

Yeah, another reason I don't use C.  For me, all that is needed is CPU, if it was there.  I don't use anything else in the iox.....h file.  I don't use ASF either.   Here are my cpu registers.  (Cpu_regs)  

 


#pragma once

#include <stdlib.h>     // for size_t in new and delete operators
#include <avr/io.h>
#include "myTypes.h"

class Cpu_regs   {

   enum Signature   {
      SPM = 0x9d,
      IOREG = 0xD8,
      };

   Register reserved_0;                       // Res   0x00
   Register reserved_1;                       // Res   0x01
   Register reserved_2;                       // Res   0x02
   Register reserved_3;                       // Res   0x03

   volatile Register configuration_change_protection;  // CCP   0x04

   Register reserved_5;                       // Res   0x05
   Register reserved_6;                       // Res   0x06
   Register reserved_7;                       // Res   0x07

   volatile Register extended_direct_addressing;       // RAMPD 0x08

   volatile Register extended_x_pointer;               // RAMPX 0x09

   volatile Register extended_y_pointer;               // RAMPY 0x0a

   volatile Register extended_z_pointer;               // RAMPZ 0x0b

   volatile Register extended_indirect;                // EIND  0x0c

   volatile Register     stack_p_low;                  // SPL   0x0d   // read low byte first
   volatile Register     stack_p_high;                 // SPH   0x0e

   struct Status   {
      Register carry                       : 1;  // C
      Register zero                        : 1;  // Z
      Register negative                    : 1;  // N
      Register twos_complement_overflow    : 1;  // V
      Register sign                        : 1;  // S
      Register half_carry                  : 1;  // H
      Register bit_copy_storage            : 1;  // T
      Register global_interrupt_enable     : 1;  // I
      };

   volatile Status status;                             // SREG  0x0f

public:
   void Disable_configuration_change_protection()   {
      configuration_change_protection = IOREG;
      }

   void Clear_extended_indirect()   {
      extended_indirect = 0;
      }

   bool Is_extended_indirect()   {          // for bootloader
      return(extended_indirect != 0);
      }

   void* operator new(size_t objsize)   {
//    return (void*)_SFR_ADDR(CPU);        // assign actual memory location of the registers
      return (void*)(0x30);                // Atmel doesn't define CPU.
      }

   };

 

 

 

And my registers are smart.  Or I should say my registers classes are.   Here is my Reset class.  Notice I can do a software reset just by calling the Do_software_reset function.  I don't have to know configuration change protection (CCP) must be disabled because the Reset class knows.

 


#ifndef RESET_REGS_H
#define RESET_REGS_H

#include <stdlib.h>     // for size_t in new and delete operators
#include <avr/io.h>
#include "myTypes.h"
#include "cpu_regs.h"

class Reset_regs   {

private:
   union Status   {
      struct  {
         Register power_on                    : 1;  // PORF
         Register external                    : 1;  // EXTRF
         Register brownout                    : 1;  // BORF
         Register watchdog                    : 1;  // WDRF
         Register PDI                         : 1;  // PDIRF
         Register software                    : 1;  // SRF
         Register reserved                    : 2;  // Reserved
         } bits;
      u_int8 byte;
   };

   volatile Status status;                             // STATUS  0x00

   struct Control   {
      Register software                    : 1;  // SWRST
      Register reserved                    : 7;  // Reserved
      };

   volatile Control control;                           // CTRL    0x01

public:

   void Do_software_reset()   {
      (new Cpu_regs)->Disable_configuration_change_protection();
      control.software = true;
      }

   bool Is_software_reset()   {
      return status.bits.software;
      }

   void Clear_software_reset()   {
      Status my_status;
      my_status.byte = 0;
      my_status.bits.software = true;
      status.byte = my_status.byte;
      }

   void Clear_resets(u_int8 which)   {
      which ^= status.byte;
      status.byte = which;
      }

void* operator new(size_t objsize)   {
   return (void*)_SFR_ADDR(RST);        // assign actual memory location of the registers
   }

   };
#endif

 

Last Edited: Mon. Aug 7, 2017 - 01:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, so it seems that if you define the SFRs as actual variables, using the attributes (gcc extensions) io_low, io and address (note: io_low doesn't seem to actually be working properly) instead of using casts like *(volatile uint8_t*) some_sfr_address, they can be used as template arguments, opening many possibilities of use in C++.

So, for example, PORTD could be:

volatile uint8_t portd __attribute__((io (0x2B), weak));

(it's weak so that it could be in a header included in multiple files)

 

Other possibility I was considering, was defining the SFRs in a global structure and placing that structure in a section mapped to the I/O space. In this case, they would also be "real variables" and be usable by C++ templates.

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

After learning and playing a bit with templates. I took another shot at a pin class using them. I’m not sure if this, exactly hits the sweet spot, or not. Still trying to understand the finer details of C++. But seems to be quite cool so far…

 

The pin class template:

template <uint8_t tPort, uint8_t tBit>
class Pin
{
public:
 static constexpr uint8_t Port = tPort;
 static constexpr uint8_t DDR = tPort-1;
 static constexpr uint8_t PIn = tPort-2;
 static constexpr uint8_t Bit = tBit;

 static constexpr void  Toggle()
 {
    *reinterpret_cast<uint16_t*>(Port) ^= (1<<Bit);
 }

 static constexpr void PullHigh()
 {
    *reinterpret_cast<uint16_t*>(Port) |= (1<<Bit);
 }

 static constexpr void PullLow()
 {
    *reinterpret_cast<uint16_t*>(Port) &= ~(1<<Bit);
 }
 
 static constexpr void SetOutput()
 {
    *reinterpret_cast<uint16_t*>(DDR) &= ~(1<<Bit);
 }

 static constexpr void SetInput()
 {
    *reinterpret_cast<uint16_t*>(DDR) |= (1<<Bit);
 }

 static constexpr void SetHighImpedance()
 {
    *reinterpret_cast<uint16_t*>(Port) &= ~(1<<Bit);
    *reinterpret_cast<uint16_t*>(DDR) &= ~(1<<Bit);
 }

 static constexpr bool Read()
 {
    return (*reinterpret_cast<uint16_t*>(PIn) & (1<<Bit));
 }
};

Then I define pins for the mega328p:

class Pins
{
  public:
 using B0 = Pin<0x25, 0>;
 using B1 = Pin<0x25, 1>;
 using B2 = Pin<0x25, 2>;
 using B3 = Pin<0x25, 3>;
 using B4 = Pin<0x25, 4>;
 using B5 = Pin<0x25, 5>;
 using B6 = Pin<0x25, 6>;
 using B7 = Pin<0x25, 7>;

 using C0 = Pin<0x28, 0>;
 using C1 = Pin<0x28, 1>;
 using C2 = Pin<0x28, 2>;
 using C3 = Pin<0x28, 3>;
 using C4 = Pin<0x28, 4>;
 using C5 = Pin<0x28, 5>;
 using C6 = Pin<0x28, 6>;

 using D0 = Pin<0x2B, 0>;
 using D1 = Pin<0x2B, 1>;
 using D2 = Pin<0x2B, 2>;
 using D3 = Pin<0x2B, 3>;
 using D4 = Pin<0x2B, 4>;
 using D5 = Pin<0x2B, 5>;
 using D6 = Pin<0x2B, 6>;
 using D7 = Pin<0x2B, 7>;
};

Then I can call functions on them:

Pins::B7::SetOutput();
Pins::B7::PullHigh();

Or write class templates that uses and accepts them, via template arguments:

template<typename tPin>
class WhatEver
{
 public:
 WhatEver()
 {
    tPin::SetOutput();
 }
 
 void flipOut()
 {
    tPin::Toggle();
 }
};

And use it to implement an application:

int main(void)
{
    WhatEver<Pins::D1> whatEver; 
    while (1)
    {
    whatEver.flipOut();
    _delay_ms(500);
    }
  }

This program uses 174 bytes of program memory, 0 bytes of data memory.

Here is the generated main.s file:

.file "main.cpp"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
 .section .text.startup.main,"ax",@progbits
.global main
 .type main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
 in r24,0xa
 in r25,0xa+1
 andi r24,253
 out 0xa+1,r25
 out 0xa,r24
.L2:
 in r24,0xb
 in r25,0xb+1
 ldi r18,2
 eor r24,r18
 out 0xb+1,r25
 out 0xb,r24
 ldi r24,lo8(1456699)
 ldi r25,hi8(1456699)
 ldi r18,hlo8(1456699)
1: subi r24,1
 sbci r25,0
 sbci r18,0
 brne 1b
 rjmp .
 nop
 rjmp .L2
 .size main, .-main
 .ident "GCC: (AVR_8_bit_GNU_Toolchain_3.6.0_1734) 5.4.0"

 

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

Last Edited: Tue. Aug 8, 2017 - 08:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can it be made with u8 and not u16 ?(make two in and out)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
template <uint8_t tPort, uint8_t tBit>

Why just uint8_t for the port? Stranger is where you do:

 static constexpr void  Toggle()
 {
    *reinterpret_cast<uint16_t*>(Port) ^= (1<<Bit);
 }

Why has the integer been cast up to a pointer to SIXTEEN bits??

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

What's the purpose of the template?

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

It's so you can instantiate it with any combination of port address and pin number.

 

If you just did it as a plain class then the port/pin would be held in member variables but when you came to do you "*port |= (1<< bit)" kind of stuff the port/bit are variable at run time so the code will be inefficient with shifts and so on.

 

Using a template commits these things in stone at the point of creation so efficient SBI/CBI style code should be generated.

 

" templates" are just a bit like "souped up" #define prepro macros at the end of the day ;-)

 

In C defines not run time functions are the "efficient" way to get tight code generated, in C++ it is templates because most things are resolved at the moment of compilation.

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

Well okay.  It looks like you are still using all that shifty ( I said shifty s-h-I-f-t-y) stuff in the iox......h file.  Just looking at it gives me the vapors and I have to lay down for a while.  smiley  It sure ain't modular with tons of that define stuff for all the modules.

 

I insist on using classes for the various I/O devices and using the this pointer.  avr-gcc looks at the value of the this pointer I use for the I/O classes (a constant).  Often it will toss it out the window and address the memory location directly, if that produces more efficient code. I don't know how my machine code compares with yours.  

 

The Xmega doesn't have much at low memory, which I think is required for SBI.  To check the SBI thing, I'll once again use virtual port 0 at address 0x10.   I added an "Add_pins_direction_out" function to "or" in a pin.

 

   (new Virtual_port_0_regs)->Add_pins_direction_out(Port_regs::Pin7);      // Port @ 0x10, reg offset 0

   void Add_pins_direction_out(u_int8 pins)   {
      Direction_out |= pins;
 246: 87 9a        sbi 0x10, 7 ; 16

 

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

clawson wrote:
Why just uint8_t for the port?

Curiosity! No real reason. I was tinkering around, wonder if it would work, since all PORT register are under 0xFF. I guess using 16bit variables for the pointers would be best since pointers are 16bits.

 

I was thinking about making the template a little more flexible. By adding two typename parameters, i.e template <typename tPortType, typename tBitType, tPortType  tPort, tBitType  tBit>. So down the road if I’m working with a uC with 32bit registers and could still use it. Could that work, any thoughts?

“If at first the idea is not absurd, then there is no hope for it.” ― Albert Einstein

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

finis wrote:

, since all PORT register are under 0xFF.

The port registers on the Xmega start at 0x600. 

 

Some of the Xmega port registers have "helper" registers that will "or in" or "and out" or "toggle" bits.  If you write one bits to the Out_data_set_pins register, those bits will be ored into the Out_data register, etc.

 

   volatile Register Out_data;                       // OUT            4
   volatile Register Out_data_set_pins;              // OUTSET         5
   volatile Register Out_data_clear_pins;            // OUTCLR         6
   volatile Register Out_data_toggle_pins;           // OUTTGL         7

 

For my nefarious purposes, the Megas are obsolete.

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

Is it my imagination or is this thread actually in the "megaAVR and tinyAVR" section? ;-)

 

So, yes, all PORT registers are under 0xFF. In fact one would rather hope that the majority are under 0x5F so they can benefit from IN/OUT access.

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

Well I scrolled to the top and I beli

clawson wrote:

Is it my imagination or is this thread actually in the "megaAVR and tinyAVR" section? ;-)

I scrolled to the top and it seems it is not your imagination.

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

finis wrote:
clawson wrote:
Why just uint8_t for the port?

Curiosity! No real reason. I was tinkering around, wonder if it would work, since all PORT register are under 0xFF. I guess using 16bit variables for the pointers would be best since pointers are 16bits.

You made the pointer *target* 16 bits.  It would have failed on a big-endian machine.

The pointer would be 16 bits regardless of the size of the target.

Iluvatar is the better part of Valar.

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

clawson wrote:

 In fact one would rather hope that the majority are under 0x5F so they can benefit from IN/OUT access.

That could be your imagination.  Over here I think it's less than 0x40.

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

steve17 wrote:
Over here I think it's less than 0x40.
Err no. The RAM addresses are:

 

0x00 .. 0x1F registers R0..R31

0x20 .. 0x3F low IO accessible to SBI/CBI

0x40 .. 0x5F higher IO accessible to IN/OUT

0x60 .. 0xFF rest of IO (LDS/STS etc)

 

The SBI/CBI and IN/OUT opcodes then make a translation to map 0x20 to their 0x00 in so called "IO memory" So for those the IO addresses are:

 

inaccessible registers R0..R31

0x00 .. 0x1F low IO accessible to SBI/CBI

0x20 .. 0x3F higher IO accessible to IN/OUT

inaccessible rest of IO (LDS/STS etc)

 

So, yes in that sense the "top" of IO is 0x3F just below 0x40. But I was talking about the complete picture in which case the upper boundary of IN/OUT is 0x5F.

 

It rather goes without saying that the SBI/CBI area is also accessible with IN/OUT (even LDS/STS) for multi-bit operations.

 

What was wrong in what I said was

clawson wrote:
under 0x5F

0x5F is the upper bound and included so that should have read "under 0x60" in fact.

 

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

Okay, I think it is 0x40 on the Xmega.  It doesn't have cpu registers r0 to r31 down there. 

 

Maybe the Mega does something strange at low addresses.  I looked briefly at the OUT instruction and it seems to have 6 bits of address which would max out at 0x3f.

 

Anyway, I'm sticking with the Xmega.

 

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

 

It looks like they fixed the address problem with templates in C++17

 

http://www.bfilipek.com/2017/01/...

 

See item N4295.

 

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

Well, I already had installed avr-gcc 7.1.0 to test (using -std=g++1z) and I'm still not able to pass things like PORTB or &PORTB as template arguments. I haven't tried to put them into a function like in that example, though.

 

edit: the ability to use auto in non-type template arguments, also a C++17 feature, seems very useful, though.

 

edit #2: Nah, it still complains "expression {whatever} has side-effects"

Last Edited: Thu. Aug 10, 2017 - 02:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PORTB has side effects.

&PORTB and const_cast<uint8_t>(&PORTB) should not have side-effects.

*( const_cast<uint8_t>(&PORTB) ) ?

Iluvatar is the better part of Valar.

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

You can't cast it. You can't use it as argument for constexpr functions. But you can use it inside the body of a constexpr function.

Basically, I can't figure it out. If anyone can explain it I'll be grateful. The error seems to come from the way PORTD is defined, as a derreference of a pointer that results from a cast.
 

#include <avr/io.h>

template <int var>
struct S {};

int constexpr PORTD_addr(){
    return reinterpret_cast <int> (&PORTD);
}

int constexpr SFR_addr(volatile uint8_t * sfr){
    return reinterpret_cast <int> (sfr);
}

int main(){
    S<PORTD_addr()>;			// this is OK
    // this generates an error: 'reinterpret_cast<volatile uint8_t* {aka volatile unsigned char*}>(43)' is not a constant expression
    //S<SFR_addr(&PORTD)>;			
    // this also generates an error: 'reinterpret_cast<volatile uint8_t* {aka volatile unsigned char*}>(43)' is not a constant expression
    //S<reinterpret_cast <int> (&PORTD)>;	
}

 

Last Edited: Thu. Aug 10, 2017 - 08:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you tried making it a const?

 

 

int constexpr SFR_addr(volatile uint8_t * const sfr){
	return reinterpret_cast <int> (sfr);
}

 

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

Hi every one, I fink it's my first post in this forum blush

I recently start looking to c++.  And I decide start form peripheral class implantation. Now I know a little bit about templates and meta programming for smaller code.

Then in this form found an example "blinky".

Downloaded pin.h file, then I tray to compile it, I got ton of errors. I fink biggest problem is with compiler flags "C++11 'constexpr' only available with -std=c++11 or -std=gnu++11"

I using Atmel studio 7 compiler version is gcc 5.4.0 then I changed compiler flag to -std=gnu++11 instead -std=gnu99, error's steal appear. May by you have any suggestions.

Thanks  

 

El Tangas wrote:

I'm experimenting with C++ too. Templates are very cool, for example. With them, you can avoid the preprocessor in many cases.

Here is a blinky program for Mega 328P:

 


 

You can also do:

    Pin<D, 0, 1, 5, 7>::as_output();

This will set all the indicated pins of port D as outputs at the same time.

 

I attached the header file to make this work. The templates generate a class for each pin combination that is used, but I never actually instantiate any object, these classes only have static members. Also, every member of these classes is "constexpr", so that it can be evaluated at compile time. The whole thing is like a super-preprocessor, so it will generate well optimized code.

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

Well, here is the whole AS7 project corresponding to that example. See if you can compile it.

 

Remember that the debug and release versions have to be configured individually.

 

 

Attachment(s): 

Last Edited: Wed. May 9, 2018 - 01:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Well, here is the whole AS7 project corresponding to that example. See if you can compile it.

 

Remember that the debug and release versions have to be configured individually.

 

Thanks for you attention. Yes you project compiles fine. and I find my mistake in project settings. Before I didn't note that in Toolchain is separate tabs for c and c++ compilers 

Once again thank you for the help.