Skip to content

Instantly share code, notes, and snippets.

@tspspi
Created September 12, 2021 17:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tspspi/b97777ffe5cc31e46dd9786f4bab4e0d to your computer and use it in GitHub Desktop.
Save tspspi/b97777ffe5cc31e46dd9786f4bab4e0d to your computer and use it in GitHub Desktop.
Example program for synchronous usage of AD7705 with an ATMega2560

AD7705 synchronous sample on ATMega2560

A simple example on how to configure the AD7705 in a synchronous way on an ATMega2560 (it works the same way on other ATMega series devices). This sample program simply reads each new ADC sample and dumps it in ASCII on the serial port.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include "./ad7705.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Asserting and deasserting chip select including the minimum delay
*/
static inline void ad7705CSAssert() {
int i;
PORTB = PORTB & 0xFE;
/* Currently hardcoding two NOP's for 16 MHz AVR */
__builtin_avr_nop();
__builtin_avr_nop();
}
static inline void ad7705CSDeassert() {
/* Currently hardcoding two NOP's for 16 MHz AVR */
__builtin_avr_nop();
__builtin_avr_nop();
PORTB = PORTB | 0x01;
}
/*
Initialize SPI for AD7705
Assuming:
PB0 Chip select (nSS) out
PB1 Serial clock (SCK) out
PB2 Master out, slave in (MOSI) out
PB3 Master in, slave out (MISO) in
PB4 Reset (nRESET) out
PB5 Data read ready (nDRDY) in
*/
void ad7705InitSPI() {
uint8_t sregOld = SREG;
cli();
DDRB = 0x17;
PORTB = 0x13; /* Deassert nSS, assert nRESET, keep clock high */
SPSR = 0x00; /* Clear SPI2X */
volatile uint8_t dummy = SPDR; /* Dummy read */
SPCR = 0x5C; // 0x5F is slowest;
/* Re-enable interrupts if they've been enabled before */
SREG = sregOld;
}
/*
Transfer functions to read or write 8 and 16 bit
values; one might also need 24 bit transfer in case
one wants to read or write calibration registers
*/
static uint8_t ad7705SPITransfer8Sync(uint8_t bDataOut) {
uint8_t spStatus;
SPDR = bDataOut;
while(((spStatus = SPSR) & 0x80) == 0) {
__builtin_avr_nop();
}
return SPDR;
}
static uint16_t ad7705SPITransfer16Sync(uint16_t bDataOut) {
uint8_t high;
uint8_t low;
high = (uint8_t)((bDataOut >> 8) & 0xFF);
low = (uint8_t)((bDataOut ) & 0xFF);
high = ad7705SPITransfer8Sync(high);
low = ad7705SPITransfer8Sync(low);
return ((((uint16_t)high) << 8) & 0xFF00) | (((uint16_t)low) & 0x00FF);
}
static uint32_t ad7705SPITransfer24Sync(uint32_t bDataOut) {
uint8_t b1;
uint8_t b2;
uint8_t b3;
b1 = (uint8_t)((bDataOut >> 16) & 0xFF);
b2 = (uint8_t)((bDataOut >> 8) & 0xFF);
b3 = (uint8_t)((bDataOut ) & 0xFF);
b1 = ad7705SPITransfer8Sync(b1);
b2 = ad7705SPITransfer8Sync(b2);
b3 = ad7705SPITransfer8Sync(b3);
return ((((uint32_t)b1) << 16) & 0x00FF0000)
| ((((uint32_t)b2) << 8) & 0x00FF0000)
| ((((uint32_t)b3) ) & 0x00FF0000);
}
/*
Note: clock setup requires CS to be asserted ...
* Writes into communication register to prime write into clock register
* Writes into clock register
*/
void ad7705ClockSetupSync(
uint8_t adMasterClock,
uint8_t adUpdateFrq,
uint8_t channel
) {
ad7705CSAssert();
ad7705SPITransfer8Sync(0x20 | channel);
ad7705SPITransfer8Sync(adMasterClock | adUpdateFrq);
ad7705CSDeassert();
}
void ad7705SetupAndCalibrateSync(
uint8_t gain,
uint8_t flags,
uint8_t channel
) {
ad7705CSAssert();
ad7705SPITransfer8Sync(0x10 | channel);
ad7705SPITransfer8Sync(flags | ((gain & 0x07) << 3) | 0x40);
ad7705CSDeassert();
}
void ad7705SetupNormalSync(
uint8_t gain,
uint8_t flags,
uint8_t channel
) {
ad7705CSAssert();
ad7705SPITransfer8Sync(0x10 | channel);
ad7705SPITransfer8Sync(flags | ((gain & 0x07) << 3));
ad7705CSDeassert();
}
uint16_t ad7705GetADCSync() {
uint16_t result;
while((PORTB & 0x20) != 0) {
__builtin_avr_nop();
}
ad7705CSAssert();
ad7705SPITransfer8Sync(0x38); /* Or 0x39 if channel 2 should be selected */
result = ad7705SPITransfer16Sync(0xFFFF);
ad7705CSDeassert();
return result;
}
void ad7705ResetSoftSync() {
ad7705CSAssert();
ad7705SPITransfer8Sync(0xFF);
ad7705SPITransfer8Sync(0xFF);
ad7705SPITransfer8Sync(0xFF);
ad7705SPITransfer8Sync(0xFF);
ad7705CSDeassert();
}
#ifdef __cplusplus
} /* extern "C" { */
#endif
#ifndef __is_included__8d335da1_79e9_48a2_aaac_8f0c5dabed13
#define __is_included__8d335da1_79e9_48a2_aaac_8f0c5dabed13 1
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __BUILTIN_AVR_NOP
#define __BUILTIN_AVR_NOP 1
static inline void __builtin_avr_nop() {
asm volatile("nop");
}
#endif
#define AD7705_UPDATE_HZ20 0x00
#define AD7705_UPDATE_HZ25 0x01
#define AD7705_UPDATE_HZ100 0x02
#define AD7705_UPDATE_HZ200 0x03
#define AD7705_UPDATE_HZ50 0x04
#define AD7705_UPDATE_HZ60 0x05
#define AD7705_UPDATE_HZ250 0x06
#define AD7705_UPDATE_HZ500 0x07
#define AD7705_MASTERCLK_MHZ1 0x00
#define AD7705_MASTERCLK_MHZ2 0x80
#define AD7705_MASTERCLK_MHZ24576 0x40
#define AD7705_MASTERCLK_MHZ49152 0xC0
#define AD7705_CH1 0x00
#define AD7705_CH2 0x01
#define AD7705_SETUP_BUFFER_ENABLE 0x02
#define AD7705_SETUP_BUFFER_DISABLE 0x00
#define AD7705_SETUP_UNIPOLAR 0x04
#define AD7705_SETUP_BIPOLAR 0x00
#define AD7705_SYNCHRONIZE 0x01
void ad7705InitSPI();
void ad7705ClockSetupSync(
uint8_t adMasterClock,
uint8_t adUpdateFrq,
uint8_t channel
);
void ad7705SetupAndCalibrateSync(
uint8_t gain,
uint8_t flags,
uint8_t channel
);
void ad7705SetupNormalSync(
uint8_t gain,
uint8_t flags,
uint8_t channel
);
uint16_t ad7705GetADCSync();
void ad7705ResetSoftSync();
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* #ifndef __is_included__8d335da1_79e9_48a2_aaac_8f0c5dabed13 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include <stdint.h>
#include <limits.h>
#include "./ad7705.h"
#include "./sysclk.h"
static void serialTX(uint8_t byte) {
while((UCSR0A & 0x20) == 0) { }
UDR0 = byte;
while((UCSR0A & 0x20) == 0) { }
}
int main() {
char bBuffer[6];
int dwBufferLen;
int i;
systickInit();
cli();
/*
Serial port: 115200 bps, 8 bits, no partiy, 1 stop bit
*/
UBRR0 = 16;
UCSR0A = 0x02;
UCSR0B = 0x10 | 0x08; /* Enable receiver and transmitter */
UCSR0C = 0x06;
ad7705InitSPI();
ad7705ResetSoftSync();
ad7705ClockSetupSync(AD7705_MASTERCLK_MHZ49152, AD7705_UPDATE_HZ50, AD7705_CH1);
ad7705SetupAndCalibrateSync(7, AD7705_SETUP_BUFFER_ENABLE | AD7705_SETUP_BIPOLAR, AD7705_CH1);
for(;;) {
uint16_t nextValue = ad7705GetADCSync();
/* Publish via serial port ... in ASCII form ... */
if(nextValue == 0) {
serialTX(0x30);
serialTX(0x0D);
serialTX(0x0A);
} else {
dwBufferLen = 0;
while(nextValue != 0) {
bBuffer[dwBufferLen] = 0x30 + (nextValue % 10);
dwBufferLen = dwBufferLen + 1;
nextValue = nextValue / 10;
}
for(i = dwBufferLen; i > 0; i = i - 1) {
serialTX(bBuffer[i-1]);
}
serialTX(0x0D);
serialTX(0x0A);
}
}
}

Copyright (c) Thomas Spielauer. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • All advertising materials mentioning features or use of this software must display the following acknowledgement: "This product includes software developed by the Thomas Spielauer.”
  • The name of the copyright holder contributors may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "./sysclk.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
System tick timer
*/
volatile unsigned long int systemMillis = 0;
volatile unsigned long int systemMilliFractional = 0;
volatile unsigned long int systemMonotonicOverflowCnt = 0;
ISR(TIMER0_OVF_vect) {
unsigned long int m, f;
m = systemMillis;
f = systemMilliFractional;
m = m + SYSCLK_MILLI_INCREMENT;
f = f + SYSCLK_MILLIFRACT_INCREMENT;
if(f >= SYSCLK_MILLIFRACT_MAXIMUM) {
f = f - SYSCLK_MILLIFRACT_MAXIMUM;
m = m + 1;
}
systemMonotonicOverflowCnt = systemMonotonicOverflowCnt + 1;
systemMillis = m;
systemMilliFractional = f;
}
unsigned long int millis() {
unsigned long int m;
uint8_t srOld = SREG;
cli();
m = systemMillis;
SREG = srOld;
return m;
}
unsigned long int micros() {
uint8_t srOld = SREG;
unsigned long int overflowCounter;
unsigned long int timerCounter;
cli();
overflowCounter = systemMonotonicOverflowCnt;
timerCounter = TCNT0;
if(((TIFR0 & 0x01) != 0) && (timerCounter < 255)) {
overflowCounter = overflowCounter + 1;
}
SREG = srOld;
return ((overflowCounter << 8) + timerCounter) * (64L / (F_CPU / 1000000L));
}
void delay(unsigned long millisecs) {
unsigned int lastMicro;
lastMicro = (unsigned int)micros();
while(millisecs > 0) {
unsigned int curMicro = micros();
if(curMicro - lastMicro >= 1000) {
lastMicro = lastMicro + 1000;
millisecs = millisecs - 1;
}
}
return;
}
void delayMicros(unsigned int microDelay) {
#if F_CPU == 20000000L
__asm__ __volatile__ (
"nop\n"
"nop\n"
);
if((microDelay = microDelay - 1) == 0) {
return;
}
microDelay = (microDelay << 2) + microDelay;
#elif F_CPU == 16000000L
if((microDelay = microDelay - 1) == 0) {
return;
}
microDelay = (microDelay << 2) - 2;
#elif F_CPU == 8000000L
if((microDelay = microDelay - 1) == 0) {
return;
}
if((microDelay = microDelay - 1) == 0) {
return;
}
microDelay = (microDelay << 1) - 1;
#else
#error No known delay loop calibration available for this F_CPU
#endif
__asm__ __volatile__ (
"lp: sbiw %0, 1\n"
" brne lp"
: "=w" (microDelay)
: "0" (microDelay)
);
return;
}
void systickInit() {
uint8_t sregOld = SREG;
cli();
TCCR0A = 0x00;
TCCR0B = 0x03; /* /64 prescaler */
TIMSK0 = 0x01; /* Enable overflow interrupt */
// PRR = PRR & (~0x20);
SREG = sregOld;
}
void systickDisable() {
TIMSK0 = 0x00;
}
#ifdef __cplusplus
} /* extern "C" { */
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>
#include <util/twi.h>
#include <stdint.h>
#define SYSCLK_TIMER_OVERFLOW_MICROS (64L * ((256L * 1000000L) / F_CPU))
#define SYSCLK_MILLI_INCREMENT (SYSCLK_TIMER_OVERFLOW_MICROS / 1000L)
#define SYSCLK_MILLIFRACT_INCREMENT ((SYSCLK_TIMER_OVERFLOW_MICROS % 1000L) >> 3)
#define SYSCLK_MILLIFRACT_MAXIMUM (1000 >> 3)
#ifdef __cplusplus
extern "C" {
#endif
void systickInit();
void systickDisable();
unsigned long int millis();
unsigned long int micros();
void delay(unsigned long millisecs);
void delayMicros(unsigned int microDelay);
#ifdef __cplusplus
} /* extern "C" { */
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment