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.

Support Forum » atmega168 PD4 (pin 6) dead?

December 29, 2012
by keith712
keith712's Avatar

here's my latest problem... i received my original nerdkit back from a friend with the comment 'it broke'... i rewired the kit including a brand new LCD... i compiled and uploaded a version of initialload that i've modified to blink an LED along with the 'congrats' message...

in run mode the LED blinks but the message does not display... the new LCD shows a quick flash but no message appears...

i then wired up my original LCD to the kit... it displays two black bars in both the program and run modes...

i'm convinced my code is OK so i thought of a way to test my kit:

i disconnected the LCD and put six LEDs on PD2-7 (pins 4,5,6,11,12,13)... i then wrote a test routine which set PD2-7 to output and then turned the LEDs on and off in a pattern...

it turns out that PD4's LED wouldn't light up... i double checked the LED's polarity and i swapped LEDs around but it's always PD4 that's dead...

i'm pretty sure that this problem is unfixable but i'd welcome any comments/suggestions... plus i wanted to pass on my crude but seemingly effective hardware testing technique... keith

December 29, 2012
by pcbolt
pcbolt's Avatar

Keith -

That is a good test. I would go one step further and jam the leg of the LED into the same breadboard hole as the MCU PD4 pin. I've had breadboards that are finicky that way. You could try using PORTC instead of PORTD for the LCD. You'd have to revise the "lcd.c" file in the "libnerkits" directory just make sure you make a copy of the original.

December 29, 2012
by keith712
keith712's Avatar

thank you thank you... i would never have thought of either of those two test... i'm going to try right now... later, k

December 29, 2012
by keith712
keith712's Avatar

i managed to jam the leads of two LEDs into PD4 (pin 6) and PD3 (pin 5) with the same results... pd3 is alive and pd4 is dead... i've started translating PDx pins into PCx and PBx pins to modify lcd.c... i may have drank too much wine for this but here goes... k

December 29, 2012
by keith712
keith712's Avatar

update:

i rewired my kit to drive the lcd with PC0-5 pins... i rewrote lcd.c to use the PCx pins instead of PDx pins... initialload and the new lcd.c compiled on the first try!!! sometimes i surprise myself... the output was just as expected on one of my lcds... my other two lcds are broke... one only displays two black bars no matter the mode and the other shows two black bars in program mode but is blank in run mode...

if anybody's interrested in my rewrite of lcd.c i'll post it...

anybody know anything about fixing lcds or is it better to put them out to pasture and buy new ones?

December 29, 2012
by keith712
keith712's Avatar

i forgot... how about my broken atmega168?... is there a fix for dead pins or do i just work around it? thanks... k

December 29, 2012
by Ralphxyz
Ralphxyz's Avatar

I have been fighting with the two black bars scenario for a couple of months now so I'd appreciate seeing your rewrite of lcd.c.

re your "broken" atmega 168 I would start from scratch completely strip the breadboard and try to load the initialload project as directed in the Nerdkits User Guide.

Ralph

December 29, 2012
by Noter
Noter's Avatar

There is no fix for a dead pin on the mcu. Probably you will want to get another chip because PCx is the ADC and you will not be able to do the adc projects with your display on that side. It's a good idea to have a few extra chips anyway just in case you accidentally fry one. And sometimes its good to swap the chip to be sure it's not a problem in the breadboard or some other unexpected thing.

If your display's are broken for sure might as well dissect and then pitch them. If you're not sure then hold on for a while until you have another setup to test them with. I've also found that different makes of displays typically need a different contrast resistor so maybe the one that looks blank needs a contrast adjustment.

December 29, 2012
by keith712
keith712's Avatar

below is my modified lcd.c... this didn't fix the two bar lcd or the blank lcd but it allowed me to check my lcds and that led me to the dead PD4 pin on my mcu...

do you think i should try increasing or decreasing the resistance of the contrast resistor?

thank you guys for keeping an eye out for me!!!

// lcd.c
// for NerdKits
// mrobbins@mit.edu
//
// modified 22dec12: function lcd_goto_position() was causing compiler error and
// then output to row 0 only
//
// modified 29dec12: drive lcd from PC0-5
//
// keithmac@maui.net
//
// PIN DEFINITIONS:
// nerdkit pinout                                            PCx pinout
// PD2-5 (pins 4,5,6,11) LCD DB4-7 (pins 11-14) data nibble     PC0-3 (pins 23-26)
// PD6 (pin 12)          LCD E (pin 6)          low/high nibble PC4 (pin27)
// PD7 (pin 13)          LCD RS (pin 4)         data/command    PC5 (pin 28)
//
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <stdio.h>
#include "lcd.h"
#include "delay.h"
// lcd_set_type_data()
void lcd_set_type_data() {
  PORTC |= (1<<PC5);                // PORTD |= (1<<PD7);
}
// lcd_set_type_command()
void lcd_set_type_command() {
  PORTC &= ~(1<<PC5);               // PORTD &= ~(1<<PD7);
}
// lcd_write_nibble(...)
void lcd_write_nibble(char c) {
  // NOTE: only 2 or 3 work in the delays here.
  // set data
  PORTC &= ~(0x0f);                 // PORTD &= ~(0x0f << 2);
  PORTC |= (c&0x0f);                // PORTD |= (c&0x0f) << 2;
  // E high
  PORTC |= (1<<PC4);                // PORTD |= (1<<PD6);
  delay_us(1);
  // E low
  PORTC &= ~(1<<PC4);               // PORTD &= ~(1<<PD6);
  delay_us(1);
}
void lcd_write_byte(char c) {
  lcd_write_nibble( (c >> 4) & 0x0f );
  lcd_write_nibble( c & 0x0f );
  delay_us(80);
}
void lcd_clear_and_home() {
  lcd_set_type_command();
  lcd_write_byte(0x01);
  delay_ms(50);
  lcd_write_byte(0x02);
  delay_ms(50);
}
void lcd_home() {
  lcd_set_type_command();
  lcd_write_byte(0x02);
  delay_ms(50);
}
void lcd_write_data(char c) {
  lcd_set_type_data();
  lcd_write_byte(c);
}
// lcd_write_int16
void lcd_write_int16(int16_t in) {
  uint8_t started = 0;
  uint16_t pow = 10000;
  if(in < 0) {
    lcd_write_data('-');
    in = -in;
  }
  while(pow >= 1) {
    if(in / pow > 0 || started || pow==1) {
      lcd_write_data((uint8_t) (in/pow) + '0');
      started = 1;
      in = in % pow;
    }
    pow = pow / 10;
  }
}
// lcd_write_int16_centi
// assumes that its measured in centi-whatevers
void lcd_write_int16_centi(int16_t in) {
  uint8_t started = 0;
  uint16_t pow = 10000;
  if(in < 0) {
    lcd_write_data('-');
    in = -in;
  }
  while(pow >= 1) {
    if(in / pow > 0 || started || pow==1) {
      lcd_write_data((uint8_t) (in/pow) + '0');
      started = 1;
      in = in % pow;
    }
    if(pow == 100) {
      if(!started) {
        lcd_write_data('0');
      }
      lcd_write_data('.');
      started = 1;
    }
    pow = pow / 10;
  }
}
void lcd_write_string(const char *x) {
  // assumes x is in program memory
  while(pgm_read_byte(x) != 0x00)
    lcd_write_data(pgm_read_byte(x++));
}
void lcd_goto_position(uint8_t row, uint8_t col) {
  lcd_set_type_command();
  // 20x4 LCD: offsets 0, 0x40, 20, 0x40+20
  uint8_t row_offset = 0;
  //switch(row) {
    //case 0: row_offset = 0; break;
    //case 1: row_offset = 0x40; break;
    //case 2: row_offset = 20; break;
    //case 3: row_offset = 0x40+20; break;
  //}
  // this fixes a compiler error and display error
  //
  row %= 4;
  if (row==0) {row_offset=col;}
  if (row==1) {row_offset=0x40+col;}
  if (row==2) {row_offset=20+col;}
  if (row==3) {row_offset=0x40+20+col;}
  lcd_write_byte(0x80 | row_offset);
}
void lcd_line_one()   { lcd_goto_position(0, 0); }
void lcd_line_two()   { lcd_goto_position(1, 0); }
void lcd_line_three() { lcd_goto_position(2, 0); }
void lcd_line_four()  { lcd_goto_position(3, 0); }
// lcd_init()
void lcd_init() {
  // set pin driver directions
  // PC5, PC4, PC0-3
  DDRC |= 0x3f;                         // DDRD |= 0xfc;
  // wait 100msec
  delay_ms(100);
  lcd_set_type_command();
  // do reset
  lcd_write_nibble(0x03);
  delay_ms(6);
  lcd_write_nibble(0x03);
  delay_us(250);
  lcd_write_nibble(0x03);
  delay_us(250);
  // write 0010 (data length 4 bits)
  lcd_write_nibble(0x02);
  // set to 2 lines, font 5x8
  lcd_write_byte(0x28);
  // disable LCD
  //lcd_write_byte(0x08);
  // enable LCD
  lcd_write_byte(0x0c);
  // clear display
  lcd_write_byte(0x01);
  delay_ms(5);
  // enable LCD
  lcd_write_byte(0x0c);
  // set entry mode
  lcd_write_byte(0x06);
  // set cursor/display shift
  lcd_write_byte(0x14);
  // clear and home
  lcd_clear_and_home();
}
int lcd_putchar(char c, FILE *stream) {
  lcd_write_data(c);
  return 0;
}
December 29, 2012
by Noter
Noter's Avatar

Decrease the contrast resistor. For a quick test, leave it out and go straight to GND if it's still blank then contrast is not the problem.

Your change to lcd.c looks good. It would be useful to fix it to where any mcu pin can be used for any lcd pin instead of restricting to a single port.

December 29, 2012
by pcbolt
pcbolt's Avatar
  • "Your change to lcd.c looks good. It would be useful to fix it to where any mcu pin can be used for any lcd pin instead of restricting to a single port."

Sounds like you're throwing down the gauntlet, Noter :-)

December 30, 2012
by keith712
keith712's Avatar

i thought about using a mix of PCx and PBx pins but by staying in one port i can take advantage of setting or clearing several bits in one register in one line...

i'm going to take up the challenge and generalize the code for any combination of pins...

maybe i could do something like this:

// edit this list to change pin-out
//
#define lcd4 = Pxn
...
#define lcd14 = Pxn
...
//
// add these two functions to lcd.c
//
int get_shift(lcd) {
  if ((lcd==Px0){return 0;}
  else if (lcd==Px1) {return 1;}
  ...
  ...
  else if (lcd==Px7) {return 7;}
  else {//error handler}
}

char get_register(lcd) {
  if (lcd==PBn) {return B;}
  else if (lcd==PCn) {return C;}
  else if (lcd==PDn) {return D;}
  else {//error handler}
}
...
//
// examples of bit clearing and setting
//
PORT+get_register(lcd4) &= ~(get_shift(lcd4) << lcd4)
...
PORT+get_register(lcd14) &= ~(get_shift(lcd14) << lcd14)
...
DDR+get_register(lcd4) |= (get_shift(lcd4) << lcd4);
...
DDR+get_register(lcd14) |= (get_shift(lcd14) << lcd14);
...

i assume that lcdn and DDR+get_register() will be string types... so i need to find out how c evaluates a string expression to a value for the compiler...

let me know what you think...

December 30, 2012
by keith712
keith712's Avatar

its getting late... i screwed up the examples of bit setting and clearing... it should be something like:

PORT+get_register(lcd4) &= ~(1 << get_shift(lcd4))
...
PORT+get_register(lcd14) &= ~(1 << get_shift(lcd14))
...
DDR+get_register(lcd4) |= (1 << get_shift(lcd4));
...
DDR+get_register(lcd14) |= (1 << get_shift(lcd14));
...
December 30, 2012
by Noter
Noter's Avatar

Didn't mean to challenge in any way but rather just give some ideas that will be useful in the future as well as a opportunity to learn more about the avr and programming in c.

I reference the AVR Libc site quite often as it is full of good information and in the FAQ there is an example of passing an I/O port to a function - How do I pass an IO port as a parameter to a function?.

Not only will it be convenient to move a pin at a time but you will really appreciate the flexibility if/when you move to another chip, maybe like the ATmega32U4. And until then you could just clean up the ratsnest on the nerdkit which can help with loose wires/black bars and such in the long run.

December 30, 2012
by keith712
keith712's Avatar

i was saying challenge with tongue-in-cheek... i need to use emoticons more...

but you're right... this is an excellent opportunity to get more familiar with c and atmega168 io...

i may not look at your links until after i have code that compiles, runs and does what i expect and then compare my amateur hack with the library's solution...

by-the-by... the above won't work unless i write an eval routine for c... which i'm not capable of plus i'm guessing would use more memory then the whole nerdkit's library... i'm spoiled by avoiding c and working in "higher level languages" (lol)...

December 30, 2012
by Noter
Noter's Avatar

Great! You'll like c once you get up to speed with it.

One thing to think about is because the pins remain fixed a dynamic type eval in code is not needed. C has preprocessor directives that do nicely in this situation so all the work is done by the compiler and renders a single instruction for manipulating bits at runtime.

December 30, 2012
by keith712
keith712's Avatar

i have a lot to learn... i don't know anything about fixed vs dynamic typing and i know just enough to get in big trouble with makefiles...

your comment about compiler directives confirms my suspicion that my code will be a brute force effort and that i might come up with a much more sophisticated algorythm once i understand more...

i've been having fun this morning... here's what i've come up with so far... i haven't dared try to insert the following into a copy of lcd.c, modifify lcd.c to use it and compile... i'm hoping you or anyone else who's interrested have some time in the next few days to scan this for blunders or worse...

//
// lcd driver functions for any given set of mcu output pins
//
// hardware:
//      atmega168
//      LCD with HD44780 protocols
//
// brief history:
//
// lcd.c was modified to use six pins in either port D or port C
//
// nerdkits pin-out      lcd pins                                  k's pin-out
// PD2-5 (pins 4,5,6,11) LCD DB4-7 (pins 11-14) data nibble        PC0-3 (pins 23-26)
// PD6 (pin 12)          LCD E  (pin 6) low or high nibble switch  PC4 (pin 27)
// PD7 (pin 13)          LCD RS (pin 4) data or command switch     PC5 (pin 28)
//
// the problem is to generalize the functions so that any mix of six pins can be used
//
// available pins for atmega168 with nerdkits skeleton hardware setup:
//              PB 1-5 (pins 15-19)
//              PC 0-5 (pins 23-28)
//              PD 2-4 (pins 4-6)
//              PD 5-7 (pins 11-13)
//
// this is a brute force algorythm. i have a feeling that i might be able to
// finesse a lot of this with a more sophisticated use of the registers,
// pointers or something else that i don't know about yet
//
// 30dec12 keithmac@maui.net
//
//*****************************************************************
//
// std library
//
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
//
// avr library
//
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
//
// nerdkits library
//
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
//
//*****************************************************************
//
// use twelve constants to hold port and pin values for six output pins to lcd
//
// port and pin constants
//
//lcd pin                          port constant  pin constant
//4  RS  data or command switch    L4port         L4pin
//6  E   low or high nibble switch L6port         L6pin
//11 DB4 data nibble bit           L11port        L11pin
//12 DB5 data nibble bit           L12port        L12pin
//13 DB6 data nibble bit           L13port        L13pin
//14 DB7 data nibble bit           L14port        L14pin
//
// char Lxxport in ("B","C","D")
// uint8_t Lxxpin in (0,1,2,3,4,5,6,7)
//
// the constants below correspond to the original nerdkits pin-out
// edit this list to change the pin-out
//
#define L4port="D"
#define L4pin=7
#define L6port="D"
#define L6pin=6
#define L11port="D"
#define L11pin=2
#define L12port="D"
#define L12pin=3
#define L13port="D"
#define L13pin=4
#define L14port="D"
#define L14pin=5
//
// following are three functions for setting, clearing and reading pins:
//      set_dir_DDRX()
//      output_PORTX()
//      read_PINX()
//
//*****************************************************************
//
// data direction register: DDRX
//
// syntax for:
//      out=>set the bit  =>flg=1=>set_dir_DDRX(Lxxport,Lxxpin,1);
//      in=> clear the bit=>flg=0=>set_dir_DDRX(Lxxport,Lxxpin,0);
//
void set_dir_DDRX(char prt, uint8_t pin, uint8_t flg) {
  if (flg==0) {
    if (prt=="B") {
      DDRB &= ~(1<<pin);
      }
    else if (prt=="C") {
      DDRC &= ~(1<<pin);
      }
    else if (prt=="D") {
      DDRD &= ~(1<<pin);
      }
    else {//error handler: prt not in ("B","C","D")}
      }
  else if (flg==1) {
   if (prt=="B") {
      DDRB |= (1<<pin);
      }
    else if (prt=="C") {
      DDRC |= (1<<pin);
      }
    else if (prt=="D") {
      DDRD |= (1<<pin);
      }
    else {//error handler: prt not in ("B","C","D")}
    }
  else {//error handler: flg not in (0,1)}
  // i may need an error handler for pin values not in (0,1,2,3,4,5,6,7)
  }
//
//*****************************************************************
//
// port register: PORTX
//
// syntax for:
//  output 1 (+5V)=>set the bit  =>flg=1=>output_PORTX(Lxxport,Lxxpin,1)
//  output 0 (0V) =>clear the bit=>flg=0=>output_PORTX(Lxxport,Lxxpin,0)
//
void output_PORTX(char prt, uint8_t pin, uint8_t flg) {
  if (flg==0) {
    if (prt=="B") {
      PORTB &= ~(1<<pin);
      }
    else if (prt="C") {
      PORTC &= ~(1<<pin);
      }
    else if (prt=="D") {
      PORTD &= ~(1<<pin);
      }
    else {//error handler: prt not in ("B","C","D")}
      }
  else if (flg==1) {
   if (prt=="B") {
      PORTB |= (1<<pin);
      }
    else if (prt=="C") {
      PORTC |= (1<<pin);
      }
    else if (prt=="D") {
      PORTD |= (1<<pin);
      }
    else {//error handler: prt not in ("B","C","D")}
    }
  else {//error handler: flg not in (0,1)}
  // i may need an error handler for pin values not in (0,1,2,3,4,5,6,7)
  }
//
//*****************************************************************
//
// while i'm at it i might as well generalize reading
// an input pin even though i won't use it in lcd.c
//
// pin value register: PINX
//
// char port in ("B","C","D")
// uint8_t pin in (0,1,2,3,4,5,6,7)
// uint8_t pin_value in (0,1)
//
// syntax for read: pin_value=read_PINX(port,pin);
//
uint8_t read_PINX(char prt, uint8_t pin) {
  if (port=="B") {
    return ((PINB &= (1<<pin))>>pin);
    }
  else if (port=="C") {
      return ((PINC &= (1<<pin))>>pin);
    }
  else if (port=="D") {
    return ((PIND &= (1<<pin))>>pin);
    }
  else {//error handler: prt not in ("B","C","D")}
  // i may need an error handler for pin values not in (0,1,2,3,4,5,6,7)
  }
//
//*****************************************************************
//
// following will be the functions that will set, clear and read pins
//
// examples of use:
//
// direction out for all six pins
//
// set_dir_DDRX(L4port,L4pin,1);
// set_dir_DDRX(L6port,L6pin,1);
// set_dir_DDRX(L11port,L11pin,1);
// set_dir_DDRX(L12port,L12pin,1);
// set_dir_DDRX(L13port,L13pin,1);
// set_dir_DDRX(L14port,L14pin,1);
//
// lcd to receive data:
//
// output_PORTX(L4port,L4pin,1);
//
// lcd to receive command:
//
// output_PORTX(L4port,L4pin,0);
//
//  lcd to receive high nibble:
//
// output_PORTX(L6port,L6pin,1);
//
// lcd to receive low nibble:
//
// output_PORTX(L6port,L6pin,0);
//
// will there be a timing problem sending one nibble in four lines?
//
// send low nibble of char c
//
// output_PORTX(L11port,L11pin,((c & 0x01) >> 0);
// output_PORTX(L12port,L12pin,((c & 0x02) >> 1);
// output_PORTX(L13port,L13pin,((c & 0x04) >> 2);
// output_PORTX(L14port,L14pin,((c & 0x08) >> 3);
//
// send high nibble of char c
//
// output_PORTX(L11port,L11pin,((c & 0x10) >> 4);
// output_PORTX(L12port,L12pin,((c & 0x20) >> 5);
// output_PORTX(L13port,L13pin,((c & 0x40) >> 6);
// output_PORTX(L14port,L14pin,((c & 0x80) >> 7);
//
December 30, 2012
by Noter
Noter's Avatar

It looks like it will work but you should test it before putting it into lcd.c. Make a little test program that uses these new routines to blink an led on some otherwise unused pin. Then if it works as expected you can make the mods to lcd.c and feel pretty confident about it working.

December 30, 2012
by JimFrederickson
JimFrederickson's Avatar

One thing that I don't think has been mentioned in this thread is "save often".

I would recommend against just "Continuous/Timed saves", (i.e. Continuous saves of every iteration of any file that is changed, or Timed saves of any file at specific time/interval.)

The chief problem with saves of that nature is that you are NEVER making all of your changes simultaneously. Often, especially with Libraries, you need to make multiple changes in multiple files for a specific purpose. So if your saves are "Continuous" you will need to sort out "which files need to go together for that purpose"... (Potentially a huge problem, and just an added difficulty that can be avoided.)

What generally works best, long term, is treating your program development as a series of steps/plateaus, (whichever metaphor works best or you.) Make sure to take time to test at each step/plateau well too.

Steps/Plateaus should be ONLY known working code, not necessarily code that has all of the desired functionality but code that does reliably run.

That way if you are adding a new function/capability to your code and run into a problem you can save your current source directory as a "failed" or "bad" source directory and then restore from the last known good step/plateaus.

Then you can slowly re-add the portions of the code you changed that are problematic to the known good step.

For me the "saves" that I make are not truly "Versions" as I treat a "Version" as a special case of a step where the code is complete enough to go into production/use on my project.

I, personally, would test/develop your new LCD.c code on a circuit wired as required by the Nerdkit LCD.C Library with a known good Micronctroller too.

That way you have "known working code" that can be written to the Microcontroller in case something in your code doesn't work.

As it is now, as you are developing your new LCD.c code you potentially will not be sure if a problem is in your new LCD.c code, in the Microcontroller, or the Wiring/Components of your circuit.

December 31, 2012
by keith712
keith712's Avatar

thanks... i'm glad i held back... i keep a separate folder with all the original source code that i downloaded from the nerdkits people... i also keep a 'diary' of dates and changes i've made to files but most of the entries are changes to makefile...

i've never kept copies of my intermediate attempts... i've always just kept fixing code, optimizing and adding comments until i'm satisfied with the output and how the code would 'read' to somebody other than me... i think this is exactly what you're warning me against...

is this procedure a solution:

   create a project history directory that holds:

       original file(s) of code to be modified

       trial code files that have ONE logical modification to the previous trial

            trial code files are saved whether they compile or not, run without errors or not

            each trial code file should have comments explaning what was intended, what actually happened and suggestions for the next modification

       version code files that have been tested for output and error handling

            the difference between a trial and a version is that the version code is in the judgement of the programmer(s) ready to be used or added to a library

       the process of trial coding, testing, documenting continues starting with the previous version until the next version is judged ready

so i'm off to write an led_blink routine that uses the functions above and set up a project folder to keep this stuff in... i better write a read_me file to explain all this stuff to myself...

Post a Reply

Please log in to post a reply.

Did you know that an electroluminescent backlight for an LCD panel requires hundreds of volts AC to run? Learn more...