Common routines

From pauljmac.com Projects
Jump to: navigation, search

C routines

RTC

I was trying to come up with good RTC for my Mails Here! project. After trying a couple different methods I settled on this one. My main focus was to keep the ISR as little as possible as it would be executing every second biased on the Timer1 interrupt to increment the counter. The current time is stored in seconds form in an unsigned short long variable named time. The function I created to display the time on my LCD in 12 hour format is as follows, the toBCD function is a lower example on this page:

void DisplayTime(unsigned short long time){
	unsigned short long temp, temp0, temp1, time1, hour, min, secs, ampm;
	if (time>=43200){
		ampm='P';
	}
	else ampm='A';
	time1=time;

	if (time<=3599){
		time1=time+43200;
	}
	
	if (time>=46800){
		time1=time-43200;
	}

	hour=time1/3600;
	temp=hour*3600;
	temp1=time1-temp;
	min=temp1/60;
	temp0=min*60;
	secs=temp1-temp0;
	LCD_cmd(0x80);
	LCD_putch(toBCD(hour, 2)+48);
	LCD_putch(toBCD(hour, 1)+48);
	LCD_putch(':');
	LCD_putch(toBCD(min, 2)+48);
	LCD_putch(toBCD(min, 1)+48);
	LCD_putch(':');
	LCD_putch(toBCD(secs, 2)+48);
	LCD_putch(toBCD(secs, 1)+48);
	LCD_putch(' ');
	LCD_putch(ampm);
}


RTC ISR

The ISR consists of this:

void high_isr(void){
	time++;
	if (time==86400){
		time=0;
	}
}

The if statement to reset the time at the end of the day. Dont forget to include your preload delay in the ISR as well as clear the interrupt! (not shown in the above example)

General LCD driver and string printer

For sending from an AVR to matrix orbital LCD via TWI or i2c interface.

#define F_CPU 20000000UL  //20MHz Clock

#include <avr/io.h>
#include <util/twi.h>
#include <avr/pgmspace.h>

#define LCD_ADDY 0x50   //Write addy for LCD

const char MSG_1[] PROGMEM = "PROGMEM test\0";  //stores the string in ROM

void send_twi(char xyz){
   TWDR = xyz;  // LOAD SLA_W into TWDR reg,
   TWCR = (1<<TWINT) | (1<<TWEN); // CLEAR TWINT bit in TWCR to start transmission
   while (!(TWCR & (1<<TWINT))){ //wait for TWINT flag set. this indicated that SLA+W was transmitted and ACK was received
      ; //wait
      }
   if ((TWSR & 0xF8) != TW_MT_DATA_ACK){ //check value of TWI status reg, mask presacaer buts, if diffrent from MT_DATA_ACK error
      ; //error
      }
}

void string_twi_P(const char *ptr){      //send string to LCD
   while (pgm_read_byte(ptr) != 0x00)
      send_twi(pgm_read_byte(ptr++));
}

void LCD_int(void){
   TWBR=10;
   TWSR=((0<<TWPS0)|(0<<TWPS1));
   TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //send start cond
   while (!(TWCR & (1<<TWINT))){ //wait for TWINT flag set, which indicates that START was transmited
      ; //wait
      }
   if ((TWSR & 0xF8) != TW_START){ // check value of TWI status, mask prescaler bits,  if diffrent from START error
      ; //error
      }
   send_twi(LCD_ADDY);
   send_twi(0xFE);   //0xFE, 0x58 clears LCD
   send_twi(0x58);
}

void LCD_stop(void){
   TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); //trans stop cond
}
   

int main (void){
   LCD_int();
   string_twi_P(MSG_1);
   send_twi(0xFE);      //select coloum/row
   send_twi(0x47);
   send_twi(0x01);
   send_twi(0x02);
   string_twi_P(PSTR("Hi mom!"));
   send_twi(0xFE);
   send_twi(0x47);
   send_twi(0x01);
   send_twi(0x03);
   string_twi_P(PSTR("It works!"));
   send_twi(0xFE);
   send_twi(0x47);
   send_twi(0x01);
   send_twi(0x04);
   string_twi_P(PSTR("Last row"));
   LCD_stop();
} 

Lumex LCD driver

http://lumex.com/ LCDs are pretty cheap. The S6A0069 chip is almost HD44780 compatible. Instead of fiddling with the built in C18 functions I decided to write my own routines Here they are.

#include	<P18cxxx.H>
#include "xlcd.h"
#include <delays.h>

// FOR USE WITH A 10MHz crystal with PLL enabled (40MHz)

void LCDdelayA( void )
{
Delay1KTCYx(16); // Delay of 1.53ms
// Cycles = (TimeDelay * Fosc) / 4
// Cycles = (15ms * 16MHz) / 4
// Cycles = 60,000
return;
}
void LCDdelayB (void)
{
Delay100TCYx(4); // Delay of >39us
// Cycles = (TimeDelay * Fosc) / 4
// Cycles = (15ms * 16MHz) / 4
// Cycles = 60,000
return;
}
void LCDdelayC (void)
{
Delay100TCYx(5); // Delay of >43us
// Cycles = (TimeDelay * Fosc) / 4
// Cycles = (15ms * 16MHz) / 4
// Cycles = 60,000
return;
}
void Edelay (void)
{
	//>500ns delay
Nop();
Nop();
Nop();
Nop();
Nop();
Nop();
Nop();
return;
}

void LCDint (void)
{		
TRIS_DATA_PORT = 0b00000000;
TRIS_RW = 0; // All control signals made outputs
TRIS_RS = 0;
TRIS_E = 0;

RW_PIN = 0; // R/W pin made low
RS_PIN = 0; // Register select pin made low
E_PIN = 0; // Clock pin made low



// Delay for more than 70ms to allow for LCD Power on reset
Delay1KTCYx(700); // Delay of >70ms
// Cycles = (TimeDelay * Fosc) / 4
// Cycles = (15ms * 16MHz) / 4
// Cycles = 60,000


// Function Set
DATA_PORT = 0b00111111;
E_PIN = 1; // Clock the cmd in
Edelay();
E_PIN = 0;
LCDdelayB();

// Display on/off control
DATA_PORT = 0b00001111;
E_PIN = 1; // Clock the cmd in
Edelay();
E_PIN = 0;
Edelay();
LCDdelayB();

// Display clear
DATA_PORT = 0b00000001;
E_PIN = 1; // Clock the cmd in
Edelay();
E_PIN = 0;
Edelay();
LCDdelayA();

// Entry Mode Set
DATA_PORT = 0b00000110;
E_PIN = 1; // Clock the cmd in
Edelay();
E_PIN = 0;
LCDdelayA();

return;
}

void send2LCD(char xy){
	TRIS_DATA_PORT = 0;
	RS_PIN = 1;
	RW_PIN = 0;
	DATA_PORT = xy;
	E_PIN=1;
	Edelay();
	E_PIN=0;
	LCDdelayC();
	}

void LCD_putch (char dx){
	char addr;
	LCD_rdy();
	send2LCD(dx);
	addr=get_DDRAM_addr();
	if(addr==0x13){
		LCD_cmd(0xC0);
		LCD_rdy();
		send2LCD(dx);
		}
	else if(addr==0x53){
		LCD_cmd(0x94);
		LCD_rdy();
		send2LCD(dx);
		}
		
	}
	
void LCD_putstr1(rom char *ptr){
	LCD_cmd(0x80);
	while (*ptr) {
		LCD_putch(*ptr);
		ptr++;
		}
	}
	
void LCD_putstr2(rom char *ptr){
	LCD_cmd(0xC0);
	while (*ptr) {
		LCD_putch(*ptr);
		ptr++;
		}
	}

void LCD_rdy(void){
	char test;
	TRIS_DATA_PORT = 0xFF;
	test = 0x80;
	while (test) {
		RS_PIN = 0;
		RW_PIN = 1;
		E_PIN = 1;
		test = DATA_PORT;
		Nop();
		E_PIN = 0;
		test &= 0x80;
		}
	TRIS_DATA_PORT = 0x00;
	}
		
void LCD_cmd(char cx){
	LCD_rdy();
	RS_PIN=0;
	RW_PIN=0;
	E_PIN=1;
	Edelay();
	DATA_PORT =cx;
	Edelay();
	E_PIN=0;
	LCDdelayA();
	}
	
char get_DDRAM_addr (void){
	char temp;
	TRIS_DATA_PORT=0xff;
	RS_PIN=0;
	RW_PIN=1;
	E_PIN=1;
	temp = DATA_PORT &0x7F;
	Nop();
	E_PIN=0;
	return temp;
}

void LCD_putdata(char *ptr, int data){
	LCD_cmd(0xC0);
	while (*ptr) {
		LCD_putch(*ptr);
		ptr++;
		}
	LCD_putch(data);
	}

void clrLCD(int line){
	int i;
	if (line==1){
		LCD_cmd(0x80); // first line
		for (i=0;i<20;i++){
			LCD_putch(0x20);	// Space
			}
		}
	else if (line==2){
		LCD_cmd(0xC0);	//2nd line
		for (i=0;i<20;i++){
			LCD_putch(0x20);
			}
		}
	}

toBCD function

This function will take HUGE number in and putout the corresponding digit you ask for. Will only work up to the Thousands digit ATM but can easily be expanded for you application.

unsigned char toBCD(unsigned short long num, int digit){
	int thousands, hundreds, tens, ones;
	thousands = num / 1000;
	num -= thousands * 1000;
	hundreds = num / 100;
	num -= hundreds * 100;
	tens = num / 10;
	num -= tens * 10;
	ones = num;
	switch (digit){
		case 1: return ones;
			break;
		case 2: return tens;
			break;
		case 3: return hundreds;
			break;
		case 4: return thousands;
			break;
		default:
			return 0;
	}
}

Int to BCD 2

Same as the one above but this one actually runs faster. This one also multiplexes the number on some 7 segments displays.

#include <P18cxxx.H>
#include <delays.h>

int num,ones,tens,hundreds,thousands,cnt;

void main (void)
{
   num=5847;
   ones=tens=hundreds=thousands=0;
   PORTD=0x00;
   TRISD=0x00;
   if (num>=1000)
      for (cnt=0;num>=1000;cnt++){
         num-=1000;
         thousands=cnt+1;
         }
   if (num>=100)
      for (cnt=0;num>=100;cnt++){
         num-=100;
         hundreds=cnt+1;
         }
   if (num>=10)
      for (cnt=0;num>=10;cnt++){
         num-=10;
         tens=cnt+1;
         }
   if (num<10)
      ones=num;
   PORTD=0b00010000 | ones;
   Delay10TCYx(10);
   PORTD=0b00100000 | tens;
   Delay10TCYx(10);
   PORTD=0b00110000 | hundreds;
   Delay10TCYx(10);
   PORTD=0b01000000 | thousands;
   Delay10TCYx(10);
}

ASM routines

Decimal to BCD

This routine will take the 3 digit decimal in the register bin and bust it up into BCD format in the registers hundreds and tens_and_ones. The most significant nibble in tens_and_ones is the tens digit and least significant is the ones digit.

binary_to_bcd
	CLRF	hundreds 
	SWAPF	bin,w 			;w  = A0*16+A1
	ADDWF	bin,w 			;w  = A0+A1
	ANDLW	b'00001111' 	;w  = A0+A1 % 16
	SKPNDC 					;if A0+A1 > 16
	ADDLW	0x16 			;w  += 16
	SKPNDC 					;if w % 16 > 10
	ADDLW	0x06 			;w  += 6
	ADDLW 	0x06 			;w  += 6
	SKPDC 					;if w < 10
	ADDLW	-0x06 			;w  -= 6

	BTFSC	bin,4 
	ADDLW	0x16 - 1 + 0x6 
	SKPDC 
	ADDLW	-0x06 

	BTFSC	bin,5 
	ADDLW	0x30 
	
	BTFSC	bin,6 
	ADDLW	0x60 

	BTFSC	bin,7 
	ADDLW	0x20 

	ADDLW	0x60 
	RLF		hundreds,f 
	BTFSS	hundreds, 0 
	ADDLW 	-0x60 

	MOVWF	tens_and_ones 
	BTFSC	bin,7 
	INCF	hundreds,f 
	return

USARTing a string

Used to transmit an entire string out of the USART.

;
       _Print  "Main Menu\r\n\n"
       _Print  "<1>  Setup\r\n"
       _Print  "<2>  Exit\r\n"
;


The _Print Macro;

;******************************************************************
;
;  _Print macro - print a string to the RS-232 port
;
_Print  macro   str             ;
        local   String, Print
        movlw   low String      ;
        movwf   PTRL            ;
        movlw   high String     ;
        movwf   PTRH            ;
        goto    Print           ;
String  dt      str,0
Print   call    PutString       ; print string
        endm
;
;******************************************************************


The PutString routine;

;******************************************************************
;
;  PutStr - setup PTRL and PTRH to string address before entry
;         - string must be terminated with a 00 byte
;
PutString
        call    GetTable        ; get a table character
        andlw   b'11111111'
        btfsc   STATUS,Z        ; a 00 byte, last character?
        return                  ; yes, return 
        call    Put232          ; output char 
        incfsz  PTRL,F
        goto    PutString
        incf    PTRH,F
        goto    PutString
;
GetTable
        movf    PTRH,W
        movwf   PCLATH
        movf    PTRL,W       
        movwf   PCL 
Uart_out
	movwf	txbyte
	BANKSEL	PIR1
WaitTX
	btfss	PIR1,TXIF	;test to see if the Tx buffer is full
	goto	WaitTX
	BANKSEL	txbyte
	movf	txbyte,W		;moves the byte to be transmited to the W reg
	BANKSEL	TXREG
	movwf	TXREG		;Tx the string!
	return
;
;******************************************************************

Delay Codes

A useful tool for creating delay routines. ASM Delay Code Generator