ATmega16 ADC Code not working - avr

#define F_CPU 1000000UL
#include <avr/io.h>
volatile uint8_t adcvalue;
int main(void)
{
DDRA =0x00;
DDRC = 0xff;
// enable adc
ADCSRA |= (1<<ADEN);
// using division factor 8
ADCSRA |= (1<<ADPS0) | (1<<ADPS1);
// enable 8 bit conversion
ADMUX |= (1<<ADLAR);
// take input from PA0
ADMUX |= (1<<MUX0);
while (1)
{
// Start conversion
ADCSRA |= (1<<ADSC);
// wait until conversion is done
while (ADCSRA & (1<<ADSC));
// save result to adcvalue
adcvalue = ADCH;
// show result on 8 leds connected to PORT C
PORTC = adcvalue;
}
return 0;
}
The above code shall take analog value from PA0 (using potentiometer) and show digital value on 8 leds connected to PORT C. When I simulate the circuit on Proteus the leds are always on even when I change the potentiometer value and there is a message saying "[AVR AD CONVERTER]Reference Value = 0".
Appreciate if you can help me know what is wrong.

// take input from PA0
ADMUX |= (1<<MUX0);
Thats wrong, with that line you take PA1/ADC1 as input, not PA0/ADC0
Check out the Datasheet at Page 218: http://www.atmel.com/images/doc2466.pdf
MUX 4..0
00000 is PA0 and
00001 is PA1
You set the value of MUX 4..0 to 00001 with
ADMUX |= (1<<MUX0);
and therefore using PA1 as Source.
Cheers

In order to remove the error message [AVR AD CONVERTER]Reference Value = 0
from proteus you should delete the following two lines in your source code:
// enable 8 bit conversion
ADMUX |= (1<<ADLAR);
// take input from PA0
ADMUX |= (1<<MUX0);
and add this line instead
ADMUX = 0b01100000;
Also, be sure to connect 5 VOLT Vcc at AVcc pin.

I don't have a datasheet here now, so what I will write is in general form.
on AVR some register shares some configurations.
In your case ADMUX shares the MUX and the 8bit convertion.
Your instruction:
ADMUX |= (1<<MUX0);
sets the bit in position MUX0 high and then it is written in or on the register.
This means that if MUX1, MUX2, MUXN was high before you will have them high after your command.
A safe initialization would be:
ADMUX &= ~(_BV(MUX0)|_BV(MUX1)|_BV(MUXN)
ADMUX |= (1<<MUX0);
The register ADMUX by memory is with all the bits setted to 0 as first ADC pin.
so in your case the first instruction:
ADMUX &= ~(_BV(MUX0)|_BV(MUX1)|_BV(MUXN)
is the only one needed.

Related

Unable to use ADC on atmega328

I am trying to make a proximity sensor using ATmega328P. I am using the onboard ADC to convert the voltage value and if it is higher than the ambient, an LED is lit.
The voltage that is being sensed is according to this circuit:
In the circuit, the VOUT is going to ADC channel 3 and should be sensed (think of the led on the right as the IR Sensor).
When the program starts, it senses 30 readings and takes their average to be used as the ambient setting. If any subsequent measurement is higher than this value, the LED should be lit.
But the LED does not light even if I place my hand above the sensor.
I have tested with just the LED to see if the IR sensor is ok. It is ok by the way.
The code for the microcontroller is as follows:
/*
* Proximity Sensor IR.c
*
* Created: 6/3/2017 2:35:33 PM
* Author : Rishav
*/
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 16000000UL
#include <util/delay.h>
int calibration()
{
unsigned int sum = 0;
for (int i=0; i<30; i++)
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
sum += (ADCH<<8)|ADCL;
}
return (sum/30);
}
int main(void)
{
unsigned int val = 0;
ADMUX |= (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(1<<MUX0); //setting the multiplexer to ADC3
ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
DDRB = 0b00000010;
DDRD |= (1<<PCINT22);
PORTD |= (1<<PCINT22);
int calib_value = calibration();
while (1)
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
val = (ADCH<<8)|ADCL;
ADCSRA |= (1<<ADIF);
if (val > calib_value)
PORTB = 0b00000010;
}
}
I think there is some problem in the code. Please help.
Some things that come to mind when looking at your code:
You are really not completely initializing the ADMUX and ADCSRA registers - everything you put in there is just 'ORed'-in. (ADLAR in ADMUX is not in a defined state, for example, ADCSRA has even more undefined bits).
After setting the reference voltage source in the ADMUX register, you are supposed to wait for the chip to switch, but don't. Most probably, your first measurement in calibration will be way off. The simplest way to address this is to do one first measurement whose result you simply ignore. (or wait some ms after you have set up ADC).
You are supposed to always read ADCL before ADCH (the AVR locks the ADC for writing further results to the result register when ADCL is read until ADCH is read as well). Your current code has an undefined read order of those 2 registers.
You have to enable the ADC first and select channel and reference voltage afterwards. It is easy to skip this fact in the datasheet.
The ADC is enabled by setting the ADC Enable bit, ADEN in ADCSRA.
Voltage reference and input channel selections will not go into effect
until ADEN is set. Datasheet page 238.
I did not check all of your settings but I am pretty sure that this must be your issue.
Example order:
void init_adc()
{
ADCSRA |= (1<<ADEN); // enable ADC
ADMUX |= (1<<MUX1) | (1<<MUX0); // channel selection ADC3 - PB3
ADMUX &= ~(1<<REFS0); // VCC as reference
ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // setting prescaler to 128
}
As mentioned a you should read ADCL first to:
ADCL must be read first, then ADCH, to ensure that the content of the
Data Registers belongs to the same conversion
I suggest to move this part into a separate function like:
uint16_t read_adc()
{
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
uint8_t adcl = ADCL;
uint8_t adch = ADCH;
ADCSRA |= (1<<ADIF);
return (adch<<8) | adcl;
}

PIC 16f1827 ADC Conversion too slow XC8

I followed this Tutorial and changed the code for my Micro-controller 16f1827. I also changed the function of the code. It should turn on a LED if the ADC Value is more than half of max. ADC Value and turn off a LED if less than half.
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#include <xc.h>
#include <pic16f1827.h>
#define _XTAL_FREQ 8000000
void ADC_Init()
{
ADCON0 = 0x81; //Turn ON ADC and Clock Selection
ADCON1 = 0x00; //All pins as Analog Input and setting Reference Voltages
}
unsigned int ADC_Read(unsigned char channel)
{
if(channel > 7) //Channel range is 0 ~ 7
return 0;
ADCON0 &= 0xC5; //Clearing channel selection bits
ADCON0 |= channel<<3; //Setting channel selection bits
__delay_ms(2); //Acquisition time to charge hold capacitor
GO_nDONE = 1; //Initializes A/D conversion
while(GO_nDONE); //Waiting for conversion to complete
return ((ADRESH<<8)+ADRESL); //Return result
}
void main()
{
unsigned int a;
TRISA = 0xFF; //Analog pins as Input
TRISB = 0x00; //Port B as Output
//TRISC = 0x00; //Port C as Output
ADC_Init(); //Initialize ADC
do
{
a = ADC_Read(0); //Read Analog Channel 0
//PORTB = a; //Write Lower bits to PORTB
//PORTC = a>>8; //Write Higher 2 bits to PORTC
if(a > 512){
PORTBbits.RB7 = 1;
}else{
PORTBbits.RB7 = 0;
}
__delay_ms(100); //Delay
}while(1); //Infinite Loop
}
The Code compiles without error in XC8. The problem is the PIC detects the ADC changes too slow. If I take the Input pin and connect it to the positive reference Value it turns on the LED with a Delay of maybe 2 Seconds. The same happens when I change the ADC Input to 0v. All changes are detected very slow. Why is the ADC working so slow?
The tutorial that you link to uses a PIC16F877A with an 8MHz crystal oscillator whereas you appear to be trying to use a PIC16F1827 with its internal oscillator instead of an external oscillator. It is not sufficient to just change the PIC header file as you have done. You must also set the required oscillator mode and take care of any other configuration options that differ between the two parts. I'm not certain, but I think that the default internal oscillator frequency for the 16F1827 is 1MHz, not 8MHz. This may go some way to explaining the problems that you are experiencing.
BTW: Don't be tempted to fudge your code. Make sure that you configure your microcontrollers correctly otherwise they will bite you on the bum sooner or later.

avr pd3 and pb3 work together as pwm

I'm trying the learn timers and pwm methods. I use the timer2 for fast pwm but i just want to use only pd3 for pwm. The code below works two pins together as pwm. How can i split these pins.
mcu is Atmega328p
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile unsigned char duty_cyc_a,duty_cyc_b;
int main(void)
{
DDRB = 0b00001000;
DDRD = 0b00001000;
PORTD = 0x00;
PORTB = 0x00;
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
TCNT2 = 0;
OCR2A = 117;
OCR2B = 117;
duty_cyc_a=0;
duty_cyc_b=255;
while (1)
{
PORTB = (1<<3);
_delay_ms(1000);
PORTB &= 0;
_delay_ms(1000);
}
}
You should look at the manual, specifically the pages at the end of the section on timer2 to see how setting the registers controls the waveform and pins. The pins are called OC2A and OC2B, which are also names for PB3 and PD3.
Another tip for readable code is to use the macros that describe the pin functions, rather than combine them into one difficult binary number. If the previous writer had done that, your job would be easier.
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
should be
TCCR2A = 1 << COM2A1 | 1 << COM2B1 | 1 << WGM21 | 1 << WGM20;
TCCR2B = 1 << CS20;
Now you can see which bits the previous author set to 1. (The rest are set to 0).
Go through the tables in the register section to decide which pins you need to set in these two control registers instead. (You won't use COM2A1, and you will need to choose different WGMx pins.)

ATMEGA328P external interrupt avr gcc won't happen

An external interrupt vector should occur when a 5v input is supplied to int0.
The interrupt should change a volatile integer flag to allow a LED to illuminate that is connected to a pin on PORTB. Compiles with no errors in Atmel studio. The problem is no change occurs when a 5v supply is sent to the int0 pin. Is this that the interrupt is not triggering?
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
volatile int pwm_flag=0;
int main(void)
{
DDRD &= ~(1 << DDD2); // Clear the PD2 pin
// PD2 (PCINT0 pin) is now an input
PORTD |= (1 << PORTD2); // turn On the Pull-up
// PD2 is now an input with pull-up enabled
EICRA |= (1 << ISC00)|(1 << ISC10); // set INT0 to trigger on Rising edge
EIMSK |= (1 << INT0); // Turns on INT0
sei(); // turn on interrupts
DDRB = 0xFF;
PORTB = 0x00;
while(1)
{
if(pwm_flag==1)//if flag is raised
{
PORTB = 0xFF;//turn on all pins of portb
pwm_flag=0;//reset flag to 0
}
}
}
ISR (INT0_vect)
{
/* interrupt code here */
pwm_flag =1;//raise flag
}
The problem is no change occurs when a 5v supply is sent to the int0 pin.
How do you send 5V to the INT0 pin? Your code is setting INT0 pin as an input with pullup so unless you short it to the ground it is at 5V.
Also, what do you mean by no change occurs? Is the LED on or off?
Another thing is that the line:
EICRA |= (1 << ISC00)|(1 << ISC10);
sets both INT0 and INT1 to occur on any logical change on appropriate pin. Bits with name ISC0x control INT0, bits called ISC1x are used to configure INT1. In your code you are mixing both and you end up with the following configuration:
ISC01 | ISC00 | meaning
-------------------------------------------------------------------------
0 | 1 | any logical change on INT0 generates an interrupt request
ISC11 | ISC10 | meaning
-------------------------------------------------------------------------
0 | 1 | any logical change on INT1 generates an interrupt request
If you are trying to supply 5v from a pin on PORTB to an LED then perhaps that is your problem. Most micro-controller pins can sink more current than they can give. Perhaps your LED is just not getting the current it needs?
Your wiring should be as follows:
Connect the positive leg of the LED to 5V. Connect the negative leg to a small resistor 100-500 Ohms. Connect the other leg of the resistor to the pin on PORTB.
Now you can write PORTB to 0x00 to turn on the LED or 0xFF to turn it off.
To test that the LED is working test the LED in your main loop by writing PORTB low and high with a noticeable delay in between.
If that works. Then test your ISR. If the ISR doesn't work a this point then the ISR is the problem.
Bear in mind that in your current configuration of EICRA you are triggering an interrupt on a rising edge. So if the pin is already high not interrupt will occur.
I have changed your code below so LOW is ON and HIGH is OFF.
int main(void){
DDRD &= ~(1 << DDD2); // set PD2 DDR as input
PORTD |= (1 << PORTD2); // set PD2 as input pull-up
EICRA |= (1 << ISC00)|(1 << ISC10); // set INT0 to trigger on rising edge
EIMSK |= (1 << INT0); // Turns on INT0
DDRB = 0xFF; // set PORTB as all outputs
PORTB = 0xFF; // set PORTB high
sei(); // turn on interrupts
while(1){
if(pwm_flag!=0){ // check flag
PORTB = 0x00; // set PORTB low
pwm_flag=0; // reset flag
}
}
ISR (INT0_vect){
pwm_flag = 1; // raise flag
}

ATtiny84 pwm led flicker

I'm fairly new to avr programming and I'm trying to simply fade 3 leds independently connected to ATtiny84 pwm pins. Right now I have code that should chnage brightness of two different leds. Here is my code:
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB |= (1 << PB2); // PWM output on PB2
TCCR0A = (1 << COM0A1) | (1 << WGM00); // phase correct PWM mode
OCR0A = 0x10; // initial PWM pulse width
TCCR0B = (1 << CS01); // clock source = CLK/8, start PWM
DDRA |= (1<<PA6); // make OC1A (DDA6) PWM output pin
TCCR1A = (1<<COM1A1) | (1<<COM1B0) | (1<<WGM00); // Clear OC1A/OC1B on Compare Match (bit 7 + 6)
// PWM, Phase Correct
TCCR1B = (1<<CS02); // 256 prescaler
while(1)
{
// change PWM pulse width every 2 seconds
_delay_ms(2000);
OCR0A = 0x10;
OCR1A = 0x10;
_delay_ms(2000);
OCR0A = 0x30;
OCR1A = 0x30;
_delay_ms(2000);
OCR0A = 0x50;
OCR1A = 0x50;
_delay_ms(2000);
OCR0A = 0xA0;
OCR1A = 0xA0;
}
}
The code is mostly copied from internet and I don't really understand the initializations, but now the led connected to PB2 works fine but the one connected to PA6 is not working right. PA6 led does change its brightness accordingly but it also flickers on and off rapidly (about 10 times a second).
First I thought it had something to do with PA6 being also MOSI pin for programming but disconnecting programmer didn't help.
Any help is appreciated! Also any tips for avr programming in general are more than welcome!
Just in case you haven't had a look yet, this is the relevant datasheet for your microcontroller: ATtiny84 Datasheet. Looking at the TCCR* timer configuration registers might reveal something.

Resources