DDR - discover by PORT

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

Hi guys

 

I'm writing a library and as part of that need to identify a DDR register based off a PORT set by the person using the library.

I wrote the following as a test, but it returns both DDRA and DDRD...

 

// Located in the library

int FindDDRs(int prt)
{
	int ddr=0xff;

	if (prt==PORTA)	ddr = DDRA; 
	if (prt==PORTB)	ddr = DDRB; 
	if (prt==PORTC)	ddr = DDRC; 
	if (prt==PORTD)	ddr = DDRD; 

	return(ddr);
}


// Located in the users main code as part of the user-defined definitions:
#define LCD_DB0_port		PORTD


// set in the main program loop to test if FindDDRs actually works
int xx = FindDDRs(LCD_DB0_port);
if (xx==DDRA) LCD_sendStr("DDRA");
if (xx==DDRB) LCD_sendStr("DDRB");
if (xx==DDRC) LCD_sendStr("DDRC");
if (xx==DDRD) LCD_sendStr("DDRD");




 

So, thinking that I had the variable type wrong for DDRA, I tried uint8_t, uint16_t and int. All return both DDRA and DDRD. The correct result should be DDRD.

 

Can anyone see where I'm going wrong?

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

When I google "LCD Library" it returns over 21000 hits, is another LCD library really needed, how will yours be different?

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

What is e.g. PORTA defined as?  Why do you think it would make any sense to compare it to an "int", which is signed 16-bit?  What is actually passed in the "prt" parameter?

 

What toolchain are you using?

 

Have you traced the code with various values of "prt"?

 

If e.g. PORTA is from the chip-include file from a mainstream C toolchain such as GCC, then PORTA is the value in the PORTA register.  So your whole approach doesn't make much sense.  "Dynamic I/O" in an AVR8 never makes much sense -- the instruction set does not handle it well.

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

While agreeing with the principle of, "is another LCD library really needed?", I rather doubt that the 21000 hits actually represent that many distinct libraries.

 

Could be just 21000 Fleury users ...

 

wink

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I created this library quite a while ago and thought I'd share is all and while I'm at it make it a user friendly as possible. Is there a problem with doing that?

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

banedon wrote:
it returns both DDRA and DDRD

What, exactly, do you mean by that?

 

The function only has a single return value.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

banedon wrote:

I created this library quite a while ago and thought I'd share is all and while I'm at it make it a user friendly as possible. Is there a problem with doing that?

Of course not!  That is what makes it a better world, good luck with the project.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

You need a lesson in C and to work out what a pointer is!

 

Think about this:

uint8_t var;

var = PORTD;

What does "var" end up with? Is it the address of where PORTD is located? Or is it the current contents at that address? Say both PORTD and PORTB both held 0x5A then if I use:

var1 = PORTB;
var2 = PORTD;

then both var1 and var2 now hold 0x5A. So if I test:

if (var1 == var2) {
    // stuff
}

that will be "true". If I wanted to uniquely identify PORTB from PORTD then I could do that not by reading the contents of the locations they represent but by checking the ADDRESS OF the locations they represent . In C the "address of" operator is '&'. So now I could write:

volatile uint8_t * addr1;
volatile uint8_t * addr2;

addr1 = &PORTB;
addr2 = &PORTD;

if (addr1 == addr2) {
    // stuff
}

Now of course it will depend which model of AVR you use here but if, for example, it were the mega16 then I know from the header file that:

C:\SysGCC\avr\avr\include\avr>grep PORT. iom16.h
#define PORTD   _SFR_IO8(0x12)
#define PORTC   _SFR_IO8(0x15)
#define PORTB   _SFR_IO8(0x18)
#define PORTA   _SFR_IO8(0x1B)

So "PORTB" has an address of 0x18 (that's a fact built into the silicon design of the mega16 and won't change) and the address of PORTD is 0x12. So these can be uniquely identified.

 

(and by the way 0x12 and 0x18 are the "wrong address" - they are really 0x32 and 0x38 but I won't bore you with the detail of why that is right now!)

 

So if you wrote:

int xx = FindDDRs(&LCD_DB0_port);

and because that is a pointer your find() function need to change to be:

int FindDDRs(volatile uint8_t * prt)
{
	int ddr=0xff;

	if (prt== &PORTA)	ddr = DDRA;
	if (prt== &PORTB)	ddr = DDRB;
	if (prt== &PORTC)	ddr = DDRC;
	if (prt== &PORTD)	ddr = DDRD; 

	return(ddr);
}

but I imagine that once again it is not "DDRA" you want to return (which means "the value currently stored in DDRA") but the address of DDRA.

 

All this, is however, completely academic as I know what you are trying to do here and the fact is that there is an easy formula for working out the DDR address given a PORT address and it relies on a pattern you see here:

 

 

That just happens to be grabbed from a mega16 datasheet but the point is that for any given port x the PINx register is at address n, the DDRx register at n+1 and the PORTx register  is at n+2. So if you know what the address of PORTB is (say) you can work out the address of PINB as it is that address minus 2.

 

This rule applies to almost all (but not quite all) ports in all tiny and mega AVRs.

 

Now it just so happens that possibly the most famous LCD library that exists for AVR (Arduino not withstanding) is the one by Peter Fleury here:

 

http://homepage.hispeed.ch/peter...

 

if you take a look at that you will see:

/*
** 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

so not only does he already employ this "trick" but he even has the "special case" handling for the one example where the relationship is not true (PORTF on the mega64 and mega128).

 

You can see him using the DDR() macro in code such as:

        /* configure data pins as output */
        DDR(LCD_DATA0_PORT) |= 0x0F;

BTW you could just "give in" and use the Fleury code as (apart from the learning experience) there's very little point in reinventing this LCD code for the 10 gazillionth time!

 

EDIT: last sentence probably not relevant if this is something already written that you are trying to make more generally available. So do steal the "&DDR = &PORT - 1" thing and use that.

Last Edited: Wed. Mar 28, 2018 - 02:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried uint16_t as well, but had the same problem, int just happens to be the last type I tried.

prt should carry PORTA, PORTB, PORTC or PORTD. I've tried PORTD and PORTB in prt:

- PORTD returns DDRA and DDRD

- PORTB returns DDRB and DDRC

This is with all vars definitions changed back to uint16_t

Tool chain wise I'm using GCC (Atmel Studio 6.2), the AVR in question is an ATmega 324P.

 

The reason for doing this is that I'm trying to write the last bit of my lirbary. Rather than depending on delays I'm trying to put a busy flag check in for the 44780 LCD.

As part of the I need to ensure that the PORT direction used for the LCD data pin 7 is set to input so the busy state of the LCD can be read. Also, I need to change the other LCD data pins to avoid possible contention.

Given that the user can define the PORT and pin for each pin used by the LCD, I need to check each to find the appropriate DDR register.

 

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

That's what is really puzzling me. My LCD displays both results when testing the 'xx' variable. I.e. It hits each test of 'xx' and two of them come back with 'yep this matches'.

It's what made me think that I had the variable type wrong and that it was clipping the results perhaps.

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

Thanks very much for the comprehensive and useful reply.  I see what you're saying and how the 'address of' works. I did try this, but got it completely wrong so assumed my approach was wrong - instead of just the syntax.

I had the pointer declaration right (although not 'volatile'):

int FindDDRs(uint8_t *prt)

Which in my mind says (and what you have confirmed): Here is the address of 'prt', not the contents, but failed to get the next bit right.

I had:

if (&prt==PORTA)	ddr = DDRA;

 

So thanks for clearing this up and for your patience. I'm (slowly) getting my head around this.

 

Finally: Yes this project IS reinventing the wheel for the 100000th time, but it has been educational and I've enjoyed it. It's also enabled me to port code to another project (non-AVR) that I've been doing off and on for a number of years and I wouldn't have been able to do that unless I'd had to learn how to program the LCD by writing this library (in fact both fed the other to a degree). But, yes, it's probably deeply boring to many as it's a very old subject and I do get that :).

 

 

 

 

 

 

Last Edited: Wed. Mar 28, 2018 - 02:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

banedon wrote:
This is with all vars definitions changed back to uint16_t
Please read #8. They are not "int" or "uint16_t" or any other form of "integer" value. What you require are addresses, not values. You already had a thread the other day where you were passing "char" into a "char *" which seems to suggest you don't understand the difference between a pass by value and a pass by reference. As this is totally fundamental to the core of the C language I would suggest you spend a little time researching that and learning when "var" is used, when "&var" is used and when "*ptr" is used. All your recent questions revolve around this concept. You will know you have succeeded in understanding all you require when you can read:

#define DDR(x) (*(&x - 1))

and see exactly what that achieves. It really is the solution to your "get DDR from PORT" question!

 

EDIT our posts crossed - good that you start to understand #8. By hte way, as I said, it is not complete because as well as taking an address (not a value) as an input it also needs to return an address, not a value as the output. The API for the function needs to look something like:

volatile uint8_t * FindDDRs(volatile uint8_t *prt);

(and the "volatile"s are important as you will cast them away other wise).

 

But really, the Fleury macro, is the "real" solution here - unlike your "find" that only "knows" about the specific ports for a specific AVR it works for all ports on all AVRs (esp with the extra "fix" for the exception to the rule).

Last Edited: Wed. Mar 28, 2018 - 02:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No worries. I love forums, but there is always the post typing lag factor and I'm not a fast typer :).

 

From what you've said your example macro says (in English)

 

Get a pointer that is equal to the pointer to what is passed in X, but minus 1.

 

BTW here's my code (which I'll now probably replace with the above example):

 

 

 

Calling code:

			volatile uint8_t *xx = FindDDRs(&PORTB);
			if (xx== &DDRA) LCD_sendStr("DDRA");
			if (xx== &DDRB) LCD_sendStr("DDRB");
			if (xx== &DDRC) LCD_sendStr("DDRC");
			if (xx== &DDRD) LCD_sendStr("DDRD");

 

 

Library code:

volatile uint8_t* FindDDRs(volatile uint8_t *prt)
{
	volatile uint8_t *ddr;

	if (prt== &PORTA)	ddr = &DDRA;
	if (prt== &PORTB)	ddr = &DDRB;
	if (prt== &PORTC)	ddr = &DDRC;
	if (prt== &PORTD)	ddr = &DDRD; 

	return(ddr);
}

 

 

[edit] Or more likely "Get a pointer that is equal to the address of what is passed in X minus 1".

Last Edited: Wed. Mar 28, 2018 - 03:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made a little modification as I am using #define and now the following works:

 

#define LCD_ddr_get(x) (&x - 1)

#define LCD_DB0_ddr  LCD_ddr_get(LCD_DB0_port)
#define LCD_DB1_ddr  LCD_ddr_get(LCD_DB1_port)
#define LCD_DB2_ddr  LCD_ddr_get(LCD_DB2_port)
#define LCD_DB3_ddr  LCD_ddr_get(LCD_DB3_port)
#define LCD_DB4_ddr  LCD_ddr_get(LCD_DB4_port)
#define LCD_DB5_ddr  LCD_ddr_get(LCD_DB5_port)
#define LCD_DB6_ddr  LCD_ddr_get(LCD_DB6_port)
#define LCD_DB7_ddr  LCD_ddr_get(LCD_DB7_port)

 

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

banedon wrote:
I created this library quite a while ago and thought I'd share is all and while I'm at it make it a user friendly as possible. Is there a problem with doing that?
I sort of disagree with you, I hope you're ok with that, If you do not like my opinion you may ignore it.

In the uC world "user friendlyness" is something different from the "consumer market".

For small uC's you have the "beginners", and they (and others also) seem to like a complete framework such as "arduino", where a lot of different libraries are designed to fit together and a user just has to add some glue to make a project.

 

Then there are the more experienced people. These ofthen want high quality compact code because they design the software once and want to sell a million (or more) of the same product.

I am completely convinced of your good intentions, but you seem to lack the knowledge and experience to write a good / efficient / user friendly library. I think you are probably better of making some fun projects for yourself to gain some experience and write some universal libraries later.

 

In your attempt to "user friendlyness" you are adding a lot of code with a lot of repetition of the same. I can not see why this is beneficial to anyone, but somtimes my imagination is a bit limited...

 

Using a whole byte port for an LCD is a good idea. It's a bit of a shame that the very popular m328 does not have a usably 8-bit port (shared with USART or Crystal, very unfortunate pinout).

 

A very simple and intuitive way to write efficient code with which you can move the LCD to a different port is with very simple (I like simple where appropiate) single line macro's.

You only have to rename the PINx PORTx and DDRx registers. If you put those together in a file with a bit of comment you are finished.

- Easy to move the LCD to another port.

- Easy to understand, simple code.

- Compact code. No messin' with (slow) port pointers.

- Compact code. No messin' with lots of if() statements for different ports.

 

A long time ago, probably before Peter Fleury wrote is HD44780 lib I wrote mine from scratch by studying the datasheet.

I wanted to re-use that library in different projects, which had different pin configurations.

I finally settled in putting the whole pin configuration for a project into "main.h" and then include that file when compiling the source code for the libraries.

Because all the pins are defined in a single file I do not even draw a schematic for a lot of simple projects (One-off, soldered on Vero-board).

The pin definition for the LCD is below.

In there you can see that I can swap between a full byte, and 4 separate pins for the LCD, just by defining a few constants.

//---------------------------------------------------------------------------
// User definitions for Lcd
//---------------------------------------------------------------------------

//#define LCD_SIZE		LCD_SIZE_1x16	// Use one of these sizes.
#define LCD_SIZE		LCD_SIZE_2x16
//#define LCD_SIZE		LCD_SIZE_2x40

#define LCD_BYTEWIDE		0	// 1 for 8-bit 0 for 4 separate pins.

#define LCD_RS_DDR		DDRD
#define LCD_RS_PORT		PORTD
#define LCD_RS_BIT		(1<<0)

#define LCD_CS_DDR		DDRD
#define LCD_CS_PORT		PORTD
#define LCD_CS_BIT		(1<<1)

#if LCD_BYTEWIDE		// Data is a whole byte.
#define LCD_DATA_PORT		PORTB
#define LCD_DATA_DDR		DDRB
#define LCD_DATA_PIN		PINB

#else				// Data are 4 individual bits.
#define LCD_DATA_PORT_BIT4	PORTB
#define LCD_DATA_PORT_BIT5	PORTB
#define LCD_DATA_PORT_BIT6	PORTB
#define LCD_DATA_PORT_BIT7	PORTB

#define LCD_DATA_DDR_BIT4	DDRB
#define LCD_DATA_DDR_BIT5	DDRB
#define LCD_DATA_DDR_BIT6	DDRB
#define LCD_DATA_DDR_BIT7	DDRB

#define LCD_DATA_PIN_BIT4	PINB
#define LCD_DATA_PIN_BIT5	PINB
#define LCD_DATA_PIN_BIT6	PINB
#define LCD_DATA_PIN_BIT7	PINB

#define LCD_DATA_BIT4		(1<<2)	// Bits of the output pins.
#define LCD_DATA_BIT5		(1<<3)
#define LCD_DATA_BIT6		(1<<4)
#define LCD_DATA_BIT7		(1<<5)

#endif	// #if LCD_BYTEWIDE

I have also attached the complete HD44780 library, so you can see how the constants #defined in "main.h" work together with the library code.

Attachment(s): 

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

Hi there

 

Thanks for your post and I appreciate your comments. I have zero problem with someone telling me where I'm going wrong if it's done constructively and with courtesy - which you have done.

I agree that I have less experience, but am doing as you suggest - making projects and learning as I go.

 

I'll download and have a look at the 44780 library that you've kindly provided to see how that's put together, but I can say that my library already does as you suggest regarding the port definitions being in main with the guts of the 44780 code being in the library.

Here's the code needed to be added to any project file which needs to use my 44780 lirbary:

 

// -------------------------- 44780 LCD definitions and library inclusion --------------------------

// set this to 4 bit mode (LCDmode_4bit) or 8 bit mode (LCDmode_8bit)
#define LCD_mode			LCD_4bit

// set the control ports & pins
#define LCD_E_port			PORTD
#define LCD_E_pin			4
#define LCD_RS_port			PORTD
#define LCD_RS_pin			7
#define LCD_RW_port			PORTD
#define LCD_RW_pin			2				// Set this to 0xff if the RW pin is not used/tied to 0V/GND

// set the port and pin numbers for the LCD data ports.
// if using 4 bit mode then still set LCD_DB0 to 3, but
// their values do not count so can be set to the values
// used with LCD_DB4-7
#define LCD_DB0_port		PORTD
#define LCD_DB1_port		LCD_DB0_port
#define LCD_DB2_port		LCD_DB0_port
#define LCD_DB3_port		LCD_DB0_port
#define LCD_DB4_port		LCD_DB0_port
#define LCD_DB5_port		LCD_DB0_port
#define LCD_DB6_port		LCD_DB0_port
#define LCD_DB7_port		LCD_DB0_port

#define LCD_DB0_pin			0
#define LCD_DB1_pin			1
#define LCD_DB2_pin			2
#define LCD_DB3_pin			3
#define LCD_DB4_pin			0
#define LCD_DB5_pin			1
#define LCD_DB6_pin			2
#define LCD_DB7_pin			3

#include "lib_LCD.h"

 

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


So all your extra "if" statements are only for deducing the DDR register from the PORT register?

I decided to keep it simple, and therefore I just declare everything (4 #define's per pin or port) in my main.h.

Because of that simplity I'm 100% sure that no extra code has to be generated for such deductions, which is small in code size and fast & easy to maintain.

 

If you want to do something fancy, you can try to make use of the order of the registers in the register file.

It usually is PORT, DDR, PIN register, but I do not dare to say that is true for every AVR ever made.

If this order is the same for all processors you could pretty easily solve your problem with a few macro's.

Macro's are resolved at compile time and can produce code which is exactly the same as if you would use hardcoded register names without even a single pointer indirection.

 

Have you looked at the internals of the "arduino" framework.

To me it is a very good example of about the worst that can be devised.

Just them making up their own pin names instead of just using the datasheet names is horrible to me.

And I believe a digitalWrite() can take > 100 cpu cycles just to toggle a single pin.

 

A long time ago I came across a project from a guy who also did not like the digitalWrite(), and he had implemented a fastdigitalWrite() library.

It had the same function as the digitalWrite() but it was implemented with macro's instead of source code.

I think it was even as fast as single sbi or cbi instructions, but certainly a lot faster then the software implementation.

The macro's themself were pretty complicated though.

 

I prefer to keep all code as simple and transparent as possible.

Sometimes I put a lot of thought in a few lines which look deceptively simple at the end.

Because of it's simplicy the code is easy to maintain and/or port to other uC's.

 

Just recently I was fooling a bit around with an STM32F103C8T6. The structure of the I/O ports is completely different there, and I looked at ARM chips from different manufacturers (Atmel, Freescale)  and they seem to have widely varying I/O structures and preripherals.

While looking around what others have invented for portability I came around a few projects that use C++ Templates for the I/O structure.

XPCC:

http://xpcc.io/getting-started/

https://github.com/roboterclubaachen/xpcc

 

Kvasir:

https://github.com/kvasir-io/Kvasir

 

bmtk:

https://www.voti.nl/bmptk/docs/index.html

 

Youtube (Davey Jones' assitant, David):

https://www.youtube.com/watch?v=A_saS93Clgk

 

This template stuff is really powerfull and I really would have liked it if projects such as arduino or mbed were using it.

It seems to be the ultimate way of reaching high code quality and portability at the same time.

I have decided against writing those templates myself though. They are also pretty complicated and a bit of a new and weird syntax to me.

The development effort needed does not seem to be justified for my situation.

Because of their complexity and the inherent way that templates work they are also pretty hard to debug.

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

Paulvdh wrote:
Using a whole byte port for an LCD is a good idea. It's a bit of a shame that the very popular m328 does not have a usably 8-bit port (shared with USART or Crystal, very unfortunate pinout).

 

Which is why I lurv my old 2313a's.  They have this wonderfully handy eight-bit port[B] right there...  If not, I'll use an even older 8535, because they have eight-bit eight-pin ports to beat the band (and can still be stuffed into (reasonably priced) sockets). 

 

OT, I know.  S.