Driver for a ADXL345, using an ATmega328p-pu and SPI communication, running it all bare metal

So as the title suggest im trying to make a driver for the ADXL345, for this project im using a ATMega328p-pu and the SPI communication protocol. Im running this all bare metal, so when i run my main.cpp it should blink an LED 6 times and then setup the ADXL for communication after the setup it should read the DEVID form the ADXL and if it is returned it should turn on another LED, the problem is that is does make the first LED blink 3 times, but then nothing happens after. Im not sure if the problem is in the code or the physical setup, but i hope someone can point me in the right direction.

  1. is it even possible to do the setup (picture 1) they way i do it, where the programmer (USB-ASP) is connected to the same pin outs as the ADXL is connected to?
  2. are there potential problem in the SPI.cpp or ADXL_driver.cpp?

I have never written a driver before or done something so from the ground up, and i thought it would be a great place to evolve my C programming skills, but im kinda stuck now and dont really know how to debug this issue, hope anyone has some advice on how to solve the problem or how to debug it myself.

I think its very hard to tell what is going on with all the wires so i have tried to draw is aswell.

(Not sure if it is important but i use a makefile to flash the microcontroller using adr dude. )

THIS IS THE SPI COMMUNICATION

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>#include "spi.h"
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#define MOSI_PIN PB3
#define MISO_PIN PB4
#define SS_PIN PB2
#define SCK_PIN PB5
#define F_CPU 8000000UL
void spi_init_master( int spi_mode ){
//Setting up the pins on PORTB which are to be used in SPI
DDRB |= (1 << MOSI_PIN) | (1 << SS_PIN) | (1 << SCK_PIN);
DDRB &= ~(1 << MISO_PIN);
//Setting up the SPCR -> SPI control register
SPCR = (1 << SPE) | (1 << MSTR);
switch (spi_mode){
case 0:
SPCR &= ~( (1 << CPOL) | (1 << CPHA) );
break;
case 1:
SPCR &= ~(1 << CPOL);
SPCR |= (1 << CPHA);
break;
case 2:
SPCR |= (1 << CPOL);
SPCR &= ~(1 << CPHA);
break;
case 3:
SPCR |= (1 << CPOL) | (1 << CPHA);
break;
default:
break;
}
}
void spi_clock_select( int clock_div ){
switch (clock_div){
case 2:
SPCR &= ~( (1 << SPR0) | (1 << SPR1) );
SPSR |= (1 << SPI2X);
break;
case 4:
SPCR &= ~( (1 << SPR0) | (1 << SPR1) );
SPSR &= ~(1 << SPI2X);
break;
case 8:
SPCR |= (1 << SPR0);
SPCR &= ~(1 << SPR1);
SPSR |= (1 << SPI2X);
break;
case 16:
SPCR |= (1 << SPR0);
SPCR &= ~(1 << SPR1);
SPSR &= ~(1 << SPI2X);
break;
case 32:
SPCR &= ~(1 << SPR0);
SPCR |= (1 << SPR1);
SPSR |= (1 << SPI2X);
break;
case 64:
SPCR |= (1 << SPR0);
SPCR |= (1 << SPR1);
SPSR |= (1 << SPI2X);
break;
case 128:
SPCR |= (1 << SPR0);
SPCR |= (1 << SPR1);
SPSR &= ~(1 << SPI2X);
break;
default:
break;
}
}
void spi_init_interrupt( void ){
SPCR |= (1<<SPIE);
}
void spi_disconnect( void ){
SPCR &= ~(1 << SPE);
}
void spi_cs_enable( void ){
PORTB &= ~(1 << SS_PIN);
}
void spi_cs_disable( void ){
PORTB |= (1 << SS_PIN);
}
/* NOTE: SPI1 is configured in 2-line unidirectional and full duplex mode,
therefore for each data sent over MOSI, data received in the MISO
pin is sampled every clock cycle, meaning that we send and receive
data at the same time although that may not what we intend. */
void spi_write( uint8_t *data, uint8_t size ){
volatile uint8_t temp;
while (!(SPSR & (1 << SPIF))) {
// Do nothing
}
for (uint8_t i = 0; i < size; i++){
SPDR = data[i]; // Writes the data to the SPI data register.
while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set.
temp = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag.
}
}
void spi_read( uint8_t *read, uint8_t size ){
for (uint8_t i = 0; i < size; i++){
SPDR = 0xFF; // Writes dummy data to initiate the SPI transfer.
while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set.
read[i] = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag.
}
}
</code>
<code>#include "spi.h" #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #define MOSI_PIN PB3 #define MISO_PIN PB4 #define SS_PIN PB2 #define SCK_PIN PB5 #define F_CPU 8000000UL void spi_init_master( int spi_mode ){ //Setting up the pins on PORTB which are to be used in SPI DDRB |= (1 << MOSI_PIN) | (1 << SS_PIN) | (1 << SCK_PIN); DDRB &= ~(1 << MISO_PIN); //Setting up the SPCR -> SPI control register SPCR = (1 << SPE) | (1 << MSTR); switch (spi_mode){ case 0: SPCR &= ~( (1 << CPOL) | (1 << CPHA) ); break; case 1: SPCR &= ~(1 << CPOL); SPCR |= (1 << CPHA); break; case 2: SPCR |= (1 << CPOL); SPCR &= ~(1 << CPHA); break; case 3: SPCR |= (1 << CPOL) | (1 << CPHA); break; default: break; } } void spi_clock_select( int clock_div ){ switch (clock_div){ case 2: SPCR &= ~( (1 << SPR0) | (1 << SPR1) ); SPSR |= (1 << SPI2X); break; case 4: SPCR &= ~( (1 << SPR0) | (1 << SPR1) ); SPSR &= ~(1 << SPI2X); break; case 8: SPCR |= (1 << SPR0); SPCR &= ~(1 << SPR1); SPSR |= (1 << SPI2X); break; case 16: SPCR |= (1 << SPR0); SPCR &= ~(1 << SPR1); SPSR &= ~(1 << SPI2X); break; case 32: SPCR &= ~(1 << SPR0); SPCR |= (1 << SPR1); SPSR |= (1 << SPI2X); break; case 64: SPCR |= (1 << SPR0); SPCR |= (1 << SPR1); SPSR |= (1 << SPI2X); break; case 128: SPCR |= (1 << SPR0); SPCR |= (1 << SPR1); SPSR &= ~(1 << SPI2X); break; default: break; } } void spi_init_interrupt( void ){ SPCR |= (1<<SPIE); } void spi_disconnect( void ){ SPCR &= ~(1 << SPE); } void spi_cs_enable( void ){ PORTB &= ~(1 << SS_PIN); } void spi_cs_disable( void ){ PORTB |= (1 << SS_PIN); } /* NOTE: SPI1 is configured in 2-line unidirectional and full duplex mode, therefore for each data sent over MOSI, data received in the MISO pin is sampled every clock cycle, meaning that we send and receive data at the same time although that may not what we intend. */ void spi_write( uint8_t *data, uint8_t size ){ volatile uint8_t temp; while (!(SPSR & (1 << SPIF))) { // Do nothing } for (uint8_t i = 0; i < size; i++){ SPDR = data[i]; // Writes the data to the SPI data register. while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set. temp = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag. } } void spi_read( uint8_t *read, uint8_t size ){ for (uint8_t i = 0; i < size; i++){ SPDR = 0xFF; // Writes dummy data to initiate the SPI transfer. while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set. read[i] = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag. } } </code>
#include "spi.h"
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define MOSI_PIN PB3
#define MISO_PIN PB4
#define SS_PIN PB2 
#define SCK_PIN PB5
#define F_CPU 8000000UL

    void spi_init_master( int spi_mode ){
        //Setting up the pins on PORTB which are to be used in SPI
        DDRB |= (1 << MOSI_PIN) | (1 << SS_PIN) | (1 << SCK_PIN);
        DDRB &= ~(1 << MISO_PIN);
        //Setting up the SPCR -> SPI control register
        SPCR = (1 << SPE) | (1 << MSTR);
        switch (spi_mode){
            case 0: 
                SPCR &= ~( (1 << CPOL) | (1 << CPHA) );
                break;
            case 1: 
                SPCR &= ~(1 << CPOL);
                SPCR |= (1 << CPHA);
                break;

            case 2:
                SPCR |= (1 << CPOL);
                SPCR &= ~(1 << CPHA);
                break;

            case 3:
                SPCR |= (1 << CPOL) | (1 << CPHA);
                break;
                
            default:
                break;
        }
    }

    void spi_clock_select( int clock_div ){
        
        switch (clock_div){
            case 2:
                SPCR &= ~( (1 << SPR0) | (1 << SPR1) );
                SPSR |= (1 << SPI2X);
                break;

            case 4:
                SPCR &= ~( (1 << SPR0) | (1 << SPR1) );
                SPSR &= ~(1 << SPI2X);
                break;

            case 8:
                SPCR |= (1 << SPR0);
                SPCR &= ~(1 << SPR1);
                SPSR |= (1 << SPI2X);

                break;

            case 16:
                SPCR |= (1 << SPR0);
                SPCR &= ~(1 << SPR1);
                SPSR &= ~(1 << SPI2X);                
                break;

            case 32:
                SPCR &= ~(1 << SPR0);
                SPCR |= (1 << SPR1);
                SPSR |= (1 << SPI2X);                
                break;

            case 64:
                SPCR |= (1 << SPR0);
                SPCR |= (1 << SPR1);
                SPSR |= (1 << SPI2X);                
                break;

            case 128:
                SPCR |= (1 << SPR0);
                SPCR |= (1 << SPR1);
                SPSR &= ~(1 << SPI2X);                
                break;

            default:
                break;
        }
    }

    void spi_init_interrupt( void ){
        SPCR |= (1<<SPIE);
    }

    void spi_disconnect( void ){
        SPCR &= ~(1 << SPE);
    }

    void spi_cs_enable( void ){
        PORTB &= ~(1 << SS_PIN);
    }

    void spi_cs_disable( void ){
        PORTB |= (1 << SS_PIN);
    }

    /* NOTE: SPI1 is configured in 2-line unidirectional and full duplex mode,
             therefore for each data sent over MOSI, data received in the MISO
             pin is sampled every clock cycle, meaning that we send and receive
             data at the same time although that may not what we intend. */

    void spi_write( uint8_t *data, uint8_t size ){

        volatile uint8_t temp;
        while (!(SPSR & (1 << SPIF))) {
            // Do nothing
        }
        for (uint8_t i = 0; i < size; i++){
            SPDR = data[i]; // Writes the data to the SPI data register.
            while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set.
            temp = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag.
        }
    }

    void spi_read( uint8_t *read, uint8_t size ){

        for (uint8_t i = 0; i < size; i++){
            SPDR = 0xFF; // Writes dummy data to initiate the SPI transfer.
            while(!(SPSR & (1 << SPIF))); // Waits for the SPIF flag to be set.
            read[i] = (uint8_t)(SPDR); // Reads the received data (if any) to clear the SPIF flag.
        }
    }

THIS IS THE ADXL DRIVER CODE

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
#include "adxl_driver.h"
#include "spi.h"
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
void ADXL345_Write( uint8_t reg_addr, uint8_t data ){
uint8_t data_to_write[2];
data_to_write[0] = SPI_WRITE | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK);
data_to_write[1] = data;
spi_cs_enable();
spi_write(data_to_write, 2);
spi_cs_disable();
}
uint8_t ADXL345_Read( uint8_t reg_addr ){
uint8_t data_to_write;
uint8_t data_to_read;
data_to_write = SPI_READ | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK);
spi_cs_enable();
spi_write(&data_to_write, 1U);
spi_read(&data_to_read, 1U);
spi_cs_disable();
return data_to_read;
}
void ADXL345_Init( ACCEL_HandleTypeDef *haccel ){
spi_init_master(3); //SPI mode 3, this means that CPOL and CPHA are both 1.
spi_clock_select(2); //Clock division factor of 2 this means we have a clock frequency of 8MHz/2 = 4 MHz
ADXL345_Write( POWER_CTL_REG, MEASURE_STANDBY );
/* Configure data format: self test, SPI 3/4 bit mode, interrupt active high/low,
full/10bit resolution, right/left justified, 2/4/8/16 g range */
ADXL345_Write( DATA_FORMAT_REG, haccel->dataformat );
/* Configure power mode and output data rate */
ADXL345_Write( BW_RATE_REG, haccel->bwrate );
/* Configure X, Y and Z axis offset values */
ADXL345_Write( OFSX_REG, haccel->ofsx );
ADXL345_Write( OFSY_REG, haccel->ofsy );
ADXL345_Write( OFSZ_REG, haccel->ofsz );
ADXL345_Enable_Int( haccel, haccel->intenable, haccel->intmap );
/* If single tap or double tap interrupts are enabled */
if ((( haccel->intenable & SINGLE_TAP_INT_ENABLED ) == SINGLE_TAP_INT_ENABLED ) ||
(( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED )){
/* Configure tap threshold */
ADXL345_Write( THRESH_TAP_REG, haccel->threshtap );
/* Configure tap duration */
ADXL345_Write( DUR_REG, haccel->dur );
/* Enable participating axes in tap detection (and double tap supressing if double tap int. enabled) */
ADXL345_Write( TAP_AXES_REG, haccel->tapaxes );
}
/* Configure latent and window for double tap detection (if enabled) */
if (( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED ){
ADXL345_Write( LATENT_REG, haccel->latent );
ADXL345_Write( WINDOW_REG, haccel->window );
}
/* If activity interrupt is enabled */
if (( haccel->intenable & ACTIVITY_INT_ENABLED ) == ACTIVITY_INT_ENABLED ){
/* Configure threshold for activity */
ADXL345_Write( THRESH_ACT_REG, haccel->threshact );
/* Enable participating axes in activity detection */
ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl );
}
/* If inactivity interrupt is enabled */
if (( haccel->intenable & INACTIVITY_INT_ENABLED ) == INACTIVITY_INT_ENABLED ){
/* Configure threshold for inactivity */
ADXL345_Write( THRESH_INACT_REG, haccel->threshinact );
/* Configure time for inactivity */
ADXL345_Write( TIME_INACT_REG, haccel->timeinact );
/* Enable participating axes in inactivity detection */
ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl );
}
/* If free-fall interrupt is enabled */
if ((( haccel->intenable & FREE_FALL_INT_ENABLED ) == FREE_FALL_INT_ENABLED )){
/* Configure freefall threshold */
ADXL345_Write( THRESH_FF_REG, haccel->threshff );
/* Configure freefall time */
ADXL345_Write( TIME_FF_REG, haccel->timeff );
}
/* Configure FIFO mode (and number of samples for the watermark interrupt, if enabled) */
ADXL345_Write( FIFO_CTL_REG, haccel->fifoctl );
/* Configure power mode (if measure bit is set, ADXL345 enters normal mode) */
ADXL345_Write( POWER_CTL_REG, haccel->powerctl );
}
void ADXL345_Read_Devid(ACCEL_HandleTypeDef *haccel) {
// Code for reading the device ID
haccel->devid = ADXL345_Read( DEVID_REG );
}
void ADXL345_Standby_Mode(ACCEL_HandleTypeDef *haccel) {
// Code for putting the ADXL345 in standby mode
haccel->powerctl &= ~MEASURE_ENABLED;
ADXL345_Write( POWER_CTL_REG, haccel->powerctl );
}
void ADXL345_Measurement_Mode(ACCEL_HandleTypeDef *haccel) {
// Code for putting the ADXL345 in measurement mode
haccel->powerctl |= MEASURE_ENABLED;
ADXL345_Write( POWER_CTL_REG, haccel->powerctl );
}
// void ADXL345_Low_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) {
// // Code for setting the ADXL345 in low power mode with specified output rate
// }
// void ADXL345_Normal_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) {
// // Code for setting the ADXL345 in normal power mode with specified output rate
// }
// void ADXL345_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) {
// // Code for putting the ADXL345 in sleep mode with specified wakeup rate
// }
// void ADXL345_Wake_Up(ACCEL_HandleTypeDef *haccel) {
// // Code for waking up the ADXL345 from sleep mode
// }
// void ADXL345_Auto_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) {
// // Code for enabling auto sleep mode with specified wakeup rate
// }
// void ADXL345_Link_Mode(ACCEL_HandleTypeDef *haccel, uint8_t autosleep_mode, uint8_t wakeup_rate) {
// // Code for enabling link mode with specified auto sleep mode and wakeup rate
// }
// void ADXL345_Manual_OffsetCal(ACCEL_HandleTypeDef *haccel, int16_t *x0g, int16_t *y0g, int16_t *z0g) {
// // Code for performing manual offset calibration
// }
// void ADXL345_Auto_OffsetCal(ACCEL_HandleTypeDef *haccel) {
// // Code for performing auto offset calibration
// }
// void ADXL345_Fifo_Bypass_Mode(ACCEL_HandleTypeDef *haccel) {
// // Code for setting the FIFO to bypass mode
// }
// void ADXL345_Fifo_Fifo_Mode(ACCEL_HandleTypeDef *haccel) {
// // Code for setting the FIFO to FIFO mode
// }
// void ADXL345_Fifo_Stream_Mode(ACCEL_HandleTypeDef *haccel) {
// // Code for setting the FIFO to stream mode
// }
// void ADXL345_Fifo_Trigger_Mode(ACCEL_HandleTypeDef *haccel, uint8_t interrupts, uint8_t trigger_pin, uint8_t samples) {
// // Code for setting the FIFO to trigger mode with specified interrupts, trigger pin, and samples
// }
// void ADXL345_Read_Axes(ACCEL_HandleTypeDef *haccel, int16_t *x_read, int16_t *y_read, int16_t *z_read) {
// // Code for reading the x, y, and z axes data
// }
// float *ADXL345_Scale_Axes(ACCEL_HandleTypeDef *haccel, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
// // Code for scaling the raw axes data
// uint8_t spi1_write;
// uint8_t spi1_read[ 6 ];
// /* Read bit (0) + multiple bit (1) + DATAX0 register address */
// spi1_write = SPI_READ | MULTIPLE_BYTE_ENABLED | DATAX0_REG;
// }
// void ADXL345_Self_Test(ACCEL_HandleTypeDef *haccel, int16_t *x_st, int16_t *y_st, int16_t *z_st) {
// // Code for performing self-test
// }
void ADXL345_Enable_Int(ACCEL_HandleTypeDef *haccel, uint8_t int_enable, uint8_t int_map) {
// Code for enabling interrupts with specified interrupt enable and interrupt map settings
/* Disable all interrupts */
haccel->intenable = 0x00;
ADXL345_Write( INT_ENABLE_REG, haccel->intenable );
/* Map interrupts to the specified INTx pin */
haccel->intmap = int_map;
ADXL345_Write( INT_MAP_REG, haccel->intmap );
/* Enable user selected interrupts */
haccel->intenable = int_enable;
ADXL345_Write( INT_ENABLE_REG, haccel->intenable );
/* NOTE: interrupts can be set active high or active low in INT_INVERT bit from DATA_FORMAT register */
}
// void ADXL345_Disable_Int(ACCEL_HandleTypeDef *haccel, uint8_t interrupts) {
// // Code for disabling specified interrupts
// }
// uint8_t ADXL345_Read_Int_Source(ACCEL_HandleTypeDef *haccel) {
// // Code for reading the interrupt source
// }
// uint8_t ADXL345_Read_Tap_Source(ACCEL_HandleTypeDef *haccel) {
// // Code for reading the tap source
// }
// uint8_t ADXL345_Read_Activity_Source(ACCEL_HandleTypeDef *haccel) {
// // Code for reading the activity source
// }
// uint8_t ADXL345_Read_FIFO_Entries(ACCEL_HandleTypeDef *haccel) {
// // Code for reading the number of entries in the FIFO
// }
</code>
<code> #include "adxl_driver.h" #include "spi.h" #include <avr/io.h> #include <util/delay.h> #include <stdio.h> void ADXL345_Write( uint8_t reg_addr, uint8_t data ){ uint8_t data_to_write[2]; data_to_write[0] = SPI_WRITE | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK); data_to_write[1] = data; spi_cs_enable(); spi_write(data_to_write, 2); spi_cs_disable(); } uint8_t ADXL345_Read( uint8_t reg_addr ){ uint8_t data_to_write; uint8_t data_to_read; data_to_write = SPI_READ | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK); spi_cs_enable(); spi_write(&data_to_write, 1U); spi_read(&data_to_read, 1U); spi_cs_disable(); return data_to_read; } void ADXL345_Init( ACCEL_HandleTypeDef *haccel ){ spi_init_master(3); //SPI mode 3, this means that CPOL and CPHA are both 1. spi_clock_select(2); //Clock division factor of 2 this means we have a clock frequency of 8MHz/2 = 4 MHz ADXL345_Write( POWER_CTL_REG, MEASURE_STANDBY ); /* Configure data format: self test, SPI 3/4 bit mode, interrupt active high/low, full/10bit resolution, right/left justified, 2/4/8/16 g range */ ADXL345_Write( DATA_FORMAT_REG, haccel->dataformat ); /* Configure power mode and output data rate */ ADXL345_Write( BW_RATE_REG, haccel->bwrate ); /* Configure X, Y and Z axis offset values */ ADXL345_Write( OFSX_REG, haccel->ofsx ); ADXL345_Write( OFSY_REG, haccel->ofsy ); ADXL345_Write( OFSZ_REG, haccel->ofsz ); ADXL345_Enable_Int( haccel, haccel->intenable, haccel->intmap ); /* If single tap or double tap interrupts are enabled */ if ((( haccel->intenable & SINGLE_TAP_INT_ENABLED ) == SINGLE_TAP_INT_ENABLED ) || (( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED )){ /* Configure tap threshold */ ADXL345_Write( THRESH_TAP_REG, haccel->threshtap ); /* Configure tap duration */ ADXL345_Write( DUR_REG, haccel->dur ); /* Enable participating axes in tap detection (and double tap supressing if double tap int. enabled) */ ADXL345_Write( TAP_AXES_REG, haccel->tapaxes ); } /* Configure latent and window for double tap detection (if enabled) */ if (( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED ){ ADXL345_Write( LATENT_REG, haccel->latent ); ADXL345_Write( WINDOW_REG, haccel->window ); } /* If activity interrupt is enabled */ if (( haccel->intenable & ACTIVITY_INT_ENABLED ) == ACTIVITY_INT_ENABLED ){ /* Configure threshold for activity */ ADXL345_Write( THRESH_ACT_REG, haccel->threshact ); /* Enable participating axes in activity detection */ ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl ); } /* If inactivity interrupt is enabled */ if (( haccel->intenable & INACTIVITY_INT_ENABLED ) == INACTIVITY_INT_ENABLED ){ /* Configure threshold for inactivity */ ADXL345_Write( THRESH_INACT_REG, haccel->threshinact ); /* Configure time for inactivity */ ADXL345_Write( TIME_INACT_REG, haccel->timeinact ); /* Enable participating axes in inactivity detection */ ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl ); } /* If free-fall interrupt is enabled */ if ((( haccel->intenable & FREE_FALL_INT_ENABLED ) == FREE_FALL_INT_ENABLED )){ /* Configure freefall threshold */ ADXL345_Write( THRESH_FF_REG, haccel->threshff ); /* Configure freefall time */ ADXL345_Write( TIME_FF_REG, haccel->timeff ); } /* Configure FIFO mode (and number of samples for the watermark interrupt, if enabled) */ ADXL345_Write( FIFO_CTL_REG, haccel->fifoctl ); /* Configure power mode (if measure bit is set, ADXL345 enters normal mode) */ ADXL345_Write( POWER_CTL_REG, haccel->powerctl ); } void ADXL345_Read_Devid(ACCEL_HandleTypeDef *haccel) { // Code for reading the device ID haccel->devid = ADXL345_Read( DEVID_REG ); } void ADXL345_Standby_Mode(ACCEL_HandleTypeDef *haccel) { // Code for putting the ADXL345 in standby mode haccel->powerctl &= ~MEASURE_ENABLED; ADXL345_Write( POWER_CTL_REG, haccel->powerctl ); } void ADXL345_Measurement_Mode(ACCEL_HandleTypeDef *haccel) { // Code for putting the ADXL345 in measurement mode haccel->powerctl |= MEASURE_ENABLED; ADXL345_Write( POWER_CTL_REG, haccel->powerctl ); } // void ADXL345_Low_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) { // // Code for setting the ADXL345 in low power mode with specified output rate // } // void ADXL345_Normal_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) { // // Code for setting the ADXL345 in normal power mode with specified output rate // } // void ADXL345_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) { // // Code for putting the ADXL345 in sleep mode with specified wakeup rate // } // void ADXL345_Wake_Up(ACCEL_HandleTypeDef *haccel) { // // Code for waking up the ADXL345 from sleep mode // } // void ADXL345_Auto_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) { // // Code for enabling auto sleep mode with specified wakeup rate // } // void ADXL345_Link_Mode(ACCEL_HandleTypeDef *haccel, uint8_t autosleep_mode, uint8_t wakeup_rate) { // // Code for enabling link mode with specified auto sleep mode and wakeup rate // } // void ADXL345_Manual_OffsetCal(ACCEL_HandleTypeDef *haccel, int16_t *x0g, int16_t *y0g, int16_t *z0g) { // // Code for performing manual offset calibration // } // void ADXL345_Auto_OffsetCal(ACCEL_HandleTypeDef *haccel) { // // Code for performing auto offset calibration // } // void ADXL345_Fifo_Bypass_Mode(ACCEL_HandleTypeDef *haccel) { // // Code for setting the FIFO to bypass mode // } // void ADXL345_Fifo_Fifo_Mode(ACCEL_HandleTypeDef *haccel) { // // Code for setting the FIFO to FIFO mode // } // void ADXL345_Fifo_Stream_Mode(ACCEL_HandleTypeDef *haccel) { // // Code for setting the FIFO to stream mode // } // void ADXL345_Fifo_Trigger_Mode(ACCEL_HandleTypeDef *haccel, uint8_t interrupts, uint8_t trigger_pin, uint8_t samples) { // // Code for setting the FIFO to trigger mode with specified interrupts, trigger pin, and samples // } // void ADXL345_Read_Axes(ACCEL_HandleTypeDef *haccel, int16_t *x_read, int16_t *y_read, int16_t *z_read) { // // Code for reading the x, y, and z axes data // } // float *ADXL345_Scale_Axes(ACCEL_HandleTypeDef *haccel, int16_t x_raw, int16_t y_raw, int16_t z_raw) { // // Code for scaling the raw axes data // uint8_t spi1_write; // uint8_t spi1_read[ 6 ]; // /* Read bit (0) + multiple bit (1) + DATAX0 register address */ // spi1_write = SPI_READ | MULTIPLE_BYTE_ENABLED | DATAX0_REG; // } // void ADXL345_Self_Test(ACCEL_HandleTypeDef *haccel, int16_t *x_st, int16_t *y_st, int16_t *z_st) { // // Code for performing self-test // } void ADXL345_Enable_Int(ACCEL_HandleTypeDef *haccel, uint8_t int_enable, uint8_t int_map) { // Code for enabling interrupts with specified interrupt enable and interrupt map settings /* Disable all interrupts */ haccel->intenable = 0x00; ADXL345_Write( INT_ENABLE_REG, haccel->intenable ); /* Map interrupts to the specified INTx pin */ haccel->intmap = int_map; ADXL345_Write( INT_MAP_REG, haccel->intmap ); /* Enable user selected interrupts */ haccel->intenable = int_enable; ADXL345_Write( INT_ENABLE_REG, haccel->intenable ); /* NOTE: interrupts can be set active high or active low in INT_INVERT bit from DATA_FORMAT register */ } // void ADXL345_Disable_Int(ACCEL_HandleTypeDef *haccel, uint8_t interrupts) { // // Code for disabling specified interrupts // } // uint8_t ADXL345_Read_Int_Source(ACCEL_HandleTypeDef *haccel) { // // Code for reading the interrupt source // } // uint8_t ADXL345_Read_Tap_Source(ACCEL_HandleTypeDef *haccel) { // // Code for reading the tap source // } // uint8_t ADXL345_Read_Activity_Source(ACCEL_HandleTypeDef *haccel) { // // Code for reading the activity source // } // uint8_t ADXL345_Read_FIFO_Entries(ACCEL_HandleTypeDef *haccel) { // // Code for reading the number of entries in the FIFO // } </code>

#include "adxl_driver.h"
#include "spi.h"
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
  
    void ADXL345_Write( uint8_t reg_addr, uint8_t data ){
        uint8_t data_to_write[2];
        data_to_write[0] = SPI_WRITE | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK); 
        data_to_write[1] = data;
        spi_cs_enable();
        spi_write(data_to_write, 2);
        spi_cs_disable();
    }
    
    uint8_t ADXL345_Read( uint8_t reg_addr ){
        uint8_t data_to_write;
        uint8_t data_to_read;
        data_to_write = SPI_READ | MULTIPLE_BYTE_DISABLED | (reg_addr & REG_ADDRESS_MASK);
        spi_cs_enable();
        spi_write(&data_to_write, 1U);
        spi_read(&data_to_read, 1U);
        spi_cs_disable();
        return data_to_read;
    }

    void ADXL345_Init( ACCEL_HandleTypeDef *haccel ){
        spi_init_master(3); //SPI mode 3, this means that CPOL and CPHA are both 1.
        spi_clock_select(2); //Clock division factor of 2 this means we have a clock frequency of 8MHz/2 = 4 MHz
        ADXL345_Write( POWER_CTL_REG, MEASURE_STANDBY );

        /* Configure data format: self test, SPI 3/4 bit mode, interrupt active high/low, 
        full/10bit resolution, right/left justified, 2/4/8/16 g range */
        ADXL345_Write( DATA_FORMAT_REG, haccel->dataformat );

        /* Configure power mode and output data rate */
        ADXL345_Write( BW_RATE_REG, haccel->bwrate );

        /* Configure X, Y and Z axis offset values */
        ADXL345_Write( OFSX_REG, haccel->ofsx );
        ADXL345_Write( OFSY_REG, haccel->ofsy );
        ADXL345_Write( OFSZ_REG, haccel->ofsz );
        ADXL345_Enable_Int( haccel, haccel->intenable, haccel->intmap );
            /* If single tap or double tap interrupts are enabled */
        if ((( haccel->intenable & SINGLE_TAP_INT_ENABLED ) == SINGLE_TAP_INT_ENABLED ) || 
            (( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED )){
            /* Configure tap threshold */
            ADXL345_Write( THRESH_TAP_REG, haccel->threshtap );

            /* Configure tap duration */
            ADXL345_Write( DUR_REG, haccel->dur );

            /* Enable participating axes in tap detection (and double tap supressing if double tap int. enabled) */
            ADXL345_Write( TAP_AXES_REG, haccel->tapaxes );
        }
        
        /* Configure latent and window for double tap detection (if enabled) */
        if (( haccel->intenable & DOUBLE_TAP_INT_ENABLED ) == DOUBLE_TAP_INT_ENABLED ){
            ADXL345_Write( LATENT_REG, haccel->latent );
            ADXL345_Write( WINDOW_REG, haccel->window );
        }

        /* If activity interrupt is enabled */
        if (( haccel->intenable & ACTIVITY_INT_ENABLED ) == ACTIVITY_INT_ENABLED ){
            /* Configure threshold for activity */
            ADXL345_Write( THRESH_ACT_REG, haccel->threshact );

            /* Enable participating axes in activity detection */
            ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl );
        }

        /* If inactivity interrupt is enabled */
        if (( haccel->intenable & INACTIVITY_INT_ENABLED ) == INACTIVITY_INT_ENABLED ){
            /* Configure threshold for inactivity */
            ADXL345_Write( THRESH_INACT_REG, haccel->threshinact );
            
            /* Configure time for inactivity */
            ADXL345_Write( TIME_INACT_REG, haccel->timeinact );

            /* Enable participating axes in inactivity detection */
            ADXL345_Write( ACT_INACT_CTL_REG, haccel->actinactctl );
        }

        /* If free-fall interrupt is enabled */
        if ((( haccel->intenable & FREE_FALL_INT_ENABLED ) == FREE_FALL_INT_ENABLED )){
            /* Configure freefall threshold */
            ADXL345_Write( THRESH_FF_REG, haccel->threshff );

            /* Configure freefall time */
            ADXL345_Write( TIME_FF_REG, haccel->timeff );
        }

        /* Configure FIFO mode (and number of samples for the watermark interrupt, if enabled) */
        ADXL345_Write( FIFO_CTL_REG, haccel->fifoctl );
        
        /* Configure power mode (if measure bit is set, ADXL345 enters normal mode) */
        ADXL345_Write( POWER_CTL_REG, haccel->powerctl );

    }

    void ADXL345_Read_Devid(ACCEL_HandleTypeDef *haccel) {
        // Code for reading the device ID
        haccel->devid = ADXL345_Read( DEVID_REG );
    }

    void ADXL345_Standby_Mode(ACCEL_HandleTypeDef *haccel) {
        // Code for putting the ADXL345 in standby mode
        haccel->powerctl &= ~MEASURE_ENABLED;
        ADXL345_Write( POWER_CTL_REG, haccel->powerctl );
    }

    void ADXL345_Measurement_Mode(ACCEL_HandleTypeDef *haccel) {
        // Code for putting the ADXL345 in measurement mode
        haccel->powerctl |= MEASURE_ENABLED;
        ADXL345_Write( POWER_CTL_REG, haccel->powerctl );
    }

    // void ADXL345_Low_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) {
    //     // Code for setting the ADXL345 in low power mode with specified output rate

    // }

    // void ADXL345_Normal_Power_Mode(ACCEL_HandleTypeDef *haccel, uint8_t output_rate) {
    //     // Code for setting the ADXL345 in normal power mode with specified output rate
    // }

    // void ADXL345_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) {
    //     // Code for putting the ADXL345 in sleep mode with specified wakeup rate
    // }

    // void ADXL345_Wake_Up(ACCEL_HandleTypeDef *haccel) {
    //     // Code for waking up the ADXL345 from sleep mode
    // }

    // void ADXL345_Auto_Sleep_Mode(ACCEL_HandleTypeDef *haccel, uint8_t wakeup_rate) {
    //     // Code for enabling auto sleep mode with specified wakeup rate
    // }

    // void ADXL345_Link_Mode(ACCEL_HandleTypeDef *haccel, uint8_t autosleep_mode, uint8_t wakeup_rate) {
    //     // Code for enabling link mode with specified auto sleep mode and wakeup rate
    // }

    // void ADXL345_Manual_OffsetCal(ACCEL_HandleTypeDef *haccel, int16_t *x0g, int16_t *y0g, int16_t *z0g) {
    //     // Code for performing manual offset calibration
    // }

    // void ADXL345_Auto_OffsetCal(ACCEL_HandleTypeDef *haccel) {
    //     // Code for performing auto offset calibration
    // }

    // void ADXL345_Fifo_Bypass_Mode(ACCEL_HandleTypeDef *haccel) {
    //     // Code for setting the FIFO to bypass mode
    // }

    // void ADXL345_Fifo_Fifo_Mode(ACCEL_HandleTypeDef *haccel) {
    //     // Code for setting the FIFO to FIFO mode
    // }

    // void ADXL345_Fifo_Stream_Mode(ACCEL_HandleTypeDef *haccel) {
    //     // Code for setting the FIFO to stream mode
    // }

    // void ADXL345_Fifo_Trigger_Mode(ACCEL_HandleTypeDef *haccel, uint8_t interrupts, uint8_t trigger_pin, uint8_t samples) {
    //     // Code for setting the FIFO to trigger mode with specified interrupts, trigger pin, and samples
    // }

    // void ADXL345_Read_Axes(ACCEL_HandleTypeDef *haccel, int16_t *x_read, int16_t *y_read, int16_t *z_read) {
    //     // Code for reading the x, y, and z axes data
    // }

    // float *ADXL345_Scale_Axes(ACCEL_HandleTypeDef *haccel, int16_t x_raw, int16_t y_raw, int16_t z_raw) {
    //     // Code for scaling the raw axes data
    //     uint8_t spi1_write;
    //     uint8_t spi1_read[ 6 ];

    //     /* Read bit (0) + multiple bit (1) + DATAX0 register address */
    //     spi1_write = SPI_READ | MULTIPLE_BYTE_ENABLED | DATAX0_REG;
    // }

    // void ADXL345_Self_Test(ACCEL_HandleTypeDef *haccel, int16_t *x_st, int16_t *y_st, int16_t *z_st) {
    //     // Code for performing self-test
    // }

    void ADXL345_Enable_Int(ACCEL_HandleTypeDef *haccel, uint8_t int_enable, uint8_t int_map) {
        // Code for enabling interrupts with specified interrupt enable and interrupt map settings
        /* Disable all interrupts */
        haccel->intenable = 0x00;
        ADXL345_Write( INT_ENABLE_REG, haccel->intenable );

        /* Map interrupts to the specified INTx pin */
        haccel->intmap = int_map;
        ADXL345_Write( INT_MAP_REG, haccel->intmap );

        /* Enable user selected interrupts */
        haccel->intenable = int_enable;
        ADXL345_Write( INT_ENABLE_REG, haccel->intenable );

        /* NOTE: interrupts can be set active high or active low in INT_INVERT bit from DATA_FORMAT register */
    }
    // void ADXL345_Disable_Int(ACCEL_HandleTypeDef *haccel, uint8_t interrupts) {
    //     // Code for disabling specified interrupts
    // }

    // uint8_t ADXL345_Read_Int_Source(ACCEL_HandleTypeDef *haccel) {
    //     // Code for reading the interrupt source
    // }

    // uint8_t ADXL345_Read_Tap_Source(ACCEL_HandleTypeDef *haccel) {
    //     // Code for reading the tap source
    // }

    // uint8_t ADXL345_Read_Activity_Source(ACCEL_HandleTypeDef *haccel) {
    //     // Code for reading the activity source
    // }

    // uint8_t ADXL345_Read_FIFO_Entries(ACCEL_HandleTypeDef *haccel) {
    //     // Code for reading the number of entries in the FIFO
    // }

THIS IS MY MAIN CODE:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>#include <stdio.h>
#include <avr/io.h>
#include "adxl_driver.h"
#include <util/delay.h>
//#define DDRB *((volatile unsigned char*)0x24)
//#define PORTB *((volatile unsigned char*)0x25) //Volatile to make sure the compiler doesnt optimize it away
//#define PB5 5
//#define F_CPU 16000000UL
#define ADXL345_DEVICE_ID 0xE5U
void pb1_blinking(){
while(1){
PORTB |= (1<<PB1); //turn on portb
_delay_ms(500);
PORTB &= ~(1<<PB1);
_delay_ms(500);
}
}
void setup() {
DDRB |= (1<<PB1); //Set portb as output
ACCEL_HandleTypeDef haccel;
haccel. ofsx = 0x00U;
haccel. ofsy = 0x00U;
haccel. ofsz = 0x00U;
haccel. bwrate = LOW_POWER_DISABLED | RATE_6_25_HZ;;
haccel. powerctl = MEASURE_ENABLED | SLEEP_DISABLED | WAKEUP_8HZ | AUTO_SLEEP_DISABLED | LINK_INACT_ACT_CONCURRENT;
haccel. intenable = ALL_INTERRUPTS_DISABLED;
haccel. intmap = 0x00U;
haccel. dataformat = SELF_TEST_DISABLED | SPI_4WIRE_MODE | INT_INVERT_ACTIVE_HIGH | FULL_RES_10BIT_MODE | JUSTIFY_LEFT | RANGE_2G;
haccel. fifoctl = FIFO_BYPASS_MODE;
ADXL345_Init(&haccel);
// Read the device ID
ADXL345_Read_Devid(&haccel);
// Check if the device ID matches the expected value
if (haccel.devid == ADXL345_DEVICE_ID) {
PORTB |= (1 << PB1); // Turn on the LED on PORTB1
}
else {
pb1_blinking();
}
}
void thre_second_blink(){
bool led_on = true;
while(led_on){
for (int i = 0; i < 6; i++){
PORTB |= (1<<PB0); //turn on portb
_delay_ms(500);
PORTB &= ~(1<<PB0);
_delay_ms(500);
}
led_on = false;
}
}
int main() {
thre_second_blink();
setup();
while(1){
PORTB |= (1<<PB0); //turn on portb
_delay_ms(500);
PORTB &= ~(1<<PB0);
_delay_ms(500);
}
return 1;
}
</code>
<code>#include <stdio.h> #include <avr/io.h> #include "adxl_driver.h" #include <util/delay.h> //#define DDRB *((volatile unsigned char*)0x24) //#define PORTB *((volatile unsigned char*)0x25) //Volatile to make sure the compiler doesnt optimize it away //#define PB5 5 //#define F_CPU 16000000UL #define ADXL345_DEVICE_ID 0xE5U void pb1_blinking(){ while(1){ PORTB |= (1<<PB1); //turn on portb _delay_ms(500); PORTB &= ~(1<<PB1); _delay_ms(500); } } void setup() { DDRB |= (1<<PB1); //Set portb as output ACCEL_HandleTypeDef haccel; haccel. ofsx = 0x00U; haccel. ofsy = 0x00U; haccel. ofsz = 0x00U; haccel. bwrate = LOW_POWER_DISABLED | RATE_6_25_HZ;; haccel. powerctl = MEASURE_ENABLED | SLEEP_DISABLED | WAKEUP_8HZ | AUTO_SLEEP_DISABLED | LINK_INACT_ACT_CONCURRENT; haccel. intenable = ALL_INTERRUPTS_DISABLED; haccel. intmap = 0x00U; haccel. dataformat = SELF_TEST_DISABLED | SPI_4WIRE_MODE | INT_INVERT_ACTIVE_HIGH | FULL_RES_10BIT_MODE | JUSTIFY_LEFT | RANGE_2G; haccel. fifoctl = FIFO_BYPASS_MODE; ADXL345_Init(&haccel); // Read the device ID ADXL345_Read_Devid(&haccel); // Check if the device ID matches the expected value if (haccel.devid == ADXL345_DEVICE_ID) { PORTB |= (1 << PB1); // Turn on the LED on PORTB1 } else { pb1_blinking(); } } void thre_second_blink(){ bool led_on = true; while(led_on){ for (int i = 0; i < 6; i++){ PORTB |= (1<<PB0); //turn on portb _delay_ms(500); PORTB &= ~(1<<PB0); _delay_ms(500); } led_on = false; } } int main() { thre_second_blink(); setup(); while(1){ PORTB |= (1<<PB0); //turn on portb _delay_ms(500); PORTB &= ~(1<<PB0); _delay_ms(500); } return 1; } </code>
#include <stdio.h>
#include <avr/io.h>
#include "adxl_driver.h"
#include <util/delay.h>
//#define DDRB *((volatile unsigned char*)0x24)
//#define PORTB *((volatile unsigned char*)0x25) //Volatile to make sure the compiler doesnt optimize it away 
//#define PB5 5
//#define F_CPU 16000000UL
#define ADXL345_DEVICE_ID 0xE5U

void pb1_blinking(){
    while(1){
        PORTB |= (1<<PB1); //turn on portb 
        _delay_ms(500);
        PORTB &= ~(1<<PB1);
        _delay_ms(500);
    }
}

void setup() {
    DDRB |= (1<<PB1); //Set portb as output
    ACCEL_HandleTypeDef haccel;
        haccel. ofsx = 0x00U;
        haccel. ofsy = 0x00U;
        haccel. ofsz = 0x00U;
        haccel. bwrate = LOW_POWER_DISABLED | RATE_6_25_HZ;;
        haccel. powerctl  = MEASURE_ENABLED | SLEEP_DISABLED | WAKEUP_8HZ | AUTO_SLEEP_DISABLED | LINK_INACT_ACT_CONCURRENT;
        haccel. intenable = ALL_INTERRUPTS_DISABLED;
        haccel. intmap = 0x00U;
        haccel. dataformat = SELF_TEST_DISABLED | SPI_4WIRE_MODE | INT_INVERT_ACTIVE_HIGH | FULL_RES_10BIT_MODE | JUSTIFY_LEFT | RANGE_2G;
        haccel. fifoctl = FIFO_BYPASS_MODE;

    ADXL345_Init(&haccel);

        // Read the device ID
    ADXL345_Read_Devid(&haccel);

    // Check if the device ID matches the expected value
    if (haccel.devid == ADXL345_DEVICE_ID) {
        PORTB |= (1 << PB1); // Turn on the LED on PORTB1
    }
    else {
        pb1_blinking();
    }
}

void thre_second_blink(){
  bool led_on = true;
  while(led_on){
      for (int i = 0; i < 6; i++){
        PORTB |= (1<<PB0); //turn on portb 
        _delay_ms(500);
        PORTB &= ~(1<<PB0);
        _delay_ms(500);
      }  
    led_on = false;
  }
}

int main() {
  thre_second_blink();
  setup();
    while(1){
      
        PORTB |= (1<<PB0); //turn on portb 
        _delay_ms(500);
        PORTB &= ~(1<<PB0);
        _delay_ms(500);
    }  
  return 1;
}

Feel free to ask any question and i can try to supply or answer any information needed to help me! Not sure if everything is explained enough.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật