ATmega C programming to Arduino lingo anyone?

Hey guys,

I am having difficulties understanding a code posted by the guys at nerdkits.com. They have a video tutorial about hacking a weight scale (see link: NerdKits - Digital Scale Strain Gauge Weight Sensor). And they give the code for the ATmega as well which I am having trouble understanding

Anyone willing to translate to Arduino lingo?
I think I understand where the part which creates the analog pins is, and there are other parts which I may understand, but the core is too much for me.
So maybe you can translate at least the main part where it does all the calculations (E.g. the switching of the polarity)
And can someone tell me if it says something else about the analog input besides creating it?

Thank you so much,
ygreq

// weighscale.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
// PC0 -- analog in
//
// PD4 - bridge excite
// PD3 - bridge excite

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  // read from ADC, waiting for conversion to finish
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  return result;
}

int main() {
  // set PD3, PD4 as outputs
  DDRD |= (1<<PD3) | (1<<PD4);

  // init ADC
  adc_init();

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;


  int16_t reading;
  while(1) {
    // set polarity +-
    PORTD |= (1<<PD3);
    PORTD &= ~(1<<PD4);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delay_us(66);
    // take reading
    //reading = adc_read();
    printf_P(PSTR("%d "), adc_read());


    // set polarity -+
    PORTD |= (1<<PD4);
    PORTD &= ~(1<<PD3);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delay_us(66);
    // take reading
    //reading = reading - adc_read();
    printf_P(PSTR("%d\r\n"), adc_read());

    // send over serial port
    //printf_P(PSTR("%d\r\n"), reading);
  } 

  return 0;
}

For the basic code change

int main() {

to

void setup () {

and add

void loop () {}

somewhere.

You probably don’t need

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

either. Not sure where the other include files go though.

There are also many function calls that need changing, I’ll have a closer look.


Rob

This

 uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

won't work and is not required, probably just use

Serial.begin (xxxx);

change

delay_us(66);

to

delayMicroseconds(66);
printf_P(PSTR("%d "), adc_read());

to

Serial.print (adc_read());
printf_P(PSTR("%d\r\n"), adc_read());

to

Serial.println (adc_read());

Rob

Here’s a version that compiles under the Arduino IDE

// PIN DEFINITIONS:
// PC0 -- analog in
//
// PD4 - bridge excite
// PD3 - bridge excite

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  // read from ADC, waiting for conversion to finish
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  return result;
}

int main() {
  // set PD3, PD4 as outputs
  DDRD |= (1<<PD3) | (1<<PD4);

  // init ADC
  adc_init();

  // init serial port
  Serial.begin(9600);


  int16_t reading;
  while(1) {
    // set polarity +-
    PORTD |= (1<<PD3);
    PORTD &= ~(1<<PD4);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds(66);
    // take reading
    //reading = adc_read();
    Serial.print (adc_read());


    // set polarity -+
    PORTD |= (1<<PD4);
    PORTD &= ~(1<<PD3);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds (66);
    // take reading
    //reading = reading - adc_read();
    Serial.println (adc_read());

    // send over serial port
    //printf_P(PSTR("%d\r\n"), reading);
  } 

  return 0;
}

whether it works or not I have no idea.

It’s also possible that a lot of that ADC stuff could be replaced with simple analogRead()s and no ADC setup.


Rob

The above code still uses main(), I believe that is OK under Arduino but just in case here is a more Arduino-ised version

// PIN DEFINITIONS:
// PC0 -- analog in
//
// PD4 - bridge excite
// PD3 - bridge excite

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  // read from ADC, waiting for conversion to finish
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  return result;
}

void loop () {}

void setup() {
  // set PD3, PD4 as outputs
  DDRD |= (1<<PD3) | (1<<PD4);

  // init ADC
  adc_init();

  // init serial port
  Serial.begin(9600);


  int16_t reading;
  while(1) {
    // set polarity +-
    PORTD |= (1<<PD3);
    PORTD &= ~(1<<PD4);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds(66);
    // take reading
    //reading = adc_read();
    Serial.print (adc_read());


    // set polarity -+
    PORTD |= (1<<PD4);
    PORTD &= ~(1<<PD3);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds (66);
    // take reading
    //reading = reading - adc_read();
    Serial.println (adc_read());

    // send over serial port
    //printf_P(PSTR("%d\r\n"), reading);
  } 

}

Rob

Hey, thank you Rob!

I checked your last sketch and it does not compile.

DDRD |= (1<<PD3) | (1<<PD4);

Error: PD3 was not declared in this scope.

I might try to declare it at the beginning, but I am afraid it should be reformulated. I checked what PD3 stands for and it seems it is a Digital Pin. In our case it is, let’s say, DigitalPin3. And it seems it’s doing a PWM with those 2 digital pins. But than I hit a wall, because I don’t understand this whole part:

int16_t reading;
  while(1) {
    // set polarity +-
    PORTD |= (1<<PD3);
    PORTD &= ~(1<<PD4);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds(66);
    // take reading
    //reading = adc_read();
    Serial.print (adc_read());


    // set polarity -+
    PORTD |= (1<<PD4);
    PORTD &= ~(1<<PD3);
    // wait 5 time constants (bw=12kHz, T=13.2us)
    delayMicroseconds (66);
    // take reading
    //reading = reading - adc_read();
    Serial.println (adc_read());

    // send over serial port
    //printf_P(PSTR("%d\r\n"), reading);
  } 

}

Sorry, I compiled that with the Mega2650 board selected, it does fail with a 328.

Off hand I can't remember the official definitions for these pins, so just add

#define PD3 8
#define PD4 16

I would normally say just use digitalWrite() but this is obviously time critical so I think staying with the direct port manipulation is the way to go.

It's not using PWM, just reversing the polarity of those two pins, presumably there's a bridge connected to them.


Rob

wow!

a bit weird that it compiles to Arduino Mega, and not Arduino Uno.

Thank you so much!

I have to go for a couple of days! I am packing up! I will give it a go when I return. :smiley:

OK, I just noticed an error though, I defined PD3 to be used as a direct bit mask so change

    PORTD |= (1<<PD4);
    PORTD &= ~(1<<PD3);

to

    PORTD |= PD4;
    PORTD &= ~PD3;

and the same in the other place they are used.


Rob