PD6,5,3 / Motor control on Baby Orangutan

I’m trying to use code on the Baby Orangutan originally written for use on an ATMega 168. I think I’ve been able to make all the requisite changes for the upgrade from 168 to 328p, and have successfully loaded and run a simple LED blink program to verify such. However, I have an LCD using the HD44780 protocol that I haven’t been able to get to operate properly. It is powering up but does not appear to be getting any data. The code I successfully used on the 168 to run the LCD used data out on PD6, PD5 and PD3 pins. The question is, as the B-Orangutan hardware has these as motor outputs, does this make them unsuitable for use as data out pins to drive my LCD as I did with my old setup?

Hello.

Have you considered using the Pololu AVR library on your Baby Orangutan? Our library has code for using an HD44780-style character LCD in 4-bit mode. If you connect your LCD to the Baby Orangutan the same way the LCD is connected to the AVR on the Orangutan SV-328 (see the schematic), our library code should work for you without modification.

The motor driver pins should not be used as LCD data or control lines. The board doesn’t even give you access to these pins, so it doesn’t make much sense to use them when there are so many other available I/O lines that would let you use both the LCD and the motor driver. If you really want to use your previous code, it shouldn’t be difficult to modify it to use different pins for the data lines. If you need help with this or with using the Pololu AVR Library, please let me know.

- Ben

Ben,

Below is the code used to set up my LCD. It was written by the guys at nerdkits; I’m trying to modify it for use on the Baby O by changing outputs on PD6,5,3 to pins that are actually available such as PD1,0 and PB5. I see where PD6 is referenced but am less clear on PD5 and PD3. My guess after looking at the data sheet and converting the hex (0xfc to 11111100) is that the line: DDRD |= 0xfc; is setting PD7-PD2 as outputs and I assume I start by changing DDRD assignment to DDRD |= 0x9f; (1001111) to set PD7 and PD4-0 as well as add DDRB |= 5 to set PB5 but don’t follow the code closely enough to know what needs modifying next. Thanks for your help.

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <stdio.h>
#include "../libnerdkits/io_328p.h"
#include "lcd.h"
#include "delay.h"

// lcd_set_type_data()
void lcd_set_type_data() {
  PORTD |= (1<<PD7);
}

// lcd_set_type_command()
void lcd_set_type_command() {
  PORTD &= ~(1<<PD7);
}

// lcd_write_nibble(...)
void lcd_write_nibble(char c) {

  // NOTE: only 2 or 3 work in the delays here.

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

  // E high
  PORTD |= (1<<PD6);
  delay_us(1);
  // E low
  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;
  }
  
  
  lcd_write_byte(0x80 | (row_offset + col));
}

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
  // (output on PD7,PD6, and PD3-6)
  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;
}

That code is almost the same as the LCD code in our library, and I think it would be easier to just use the pin assignments our library expects (one that uses free Baby Orangutan I/O pins) along with the library code rather than trying to edit the nerdkits code to support a different pin assignment. Is there a particular reason why you’d prefer the more challenging approach? I think our library supports all the nerdkits LCD functions and more, and if you’re worried about getting old code that used the nerdkits routines to work, you could always write wrapper functions with the same names as the nerdkits ones that just call the equivalent routines from the Pololu AVR library. Plus, using the Pololu AVR library gives you access to a whole host of other routines for things like efficient digital I/O, reading analog inputs, controlling the motor driver, reading input pulses, generating servo pulses, playing music on a buzzer (if you connect one to the appropriate pin), etc.

If you’d still prefer to get the nerdkits code working, let me know and I’ll help you change it when I have more time.

Your DDRD code is right, but your DDRB code isn’t. 0x05 is equivalent to 00000101, which would set pins PB0 and PB2 as outputs when ORed with DDRB. If you want to set PB5 as an output, you need to OR DDRB with 00100000 (32 or 0x20). One easy way to accomplish this is:

DDRB |= (1 << 5); // or DDRB |= (1 << PORTB5);

- Ben

Ben,

I’d like to modify the code I have for a few reasons: as you surmised, I do have a handful of programs that are already running using the current LCD setup. I also have several proto boards with pins hardwired to lcd and other components using that code, so that if I could minimize pin changes, it would help. Last but not least, as is obvious, I could use a better understanding of the lcd code and think if I could see how to make the changes I’d have a better grasp of how the whole piece of code works.

BTW fully copy your explanation of the proper way to set DDRB to PB5 and the bit logic involved vice what I wrote, I must have just been a little punchy when I hit post.

I understand I’m asking for a lot of help and appreciate your assistance.

Eric S.

Ben,

I took a look at the Pololu AVR LCD code, the helpful commenting included in the pololu code helped me to understand what was going on and make mods to my code to shift 3 of the data output pins to lcd from PD6 to PB5 and PD5,3 to PD1,0. The changes shown below work to make those changes with the following caveat - data-in lcd pins 11-14 now go in order from PD0,1,2,4 so I had to change my wiring beyond the three inputs from PD6,5,3.

Changes from the code above:

// (output on PD7,PD6, and PD3-6)
DDRD |= 0xfc;
to
// (output on PD7,PD4, and PD2,1,0 and PB5)
DDRD |= 0x97; //10010111
DDRB |= 0x20; //00100000

and
PORTD &= ~(0x0f << 2);
PORTD |= (c&0x0f) << 2;
to
// set data
PORTD &= ~(0x17); //~10111
PORTD |= (c&0x08) << 1 | (c&0x07);

and

PORTD |= (1<<PD6);
delay_us(1);
// E low
PORTD &= ~(1<<PD6);
delay_us(1);

to
PORTB |= (1<<PB5);
delay_us(1);
// E low
PORTB &= ~(1<<PB5);
delay_us(1);

I apologize for my delayed reply. Do the above changes work for you? They look correct to me.

- Ben

Ben,

Yes, the changes posted above worked.

Eric