Author Topic: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS  (Read 5085 times)

0 Members and 1 Guest are viewing this topic.

Offline neko efecktzTopic starter

  • Regular Contributor
  • *
  • Posts: 153
  • Country: au
#define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« on: July 07, 2016, 01:16:56 am »
I'm using a PIC 16F877A for a LCD project and had a thought.
I was wondering when using   #define LCD_DATA    PORTB
Is it possible to only define the lower 4 bits.

If I use #define LCD_DATA    PORTB,  I'm using  B0 - B3 for the LCD display
B6 and B7 for ICSP and B4 and B5 are going to waste.
No real big deal with the 40 pin chips but on a smaller pin count it means loosing 2 I/O pins.
As you know I need a minimum of 6 data lines to operate a LCD.
If I could define just the lower 4  bits, the other four bits could be utilized for other purposes.

Just Curious.
Thankyou
Bill
« Last Edit: July 07, 2016, 01:30:57 am by neko efecktz »
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12862
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #1 on: July 07, 2016, 01:40:44 am »
So use RB4 & RB5 for RS and R/W (or E if you are running it write only with R/W grounded). 
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2550
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #2 on: July 07, 2016, 01:52:40 am »
I have only used this on PCs.  Don't know if the XC8 compiler will work for a register.  You will need to read the register into nibbles, make change and write the nibble back to the register.  Try this and let us know....

Code: [Select]
struct nibbles_t{
   uint8_t  lowNibble : 4;
   uint8_t  highNibble : 4;
} nibbles;
 

Offline neko efecktzTopic starter

  • Regular Contributor
  • *
  • Posts: 153
  • Country: au
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #3 on: July 07, 2016, 01:54:01 am »
I tried that but it didn't work.
the whole port is assigned as a data port and I believe that it's only when in ICSP mode that the B4 and B5 would be available to use .

Bill
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2550
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #4 on: July 07, 2016, 01:56:02 am »
That's why I said you need to read the whole port into a local variable, make the changes and write the whole register back to the port.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12862
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #5 on: July 07, 2016, 02:09:59 am »
Best *NOT* to read the port to make changes. Instead maintain a shadow copy, do your bit-twiddling in that and copy it to the actual port in one swell foop.  See RMW and solutions for it @ Microchip's Forum
 

Offline neko efecktzTopic starter

  • Regular Contributor
  • *
  • Posts: 153
  • Country: au
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #6 on: July 07, 2016, 02:10:56 am »
Gidday Mark and Ian.
good to hear from you again.
I have Just completed the PCB for my project you both helped me with a few weeks ago.
i will try Marks suggestion  at a later date.
As I said I'm using a PIC 16F877A so I have plenty of i/o for my current project.
The next step is analog input to display temperature etc.
I designed the board as a stand alone multi purpose board with no input driving circuits, just pullup resistors.
I will Post pictures when Its done, probably in a week or so.
The driving circuits will be on another board.  opto couplers, transistors , opamps etc.

Good to get advice that I can use.
Got to get ready for work now


Thank you for your help.

Bill
 

Offline ovnr

  • Frequent Contributor
  • **
  • Posts: 658
  • Country: no
  • Lurker
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #7 on: July 07, 2016, 02:38:58 am »
Your issue is that the library you're using doesn't (appear to) support using individual pins as opposed to an entire port.

Find one that does it. There are plenty around, and a HD44780 is really easy to drive anyhow.
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2550
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #8 on: July 07, 2016, 02:51:12 am »
Your issue is that the library you're using doesn't (appear to) support using individual pins as opposed to an entire port.

Find one that does it. There are plenty around, and a HD44780 is really easy to drive anyhow.

The default XC8 header file for the pic16f877a DOES support individual port pins.   However, that's NOT what he's trying to do and doing 4 individual pin writes would cause unwanted glitches on the 4 pin nibble.


Bill - A thought I just had is that if you use the upper nibble as inputs, it doesn't matter what you wrote to the upper half of the register.
« Last Edit: July 07, 2016, 02:55:56 am by MarkF »
 

Offline ovnr

  • Frequent Contributor
  • **
  • Posts: 658
  • Country: no
  • Lurker
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #9 on: July 07, 2016, 02:51:20 pm »
The default XC8 header file for the pic16f877a DOES support individual port pins.   However, that's NOT what he's trying to do and doing 4 individual pin writes would cause unwanted glitches on the 4 pin nibble.


Bill - A thought I just had is that if you use the upper nibble as inputs, it doesn't matter what you wrote to the upper half of the register.

The LCD library. Obviously the PIC headers themselves have individual pins.

And if he's actually driving a HD44780, any glitches are not an issue.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12862
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #10 on: July 07, 2016, 03:08:25 pm »
The problem is that BSF/BCF on a port or any logical operation that reads then writes back to the port can cause other output pins on the same port to change state unexpectedly.   The ONLY 100% safe way to update a port with more than one output pin on the midrange and baseline PIC families is to write the whole port with the correct states of *EVERY* output pin, *WITHOUT* reading those states from the port - which means your code must keep track of them separately. See the link in reply #5 for the gory details.
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #11 on: July 07, 2016, 04:17:09 pm »
That's why I said you need to read the whole port into a local variable, make the changes and write the whole register back to the port.

I prefer not to read the port.  Instead I keep a 'shadow copy' of the port contents in memory.  I can read the memory version, make whatever changes are needed and then write it back to memory and to the port.  I can't imagine it being necessary but if I wanted, I could use the memory version to make state change decisions.
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2550
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #12 on: July 07, 2016, 08:49:11 pm »
That's why I said you need to read the whole port into a local variable, make the changes and write the whole register back to the port.

I prefer not to read the port.  Instead I keep a 'shadow copy' of the port contents in memory.  I can read the memory version, make whatever changes are needed and then write it back to memory and to the port.  I can't imagine it being necessary but if I wanted, I could use the memory version to make state change decisions.

I agree.  It will also run faster.  Lots of options.  If updating more than one bit, write the whole port at one time and save headaches later for this project or others where the timing is critical.
 

Offline neko efecktzTopic starter

  • Regular Contributor
  • *
  • Posts: 153
  • Country: au
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #13 on: July 08, 2016, 12:41:39 am »
Hi Guys, I'm back
Worked till near 1:00 AM, about 8 Hours sleep and back to it.
Thank you to you all for your contributions and suggestions.
When I get a few spare hours I will try a few of the suggestions.
The MPLAB HELP is of very little help with this one.
I Tried to look up lowNibble - hiNibble and variations of these and the only thing that comes up is nibble
all to do with the input of the LCD.

Why is that so much Sample code in the MPLAB HELP and online is in some foreign language called ASSY.   
What part of the world can I find ASSY anyway,
What type of industries do the ASSYans have.
I cant find it on my world globe.

Keep up the good work.
Bill.


« Last Edit: July 08, 2016, 01:55:50 am by neko efecktz »
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2550
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #14 on: July 08, 2016, 03:59:11 am »
The assembly language is basic the machine code (ones and zeros) of the program.  When programming in assembly language, you're are writing code using the native hardware instructions built into the chip.

Back in the day when processors were slow, assembly language was the only way to get the required speed.  Now with the fast processors, languages like C/C++ and Fortran are getting farther and farther from the hardware instruction set and doing most of the detailed work for you.
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #15 on: July 08, 2016, 05:04:15 am »
If you keep a shadow value, load it into the accumulator, AND with 0xF0 (or whatever) to clear the bits you are working on and save the ones you aren't.  Then OR in the new values, save the value and also write it to the port.

in C
 myPortVal &= 0xF0; // set the 4 low bits to 0
 myPortVal |= <my new bits>;  update only the low four bits, make sure the upper 4 bits are 0
 PORTx = myPortVal;  // now write it to the port

Something like that. Clearly, assembly language will have less code but it probably doesn't matter.  The point is to avoid reading the port and to preserve the state of pins that shouldn't be messed with.

You can also do something like this to just set a single bit

 myPortVal &= ~(1 << bitNum); // set the bit in the shadow value to zero
 myPortVal |= (1 << bitNum); // set the bit in the shadow value to one
 PORTx = myPortVal;

Obviously there will be code somewhere else that handles the upper 4 bits while preserving the lower 4.

EDIT: Corrected mask, originally 16 bits - oops!

« Last Edit: July 11, 2016, 01:17:57 pm by rstofer »
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 12862
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #16 on: July 08, 2016, 07:14:10 am »
If you keep a shadow value, load it into the accumulator, AND with 0xFF00 (or whatever) to clear the bits you are working on and save the ones you aren't.  Then OR in the new values, save the value and also write it to the port.

in C
 myPortVal &= 0xFF00; // set the 4 low bits to 0

Errr. 0xFF00?
We are talking about 8 bit data bus devices here.  The ports are at most 8 bits wide, so the high byte of your 16 bit literal is lost by truncation.  I think you meant 0xF0.
 

Offline macboy

  • Super Contributor
  • ***
  • Posts: 2256
  • Country: ca
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #17 on: July 08, 2016, 03:16:45 pm »
I'm using a PIC 16F877A for a LCD project and had a thought.
I was wondering when using   #define LCD_DATA    PORTB
Is it possible to only define the lower 4 bits.

If I use #define LCD_DATA    PORTB,  I'm using  B0 - B3 for the LCD display
B6 and B7 for ICSP and B4 and B5 are going to waste.
No real big deal with the 40 pin chips but on a smaller pin count it means loosing 2 I/O pins.
As you know I need a minimum of 6 data lines to operate a LCD.
If I could define just the lower 4  bits, the other four bits could be utilized for other purposes.

Just Curious.
Thankyou
Bill
Adding the line #define LCD_DATA    PORTB doesn't waste anything, and truthfully, it doesn't really do anything either!
The #define is a compiler directive, and it allows you to substitute text. So anywhere that you have typed "LCD_DATA", the compiler's pre-processor will replace that text with "PORTB" before compiling. That line does absolutely nothing else. It doesn't reserve PORTB for any specific use, it doesn't configure anything, it literally does nothing other than accomplish a textual search and replace. It only makes your code more readable by allowing you to refer to PORTB with a more meaningful name.

You can still use other bits of PORTB for anything else. E.g. if you have a "busy" LED connected to Port B bit 7, you could also add:
#define BUSY_LED PORTBbits.RB7
Then you can just do BUSY_LED = 1; or BUSY_LED = 0; to turn it on and off. This won't affect the bits you are using for the LCD.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #18 on: July 08, 2016, 06:23:03 pm »
Quote
If I could define just the lower 4  bits,

It depends on how the library is written. You will need the source code to determine that.
================================
https://dannyelectronics.wordpress.com/
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #19 on: July 08, 2016, 07:16:01 pm »
I'm using a PIC 16F877A for a LCD project and had a thought.
I was wondering when using   #define LCD_DATA    PORTB
Is it possible to only define the lower 4 bits.

If I use #define LCD_DATA    PORTB,  I'm using  B0 - B3 for the LCD display
B6 and B7 for ICSP and B4 and B5 are going to waste.
No real big deal with the 40 pin chips but on a smaller pin count it means loosing 2 I/O pins.
As you know I need a minimum of 6 data lines to operate a LCD.
If I could define just the lower 4  bits, the other four bits could be utilized for other purposes.

Just Curious.
Thankyou
Bill

Duh...  I messed up!  Mask would be just 0xF0.

Adding the line #define LCD_DATA    PORTB doesn't waste anything, and truthfully, it doesn't really do anything either!
The #define is a compiler directive, and it allows you to substitute text. So anywhere that you have typed "LCD_DATA", the compiler's pre-processor will replace that text with "PORTB" before compiling. That line does absolutely nothing else. It doesn't reserve PORTB for any specific use, it doesn't configure anything, it literally does nothing other than accomplish a textual search and replace. It only makes your code more readable by allowing you to refer to PORTB with a more meaningful name.

You can still use other bits of PORTB for anything else. E.g. if you have a "busy" LED connected to Port B bit 7, you could also add:
#define BUSY_LED PORTBbits.RB7
Then you can just do BUSY_LED = 1; or BUSY_LED = 0; to turn it on and off. This won't affect the bits you are using for the LCD.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #20 on: July 11, 2016, 11:10:32 am »
"You can still use other bits of PORTB for anything else. "

If the code writes to the whole port, whatever value there would be overwritten and you get random States on that pin.

================================
https://dannyelectronics.wordpress.com/
 

Offline Philfreeze

  • Regular Contributor
  • *
  • Posts: 123
  • Country: ch
Re: #define LCD_DATA PORTB, WELL JUST THE LOWER 4 BITS
« Reply #21 on: July 11, 2016, 01:04:26 pm »
How I do it:

Code: [Select]
/*******************************************************************************
**********                       global types                         **********
*******************************************************************************/
// generic port bits
typedef struct
{
unsigned RA0:1;
unsigned RA1:1;
unsigned RA2:1;
unsigned RA3:1;
unsigned    :4;
} porta_t;

typedef struct
{
unsigned RB0:1;
unsigned RB1:1;
unsigned RB2:1;
unsigned RB3:1;
unsigned RB4:1;
unsigned RB5:1;
unsigned RB6:1;
unsigned RB7:1;
} portb_t;

typedef struct
{
unsigned RC0:1;
unsigned RC1:1;
unsigned RC2:1;
unsigned RC3:1;
unsigned RC4:1;
unsigned RC5:1;
unsigned RC6:1;
unsigned RC7:1;
} portc_t;

typedef struct
{
unsigned RD0:1;
unsigned RD1:1;
unsigned RD2:1;
unsigned RD3:1;
unsigned RD4:1;
unsigned RD5:1;
unsigned RD6:1;
unsigned RD7:1;
} portd_t;

// specific port bits
typedef struct
{
unsigned LEDB :1;
unsigned LEDG :1;
unsigned :6;
} outputa_t;

typedef struct
{
unsigned :6;
unsigned HEATER0:1;
unsigned HEATER1:1;
} outputb_t;

typedef struct
{
unsigned :7;
unsigned LEDR :1;
} outputc_t;


typedef struct
{
unsigned LCD :4;
unsigned :4;
} inputc_t;

typedef struct
{
unsigned BTN :1;
unsigned DIP0 :1;
unsigned DIP1 :1;
unsigned DIP2 :1;
unsigned :4;
} inputd_t;

/*******************************************************************************
**********                      global defines                        **********
*******************************************************************************/
// generic registers
#define PORTAbits (*(volatile porta_t *)(&PORTA))
#define PORTBbits (*(volatile portb_t *)(&PORTB))
#define PORTCbits (*(volatile portc_t *)(&PORTC))
#define PORTDbits (*(volatile portd_t *)(&PORTD))

#define PINAbits (*(volatile porta_t *)(&PINA))
#define PINBbits (*(volatile portb_t *)(&PINB))
#define PINCbits (*(volatile portc_t *)(&PINC))
#define PINDbits (*(volatile portd_t *)(&PIND))

#define DDRAbits (*(volatile porta_t *)(&DDRA))
#define DDRBbits (*(volatile portb_t *)(&DDRB))
#define DDRCbits (*(volatile portc_t *)(&DDRC))
#define DDRDbits (*(volatile portd_t *)(&DDRD))

// application specific
#define OUTPUTA (*(volatile outputa_t *)(&PORTA))
#define OUTPUTB (*(volatile outputb_t *)(&PORTB))
#define OUTPUTC (*(volatile outputc_t *)(&PORTC))

#define INPUTC (*(volatile inputc_t *)(&PINC))
#define INPUTD (*(volatile inputd_t *)(&PIND))

This is just part of a random project.
This isn't really an efficient way of doing it and if every clock cycle counts I wouldn't do it like that but the nice thing about this is that it is compatible with every MCU architecture and every compiler.

What I do is just creating a struct were I give certain bits names and then I make a define where I cast the address of the register to this struct, very easy.


Edit:

If you really want to you could probably do something like:
Code: [Select]
typedef struct
{
union
{
    struct
{
        unsigned LCD0: 1;
        unsigned LCD1: 1;
        unsigned LCD2: 1;
unsigned LCD3: 1;
    } LCDbits;
    unsigned all :4;
} LCD;
unsigned :4;
} output_t;
but I have never tested something like that.
« Last Edit: July 11, 2016, 01:16:16 pm by Philfreeze »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf