/* | |
* GccApplication2.cpp | |
* | |
* Created: 7/15/2017 11:32:42 PM | |
* Author : Thomas Brannan | |
* | |
* This is starter kit project 3, the 'Love-O-Meter' (temperature sensor). | |
* ... Note that this version uses an EXTERNAL temperature sensor. | |
* | |
* References: | |
* AVRFreaks guide to the ADC for a faster analogRead() @ http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-adc?page=all | |
* AVRFreaks guide to Serial communications (USART) @ http://www.avrfreaks.net/forum/tut-soft-using-usart-serial-communications?page=all | |
* qeewiki USART guide @ https://sites.google.com/site/qeewiki/books/avr-guide/usart | |
* Jame's blog Sample Code for atmega328p Serial Communication @ http://jamesgregson.blogspot.com/2012/07/sample-code-for-atmega328p-serial.html | |
* Max Embedded guide to AVR USART @ http://maxembedded.com/2013/09/the-usart-of-the-avr/ | |
* [TUT] [C] Bit manipulation (AKA "Programming 101") @ http://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101?page=all | |
* Transmitting 32 bit float in 8 bit UART @ https://e2e.ti.com/support/dsp/tms320c6000_high_performance_dsps/f/115/t/12204 | |
* Unions by cpluscplus.com @ http://www.cplusplus.com/doc/tutorial/other_data_types/ | |
* Guide to USART, including redirecting STD I/O @ https://appelsiini.net/2011/simple-usart-with-avr-libc/ | |
* Using Standard IO streams in AVR GCC @ http://www.embedds.com/using-standard-io-streams-in-avr-gcc/ | |
* Newbie's Guide to AVR timers (see: ctc mode) @ http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-timers?name=PNphpBB2&file=viewtopic&t=50106 | |
* Using the EEPROM memory in AVR-GCC pdf: ~/desktop/m/arduino | |
* Secret Arduino Voltmeter @ https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/ | |
* ADC on atmega328 part 1 @ http://www.embedds.com/adc-on-atmega328-part-1/ | |
*/ | |
/* F_CPU..............Speed of ATmega328P is 8E6Hz | |
* FOSC...............Clock speed is 2*F_CPU | |
* USART_BAUDRATE.....9600 bits/s data transfer rate | |
* BAUD_PRESCALE......Equation for BAUD_PRESCALE from qeewiki | |
* With our 16E6 clock frequency, this equation sets a ~103 prescale, which results in an acceptable 0.2% error | |
*/ | |
#define F_CPU 8000000UL | |
#define FOSC 16000000UL | |
#define USART_BAUDRATE 9600 | |
#define BAUD_PRESCALE FOSC/16/USART_BAUDRATE - 1 | |
#define AUTO_TRIGGER_MODE | |
/* avr/io.h is standard | |
* stdio.h is used for printf() | |
* avr/eeprom.h is used to read/write from nonvolatile memory | |
*/ | |
#include <avr/io.h> | |
#include <stdio.h> | |
#include <avr/eeprom.h> | |
#include <util/delay.h> | |
// uart_putchar sends a single byte over USART. | |
/* @return int ... Error if not 0. | |
* @param char c ... The byte of data to write to USART. | |
* @param FILE* stream ... The direction of the data? | |
*/ | |
int uart_putchar(char c, FILE* stream) { | |
if (c == '\n') { | |
uart_putchar('\r', stream); | |
} | |
loop_until_bit_is_set(UCSR0A, UDRE0); | |
UDR0 = c; | |
return 0; | |
} | |
// uart_getchar writes a single byte over USART | |
/* @retrun UDR0 ... The I/O register, returns this because it is the data that has been recieved | |
* @param FILE* stream ... | |
*/ | |
int uart_getchar(FILE* stream) { | |
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */ | |
return UDR0; | |
} | |
// readVcc() is taken from the 'Secret Voltmeter' article | |
/* I (Thomas Brannan) have made the following four modifications to readVcc(): | |
* I have modifierd it to work with auto-trigger mode | |
* I have also tried modifying the function to reset the ADMUX MUX[3:1] bits | |
* I have made it return the number of volts instead of millivolts | |
* I have made it read ADCL and ADCH at the same time with result = ADC | |
*/ | |
long readVcc() { | |
// Save the state of the ADMUX register so we can reset it when we're done here | |
uint8_t tempADMUX = 0x0; | |
tempADMUX |= ADMUX; | |
// Read 1.1V reference against AVcc | |
// set the reference to Vcc and the measurement to the internal 1.1V reference | |
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) | |
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); | |
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) | |
ADMUX = _BV(MUX5) | _BV(MUX0); | |
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) | |
ADMUX = _BV(MUX3) | _BV(MUX2); | |
#else | |
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); | |
#endif | |
_delay_ms(2); // Wait for Vref to settle | |
#ifndef AUTO_TRIGGER_MODE | |
ADCSRA |= _BV(ADSC); // Start conversion | |
while (bit_is_set(ADCSRA,ADSC)); // measuring | |
#endif | |
long result = ADC; | |
ADMUX = tempADMUX; // reset the value of the ADMUX register. | |
result = 1125.3L / result; // Calculate Vcc (in V); 1125.3 = 1.1*1023 | |
return result; // Vcc in volts | |
} | |
// Main() | |
int main(void) { | |
// Set up analog input | |
/* Refer to section 24.9 of the datasheet for a discussion of registers! | |
* For the ADMUX register, see section 24.5 'Changing Channel or Reference Selection'... | |
* ... Setting REFS0 without REFS1 will make voltage reference be AVCC with external capacitor at AREF pin. | |
* For the ADCSRA register, see section ???... | |
* ... Setting the ADATE bit will put the ADC in auto triggering mode, so that we do not have to set a bit for every conversion. | |
* ... Setting the ADSC bit will start ADC conversions. | |
* ... Setting the DPS2, ADPS1 and ADPS0 bits of the ADCSRA register will set the prescaler to 128; ADCfrequency = F_CPU/prescaler = 8E6Hz/128 = 125kHz | |
* ... Setting the ADEN bit in the ADSCRA register activates the ADC. | |
*/ | |
ADMUX |= _BV(REFS0); | |
ADCSRA |= (_BV(ADATE) | _BV(ADSC) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) | _BV(ADEN)); | |
// Set up timer | |
/* For the TCCR1A regsiter... | |
* ... Setting WGM12 without WGM11 or WGM10 will put the counter in CTC mode. | |
* For the TCCR1B register... | |
* ... Setting CS12 without CS11 or CS10 will set the prescale to 256. | |
* For the OCR1A register... | |
* ... Setting this register to 31249 will mean that each second the timer will set a flag in TIFR1 bit OCF1A. ERROR: It updates too slowly! | |
*/ | |
TCCR1A |= _BV(WGM12); | |
OCR1A = 31249; | |
TCCR1B |= (_BV(CS12)); | |
// Set up Serial communication | |
/* The UBRRnH and UBRRnL registers must be set with the baud prescale. | |
* There are three rgisters that control USART settings: UCSRnA, UCSRnB, UCSRnC. | |
* For the UCSR0A register... | |
* ... Setting the TXEN0 bit enables the USART Transmitter. | |
* ... Setting the RXEN0 bit enables the USART Receiver. | |
* For the UCSR0B register... | |
* ... Setting the TXEN0 bit enables the USART Transmitter. | |
* ... Setting the RXEN0 bit enables the USART Receiver. | |
* For the UCSR0C register... | |
* ... Setting the UCSZ01 bit and the UCSZ00 bit will set the frame size to 9 bits in a frame the Receiver and Transmitter use. | |
*/ | |
UBRR0H = ((BAUD_PRESCALE) >> 8); | |
UBRR0L = BAUD_PRESCALE; | |
UCSR0B |= (_BV(TXEN0) | _BV(RXEN0)); | |
UCSR0C |= (_BV(UCSZ01) | _BV(UCSZ00)); | |
// Redirect STDOUT to USART | |
fdevopen(&uart_putchar, &uart_getchar); | |
// Configure GPI/O pins 2, 3, and 4 as OUTPUT. | |
DDRD |= (_BV(DDD4) | _BV(DDD3) | _BV(DDD2)); | |
// Declare some variables | |
uint16_t tempReading; | |
float volts; | |
float degreesCelcius; | |
char input; | |
long vcc; | |
// Main While Loop | |
/* Once per second... | |
* ... Calculate the temperature. | |
* ... Update Serial communication | |
* ... Control 3 LEDs | |
*/ | |
while (1) { | |
// TODO: Get input from the terminal. | |
// Check to see if one second has passed. | |
if (TIFR1 & _BV(OCF1A)) { | |
// Clear the OCF1A flag in TIFR by setting it (this is an unusual bx common to interrupt flags in the AVR) | |
TIFR1 = _BV(OCF1A); | |
// Measure Vcc | |
vcc = readVcc(); | |
// Calculate the temperature in celcius 0 <= tempReading <= 1023 --> 0 <= volts <= 5.0 --> -50 <= degreesCelcius <= 450 | |
tempReading = ADC; | |
volts = (tempReading * vcc) / 1023; | |
degreesCelcius = (volts - 0.5) * 100; | |
// Update Serial communication | |
printf("Vcc = %E\ttempReading = %E\tvolts = %E\tdegreesCelcius = %E\n", vcc, tempReading, volts, degreesCelcius); | |
// Turn LEDs ON/OFF to visually represent degreesCelcius. | |
if (degreesCelcius < 15) { | |
PORTD = 0x00; | |
} else if (degreesCelcius < 25) { | |
PORTD = (_BV(PORTD3) | _BV(PORTD2)); | |
} else { | |
PORTD = (_BV(PORTD4) | _BV(PORTD3) | _BV(PORTD2)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment