Hi, I have a question regards the programming. To you info, I am using arduino Mega AT2560 to carry out the project.
In my program, I have set a time delay of 0.25ms (4k Hz) to toggle the PORTE output with the variable of SAB, SAB will be replace with A (0x00) and B (0xFF) alternatively. However, there is no output when I use a scope to measure.
During troubleshooting, I found out that when I insert Serial.begin(9600), the program works and gives the output waveform as expected and will not display the output waveform when I remove Serial.begin(9600).
Therefore, I am curious to know why Serial.begin(9600) must be included in the program because in arduino blinking example, Serial.begin(9600) is not included in the program.
Below are the coding that I have used in my project.
Thank you in advanced.
// This program is run on arduino Mega AT 2560.
int i = 1;
int SAB = 0x00;
int A = 0x00;
int B = 0xFF;
void setup()
{
Serial.begin(9600); // Scope will display output when this line is included
DDRE = DDRE | B11111100;
PORTE = SAB;
cli(); // Stop interrupts
// Reset control register to start from disable
TCCR3A = 0; // Reset entire TCCR3A to 0
TCCR3B = 0; // Reset entire TCCR3B to 0
// Set prescaler by changing CS10 CS11 and CS 12
TCCR3B |= B00001001; // NO prescalar, timer is set to 0 once compare value is reached
// Enable compare match mode on register A
TIMSK3 |= B00000010; // Set OCIE3A to 1 to enable compare match A
OCR3A = 4000; // Set compare register value (FOSC/(PRESCALE/FREQUENCY DESIRED))
sei(); // Enable interrupts
}
void loop()
{
if (i == 1)
{
SAB = A; // Hold value in variable A
}
else
{
SAB = B; // Hold value in variable B
}
}
ISR(TIMER3_COMPA_vect)
{
PORTE = SAB;
if(i == 1)
{
i = 0;
}
else
{
i = 1;
}
}
The program is used to practice timer interrupt using Timer 3 in Arduino Mega AT2560.
Why is it not volatile?
Why is that an int?
Why is that int (2 bytes) accessed non-atomic in the loop?
I declare int i = 1; to allow variable SAB to hold value A which declare in the void loop(), once the compare match interrupt condition is fulfill (reaches OCR3A = 4000), the interrupt sub routine, i.e. ISR(TIMER3_COMPA_vect), will take over to update the value of SAB to PORTE and update the value of i, and then the program should return to the void loop().
Thank you for sharing the post.
Regarding the questions that you asked, first, I would like to admit that I am still new to Arduino and I have not known that volatile should be used if the variable name is used in the interrupt routine and void loop.
The reason I use int as global variable because I apply some C programming syntax and I thought that int is can be used as the global variable in both interrupt routine and the void loop() because I did not know there is a variable called volatile.
May I know what do you meant by the third question?
Please stay away from interrupts and direct port access until you can answer all related questions. Until then you violate very many rules which you obviously are not aware of.
Read the whole article, there is all the explanation you are asking for.
Variables with more than one byte (size), have to be accessed with interrupts disabled,
so none of the bytes can be changed by the ISR while they are accessed,
or be accessed by the ISR while being only partially changed.
Ints have two bytes, so besides having to be volatile,
any access (usually a copy/store) has to happen with disabled interrupts.
Your i (btw, a very silly name for a global variable) only has two states,
so a bool, or at least a uint8_t would be sufficient.
That would allow you to access the i without disabling/enabling interrupts.
Why are you using DDRE/PORTE? Only four of the 8 bits show up on Arduino pins:
PE0 is Pin 0 (RX)
PE1 is Pin 1 (TX)
PE4 is Pin 2
PE5 is Pin 3
PE2, PE3, PE6, and PE7 don't go to any Arduino pins.
It seems likely that Serial (which uses Pins 0 and 1) is going to interfere with your use of PORTE.
That seems unnecessarily harsh.
How is one supposed to learn about volatility and atomicity if they "stay away from interrupts"?
Given a loop like:
int i = 0;
while (i == 0) {
// code that doesn't change i
}
// more code
The compiler will happily say "i is zero and never changes, that's an infinite loop and I can optimize by omitting all the "more code" part because it will never be reached." The "volatile" keyword on a variable declaration tells the compile that the value can change due to "non-visible effects", and needs to be actually checked every time it is referenced.
as for atomicity (which isn't actually relevant in your case, since you're only using 0 and 1 (thus the complaint about using "int" instead of "byte", "uint8_t", or "char"))...
If your ISR had i += 1; and happened to be incrementing from 0xFF to 0x100, the non-ISR code could theoretically read the upper byte as 0 (from before the increment) AND the lower byte as 0 (from after the increment) and incorrectly decide that the value was 0, even though it wasn't even close. Whether that actually happens, and whether it actually matters for particular code, is complicated to determine, so a brute force solution is to make sure that no interrupts occur in between reading the two bytes.
Doing things by traditional means will not reveal the Serial.begin() problem. Once some code works this way one can start to optimize it and find out which exact change causes which trouble.
Serial.begin() will probably set pin 1 (PE1) as an output pin. The OP has not said which pin he checked with the scope but this could be an explanation if he checked pin 1
You have to set the correct values for TCCR3A. Try to google it. The following will give a pulse.
TCCR3A = _BV(COM3A1) | _BV(WGM31) | _BV(WGM30);
Try the code:
// This program is run on arduino Mega AT 2560.
int i = 1;
int SAB = 0x00;
int A = 0x00;
int B = 0xFF;
void setup()
{
// Serial.begin(9600); // Scope will display output when this line is included
DDRE = DDRE | B11111100;
PORTE = SAB;
cli(); // Stop interrupts
// Reset control register to start from disable
TCCR3A = 0; // Reset entire TCCR3A to 0
TCCR3B = 0; // Reset entire TCCR3B to 0
TCCR3A = _BV(COM3A1) | _BV(WGM31) | _BV(WGM30);
// Set prescaler by changing CS10 CS11 and CS 12
TCCR3B |= B00001001; // NO prescalar, timer is set to 0 once compare value is reached
// Enable compare match mode on register A
TIMSK3 |= B00000010; // Set OCIE3A to 1 to enable compare match A
OCR3A = 4000; // Set compare register value (FOSC/(PRESCALE/FREQUENCY DESIRED))
sei(); // Enable interrupts
}
void loop()
{
if (i == 1)
{
SAB = A; // Hold value in variable A
}
else
{
SAB = B; // Hold value in variable B
}
}
ISR(TIMER3_COMPA_vect)
{
PORTE = SAB;
if(i == 1)
{
i = 0;
}
else
{
i = 1;
}
}