I am trying to read a analogue voltage using the ADC in attiny85. But the ADC register always read 1023 irrespective of what input is given.
Moreover when the ADC pin is measured with a multimeter it shows nearly 3.1V. I aasumed that it is pulled up but the fact is, when i connect the pin to its analog input , the voltage at the pin disturbs the input voltage circuit. I dont know why this happens. the same code worked well before 6 months but now it is not. reason unknown. could anyone explain me what I'm actually doing wrong? I am using USBasp as my programmer and attiny85 as my target microcontroller, arduino as my compiler. Also i tried compiling using WinAVR but still the analogue input pin is at a voltage near 3.1V.
thanks in advance:)
#define F_CPU 16000000UL
#define myTx PB1 //PB1
#define myRx PB0 //PB0
#define ADC_CH_2 PB4
#define ADC_CH_3 PB3
#include <SoftwareSerial.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
float ADCval;
int i = 0, p;
SoftwareSerial myPort(myRx, myTx); //rx,tx
ISR(ADC_vect) {
p = ADCW;
ADCval = (float)p * 5.00f / 1024.0f;
//logging the data
myPort.print(i++);
myPort.print(" ADC: ");
myPort.print(p);
myPort.print(" voltage: ");
myPort.println(ADCval);
}
int main(void) {
myPort.begin(9600);
MCUCR &= ~(1 << PUD); //disabling Pull Up Disable i.e, enabling pullups
//I/O configuration
DDRB &= ~(1 << ADC_CH_2) & ~(1 << ADC_CH_3); //configuring as input
PORTB |= (1 << ADC_CH_2) | (1 << ADC_CH_3); // writing 1 to an input pin activates pullup-resistor
DIDR0 |= (1 << ADC_CH_2) | (1 << ADC_CH_3); // disable digital buffer
myPort.print("DDRB: ");
myPort.println(DDRB);
myPort.print("PORTB: ");
myPort.println(PORTB);
//ADC configuration
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADIE); //enable conversion complete interrupt
ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2); // prescaler 128 - 16000000/128=125khz;
myPort.print("ADCSRA: ");
myPort.println(ADCSRA);
ADMUX &= ~(1 << ADLAR); // right most shift in ADCH and ADCL i.e, ADCH has two MSB bits and ADCL has 8 LSB bits
ADMUX |= (1 << REFS1) | (1 << REFS2); ADMUX &= ~(1 << REFS0); //Vref as 2.56V
ADMUX |= (1 << MUX1) | (1 << MUX0) ; ADMUX &= ~(1 << MUX2) & ~(1 << MUX3); //adc3
sei(); // enable all interrupts
myPort.print("ADMUX: ");
myPort.println(ADMUX);
while (1)
{
_delay_ms(1000);
ADCSRA |= 1 << ADSC;
myPort.print("DDRB: ");
myPort.println(DDRB);
myPort.print("ADMUX: ");
myPort.println(ADMUX);
myPort.print("ADCSRA: ");
myPort.println(ADCSRA);
myPort.print("PORTB: ");
myPort.println(PORTB);
}
return 0;
}
Update
the following image describes my output of different ADC channels for the same input voltage.
output of ADC channel 2
output of ADC channel 3
When the ADC configured with 2.56V as a reference voltage then all voltages at level 2.56 and above will be read as ADC's maximum value, i.e. 1023.
The same goes for 3.1 V.
Probably the issue is in the enabled internal pull-up:
PORTB |= (1 << ADC_CH_2) | (1 << ADC_CH_3); // writing 1 to an input pin activates pullup-resistor
Enabled pull-up will source additional current and change the voltage at the input. You should never use internal pull-ups with ADC since the pull-up's value is different from part to part in range 20k...50k, and it is hard to predict exact value.
You should disable it:
PORTB &= ~(1 << ADC_CH_2) & ~(1 << ADC_CH_3); // disable pull-ups
Use external pull-up of the known value, if needed.
Related
enter image description hereI have been fighting alot to get this thing work but unfortunately, it is not working as needed. The sensor is extremely sensitive under little light it detect high, it has to be pitch dark in order to read low signal. Please let me know where am i wrong. I have directly connected solar panel to PA1 of ATTINY44 IC.
NOTE: Currently, I am not using any resistor with Attiny44A.
solar panel V=5V; Current=30mA.
Battery voltage = 3.7~4.2V, 1200mAH.
Here's my schematic
I don't believe that there is any power issue because i tried executing same code with 5v Arduino supply. Even if tiny bit of light falls onto it, the led turns ON.
I dont want it to be extremely sensitive
#include <avr/io.h>
int led =0;
bool Stop=false;
//int solar = 3;
void setup() {
pinMode (led, OUTPUT);
//pinMode (solar, INPUT);
ADMUX =
(1 << REFS1) | // Sets ref. voltage to Vcc, bit 1
(0 << REFS0) | // Sets ref. voltage to Vcc, bit 0 //internal 1.1v voltage reference
(0 << MUX5) | // use ADC1 for input (PA1), MUX bit 5
(0 << MUX4) | // use ADC1 for input (PA1), MUX bit 4
(0 << MUX3) | // use ADC1 for input (PA1), MUX bit 3
(0 << MUX2) | // use ADC1 for input (PA1), MUX bit 2
(0 << MUX1) | // use ADC1 for input (PA1), MUX bit 1
(1 << MUX0); // use ADC1 for input (PA1), MUX bit 0
ADCSRA =
(1 << ADEN) | // Enable ADC
(1 << ADPS2) | // set prescaler to 16, bit 2
(0 << ADPS1) | // set prescaler to 16, bit 1
(0 << ADPS0); // set prescaler to 16, bit 0
ADCSRB =
(1 << ADLAR); // left shift result (for 8-bit values)
// (0 << ADLAR); // right shift result (for 10-bit values)
}
void loop() {
ADCSRA |= (1 << ADSC); // start ADC measurement
while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
//ADCH = (solar);
if (ADCH >= 128)
{
digitalWrite(led,HIGH);
//Stop=true;
}
else
{
// Stop=false;
digitalWrite (led, LOW);
// ADC input voltage is less than half of VCC
}
return 0;
}
This is my code for your reference.
EDIT NEW PICTURE
void setup() {
Serial.begin(9600);
Serial.println("Setup completed.");
}
void loop() {
// Read external battery VCC voltage
Serial.print("Bat: ");
uint16_t batVolts = getBatteryVolts();
Serial.print(batVolts);
Serial.print(" - ");
Serial.println(getBatteryVolts2());
delay(500);
}
// One way of getting the battery voltate without any double or float calculations
unsigned int getBatteryVolts() {
//http://www.gammon.com.au/adc
// Adjust this value to your boards specific internal BG voltage x1000
const long InternalReferenceVoltage = 1100L; // <-- change this for your ATMEga328P pin 21 AREF value
// REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc external reference
// MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Selects channel 14, bandgap voltage, to measure
ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);
// Let mux settle a little to get a more stable A/D conversion
delay(50);
// Start a conversion
ADCSRA |= _BV( ADSC );
// Wait for conversion to complete
while ( ( (ADCSRA & (1 << ADSC)) != 0 ) );
// Scale the value - calculates for straight line value
unsigned int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L;
return results;
}
// A different way using float to determine the VCC voltage
float getBatteryVolts2() {
// You MUST measure the voltage at pin 21 (AREF) using just a simple one line sketch consisting
// of: analogReference(INTERNAL);
// analogRead(A0);
// Then use the measured value here.
const float InternalReferenceVoltage = 1.1; // <- as measured (or 1v1 by default)
// turn ADC on
ADCSRA = bit (ADEN);
// Prescaler of 128
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2);
// MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) - Selects channel 14, bandgap voltage, to measure
ADMUX = bit (REFS0) ;
ADMUX |= B00000000; //I made it A0 //ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
// let it stabilize
delay (10);
// start a conversion
bitSet (ADCSRA, ADSC);
// Wait for the conversion to finish
while (bit_is_set(ADCSRA, ADSC))
{
;
}
// Float normally reduces precion but works OK here. Add 0.5 for rounding not truncating.
float results = InternalReferenceVoltage / float (ADC + 0.5) * 1024.0;
return results;
}
I tried executing this program but it did not work i believe there is some issue with my pin declaration or circuit. Please check. I want the code to read my voltage but it constantly reads wrong value and it is not even reading from A0
enter image description here
I just changed this part of the code
enter image description here
Unfortunately you did not follow my advice to study the linked information at https://github.com/RalphBacon/Arduino-Battery-Monitor. Especially those provided at http://www.gammon.com.au/adc
Instead you obviously messed with the first snipped you found without understanding what it does.
Otherwise I cannot explain why you would change
ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);
to
ADMUX = bit (REFS0) ;
ADMUX |= B00000000;
You don't want to read analog channel 0. You want to read the bandgap voltage (which is used as internal reference voltage).
There's the reference voltage, the ADC value and the measured voltage.
Usually you would use a known reference voltage and the ADC value to calculate the measured voltage.
V = ADC * Aref / 1023
But in this case you use the ADC voltage and the known measured voltage to calculate the reference voltage, which is the voltage of your battery connected to Aref.
Aref = V_bandgap * 1023 / ADC
But in order to do that you must set the ADMUX register to measure the internal voltage reference (1.1V) using an external reference voltage.
My goal is to output a PWM signal from my ATtiny84 with 1ms high at 50hz. The clock is operating at 1mhz, so I've set the compare output mode such that the output pin should be cleared for 19000 clock ticks and be set for 1000.
The device is powered at 5V and I have an oscilloscope reading a flat 5V output from pin A5 (OC1B), no modulation. My code is here:
#include <avr\io.h>
void init_pwm_generator()
{
PORTA &= ~(1 << (PORTA5));
ICR1 = 20000;
TCCR1A = (1<<COM1B1) | (1<COM1B0) | (1<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
}
int main(void)
{
DDRA |= (1 << (DDA5));
init_pwm_generator();
while(1)
{
OCR1B = ICR1 - 1000;
}
}
I can't figure out why this isn't working!
See the datasheet chapter 12.5 Input Capture Unit at page 91
The ICR1 Register can only be written when using a Waveform Generation mode that utilizesthe ICR1 Register for defining the counter’s TOP value. In these cases the Waveform Genera-tion mode (WGM13:0) bits must be set before the TOP value can be written to the ICR1Register.
So, correct initialization sequence will be as follows:
// Init the timer
TCCR1A = (1<<COM1B1) | (1<COM1B0) | (1<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12);
ICR1 = 19999; // set the top value
OCR1B = 19000; // set compare match value
TCCR1B |= (1<<CS10); // start the timer
Please note, for the timer period to be 20000 ticks you have to set TOP value to 19999 (since the timer counts from zero)
Where do I look to find code that signifies that it uses interrupts? I've gone through wiring.c in the subfolder of Arduino, but it only leads to the function.
The issue is that when I enable CTC mode for Timer/Comp0, the LCD prints out complete jibberish, but when I disable CTC mode, it works perfectly fine.
Here is the timer initialization code:
void timerCompare0_ini(void){ // -Initialization of the Timer Compare 0
TCCR0A = 0; // This regulates the menu navigation arrow to show where user is pointing
TCCR0B = 0;
TCNT0 = 0;
OCR0B = 256;
// TCCR0A |= (1 << COM0B0) | (1 << COM0B1);
TCCR0A |= (1 << WGM01); // -CTC mode
TCCR0B |= ((1 << CS02) | (1 << CS00)); // -1024 prescaler
TIMSK0 |= (1 << OCIE0B); // -Enable timer compare interrupt
}
wiring.c is good place. delay() uses micros() which depends on running timer0 and overflow interrupt. delayMicroseconds() does internal pause based on instructions. BTW But both method has bug because when watchdog is set shorter than requested delay it reboots CPU. I.e. you need implement own functions.
I am working on the serial communication of my MultiWii Pro board, which is based on a atmega2560. I am using avr-gcc to compile and avrdude to program.
Here is my problem. I am trying to get atmega2560 to send something (hex value) to the terminal. However, regardless of the value assigned to UDR2 and regardless the value I assigned to UBRR2L and UBRR2H, the terminal output is always 0xff if I set the terminal baud-rate at 9600, and 0xff if I set the terminal baud-rate at 115200.
Here is my code
#define F_CPU 8000000UL
#define BAUDRATE 19200 //The baudrate to use
#define BAUD_PRESCALLER ((F_CPU / (BAUDRATE * 16UL))-1)
static void InitializeUART()
{
UBRR2L = (uint8_t)(BAUD_PRESCALLER);
UBRR2H = (uint8_t)(BAUD_PRESCALLER>>8);
UCSR2B |= (1<<RXEN2) | (1<<TXEN2); //RX TX Enable
UCSR2C |= (1<<USBS2)|(1<<UCSZ21)|(1<<UCSZ20);
}
And my sending function
void USART2Write(char data)
{
while( !(UCSR2A & (1<<UDRE2)));
UCSR2B &= ~(1<<TXB82); //in the case if there are more than 8 bits of data
if(data & 0x100)
{
UCSR2B |= (1 << TXB82);
}
UDR2 = data;
}
In my case, the baudrate of my code is 19200. The terminal baudrate is also 19200. No matter what I assigned to UDR2, the output will always be 0x15.
Here is my fuse setting
Low High Extended
0xFF 0xD8 0xFD
UCSR2C |= (1<<USBS2)|(1<<UCSZ21)|(1<<UCSZ20);
USBS2 sets 2 stop bits. Is this intentional?
void USART2Write(char data){
while( !(UCSR2A & (1<<UDRE2)));
UCSR2B &= ~(1<<TXB82); //in the case if there are more than 8 bits of data
if(data & 0x100) {
UCSR2B |= (1 << TXB82);
}
UDR2 = data;
}
If you really want to use 9 data bits, UCSZ22, UCSZ21 and UCSZ20 have to be set. YOu only set UCSZ21 and UCSZ20
UCSR2C |= (1<<USBS2) | (1<<UCSZ21) | (1<<UCSZ20);
so I guess that USBS2 is indeed not what you want here. Maybe you were confused because the flag UCSZ22 is in the UCSR2B register.
So assuming you want 9 data bits and one stop bit use something like this:
static void InitializeUART() {
UBRR2L = (uint8_t)(BAUD_PRESCALLER);
UBRR2H = (uint8_t)(BAUD_PRESCALLER>>8);
UCSR2B |= (1 << RXEN2) | (1 << TXEN2) | (1 << UCSZ22);
UCSR2C |= (1 << UCSZ21) | (1 << UCSZ20);
}
Another thing: Your variable data is of type char and char is normally 8 bit wide. So the condition if(data & 0x100) is never satisfied.