#define LCD_DATA    PORTB,    WELL JUST THE LOWER 4 BITS - Page 1
Forum section: electronics / beginners

author: neko efecktz
author_rank: Regular Contributor
timestamp: July 07, 2016, 01:16:56 am
content: I'm using a PIC 16F877A for a LCD project and had a thought.I was wondering when using#define LCD_DATA PORTBIs it possible to only define the lower 4 bits.If I use#define LCD_DATA PORTB, I'm using B0 - B3 for the LCD displayB6 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.ThankyouBill

author: Ian.M
author_rank: Super Contributor
timestamp: July 07, 2016, 01:40:44 am
content: So use RB4 & RB5 for RS and R/W (or E if you are running it write only with R/W grounded).

author: MarkF
author_rank: Super Contributor
timestamp: July 07, 2016, 01:52:40 am
content: 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:struct nibbles_t{uint8_t lowNibble : 4;uint8_t highNibble : 4;} nibbles;

author: neko efecktz
author_rank: Regular Contributor
timestamp: July 07, 2016, 01:54:01 am
content: 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

author: MarkF
author_rank: Super Contributor
timestamp: July 07, 2016, 01:56:02 am
content: 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.

author: Ian.M
author_rank: Super Contributor
timestamp: July 07, 2016, 02:09:59 am
content: 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. SeeRMW and solutions for it @ Microchip's Forum

author: neko efecktz
author_rank: Regular Contributor
timestamp: July 07, 2016, 02:10:56 am
content: 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 nowThank you for your help.Bill

author: ovnr
author_rank: Frequent Contributor
timestamp: July 07, 2016, 02:38:58 am
content: 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.

author: MarkF
author_rank: Super Contributor
timestamp: July 07, 2016, 02:51:12 am
content: Quote from: ovnr on July 07, 2016, 02:38:58 amThe 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.

author: ovnr
author_rank: Frequent Contributor
timestamp: July 07, 2016, 02:51:20 pm
content: Quote from: MarkF on July 07, 2016, 02:51:12 amThe LCD library. Obviously the PIC headers themselves have individual pins.And if he's actually driving a HD44780, any glitches are not an issue.

author: Ian.M
author_rank: Super Contributor
timestamp: July 07, 2016, 03:08:25 pm
content: 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.

author: rstofer
author_rank: Super Contributor
timestamp: July 07, 2016, 04:17:09 pm
content: Quote from: MarkF on July 07, 2016, 01:56:02 amI 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.

author: MarkF
author_rank: Super Contributor
timestamp: July 07, 2016, 08:49:11 pm
content: Quote from: rstofer on July 07, 2016, 04:17:09 pmI 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.

author: neko efecktz
author_rank: Regular Contributor
timestamp: July 08, 2016, 12:41:39 am
content: Hi Guys, I'm backWorked 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 nibbleall 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.

author: MarkF
author_rank: Super Contributor
timestamp: July 08, 2016, 03:59:11 am
content: 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.

author: rstofer
author_rank: Super Contributor
timestamp: July 08, 2016, 05:04:15 am
content: 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 CmyPortVal &= 0xF0; // set the 4 low bits to 0myPortVal |= <my new bits>; update only the low four bits, make sure the upper 4 bits are 0PORTx = myPortVal; // now write it to the portSomething 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 bitmyPortVal &= ~(1 << bitNum); // set the bit in the shadow value to zeromyPortVal |= (1 << bitNum); // set the bit in the shadow value to onePORTx = 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!

author: Ian.M
author_rank: Super Contributor
timestamp: July 08, 2016, 07:14:10 am
content: Quote from: rstofer on July 08, 2016, 05:04:15 amErrr.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.

author: macboy
author_rank: Super Contributor
timestamp: July 08, 2016, 03:16:45 pm
content: Quote from: neko efecktz on July 07, 2016, 01:16:56 amAdding the line#define LCD_DATA PORTBdoesn't waste anything, and truthfully, it doesn't reallydoanything 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"beforecompiling. 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.RB7Then you can just doBUSY_LED = 1;orBUSY_LED = 0;to turn it on and off. This won't affect the bits you are using for the LCD.

author: dannyf
author_rank: Super Contributor
timestamp: July 08, 2016, 06:23:03 pm
content: QuoteIt depends on how the library is written. You will need the source code to determine that.

author: rstofer
author_rank: Super Contributor
timestamp: July 08, 2016, 07:16:01 pm
content: Quote from: macboy on July 08, 2016, 03:16:45 pm

author: dannyf
author_rank: Super Contributor
timestamp: July 11, 2016, 11:10:32 am
content: "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.

author: Philfreeze
author_rank: Regular Contributor
timestamp: July 11, 2016, 01:04:26 pm
content: How I do it:Code:/***************************************************************************************** global types *****************************************************************************************/// generic port bitstypedef 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 bitstypedef 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: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.