Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

ADXL345 Accelerometer and 16F628A in Hi-Tech C

Status
Not open for further replies.

ClydeCrashKop

Well-Known Member
Most Helpful Member
This program displays the 3 axis output from an ADXL345 Accelerometer and 16F628A in Hi-Tech C. Specifically the little GY-291 board available cheap on eBay. There are pull-ups on the board to solve the 3 volt problem.
I use Nigel Goodwin's tutorials in asm... as a starting place for C programs and subroutines.
Thank you Nigel and Ian.

C:
// ADXL345 Accelerometer and 16F628a in Hi-Tech C
// Using Bit Bang I2C on RA6 & RA7 and LCD on port B
// This uses up all but 167 bytes of program memory
// the div_t averaging routine uses 414 bytes
// The wiring for the LCD using Hitachi HD44780 is at
// http://www.winpicprog.co.uk/pic_tutorial_lcd_board.htm
 
 
#include <pic.h>                // pic specific identifiers
#include <stdlib.h>
#include <stdio.h>
#define _XTAL_FREQ  4000000        // Xtal speed
__CONFIG(0x3D38);                // Config bits
 
char  IDDV =0;
#define  BW_RATE            44    //0x2C
#define  POWER_CTL          45    //0x2D
#define  DATA_FORMAT        49    //0x31
#define  DATAX0             50    //0x32
#define  DATAX1             51    //0x33
#define  DATAY0             52    //0x34
#define  DATAY1             53    //0x35
#define  DATAZ0             54    //0x36
#define  DATAZ1             55    //0x37
#define  FIFO_CTL           56    //0x38                        1010011   0x53   Device
#define CHIP_Write    0xA6        // adxl345 address for writing    10100110  0xA6   Write
#define CHIP_Read    0xA7        // and reading                    10100111  0xA7   Read
//#define DEVICE1 (0x53)
 
#define LCD_PORT    PORTB            //
#define LCD_TRIS    TRISB
#define LCD_RS        RB4
#define LCD_RW        RB6
#define LCD_E        RB7
 
#define I2C_PORT    PORTA            // I2C port configuration
#define I2C_TRIS    TRISA
#define SDAI        RA7
#define SCLI        RA6
#define SDA            TRISA7        // in C  we can control the input / output
#define SCL            TRISA6        // by one simple command.
 
 
        // Required prototypes
 
void LCD_init(void), LCD_cmd(unsigned char ch), LCD_busy(void);
void LCD_printC(const char *str), LCD_printR(char *str);
void LCD_goto(char line, char column), LCD_clr(void);
void LCD_cur(unsigned char ch), pulse_E(void), LCD_hex(int value);
void LCD_char(unsigned char ch), LCD_charD(unsigned char ch);
void convert(int numb);
 
unsigned char HEX_Table[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
                            0x37, 0x38, 0x39, 0x41, 0x42, 0x43,
                            0x44, 0x45, 0x46};
// variables
unsigned char TenK, Thou, Hund, Tens, Ones;
 
 
void I2C_Sendnack(void), I2C_ack(void), I2C_Stop(void), clock(void);
void I2C_Start(void), I2C_Write(unsigned char ch);
char I2C_nack(), E_Write(int addr, unsigned char ch);
unsigned char I2C_Read(void);
unsigned char E_Read(int addr);
 
 
void ini_adxl345()
{
E_Write(FIFO_CTL,0x9f);     
E_Write(DATA_FORMAT,0x09);  
E_Write(BW_RATE,0x0d);      
E_Write(POWER_CTL,0x08);  // activate  
}
 
unsigned char LCDbuffer[17];// a screen buffer big enough for 16 chars + terminator
char ErrFlags;            // Read error. (I don't use it, but you might!)
    
// The SDA and SCL pins are ALWAYS set at 0 we use tristated pins to
// control the control.. while SDA and SCL are inputs the external pullup
// resistors give a high conditon whilst when SDA or SCL are outputs
// the control lines are pulled low.
 
 
void main(void)                            // program entry
    {
    int index = 0;                        // text variable
    OPTION_REG = 0x88;                        // timer pre-scaler
    CMCON = 0x7;                        // Comparitors off
    LCD_TRIS = 0b00000000;                // LCD port as outputs
    I2C_TRIS = 0b11111111;                // I2C port as inputs
    __delay_ms(150);
    RA6 = 0;                // SDC clock
    RA7 = 0;                // SDA Data
    I2C_PORT = 0;
    LCD_init();                            // Initalise screen
    ini_adxl345();                        // Initalise Accelerometer
    __delay_ms(30);                        // Helps Initalise screen
//    LCD_goto(1,0);
//    LCD_printC("Writing..");
/*
    LCD_goto(1,0);
      int id, pow, bw_rate;
      id= E_Read(IDDV);  // should be 229
     __delay_ms(10);
      pow=E_Read(0x2D);  //should be 8 to activate
     __delay_ms(10);
      bw_rate =E_Read(0x2C);  //BW_RATE 0x2C is 13
        sprintf(LCDbuffer,"id=%3d pow=%3d BW_RATE=%3d",id,pow,bw_rate);
        LCD_printR(LCDbuffer);        // use sprintf to format
*/
 
    signed long x,y,z;
    signed long xhi,xlo,yhi,ylo,zhi,zlo;
    signed long Xaccumulate, Yaccumulate, Zaccumulate;
    signed long Xaverage, Yaverage, Zaverage;
    int i;
    while(1)                            // endless Loop
        {
 
    Xaccumulate = Yaccumulate = Zaccumulate = 0;
 
    for (i=0; i<16; i++) {  // Read sequentially 16 times then get an average.
    ErrFlags = 0;                                    // Clear error
    I2C_Start();
    I2C_Write(CHIP_Write);
    if(!I2C_nack()) ErrFlags = 1;                        // Whoops!! problems
 
    I2C_Write((unsigned char) DATAX0 & 0xff);    // DATAX0 is the first of 6 bytes
    if(!I2C_nack()) ErrFlags = 1;                // to read sequentially
    I2C_Start();
    I2C_Write(CHIP_Read);                            //
    if(!I2C_nack()) ErrFlags = 1;
 
    xlo = I2C_Read();                                // read character
    I2C_ack();
    xhi = I2C_Read();                                // read character
    I2C_ack();
    ylo = I2C_Read();                                // read character
    I2C_ack();
    yhi = I2C_Read();                                // read character
    I2C_ack();
    zlo = I2C_Read();                                // read character
    I2C_ack();
    zhi = I2C_Read();                                // read character
    I2C_Sendnack();
    I2C_Stop();
 
    Xaccumulate += ((xhi<<8) | xlo); //Xaccumulate = Xaccumulate + (xhi*256 + xlo)
    Yaccumulate += ((yhi<<8) | ylo);
    Zaccumulate += ((zhi<<8) | zlo);
 
  }  // for loop
 
//    x= (xhi<<8) | xlo;
//    y= (yhi<<8) | ylo;
//    z= (zhi<<8) | zlo;
 
// Calculate average accelerometer readings over last 16 samples
 
/* *** Other methods of dividing gave me results in the +/- thousands ***
DIV   From Hi-Tech C manual
Synopsis
#include <stdlib.h>
div_t div (int numer, int demon)
Description
The div() function computes the quotient and remainder of the numerator divided by the denominator.
Example
#include <stdlib.h>
#include <stdio.h>
void
main (void)
{
div_t x;
x = div(12345, 66);
printf("quotient = %d, remainder = %d\n", x.quot, x.rem);
}
See Also
udiv(), ldiv(), uldiv()
Return Value
Returns the quotient and remainder into the div_t structure.
*/
 
div_t xavg ; // div_t is a structure explained above
div_t yavg ;
div_t zavg ;
 
xavg = div(Xaccumulate, 16);
yavg = div(Yaccumulate, 16);
zavg = div(Zaccumulate, 16);
 
LCD_goto(1,0);
        sprintf(LCDbuffer,"X=%5d",xavg.quot);
        LCD_printR(LCDbuffer);        // use sprintf to format
 
LCD_goto(1,9);
        sprintf(LCDbuffer,"Y=%5d",yavg.quot);
        LCD_printR(LCDbuffer);        // use sprintf to format
 
LCD_goto(2,4);    
sprintf(LCDbuffer,"Z=%5d",zavg.quot);
        LCD_printR(LCDbuffer);        // use sprintf to format
//__delay_ms(25);
__delay_ms(250);  // Just so the numbers don't change too fast.
 
 
/*
// This reads byte registers one at a time.
   x=(E_Read(DATAX1)*256) | (E_Read(DATAX0));
   y=(E_Read(DATAY1)*256) | (E_Read(DATAY0));
   z=(E_Read(DATAZ1)*256) | (E_Read(DATAZ0));
 
LCD_goto(1,0);
        sprintf(LCDbuffer,"x=%5d",x);
        LCD_printR(LCDbuffer);        // use sprintf to format
 
LCD_goto(1,12);
        sprintf(LCDbuffer,"y=%5d",y);
        LCD_printR(LCDbuffer);        // use sprintf to format
 
LCD_goto(2,0);    
sprintf(LCDbuffer,"z=%5d",z);
        LCD_printR(LCDbuffer);        // use sprintf to format
*/
 
 
        } //while
    } //main(void)
 
// Write to eeprom this returns a 0 if it fails or a 1 if all gooes well
// For byte eeproms and RTC devices.. comment out the Hi address byte
// and the I2C_nack() that follows it in both these functions.
 
// This is random writing. Write to a specified address
 
char E_Write(int addr, unsigned char ch)
    {
    I2C_Start();                                    // Send a start condition
    I2C_Write(CHIP_Write);                            // chip write address
    if(!I2C_nack()) return 0;                        // wait for ack
//    I2C_Write((unsigned char) (addr >> 8 & 0xff));    // Comment out if single byte addess
//    if(!I2C_nack()) return 0;                        // wait for ack
    I2C_Write((unsigned char) addr & 0xff);            // low address
    if(!I2C_nack()) return 0;                        // wait for ack
    I2C_Write(ch);                                    // Write values to Eeprom
    if(!I2C_nack()) return 0;
    I2C_Stop();                                        // Send a stop.
    return 1;                                        // All went well
    }
 
 
// The function takes an address and Returns a character
 
// This is random reading. Read from a specified addess
unsigned char E_Read(int addr)
    {
    unsigned char byte;
    unsigned char ch;
    ErrFlags = 0;                                    // Clear error
    I2C_Start();
    I2C_Write(CHIP_Write);
    if(!I2C_nack()) return  1;                        // Whoops!! problems
//    I2C_Write((unsigned char)( addr >> 8 & 0xff));    // Comment out if single byte addess
//    if(!I2C_nack()) return  1;
    I2C_Write((unsigned char) addr & 0xff);            //
    if(!I2C_nack()) return 1;
    I2C_Start();
    I2C_Write(CHIP_Read);                            //
    if(!I2C_nack()) return  1;
    ch = I2C_Read();                                // read character
    I2C_Sendnack();
    I2C_Stop();
    return ch;
    }
 
void I2C_Start()        //  start combo
    {
    SDA = 1;
    SCL = 1;
    SDA = 0;            // bring SDA low while SCL is high
    SCL = 0;
    }
 
void I2C_Stop()            // stop combo
    {
    SCL = 0;
    SDA = 0;
    SCL = 1;
    SDA = 1;            // bring SDA high while SCL is high
    }
 
unsigned char I2C_Read()
    {
    char index;
    unsigned char byte = 0;                        // initialize in byte
    for(index = 0x80; index > 0 ;index>>=1)        // using index as a mask
        {                                        // cycles through bit 7 to bit 0
        if(SDAI) byte += index;                    // read in the SDA line
        clock();
        }
    return byte;                                // 8 bits in...
    }
 
void I2C_Write(unsigned char ch)
    {
    char index;
 
    for(index = 0x80; index > 0 ;index>>=1)        // using index as mask
        {
        if(ch & index)                             // bit high or low
            SDA = 1;
        else
            SDA = 0;
        clock();
        }
    }
 
void I2C_ack()                            // generate an ack
    {
    SDA = 0;
    clock();
    SDA = 1;
    }
 
void I2C_Sendnack()                        // generate a not ack
    {
    SDA = 1;
    clock();
    }
 
char I2C_nack()                            // recive a not ack
    {
    unsigned char timeout = 255;
    SDA = 1;
    SCL = 1;
    while(SDAI)                            // wait till SDA goes low
        {
        if(timeout-- == 0) return 0;    // whoops!! no ack recieved
        }
    SCL = 0;
    return 1;
    }
 
void clock()                            // Might work without the nop
    {
    SCL=1;
    asm("nop");
    SCL=0;
    }
 
void convert(int numb)                // Takes a interger number
    {                                // and converts to single
    TenK = numb / 10000;            // decimal numbers.
    Thou = (numb % 10000)/ 1000;    //
    Hund = (numb % 1000) / 100;
    Tens = (numb % 100) / 10;
    Ones = numb % 10;
    }
 
// In C we can use Pointers, Pointers are advanced programming
// and can tie you in knots. The simplest pointer to use is the
// array pointer using "rom char" allows the pointer to
// point to constant character in code section.
 
void LCD_printC(const char * str)    // This passes the start a ROM character array
    {                                // by default the pointer points to data section
    while(*str != 0)                // while the character pointed to isn't 0
        LCD_char(*str++);            // print out the character, then increment
    }                                // the pointer down the array
 
void LCD_printR(char * str)             // This passes the start of a RAM character array
    {                                // by default the pointer points to data section
    while(*str != 0)                // while the character pointed to isn't 0
        LCD_char(*str++);            // print out the character, then increment
    }                                // the pointer down the array
 
 
void LCD_init()
    {
    LCD_cmd(0x20);                    // 4 bit
    LCD_cmd(0x28);                    // display shift
    LCD_cmd(0x6);                    // character mode
    LCD_cmd(0xc);                    // display on / off and cursor
    LCD_clr();                        // clear display
    }
 
void LCD_hex(int value)
    {
    char data;
    data = value >> 4 & 0xf;
    data = HEX_Table[data];                 // send upper nibble
    LCD_char(data);
    data = value & 0xf;                        // send lower nibble
    data = HEX_Table[data];
    LCD_char(data);
    }
 
void LCD_goto(char line, char column)        // combines line and lineW
    {
    unsigned char data = 0x80;                // default to 1
    if(line == 2)data = 0xc0;                // change to 2
    data += column;                            // add in  column
    LCD_cmd(data);
    }
 
void LCD_clr()
    {
    LCD_cmd(1);                                // Clr screen
    }
 
void LCD_cur(char on)
    {
    unsigned char cur = 0xc;                // cursor off
    if(on) cur = 0xd;                        // cursor on
    LCD_cmd(cur);
    }
 
void LCD_busy()
    {
    unsigned char BUSY = 0;
    while(1)
        {
        LCD_TRIS = 0x0f;                    // port to read mode
        LCD_RS = 0x0;
        LCD_RW = 0x1;                        // set to read
        LCD_E = 0x1;
        BUSY = LCD_PORT & 0xf;                // high byte comes first
        __delay_us(5);                        // so busy flag is on bit 3
        LCD_E = 0x0;
        LCD_E = 0x1;
        __delay_us(5);                        // dummy read
        LCD_E = 0x0;
        LCD_RW = 0x0;                        // set to write
        LCD_TRIS = 0x00;                    // port to write mode
        if(!(BUSY & 0x8 )) return;            // AND with bit 3
        }
    }
 
void LCD_cmd(unsigned char ch)
    {
    LCD_PORT = ch >> 4 & 0xf;
    LCD_RS = 0;
    pulse_E();
    LCD_PORT = ch & 0xf;
    LCD_RS = 0;
    pulse_E();
    __delay_ms(5);
    }
 
void LCD_charD(unsigned char ch)
    {
    ch+=0x30;
    LCD_char(ch);
    }
 
void LCD_char(unsigned char ch)
    {
    LCD_PORT = ch >> 4 & 0xf;
    LCD_RS = 1;
    pulse_E();
    LCD_PORT = ch & 0xf;
    LCD_RS = 1;
    pulse_E();
    __delay_ms(5);;
    }
 
void pulse_E()
    {
    LCD_E = 1;
    __delay_us(1);
    LCD_E = 0;
    }
 

Attachments

  • 628A Accelerometer.c
    12.5 KB · Views: 165
Last edited:
I just discovered something while setting up another computer for programming PICs.
HI-TECH C version 9.83 uses OPTION_REG and compacts programs better.
HI-TECH C version 9.65 uses OPTION and at least on this program uses 110 bytes more memory than the 16F628a has.
That explains why I have always had to change OPTION to OPTION_REG in Ian's programs.
Use HI-TECH C version 9.83 or better for this program.
 
You have big chunks of code that is commented out mixed with the actual application code. Because of that the code is very hard to read without color-coding. use the CODE=C tags when posting C code.
 
Thanks MisterT, That looks much better.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top