NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Reprogramming LCD pins

May 23, 2009
by andy_b1986
andy_b1986's Avatar

Hello Im trying to reprogramme the chips lcd functions in order to move one pin 'PD3'. This is so that i can use the lcd whilst using this pin number for the RC Car project. I tried just unplugging it to see what this particular pin did (hoping that it wouldnt be needed) but it seems to be the pin which updates or refeshes the screen as the screen freezes when PD3 is unplugged.

I have had a look at the datasheet and cross referenced with the header file lcd.h and the lcd.c files in the library. To be honest all i could do was to find the pin number as i have no idea what to look for.

Has anyone got an idea, or has done the recoding in order for whatever information is coming out of that pin, to be output on a different pin?

May 23, 2009
by wayward
wayward's Avatar

Hello,

in the NerdKits setup, PD3 is second-lowest bit of the command/data bus between the microcontroller and the LCD driver. That means: if you detach it, LCD driver will not be getting valid commands to initialize, clear screen, move the cursor, etc. nor will it be receiving any valid data to print.

There are ways to free the pin(s) that you need, but they all require you to modify the lcd.c file. Commands and data are sent by directly changing the corresponding pins' values, so if you want to shift the pins around, you will have to modify the code so that outgoing bits are mapped to appropriate pins.

If you have an 74 series 174 flip-flop lying around, you can use it to build a two-wire interface to the uC (see my Vicious Alarm Clock thread). Otherwise, you'll have to play with the pins and bits.

Good luck, and keep us posted!

May 24, 2009
by andy_b1986
andy_b1986's Avatar

when you say pins and bits do you mean the '0xfc' address name for the group of pins and then the bit number so PD3 would be bit number 3? i was up late last night reading the datasheet so I might have confused something. How would I then tell this specific pin to be output on say pin number PB3/OC2A?

in the lcd.c file there is a piece of code

//lcd_init()
void lcd_init(){
//set pin driver directions
//(output on PD7, PD6, and PD3-6)
DDRD |= 0xfc;

is this the section which i need to change? do i need to do anything else to the existing lcd.c file?

Unfortunately i dont have any flip flops and i am running out of time for my project any help would be much appreciated! Im quite new to this and so there are still a lot of walls to climb!

cheers

May 24, 2009
by wayward
wayward's Avatar

Yes, that is one part of what you need to change. Since you want PD3 free, first you need to skip setting its data direction to 1 (which sets it as output). The line in the code

//set pin driver directions
//(output on PD7, PD6, and PD3-6)
DDRD |= 0xfc;

is just a shorthand for: DDRD |= (1<<PD7)|(1<<PD6)|(1<<PD5)|(1<<PD4)|(1<<PD3)|(1<<PD2) (the comment above is wrong, it should read "output on PD7, PD6, and PD2-5"). This sets all the aforementioned pins to output. If you're a stranger to bitwise operations and shifting, a quick explanation: PD0..7 are numbers from 0 to 7. Bits in a byte are also ordered from 0 to 7, but we can't access them directly; we have to get to them using AND/OR (& and | in C) bitwise operations. Another important operation is binary shift, << and >>, which shifts all the bits in a variable a certain number of positions to the left or to the right. So:

DDRD |=  (calculate the right-hand side of the operation and OR it with DDRD's current value)
(1<<PD7) = 1 << 7  =  [00000001] << 7  =  [10000000] (128, or 0x80)
(1<<PD6) = 1 << 6  =  [00000001] << 6  =  [01000000] ( 64, or 0x40)
(1<<PD5) = 1 << 5  =  [00000001] << 5  =  [00100000] ( 32, or 0x20)
(1<<PD4) = 1 << 4  =  [00000001] << 4  =  [00010000] ( 16, or 0x10)
(1<<PD3) = 1 << 3  =  [00000001] << 3  =  [00001000] (  8, or 0x08)
(1<<PD2) = 1 << 2  =  [00000001] << 2  =  [00100100] (  4, or 0x04)
----------------------------------------- OR these together:
                                          [11111100] (252, or 0xfc)

So DDRD bits will be ORed with ones, except for the last two. This turns the highest six bits on. Of course, we could have written: DDRD = 0xfc; and that would've worked -- but the reason why we don't is because we might be using PD1 and PD0 somewhere else, and this would force their data direction to change to zero (input). So instead we OR the value of DDRD with ones and zeroes, where 1 will turn a bit on, and 0 will leave it as it was, 0 or 1, regardless.

If you drop PD3 from this, your magic value is 252-8, or 0xf4. So that's the first thing you need to do in order to free PD3 for your own nefarious purposes. But that is just the beginning. You also need to select one pin to replace PD3: say, PB3. Then, you will need to add this to the initialization:

DDRD |= 0xf4;
DDRB |= (1<<PB3);   // set PB3 to output

And you also need to comb the code and find all references to DDRD and PORTD; examine if the operation on them touches PD3; if it does, make it NOT touch it and instead divert the corresponding bit to DDRB/PORTB, pin PB3. For example, from lcd_write_nibble(char c):

PORTD &= ~(0x0f << 2);
PORTD |= (c&0x0f) << 2;

This ANDs PORTD with the complement of 0x0f shifted to the left by two. 0x0f is 00001111; shift by two gives 00111100; complement is 11000011; ANDing a port with 11000011 has the effect of clearing its pins Px2, Px3, Px4 and Px5. Since we don't want to touch PD3, we have to throw it out of the clearing mask: 11000011 becomes 11001011. You can substitute this value directly using its decimal (203) or hexadecimal (0xcb) notation, but it's better to write the bitmask clearly in the code and use those bitwise operations which illustrate your intent. So:

PORTD &= ~(0x0d << 2);    // bitmask 1101 : second bit is skipped, << 2 would map it to PD3
PORTD |= (c & 0x0d) << 2; // bits from c minus the second one are written to port D, pins 2..5

// now send the maverick bit to PB3
PORTB &= ~(1 << PD3);     // clear PD3
PORTB |= (c & 0x02) << 2; // C & 0x02 gets the second bit; we shift it to the left twice more
                          // so it comes straight to the position of PB3, and finally OR it in

Make a backup of lcd.c, do this through the rest of the code, and try it out!

Cheers, Zoran

May 24, 2009
by mcai8sh4
mcai8sh4's Avatar

@wayward - Thanks for the explanation, really helped me to grasp this idea!! I've re-read the explanation in the Nerdkits guide a few times, whilst it makes sense, it hadn't fully sunk in. Just a quick question... from what I see here...

[quote]So DDRD bits will be ORed with ones, except for the last two. This turns the highest six bits on. Of course, we could have written: DDRD = 0xfc; and that would've worked -- but the reason why we don't is because we might be using PD1 and PD0 somewhere else, and this would force their data direction to change to zero (input). So instead we OR the value of DDRD with ones and zeroes, where 1 will turn a bit on, and 0 will leave it as it was, 0 or 1, regardless.[/quote]

I would be better using "PORTC |= t.minutes;" in my binary clock program - I don't think it matters for what I'm doing, but it sounds like it would be good practice. If you get a chance, please have a quick review of my code HERE

Also why use hex opposed to base10 - is that just a tradition or are there deeper reasonings?

Thanks once again

May 24, 2009
by andy_b1986
andy_b1986's Avatar

thanks a lot guys i will have a go at this tonight really starting to make sense cheers

May 24, 2009
by wayward
wayward's Avatar

Sure, you can use PORTC |= t.minutes; but you have to erase the bits first, or else whatever was already there will get ORed with the new value -- not what you want. When we are assigning a value to all bits in a port, we simply do:

PORTC = t.minutes;

instead of functionally equivalent, but in this case more contrived:

PORTC &= 0;
PORTC |= t.minutes;

As a rule of thumb, use "AND with zeroes; OR with bits" when you only want to turn a number of bits on a port, but leave the other bits unchanged. If you are simply outputting (or reading) a full 8-bit value, just use the assignment, or read from the port: PORTC = byte or byte = PINC.

Why hex vs. decimal notation? It's just a matter of convenience, really -- after a while, you learn the hexadecimal digits, which have a superconvenient property that one digit fully maps to 4 bits, so any 8-bit value can be written using two hex digits. When you see "0xfc", for example, you can immediately think of "f c = 1111 1100", because those 16 groups of bits tend to etch themselves in your mind, so after a while, reading a hex number becomes almost the same as seeing the bits typed out. :)

I didn't have the stamina to go through your entire program, but do try it out and write us if something doesn't work, then we can see how to fix it.

May 24, 2009
by mcai8sh4
mcai8sh4's Avatar

All makes sense now, thanks. I'll play around with different methods until it becomes second nature.

Thanks again

May 24, 2009
by andy_b1986
andy_b1986's Avatar

thanks a lot wayward!! it worked!! I tried it without checking the rest of my code and it didnt work first time (nothing ever does ive come to realise). i was testing by using the servo squirter tutorial to update the screen when a command was sent. This code had two lines of code which i just took out as they were being sent to PB3

these were

if(tc=='0') PORTB &= ~(1<<PB3);
if(tc=='1') PORTB |= (1<<PB3);

i just took these two lines out and now the screen is updating to this pin. I can now put the chip and all the other bits into a permanent circuit which will be nicer than the breadboard with all the wires coming out the lcd

thanks again!

May 24, 2009
by andy_b1986
andy_b1986's Avatar

"i just took these two lines out and now the screen is updating to this pin" meant to say screen is refreshing with PB3 set to the output that PD3 used to be

May 25, 2009
by andy_b1986
andy_b1986's Avatar

Hello again

It all worked well until i put it into a circuit i made. The chip is still controllable through terminal and when I GND pin 14 the screen still does its original function with blocks across line one. However when trying to run the chip normally the same blocks appear on the first line of the LCD and i have checked my wiring loads. The lcd is the HD44780 type,

Here is a list of what I have connected each pin to,

LCD Pin 1 = GND Rail LCD Pin 2 = +5v Rail LCD Pin 3 = GND Rail LCD Pin 4 = MCU Pin 13 'PD7' LCD Pin 5 = GND Rail LCD Pin 6 = MCU Pin 12 'PD6'

LCD Pin 11 = MCU Pin 4 'PD2' LCD Pin 12 = MCU Pin 17 'PB3' LCD Pin 13 = MCU Pin 6 'PD4' LCD Pin 14 = MCU Pin 11 'PD5'

Any ideas?

May 25, 2009
by andy_b1986
andy_b1986's Avatar

apologies for previous layout

LCD Pin 1 = GND Rail 
LCD Pin 2 = +5v Rail 
LCD Pin 3 = GND Rail 
LCD Pin 4 =  MCU Pin 13 'PD7' 
LCD Pin 5 = GND Rail 
LCD Pin 6 =  MCU Pin 12 'PD6'

LCD Pin 11 =  MCU Pin 4 'PD2' 
LCD Pin 12 = MCU Pin 17 'PB3' 
LCD Pin 13 =  MCU Pin 6 'PD4' 
LCD Pin 14 = MCU Pin 11 'PD5'
May 25, 2009
by andy_b1986
andy_b1986's Avatar

ok ive jsut put everything back onto the breadboard and everything works fine there must be a short happening somewhere in my circuit

May 25, 2009
by wayward
wayward's Avatar

Yeah, that sounds like an issue with wiring.

May 25, 2009
by andy_b1986
andy_b1986's Avatar

These are the voltage readings coming out of the chip when on the breadboard;

LCD Pin 1 = GND Rail 
LCD Pin 2 = +5v Rail 
LCD Pin 3 = GND Rail 
LCD Pin 4 =  MCU Pin 13 'PD7' 4.97v
LCD Pin 5 = GND Rail 
LCD Pin 6 =  MCU Pin 12 'PD6' 0.01v

LCD Pin 11 =  MCU Pin 4 'PD2' 0.01v
LCD Pin 12 = MCU Pin 17 'PB3' 0.01v
LCD Pin 13 =  MCU Pin 6 'PD4' 0.01v
LCD Pin 14 = MCU Pin 11 'PD5' 0.01v

... and these are the voltage readings coming out of the chip when in my circuit;

LCD Pin 1 = GND Rail 
LCD Pin 2 = +5v Rail 
LCD Pin 3 = GND Rail 
LCD Pin 4 =  MCU Pin 13 'PD7' 4.97v
LCD Pin 5 = GND Rail 
LCD Pin 6 =  MCU Pin 12 'PD6' 0.00v

LCD Pin 11 =  MCU Pin 4 'PD2' 0.00v
LCD Pin 12 = MCU Pin 17 'PB3' 0.00v
LCD Pin 13 =  MCU Pin 6 'PD4' 0.00v
LCD Pin 14 = MCU Pin 11 'PD5' 0.00v

does this mean i have a grounding issue somewhere?

May 25, 2009
by wayward
wayward's Avatar

Well, this is all well within the measuring tolerance, plus the circuit you have is probably adding a little skew. If the LCD works outside of your circuit, then the software controlling it is correct, there's no two ways about it. If it stops working when you put it in a circuit, as far as my knowledge goes, it can be caused by one or several issues:

  • Something somewhere is shorted. Check the entire circuitry, not just the LCD.
  • Your MCU isn't operating at all. Check all the pins and check that the crystal is appropriate for your voltage (1-20MHz for 4.5-5.5V VCC).
  • Your circuit may be too noisy for the MCU to operate reliably. Are you using the 0.1uF bypass capacitor across VCC/GND?

If all else fails, you can always use the tried-and-true method of printf()-style debugging, only adjusted for embedded electronics :) Simply add one resistor-LCD pair to your circuit, connect it to GND and one pin on the MCU, then just below the lcd_init() call, set that pin to source current (appropriate DDR and PORT bits on). If the LED stays off, you'll know that the MCU isn't running your program at all, and that's a start.

May 26, 2009
by andy_b1986
andy_b1986's Avatar

the software is correctly working as i can communicate with the kit from laptop. I have had a look on the entire circuit and have tried resoldering some joins everthing looks ok to me and none of the solder is crossing tracks. I have buzzed through a lot of it with ease. What would the capacitor do? would it reduce noise?

May 26, 2009
by wayward
wayward's Avatar

Yes, the capacitor that connects across pins 7 and 8 in all of our designs serves to filter out a good portion of the AC component in the power supply to the microcontroller unit. Capacitors serving that purpose are called "bypass capacitors". What kind of a capacitor and what capacitance to use depends on the nature of the circuit and the characteristics of the noise, but 0.1 uF ceramic capacitor seems to be a popular choice for 5V circuits. Here's a rather informative text about them.

I really have very little experience with electronics — all I can recommend at this point is to make sure you're using a bypass capacitor, doubly so if you're powering from a transformer, and also beware of radio interference (best to test with a battery first).

Post a Reply

Please log in to post a reply.

Did you know that LEDs (light emitting diodes) only conduct current in one direction, like normal diodes? Learn more...