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
}
Related
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;
}
#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.
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.)
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.
I am fairly new to programming microcontrollers. I have some experience with the Arduino, but after I had nearly finished my project, I deceided to moved my current project to something cheaper and smaller. So I am now using the AVR ATmega32 with Atmel studio.
I'm trying to use the ATmega32 to communicate to a MAX7219 chip for multiplexing with an led matrix. however, I do have multiple different devices that I want to communicate with.
How can I communicate with the device without actually using the SPI pins provided on the microcontroller? I have made a test project, but there seems to be something wrong and I can't figure it out what the issue is. I think I managed to get it in test mode, because all LEDs are lit, but I can't get it to do anything after that. I can't even clear the display/shut it down. I have checked the wiring over again. Is my coding perhaps incorrect as far as pin configuration and assigning the pins? Are there any suggestions for this or a better way to write my code?
Here is the link to the MA7219 datasheet
//test
#include <avr/io.h>
int main(void)
{
DDRB = 0b00000111; // pin 1(data), 2(clock) and 3(latch) are outputs
PORTB = 0 << PINB0; // data pin 1 is low
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low
uint16_t data;
data = 0b0000110000000000; // data to shift out to the max7219
//read bit
uint16_t mask;
for (mask = 0b0000000000000001; mask>0; mask <<= 1)
{
//iterate through bit mask
if (data & mask)
{ // if bitwise AND resolves to true
// send one
PORTB = 1 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}
else{ //if bitwise and resolves to false
// send 0
// send one
PORTB = 0 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}
}
PORTB = 1 << PINB2; // latch all the data
PORTB = 1 << PINB0; // data pin 1 is high
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low
}
Yes, your bit-bang code has a problem in that you assign the entire value of the register each time without preserving the existing value. Hence you erase your data signal at the instant you drive your clock, violating the hold time of the receiver and resulting in unpredictable operation.
Instead of assigning pins with =, you should set them with |= or clear them with &= ~(value)
For example:
PORTB = 1 << PINB0; //drive data
// tick
PORTB |= 1 << PINB1; //clock high PRESERVING data
// tock
PORTB &= ~(1 << PINB1); //clock low
You may also need to insert a slight delay between pin operations.
Technically, given that you are already using an if for the data state, you could also re-drive the data signal with an OR in the assignment, for example
if (data & mask)
{ // if bitwise AND resolves to true
// send one
PORTB = 1 << PINB0;
// tick
PORTB = (1 << PINB1) | (1 << PINB0);
// tock
PORTB = 0 << PINB1 | (1 << PINB0);
}
else{ //if bitwise and resolves to false
// send 0
// send one
PORTB = 0 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}