NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » My first .h file usage

January 24, 2011
by Ralphxyz
Ralphxyz's Avatar

Geesch I have only been doing this for a year now you'd think I'd gotten beyond this.

So I have "tried" to use a .h file with my code. It actually worked the first time imagine that, first time success!!

My .c code page was getting to cluttered so I decided to try to use a .h file for fixed (non changing) code.

What I need is further instruction on how to clean up the .c file further.

Here is the .c file followed by the .h file.

/*
    * This is a simple program outputting a pulse
    * it uses the NerdKits tempsensor.c program for ADC detection
    * The next intineration I will use PWM to do this. 
*/

#define F_CPU 14745600
#define ICP PINB0

// PIN DEFINITIONS:
//
// PB4 pulse output
// PC0 ADC voltage detection from wiper of potentialometer

#define F_CPU 14745600

#include <stdio.h>
#include <math.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"

#include "PULSER.h"

int main()
{
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

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

  while(1) {
    // take 100 samples and average them!
    temp_avg = 0.0;
    for(i=0; i<100; i++) {
      last_sample = adc_read();
      this_temp = sampleToFahrenheit(last_sample);
      // add this contribution to the average
      temp_avg = temp_avg + this_temp/100.0;
    }

    /* PIN18 (PB4) ON (high) for simulation of revolutions //*/
     DDRB |= (1<<PB4);    //Data Direction Output  // LED as output
        {
         PORTB |= (1<<PB4);     // turn on LED
        delay_ms(50);           // ON for 50 milliseconds 
        PORTB &= ~(1<<PB4);     // turn off LED
        delay_ms(last_sample);  // OFF from pot setting (lower pot faster flash)    
        }

    // write message to LCD
    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024   "));

    // write message to serial port
    printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
  } 
  return 0;
}

The PULSER.h file

    // From tempsensor.c
    // for NerdKits with ATmega168
    // mrobbins@mit.edu

    // PIN DEFINITIONS:
    //
    // PC0 -- temperature sensor analog input
    // holder variables for temperature data
    uint16_t last_sample = 0;
    double this_temp;
    double temp_avg;
    uint8_t i;

    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() {
      // read from ADC, waiting for conversion to finish
      // (assumes someone else asked for a conversion.)
      // 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);

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

      return result;
    }

    double sampleToFahrenheit(uint16_t sample) {
      // conversion ratio in DEGREES/STEP:
      // (5000 mV / 1024 steps) * (1 degree / 10mV)
      //    ^^^^^^^^^^^      ^^^^^^^^^^
      //     from ADC         from LM34
      return sample * (5000.0 / 1024.0 / 10.0);  
    }

I do not know what happen to the line numbers usually don't they appear when one highlights a code section? Maybe it's because of doing two code selections, well no matter I think you can see that it is readable code.

So is this it? Like I said it works but it still looks kinda cluttered.

Thanks again as always for your help.

I have thick skin (I have been over on AVRfreaks and survived) so don't hedge your comments.

Oh and if anyone wants to know how to flash a led based on a pot (Potentiometer) here you go.

Put the pot wiper to PIN23 (PC0) and a LED on PIN18 (PB4). You will get a ADC: reading on the LCD as you do with the tempsensor project and a variable flashing LED depending on the position of the pot.

Ralph

January 26, 2011
by Hexorg
Hexorg's Avatar

Ralphxyz, generally, you only put stuff like function declaration and pre-processor directives.

Just have a look at what libnerdkits/delay.h and libnerdkits/delay.c has in common/different.

My suggestion would be to make main.c file, that'd have the main() function, and add "#include "pulser.h" in it. In pulser.h put only function definitions:

#ifndef __PULSER_H_
#define __PULSER_H_

void adc_init();
uint16_t adc_read();

double sampleToFahrenheit(uint16_t sample);
#end

and have pulser.c with code

// From tempsensor.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input
// holder variables for temperature data

#include "pulser.h"

uint16_t last_sample = 0;
double this_temp;
double temp_avg;
uint8_t i;

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() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // 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);

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

  return result;
}

double sampleToFahrenheit(uint16_t sample) {
  // conversion ratio in DEGREES/STEP:
  // (5000 mV / 1024 steps) * (1 degree / 10mV)
  //    ^^^^^^^^^^^      ^^^^^^^^^^
  //     from ADC         from LM34
  return sample * (5000.0 / 1024.0 / 10.0);  
}

then, when you compile, instead of running

avr-gcc main.c -o upload

run

avr-gcc main.c pulser.c -o upload
January 31, 2011
by Ralphxyz
Ralphxyz's Avatar

Hexorg, this brings me to a edge of a cliff. I am probable going to jump off and try it but thought I'd ask for some help first.

"then, when you compile, instead of running

avr-gcc main.c -o upload

run

avr-gcc main.c pulser.c -o upload"

I have never used the avr-gcc command I just do "make".

So what do I use as a Makefile? Especially for pulser.c?

I am completely lost here, but really appreciate your suggestion it almost makes sense (well it makes perfect sense I just have not fully comprehended it).

Ralph

Post a Reply

Please log in to post a reply.

Did you know that any circuit of voltage sources and resistors can be simplified to a "Thevenin" equivalent circuit? Learn more...