Can anybody figure out why my external interrupt 0 doesn't work? I am using an AVR STK 500 board with an ATmega164P on it. Is it because the pin D2 has got two functions?
#include <asf.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
ISR(INT0_vect)
{
PORTB = 1;
for(int i = 0; i < 7; i++)
{
_delay_ms(500);
PORTB << 1;
}
}
int main (void)
{
board_init();
sei();
PORTD = 0xFF;
DDRD = 0x00;
PORTB = 0x00;
DDRB = 0xFF;
while(1)
{
PORTB = PIND;
}
}
I guess you don't enable the external interrupt.
Look at the data sheet section 10.2.
10.2.2 EIMSK – External Interrupt Mask Register
When an INT2:0 bit is written to one and the I-bit in the Status Register (SREG) is set (one), the corresponding external pin interrupt is enabled. The Interrupt Sense Control bits in the External Interrupt Control Register,
EICRA, defines whether the external interrupt is activated on rising or falling edge or level sensed.
So you have to set
EIMSK |= (1 << INT0);
to enable INT0 and potentially EICRA to define what signal edge you want to react at.
Related
The below is a code to enable interrupt INT0_vect when PD2 is pressed. The code doesn't ever execute the ISR, but always executes the counter loop from 0 to 9 on the 7 segment in PORT C in the main Function. Also tried the sei(); instead of enabling the I-bit in the SREG. Any ideas?
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define ISR_INT0_PD2 INT0_vect
ISR( ISR_INT0_PD2 ){
PORTC = 0x00;
_delay_ms(100);
}
int main(void)
{
int i=0;
DDRC=0xff; //portc is o/p
PORTC=0x00; // all pins on portc is 0 volt
MCUCR |= (1<<1); // falling edge
GICR |=(1<<6); // enable INT0 set pin6
SREG |=(1<<7); // set GIE pin7
while(1)
{
for(i=0;i<10;i++)
{
PORTC=i;
_delay_ms(1000);
}
}
}
[Below is the screenshot from the simulator I've been using]
For the interrupt to execute you need to call sei() defined in <avr/interrupt.h>.
https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html#gaad5ebd34cb344c26ac87594f79b06b73
EDIT: I was mistaken when I removed the line SREG |= (1 << 7) according to my link that is equivalent to sei(); After I wrote the below example I realized the registers are named differently on the ATMega32, so unfortunately the code below won't run.
Based on the data sheet for an ATMega32 your code should work, have you tried removing the for loop and driving PORTC to logic high (e.g. PORTC = 255)? I noticed when I was writing the code for the ATMega168 that the LED I was using was very dim with the code in your while loop. Also check that INT0 pin is connected via pull-up/pull-down resistor.
This is the code that works on my ATMega168, if I swap the register names to the ones used on the ATMega32 I end up with your code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define ISR_INT0_PD2 INT0_vect
ISR( ISR_INT0_PD2 ){
// If there is a logic change on any pin hold the pins attached to PORTC low for 100ms.
PORTC = 0;
_delay_ms(100);
// Relase PORTC to logic high.
PORTC = 255;
}
int main(void)
{
DDRC = 255; // Set all pins on PORTC to be outputs.
PORTC= 255; // Set all pins on PORTC to be logic high.
EIMSK = 0b00000001; // Set external interupt request enable.
EICRA = 0b00000001; // Set the external interrupt control register A to so that
// any logical change on INT0 generates an interrupt request.
sei(); // Set global interupts enable.
while(1)
{
PORTC=255; // Blink the entire PORTC bank.
_delay_ms(20);
PORTC=0;
_delay_ms(20);
}
}
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 4 years ago.
I'm not really that familiar with C so please bear with me...
I'm writing some simple code for a watchdog timer on an Atmel Tiny 85, developing in AtmelStudio. I want to access the clock() function of the time.h library:
#include <time.h>
.....
void main() {
clock_t start = clock();
....
}
Unfortunately the compiler complains that clock is an undefined reference. Every code example I have looked up on the web seems to be doing what I am doing. Is there some fundamental thing I am missing? Thanks!
Here is the full code:
#define F_CPU 1000000UL // 1 MHz
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <time.h>
#include <stdio.h>
#include <avr/wdt.h>
#include <stdint.h>
#include <util/atomic.h>
#define LED_PORT PB1
#define RST0_PORT PINB3
#define RST1_PORT PINB4
#define HEARTBEAT PINB0
volatile int HEARTBEAT_RECEIVED;
#define CLOCKS_PER_SECOND 1000000;
const int TIMEOUT_PERIOD = 120; //reset raspberry pi after this many seconds
void set_output();
void set_input();
void set_interrupts();
void run_timer(clock_t start);
void restart_raspberry_pi();
int main(void)
{
clock_t start;
HEARTBEAT_RECEIVED = false;
//Configure IO pins
set_output();
set_input();
PORTB &= ~(1 << RST0_PORT); // Set pin low (common ground pin for pi reset)
PORTB &= ~(1 << RST1_PORT); // Set pin low (initialize for no pi reset)
//Configure and enable interrupts
set_interrupts();
while (1) {
sei();
start = clock();
run_timer(start);
}
return (0);
}
void run_timer(clock_t start) {
double time_elapsed;
do {
_delay_ms(1000);
if (HEARTBEAT_RECEIVED) { //If heartbeat detected, reset timer.
HEARTBEAT_RECEIVED = false;
return;
}
time_elapsed = ( clock() - start ) / CLOCKS_PER_SECOND;
} while (time_elapsed < TIMEOUT_PERIOD);
restart_raspberry_pi(); //Timeout period has elapsed, reset the pi
}
ISR(PCINT0_vect) {
//Indicate that a heartbeat has been received
HEARTBEAT_RECEIVED = true;
}
void restart_raspberry_pi() {
cli();
PORTB |= (1 << RST1_PORT); // Set pin high (sets RUN high to reset)
_delay_ms(500);
PORTB &= ~(1 << RST1_PORT); // Set pin low (release reset control)
}
void set_output()
{
//The DDxn bit in the DDRx Register selects the direction of this pin.
//If DDxn is written logic one, Pxn is configured as an output pin.
//If DDxn is written logic zero, Pxn is configured as an input pin.
//PORTB = (0<<PB0) | (1<<PB3);
DDRB = (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1); // Set pins as output.
}
void set_input()
{
DDRB |= (0<<DDB0); // set pin 0 as input. Added |=.
}
void set_interrupts()
{
GIMSK |= (1 << PCIE); // pin change interrupt enable
PCMSK |= (1 << PCINT0); // pin change interrupt enabled for PCINT0
}
#Shawn had it right....I dove into the time.h library code available to me and apparently:
Section 7.23.2.1 clock()
The type clock_t, the macro CLOCKS_PER_SEC, and the function clock() are not implemented. We
consider these items belong to operating system code, or to application code when no operating
system is present.
So I guess that's a lesson for me. Thanks for the help.
I am new to AVR programming, so sorry if question is trivial.
Using :
OS : Windows7
IDE : Atmel studio
uC = m328p
Pins:
ADC signal - ADC0/PC0
LED_values - (PB0 - PB7)
LED_START - PD1
LED_LIGHT - PD0
BUTTON - PD2
Goal: When you press the button it turns On the LED_START and it needs to start with conversion.
AVR gets interrupt and starts ADC conversion. Basically program has two interrupts. I know that INT0 interrupt has highest priority.
I dont know how to deal with them.
I have tried several things like adding global variable "start" and changing it. And also when i only set LED START it turns On and it stays in that state until LED_values reach certain value, then LED START turns Off by it self.
So please can you show me how to handle two interrupts so that fulfills stated goal and explain me what im doing wrong.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define F_CPU 1000000UL
#define BIT_IS_SET(byte, bit) (byte & (1 << bit))
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))
typedef enum{false, true} bool;
bool previousState = false;
bool start = false;
char num;
void setup();
void loop();
void ADC_init();
void EI_init(); // External Interrupt
int main(void)
{
setup();
loop();
}
void setup(){
DDRC &= ~(0x1); // LDR Input
DDRB = 0xFF; //LEDs value Output
DDRD |= 0x3; //LED light LED start Output
DDRD &= ~(1 << PIND2); //Button Input
}
void loop(){
PORTD |= (1 << PIND2);
EI_init();
ADC_init();
sei();
if(start){
ADCSRA |= (1 << ADSC);
}
while(1){}
}
void ADC_init(){
ADMUX = 0x60;
ADCSRA = 0x8B;
ADCSRB = 0x0;
ADCH = 0x0;
}
ISR(ADC_vect) {
PORTB = ADCH; // assign contents of ADC high register to Port D pins
int b = (int)ADCH;
if(b > 180) { //100
PORTD = 0x1;
}else{
PORTD &= ~(0x1);
}
_delay_ms(100);
ADCSRA |= (1 << ADSC); // start next ADC
}
void EI_init(){
EIMSK |= (1 << INT0); // Interrupt enabled
EICRA |= (1 << ISC00); // any state change
}
ISR(INT0_vect){
if(BIT_IS_CLEAR(PORTD,PIND2)){
start = true;
}else{
start = false;
}
}
Here is scheme : scheme
First of all, you should make start be volatile since it is being used by both the main loop and the interrupt. The volatile keyword tells the compiler that the variable might be modified by things outside of its control, so it cannot optimize away any reads or writes to the variable:
volatile bool start = false;
Secondly, you probably want to remove this line you wrote at the end of loop:
while(1){}
That line is bad because it causes your program to go into an infinite loop where it does nothing. I think you actually want the code you wrote about it in the loop function to run multiple times.
Secondly, after you detect that the start flag has been set, you probably need to set it to 0, or else it will just be 1 forever.
Third, setting start to false in the INT0 ISR might be a bad idea, because it might get set to false before you main loop has a chance to observe it being true and handle the event. I guess it really depends on exactly what you are trying to do. You could try adding details to your question about exactly what problem you are trying to solve using the AVR. See What is the XY problem?.
There are probably other issues with your code that need to be debugged. Can you think of any ways to make this simpler? Maybe you can reduce the number of interrupts you are using. To debug, you can try blinking some LEDs to figure out what parts of your program are executing.
I am trying to create a C program which receives a char via UART, "prints" the correspondent binary by turning on 8 leds in my breadboard and send the char back to the transmitter.
Here is the code I am using:
//CPU clock
#define F_CPU 1000000UL
//Baud
#define BAUD 9600
//Baud rate
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
#include <avr/io.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdint.h>
//Communication Parameters:
//8 bits of data
//1 bit stop
//No parity
void uart_init(void){
//Bit 7 - RXCIEn: RX complete interrupt enable
//Bit 6 - TXCIEn: TX complete interrupt enable
//Bit 5 - UDRIE: USART data register empty interrupt enable
//Bit 4 - RXENn: Receiver enable
//Bit 3 - TXENn: Transmitter enable
UCSR0B = 0b10011000;
//Bit 7 - RXCn: USART receive complete.
//Bit 6 - TXCn: USART transmit complete
//Bit 5 - UDREn: USART data register empty
UCSR0A = 0b00000000;
//Bit 11:0 – UBRR11:0: USART baud rate register
//Whereas H are the higher bits and L the lower bits
//It comes from the setbaud.h
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
//Bit 7:6 - UMSELn1:0: USART mode select
//00 Asynchronous USART
//01 Synchronous USART
//11 Master SPI
//Bit 5:3 - Reserved bits in MSPI mode
//Bit 2 - UDORDn: Data order
//Bit 1 - UCPHAn: Clock phase
//Bit 0 - UCPOLn: Clock polarity
UCSR0C = 0b10000110;
}
// function to send data
void uart_transmit (uint8_t data)
{
while (!( UCSR0A & (1<<UDRE0))); // wait while register is free
UDR0 = data; // load data in the register
}
int main (void)
{
//Starts UART
uart_init();
//All led GPIOs as output
DDRB = 0xFF;
DDRC = 0x01;
//Enabling interrupts
sei();
while(1)
{
;
}
return 0;
}
ISR(USART_RX_vect)
{
//Variable to hold the incoming char
uint8_t received_bit = UDR0;
PORTC ^= 0x01;
PORTB = 0x00;
PORTB = received_bit;
uart_transmit(received_bit);
}
When I flash it to the chip and start using it, I get a weird behaviour.
I am sending a "U" which is a nice binary 01010101 to compare with.
However I am getting weird answers back from my chip:
My questions regarding UART under an ATMEGA168a are the following:
When setting the F_CPU am I supposed to stay with the 1MHZ used by the ATMEGA168a or do I have to use the one of my transmitter (Intel i7)? Could it be the problem?
When does the UDR0 gets "updated"? Whenever I hit the enter to send the character to chip via Terminal?
What could be generating this issue?
In the function uart_init() you set bits 7:6 to 10 which is a reserved state according to the ATMega 168A manual. To get the desired asynchronous UART functionality, set them to 00:
UCSR0C = 0b00000110;
The other reason why your example was not working was the baudrate settings, as explained in my comment below.
You already included the <util/setbaud.h> header file, which contains macros to make UART setup easier. Look here for the documentation. These macros take the input provided by you in F_CPU and BAUDRATE and calculate the settings for the UART configuration registers (UBRRH_VALUE and UBRRL_VALUE).
You used it almost correctly, however to take advantage of the UART baudrate doubling feature of the ATmega, add the following code after setting the UBRR0H/L value:
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
This sets or clears the U2X0 bit dependent on the calculations of the setbaud macros.
Also, I believe you can remove the line
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)
because that's exactly what setbaud.h does.
I'm making some system that measure the environment light and turn off or on the light switch. To do this I have to use Atmega micro controller. The light measuring is done using LDR. LDR always output an Analog value and I have to convert it to digital value using AVR's ADC feature. I only have small knowledge in micro-controller programming. I write some code but I have no idea how to turn on relay switch using AVR.
this is my code
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
int main(void)
{
ADCSRA |= 1<<ADPS2;
ADMUX |= 1<<ADLAR;
ADCSRA |= 1<<ADIE;
ADCSRA |= 1<<ADEN;
sei();
ADCSRA |= 1<<ADSC;
while(1)
{
}
}
ISR(ADC_vect)
{
char adcres[4];
itoa (ADCH, adcres, 10);
PORTC=0x01; // Turn ON relay switch
ADCSRA |= 1<<ADSC;
}
I want to measure analog values using attached LDR and convert them in to digital values. Then after some per-define number relay should turn on and
I need somethins like this
lux = ldr_digital_value
if (lux > 5 )
{ PORTC=0x00; }
else
{ PORTC=0x01; }
How can I do that ?
assuming an ATmega8 (there are some differences between avrs)
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
volatile unsigned char lux=0; // the trick is the volatile.
int main(void)
{
ADCSRA = 1<<ADPS2; //slowest clock, ok
ADMUX = 1<<ADLAR; // you want 8 bit only? also channel 0 selected and external VREF.
// I suggest you to use Internal AVCC or internal 2.56V ref at first
ADCSRA |= 1<<ADIE; // with interrupts wow!
ADCSRA |= 1<<ADEN; // enable!
sei();
ADCSRA |= 1<<ADSC; // start first convertion
while(1)
{
if (lux > 5 ) //please choose an appropiate value.
{ PORTC=0x00; }
else
{ PORTC=0x01; }
}
}
ISR(ADC_vect)
{
lux =ADCH;
ADCSRA |= 1<<ADSC;
}