…and it’s totally awesome! Actually, it worked last night, but I didn’t get around to posting it. I struggled for a few hours before realizing that I had neglected a pull-down resistor for the MISO line, which I put a protection diode on for when the LCD was running. I even missed it looking with an osciliscope, since I connected it to the anode side of the diode. Anyway, here’s the overly long single file code:
/*SCP1000 Pressure Sensor SPI Test for Atmega8 Orangutan
Adam Borrell
Mobile Robotics Lab
University of Michigan
6/30/07*/
#define LCD_Clear 0x01
#define LCD_Line1 0x80
#define LCD_Line2 0xC0
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
void SPI_MasterPreInit();
void pressureInit();
void pressureWrite(unsigned char addr, unsigned char data);
int pressureRead16(unsigned char addr);
void LCDInit(void);
void LCDSendData(unsigned char data);
void LCDSendCommand(unsigned char command);
void LCDPrintString(const unsigned char *string);
void LCDGotoXY(unsigned char x,unsigned char y);
int main(){
unsigned char buf[8];
int i,j=0;
DDRD|=(1<<PD1);//set LED pin to output
PORTD|=(1<<PD1);//turn on LED
SPI_MasterPreInit();
pressureInit();
LCDInit();
while(1){
if(PINC&(1<<PC4)){//new data ready
PORTD^=(1<<PD1);//toggle LED
SPCR=(1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<CPOL)|(1<<SPR0);//Enable SPI (takes over MISO function), Master, set clock rate fck/16 (500KHz)
itoa(pressureRead16(0x20),buf,10);
SPCR&=!(1<<SPE);//Disable SPI (releases MISO function)
LCDSendCommand(LCD_Clear);
LCDSendCommand(LCD_Line1);
LCDPrintString(buf);
itoa(++j,buf,10);
LCDSendCommand(LCD_Line2);
LCDPrintString(buf);
}
}
return 0;
}
void SPI_MasterPreInit(){//initialize SPI without enabling/compromising LCD pins
PORTC|=(1<<PC5);//bring PC5 high first
DDRC|=(1<<PC5);//Set PC5 (my chosen SS) to output
DDRC&=~(1<<PC4);//Set PC4 (Data Ready Interrupt) to input
DDRB|=(1<<PB2)|(1<<PB3)|(1<<PB5);//Set SS (must be output or externally brought high), MOSI and SCK output, all others user defined
}
void pressureInit(){
int i;
for(i=0;i<5;i++){
_delay_ms(20);//startup delay
}
SPCR=(1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<CPOL)|(1<<SPR0);//Enable SPI (takes over MISO function), Master, set clock rate fck/16 (500KHz)
pressureWrite(0x02,0x2D);//Configure SCP1000 with low noise configuration
pressureWrite(0x01,0x03);
pressureWrite(0x03,0x02);
for(i=0;i<5;i++){
_delay_ms(20);
}
pressureWrite(0x03,0x0A);//Select High Resolution Mode
SPCR&=!(1<<SPE);//Disable SPI (releases MISO function)
}
void pressureWrite(unsigned char addr, unsigned char data){
PORTC&=!(1<<PC5);//bring SS Low
SPDR=((addr<<2)|(1<<1));//Transmit shifted address with write bit
while(!(SPSR&(1<<SPIF)));//Wait for transmission complete
SPDR=data;//transmit data
while(!(SPSR&(1<<SPIF)));//Wait for transmission complete
PORTC|=(1<<PC5);//bring SS high
}
int pressureRead16(unsigned char addr){
int data=0;
PORTC&=!(1<<PC5);//bring SS Low
SPDR=(addr<<2);//Transmit shifted address with read bit
while(!(SPSR&(1<<SPIF)));//Wait for transmission complete
SPDR=0x00;//transmit blank data to generate clock ticks
while(!(SPSR&(1<<SPIF)));//Wait for transmission complete
data=(SPDR<<8);//grab bits
SPDR=0x00;//transmit blank data to generate clock ticks
while(!(SPSR&(1<<SPIF)));//Wait for transmission complete
data|=SPDR;//grab bits
PORTC|=(1<<PC5);//bring SS high
return data;
}
/*
Send lower 4 bits of data byte to display
*/
void LCDSendNibble(unsigned char data){
data&=0x0F; //lower 4 bits of data
data<<=3;
PORTB&=~0x38; //clear LCD bus bits 456
PORTB|=(data & 0x38); //or in data
data<<=1;
PORTD&=~0x80; //same for top bit on PORTD.7
PORTD|=(data & 0x80);
_delay_us(1);
PORTD|=(1<<4); //E = 1
_delay_us(2); //required minimum 1 us delay
PORTD &= ~(1<<4); //E = 0
_delay_us(1);
}
/*
Send a character to the LCD display
*/
void LCDSendData(unsigned char data){
PORTD&=~(1<<3); //R/W =0
PORTD|=(1<<2); //RS = 1;
LCDSendNibble(data>>4);
LCDSendNibble(data);
_delay_us(100); //38 us typically needed to complete this action
}
/*
Send a command to display. Required delay time depends on the command and the
LCD controller clock frequency -- here assumed to be the minimum 190 kHz
*/
void LCDSendCommand(unsigned char command){
PORTD&=~(3<<2); // R/W=0, RS = 0;
LCDSendNibble(command>>4);
LCDSendNibble(command);
_delay_ms(3); //maximum required is 2.1 ms for "clear display"
}
// print a string constant
void LCDPrintString(const unsigned char *str){
while(*str!=0)LCDSendData(*str++);
}
// set print position to (x,y) where y=line number (0 or 1), x = character position 0, 1, etc.
void LCDGotoXY(unsigned char x, unsigned char y){
volatile unsigned char ddram_addr;
ddram_addr=0x80; //initialize data ram address to 0 (default)
if(y==1)ddram_addr=0xC0; //start print at line 2, DDRAM address 0x40
LCDSendCommand(ddram_addr+(x&0x7F));
}
/*
Initialize the LCD Display, timing requirements taken from datasheet
Set PORTB.3,4,5 to DB 4,5,6
Set PORTD.2,3,4,7 to RS, R/W, E and DB7
Send required start-up sequence to set 4 bit interface, 2 lines, 5x8 characters and clear display
*/
void LCDInit(void){
DDRB|=(7<<3);
DDRD|=(1<<7)|(7<<2);
PORTD&=~(7<<2); // E=0,R/W=0, RS = 0;
_delay_ms(20); //required startup sequence from power-on (see datasheet)
LCDSendNibble(0x03); //set interface=8 bits
_delay_ms(10); //wait at least 5 ms
LCDSendNibble(0x03); //set interface=8 bits
_delay_ms(1); //wait at least 100 us
LCDSendNibble(0x03); //set interface=8 bits
_delay_ms(1); //wait at least 100 us
LCDSendNibble(0x02); //set interface=4 bits
_delay_ms(1); //delays after this are built into SendCommand
LCDSendCommand(0x28); //set interface=4 bits, 2 lines, 5x8 characters
LCDSendCommand(0x08); //display off, cursor off, blink off
LCDSendCommand(0x01); //clear display
LCDSendCommand(0x06); //entry mode set, cursor shifts right after character rcvd.
LCDSendCommand(0x0D); //0b01DCB D=1:Display on, C=1:cursor on, B=1:Blink on
}
-Adam