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.
- 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?
- 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
#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
#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:
#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.