2

I was trying to implement my own code to control a stepper motor on arduino uno, however I noticed that the pins were always HIGH despite what was written to them. I started to erase the code to leave only the crucial part to troubleshoot it and was left with the following

#define IN1  8
#define IN2  9
#define IN3  10
#define IN4  11
long int *period;    

void setup() {
pinMode(IN1, OUTPUT); 
pinMode(IN2, OUTPUT); 
pinMode(IN3, OUTPUT); 
pinMode(IN4, OUTPUT); 
*period=256;
}

void loop() {
 digitalWrite(IN1, LOW); 
 digitalWrite(IN2, LOW);
 digitalWrite(IN3, LOW);
 digitalWrite(IN4, HIGH);
}

The period statements were leftovers from the previous code, and didn't think of them too much but once I deleted them the code started working. More over, the code worked if the period value was below 256. Once it reached 256 the pins weren't pulling low.

The problem is solved now, however I am interested why would a value of a variable cause such a behavior?

  • 1
    You have created a pointer, but didn't tell the compiler where that pointer is pointing to. Then you write a value to where the pointer is pointing to. – Jot Nov 7 '18 at 14:38
  • until 255 overwrites 1 byte in random location in memory. 256 overwrites two bytes – Juraj Nov 7 '18 at 14:42
  • 1
    @Juraj always 4 bytes are written (or the number of bytes that contains a long int), even if values < 256 are written (than the other MSB bytes) will be filled with 0. – Michel Keijzers Nov 7 '18 at 15:07
  • @MichelKeijzers, perhaps the byte affected by 256 should be zero. with 255 it stays zero. with 256 it is 1. it simply happens that the sketch works if he uses 255 and doesn't work with 256. I explained the difference – Juraj Nov 7 '18 at 15:27
8

You are creating a pointer variable, not a normal variable.

That pointer variable is, until told otherwise, pointing at address 0x00. It covers 4 bytes.

Addresses 0x00 to 0x1F are the internal CPU registers R0 to R31. Your pointer variable points to R0 plus three more addresses above it (a long is 4 bytes in total).

So when you write to your pointer you are directly writing to CPU registers R0, R1, R2 and R3.

According to the AVR-GCC ABI:

  • R0 is the scratch register
  • R1 is the zero register
  • R2 & R3 are general purpose (call-saved) registers.

So if you write 255 to your pointer variable you are doing:

R0 = 255
R1 = 0
R2 = 0
R3 = 0

And that's no big deal: some things may go a little awry, but nothing major.

However, writing 256 results in:

R0 = 0
R1 = 1
R2 = 0
R3 = 0

The big thing here is that the zero register R1 is now 1, not zero. The ABI states:

R1 always contains zero. During an insn the content might be destroyed, e.g. by a MUL instruction that uses R0/R1 as implicit output register. If an insn destroys R1, the insn must restore R1 to zero afterwards. This register must be saved in ISR prologues and must then be set to zero because R1 might contain values other than zero. The ISR epilogue restores the value. In inline assembler you can use __zero_reg__ for the zero register.

So R1 must be zero except for a brief moment when you use it for something else. Since you're setting it to 1 and leaving it as one, any other operations that then rely on that register being zero will have a hard time knowing just what they are doing.

Having a "zero" register is a common thing. There are many times (especially in a RISC architecture) when you want the number zero - and having a register that is guaranteed to always be zero saves a lot of time. For instance, in MIPS the register $0 is hard-wired to be zero and can never change. AVR doesn't have that luxury, but the GCC compiler imposes the rule for R1 which you have to abide by - since there are huge amounts of library code and other auto-generated code that rely on that register being zero.

So when it's set to one, all gloves are off, and no one can ever really know just what is happening - you'd have to disassemble the code (or get an intermediate listing out of the compiler) to see just what is using R1, and work out what the effect of that being 1 instead of 0 would be.

As an example, digitalWrite has this:

        if (val == LOW) {
 370:   11 11     cpse r17, r1
 372:   05 c0     rjmp .+10          ; 0x37e <digitalWrite+0x50>

That's "COMPARE SKIP IF EQUAL". It's looking at the value you pass (val) to see if it's equal to zero - by using R1 as the zero value. But if R1 isn't zero then it will never know that what you passed as LOW is LOW - instead it would find HIGH as LOW, since HIGH is 1 and R1 is 1. So you've basically swapped HIGH and LOW over as far as digitalWrite is concerned.

There are also places where the number 255 is wanted. This is easy to generate with "eor r1,r1" - exclusive-or of the zero register. EOR turns bits off that are on and on that are off - so zero becomes 255. Except if zero is actually 1, you end up with 254 not 255, and more strange things happen.

1

You have an uninitialized pointer:

long int *period;    

It will point to zero (or a register map, see comment Edgar Bonnet below), but you write 256 to it. Since a long pointer points to 4 bytes, if you write values upto 255 only one byte will be filled (and others are 0), when you write a value from 256, another byte will be filled with a non-zero value.

Anyway, uninitialized pointers can result in strange effects, so initialize it like:

long int period;

long int* periodPointer = &period;

And set the content by

*periodPointer = 256;
  • 2
    Re “an undetermined address”: this is not the case here. Being a global, it is guaranteed to be initialized to zero by the C runtime. On an Uno, address zero is mapped to the register file, which can indeed produce weird results. – Edgar Bonet Nov 7 '18 at 15:08
  • @EdgarBonet Thanks (didn't know about the register file, I should have known about the global). Just I'm always used to initialize every variable myself. – Michel Keijzers Nov 7 '18 at 15:36
  • Thanks for the info and advice! But I just don't understand how whatever the variable contains or how the pointer is initialized would affect the pin state. Is the pin register somehow affected by the code? – Anthropomorphous Dodecahedron Nov 7 '18 at 15:44
  • That can be hard to find out ... however, what is most important, you hare changing 4 bytes 'somewhere' in the Arduino. This can (in theory) mean some execution code is changed, that the pin registers are changed, that some variables used either by the program or by the library are changed. To really find it out, you should check the value of the pointer, and (manually?) create a memory map of the Arduino (if at all possible). – Michel Keijzers Nov 7 '18 at 15:50
  • 1
    Addresses 0x00 to 0x1F are the internal CPU registers R0 to R31. By writing to 0x00-0x03 you are affecting R0, R1, R2 and R3. R1, according to the AVR-GCC ABI should always be 0. It can be used temporarily, but you must reset it to zero afterwards. Since 256 will set R1 to 1, R1 is no longer zero - so all future operations that assume it is zero will be incorrect. And chaos ensues. – Majenko Nov 7 '18 at 17:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.