I'm not sure if this is happening when assigning a variable specifically but when debugging the assembly code, the compiler executes RJMP $+0000 where it hangs the program.
EDIT: I added included libraries if that's relevant
#define __DELAY_BACKWARD_COMPATIBLE__
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/delay.h>
#include <stdint.h>
void ReadTemp(uint8_t address){
ADCSRA = ADCSRA | 0b10000111; //enable ADC, CLK/128 conversion speed
ADMUX = ADMUX | 0b01000000; //Use internal 2.56V Vref and PA0 as input, right-hand justified
ADCSRA |= (1 << ADSC); //start conversion
while(!(ADCSRA & (1 << ADIF))) {} // wait until process is finished;
uint8_t low_value = ADC & 0x00FF;
// or low_value = ADCL;
uint8_t high_value = ADC & 0xFF00; //problem here
...
}
I don't know what any of this is doing, but I do see an error in the bitwise math.
uint8_t low_value = ADC & 0x00FF;
uint8_t high_value = ADC & 0xFF00; //problem here
low_value and high_value are both 8 bits (uint8_t). I am going to go out on a limb here and say ADC is 16 bit. For high_value, you are anding ADC with 0xFF00 then truncating the value to 8 bit. high_value will always be zero.
What should be done is:
uint8_t high_value = (ADC & 0xFF00) >> 8;
This will grab the left byte of ADC and shift it right by 8 bits then assign it to the high_value byte storage giving you the correct value.
How you are doing low_value is correct. As a matter of fact, you could simply do:
uint8_t low_value = ADC;
There is something somewhat suboptimal here:
uint8_t low_value = ADC & 0x00FF;
You are reading ADC, which is a 16-bit register, implemented as a pair
of 8-bit registers. As shown in the disassembly, this requires two in
instructions, one per byte. And then you are just throwing away one of
those bytes. You may think that the compiler is smart enough to avoid
reading the byte it is going to discard right away. Unfortunately, it
cannot do that, as I/O registers are declared as volatile. The
compiler is forced to access the register as many times as the source
code does.
If you just want the low byte, you should read only that byte:
uint8_t low_value = ADCL;
You then wrote:
uint8_t high_value = ADC & 0xFF00;
As explained in the previous answer, high_value will be zero. Yet the
compiler will have to read the two bytes again, because the I/O register
is volatile. If you want to read the high byte, read ADCH.
But why would you want to read those two bytes one by one? Is it to put
them back together into a 16-bit variable? If this is the case, then
there is no need to read them separately. Instead, just read the 16-bit
register in the most straight-forward fashion:
uint16_t value = ADC;
A long time ago, gcc didn't know how to handle 16-bit registers, and
people had to resort to reading the bytes one by one, and then gluing
them together. You may still find very old example code on the Internet
that does that. Today, there is absolutely no reason to continue
programming this way.
Then you wrote:
//problem here
Nope, the problem is not here. That is not what generated the rjmp
instruction. The problem is probably right after that line, in the code
you have chosen not to post. You have some bug that manifests itself
only when optimizations are turned on. This is typical of code that
produces undefined behavior: works as expected with optimizations off,
then does weird “unexplainable” things when you enable optimizations.
Related
I dont know why but instead of increasing brightness, LED pulses, the period between each pulse is getting shorter. This is copied code from tutorial, in his video it worked fine but for me it didnt, even in simulator. How can that happen?
Using avr 328p.
#define F_CPU 20000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
double dutyCycle = 0;
int main(void)
{
DDRD = (1 << PORTD6);
TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01);
TIMSK0 = (1 << TOIE0);
OCR0A = (dutyCycle/100.0)*255.0;
sei();
TCCR0B = (1 << CS00) | (1 << CS02);
while(1)
{
_delay_ms(100);
dutyCycle += 10;
if(dutyCycle > 100){
dutyCycle = 0;
}
}
}
ISR(TIMER0_OVF_vect){ OCR0A = (dutyCycle/100.0)*255;}
1) If some variable is used simultaneously in the main code and in an interrupt, then it has to be marked as volatile. Then every read or write to it will be compiled as reading/writing of the corresponding memory cell. Otherwise, the compiler can optimize, minimizing memory access. So, writing to the variable inside the main program will not be visible in the interrupt.
2) Why are you using double? Do not use floating point unless it strongly necessary. AVR has no hardware support for the floating-point arithmetic, so each floating-point operation will be represented as multiple operations. In your example, nothing stops from use integer variable which changes from 0 to 255. Even if you want to use 0-100 range variable, you can recalculate it using integer arithmetics.
3) Be aware updating variables which are more than 1 byte long. AVR is an 8-bit architecture. That means, updating of variables in memory more than 8-bit wide, requires a series of multiple operations. double which is 8 bytes long, requires too much of such operations. The interrupt may fire in any moment in the middle of that series, that means, the value of the variable, obtained inside the ISR will be updated only partially, causing unpredictable results. In the main code eclose in cli() - sei() any update of variables which are used inside the ISR and more than 1 byte wide.
3) Avoid hard calculations in the ISR. As a rule of thumb: any ISR should complete as soon as possible, all intense calculations should be placed outside the ISR.
4) In this example, you need no ISR at all! You can write OCR0A just inside the main code.
I'm a begginer in programming AVR microcontroler and I get a lot of headacke sometimes from reading the datasheets.
I'm trying to make a communication between my AVR and PC just to send some caracters and receive it on my computer.
There are two lines I don't understand from the whole program and that is:
void USART_init(void)
{
UBRRH = (uint8_t)(BAUD_PRESCALLER>>8); <---- this one!
UBRRL = (uint8_t)(BAUD_PRESCALLER); <--- and this one
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<UCSZ0)|(1<<UCSZ1)|(1<<URSEL);
}
Datasheet
Why do I have to shift BAUD_PRESCALLER with 8? If BAUD_PRESCALLER is a number and shifting that number with 8 doesn't mean the result will be zero?(Because we are shifting it too many times)
From the datasheet I understand that UBRRH contains the four most significant bits and the UBRRL contains the eight least signicant bits of the USART baut rate.(Note:UBBR is a 12-bit register)
So how actually we put all the required numbers in the UBBR register?
You have to shift it right 8 bits because the result of BAUD_PRESCALLER is larger than 8 bits. Shifting it right 8 bits gives you the most significant byte of a 16-bit value.
For example, if the value of BAUD_PRESCALAR is 0x123 - then 0x1 would be assigned to UBRRH and 0x23 would be assigned to UBRRL.
If the library was smart it could also perform sanity checking on the BAUD_PRESCALAR to make sure it fits in 16bits. If it can't, that means you cannot achieve the baud rate you want given the clock you are using. If you're UBRRx is truly 12bits, the sanity check would look something like this:
#if BAUD_PRESCALAR > 0xFFF
#error Invalid prescalar
#endif
I'm a beginner in this field. My goal is to change the output of 8 LEDs (which are connected to PORTA) according to the potentiometer. I have connected the middle line of the potentiometer to PF0, which is ADC0. I also connected the other two lines to the 5V and ground.
I know there's no problem with the chip or connection because the LEDs are working just fine.
But no matter how I change the code below (what I mean by changing is by slightly changing the ADMUX and ADCSRA registers) no output is shown!
I am using atmega128 with 16MHZ clock. Below is the code that I'm trying to solve.
#include <asf.h>
#include <avr/io.h>
#define F_CPU 16000000L
int init_board(void)
{
DDRA=0xff;
PORTA=0x01;
}
int ADC_init(void)
{
//ADCSRA
ADCSRA = 0b10000111;
//ADMUX
ADMUX = 0b01100000; // middle line connected to ADC0
}
int main (void)
{
init_board();
ADC_init();
ADCSRA |= (ADSC >> 1);
while(1)
{
if(ADSC == 0)
{
uint8_t disp_value = ADCL;
PORTA = disp_value;
delay_ms(200);
ADCSRA |= (ADSC >> 1);
}
}
}
I have no idea why the code doesn't work.
I suppose it's because it didn't set my register correctly, but I've followed all the instructions on the atmega128 datasheet.
First issue is your bit shifting, it should be ADCSRA |= (1 << ADSC).
Next issue is results reading. You set fifth bit of ADMUX to 1, so ADLAR=1 and in that mode result is left adjusted so you should read ADCH.
Moreover when you switch to 10-bit resolution, i.e. you start working with multi-byte results, be aware that reading only ADCL is not enough, see datasheet 23.3 for explanation: "Once ADCL is read, ADC access to data registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled."
Lastly, using hardcoded delays for reading is not good practice especially when you change code later to read ADC as fast as possible. In such case after conversion start you should check if ADIF flag is set or react with interrup when ADEN is set. Refer to datasheet for details.
I'm using an ATmega328. I am currently doing several measurements using the 10-bit ADC. I would like to store the values it converts in variables in order to be able to operate with them. For example:
int a;
(...)
ADMUX = 0b01000011; //Vref = 5V, ADC3
ADCSRA |= (1<<ADSC); //Starts conversion
while(!(ADCSRA & (1<<ADIF))); //Wait until it finishes
ADCSRA |= (1<<ADIF); //Clear flag
Suppose that the ADC stored the value 576 in ADCH:ADCL. Is it possible to achieve, somehow, the variable a to take that same value? (i.e. a=576;).
The full 16-bit result register should be accessible as such:
a = ADC;
But if you want to read both parts manually, then
a = ADCL;
a |= ADCH << 8;
That has to be done in two separate statements to force ADCH to be read last. The I/O modules have a temporary register to hold the high byte, preventing the module itself from corrupting the read value if it changes the value of the register. (i.e. if the ADC finishes another conversion and stores the new value.)
If you have interrupts that access the ADC (or need to use the value at a), you'll need to disable them for the duration of the access (that also goes for a = ADC, since it also compiles into multiple 8-bit reads).
The answer above is close, still comes up a little short based on the recommendations from the chip manufacturer. To be safe you should follow the instructions in the authoritative reference linked below.
Simply turn off interrupts just before and restore them after:
unsigned int a; // 16-bit word
// other code
cli();
a = ADCL;
a |= ADCH << 8;
sei();
Every example in this reference from the chip manufacturer follows this pattern for atomic 16-bit reads and writes. Reference: AVR Application Note 072.
I have some code which should read the values of a couple of ADC pins, each time around the commutator loop.
static uint16_t adc0;
static uint16_t adc1;
void init(void) {
...
hw_configure_adcs();
...
}
void loop(void) {
...
adc0 = hw_read_adc(0);
adc1 = hw_read_adc(1);
...
}
void hw_configure_adcs(void) {
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
}
uint16_t hw_read_adc(uint8_t n) {
ADMUX = (1<<REFS0) | (n & 0x07);
ADCSRA |= (1<<ADSC); // start conversion
uint16_t count;
for (count = 0; !(ADCSRA & (1<<ADIF)); count++); // wait for conversion to complete
// ADCSRA |= (1<<ADIF); // tried with and without this
return (ADCH << 8) | ADCL; // return ADC value
}
What I see is odd: The values of adc0 and adc1 are set to the same value and never change, until the AVR chip is restarted/reflashed.
(The value is 0x00d1 at 0.71V and 0x0128 at 1.00V, which seems reasonable.)
I have tried:
Turning the voltage down: adc0 and adc1 stay constant and only go down when the AVR code is reflashed (and hence the chip restarted).
Turning the voltage up: adc0 and adc1 stay constant and only go up when the AVR code is reflashed (and hence the chip restarted).
Returning count from hw_read_adc() instead of the ADC value: This returns varying numbers between 0x34 and 0x38 which are different for the two ADCs and continuously vary over time.
From these tests, I infer that the ADCs are being read, but that I am missing some "clear ADCH and ADCL and get them ready to accept the new reading" step.
I have re-read section 23 of http://www.atmel.com/images/Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet.pdf many times, but have obviously overlooked something vital.
After much googling, I found: http://www.avrfreaks.net/forum/adc-only-happens-once-reset
The problem was that return (ADCH << 8) | ADCL; was compiled so that it read the high register first (as you might expect).
Page 252 of the datasheet says: "Otherwise, ADCL must be read first, then ADCH".
Changing my code to return ADC fixed the problem.
My guess as to what was happening is:
The read from ADCH occurred.
The read from ADCL had the effect of locking the ADC-result, to prevent tearing.
The next ADC read had nowhere to write its result, as the ADC-result was locked.
Repeat...
The Problem with your code is you read ADCH first.
ADCL must be read first, then ADCH, to ensure that the content of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access to Data Registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled.
So the correct code should be-
return ADCL | (ADCH << 8) ;