Mkr zero freezes when I try to read a non-existent pin

yesterday I found a code that will lock up an arduino mkr zero.

void setup() {
  // put your setup code here, to run once:
analogRead(1<<3);
}

void loop() {
  // put your main code here, to run repeatedly:

}

I'm pretty sure that the Arduino tries to read a pin that doesn't exist and just freezes up because of it. you can put it into DFU mode to upload stuff to the Arduino, and after you upload a good program the problem goes away.

does anyone know why using bit shift in the analogRead makes the Arduino crash, but just trying to read pin 1000 doesn't?

This is a bitwise operation, and should give you 8 dec, 1000 bin
Have you tested with other numbers, or a valid analog pin number?

the valid pin numbers work as expected and trying to read the pins 100 to 262 do nothing, but when you try to read from pin 263 the Arduino freezes.

I did some research, and you need to have an "a" before the pin number to reference an analog pin.
(A0, A1, etc.)
So, using integers in an analog read is definitely going to cause errors.

why are you even allowed to try to read from integers in the first place? trying to analog read from "A7" causes an error but trying to analog read from "7" doesn't.

Ax are #defines; e.g. on an Uno, A0 is replaced by 0, A1 by 1 etc. So they are actually integers. If your board does not have A7, it will not have A7 defined and the compiler will throw an error.

But the compiler expects an integer (or byte, not sure) and any valid integer is acceptable for the compiler.

This is really curious. As @sterretje said, the 'A' pin numbers are just macros, and any number that is not valid should not cause the MCU to hang up.
You are seeing trouble with:
263 dec
0001 0000 0111 bin
and higher.

May need to dig a little more into the function analogRead

EDIT: it expects a uint8_t, so even if it accepts only the last 8 bits, that gives 7 dec (0111 bin).

I'm not sure if this will help, but I found every pin that causes the Arduino to hang.

00000111  7     //the analogRead function turns this value into 22
00001000  8     //the analogRead function turns this value into 23
00010110  22
00010111  23
00100101  37
00100110  38
00100111  39
00101001  41
00101010  42
00101011  43
00101100  44
00101101  45
00101111  47
00110100  52

I'm pretty sure that every other pin that causes the Arduino to hang is just truncated to one of these values.

@cncnotes The reason why the microcontroller crashes, is because the analogRead()'s pin number argument is used to index an array (called g_APinDescription in the board's variant.cpp file). This array defines the properties of each of the board's pins.

However, since the bounds of this array are not checked, (presumably because the SAMD21 core code has to deal with the various boards with varying pin counts), if the index is too large, the code ends up accessing an invalid locations in memory.

1 Like

Thanks for typing that up.
I looked at wiring-analog in Arduino and couldn't see anything obvious that should cause a hang up.
I believe the MKR zero uses a SAM D21.
Here is an interesting article on setting up the ADC. There are some while statements in there, and I wonder if one of them may be causing the problem.

ok, I did some digging into the source, and I found what is causing the hang, and how to fix it.

the hang happens in the function pinPeripheral in the file wiring_private.c at line 96 or 105 (it changes depending on an if statement) when the function tries to multiplex the pin number.

this can be easily fixed by adding the code below to the analogRead function at line 135 in the file wiring_analog.c

  if (g_APinDescription[pin].ulPinType != PIO_ANALOG){
    return  -1;
  }

this will check if the pin is an analog pin before trying to read/multiplex it.

EDIT: I made a github issue, you can find it here if you want

1 Like

Hi @awesome_tornado

The reason why the MKRZero crashes is because you're entering an incorrect pin number into the analogRead() function. The MKRZero doesn't have an analog pin 8, or 1 << 3.

In your code:

 if (g_APinDescription[pin].ulPinType != PIO_ANALOG){
    return  -1;
  }

What happens if the pin number index the exceeds the bounds of the g_APinDescription array, as it does with values like 37 to 52 that you tried above?

With all due respect, you're essentially solving a problem of your own creation.

Excellent work!
Ideally, the compiler should return an error if the pin number is out of bounds, as you note in your code. Certainly, would not want the chip to hang up.

1 Like

Hi,

I have a solution too, just don't use a non existent pin!!!
I have not had your problem, and I think it is because I do not try to use pins my controller does not have.. KISS...

Why would you do this?

analogRead(1<<3);

Tom..... :smiley: :+1: :coffee: :australia:

1 Like

@cncnotes On the contrary, it's not excellent. As @TomGeorge says, just don't use a non existent pin.

The compiler won't catch the out of bounds array, because it's a logical error rather than a syntactic one.

@awesome_tornado The analogRead() function by its nature is usually called by the majority of users in the loop() function using a fixed and valid pin number. This means any input checks would be unnecessarily called everytime the function is executed, which is a completely inefficient use of the microcontroller's time.

By all means add input and bounds checks to your own sketch, but please do not impose this unnecessary change on everyone else, by raising it as an issue on the Arduino SAMD21 core code on Github.

there is no out of bounds error that needs to be caught.

the Arduino uno lets you analogRead any pin number without hanging, this hang is not intentional.

a single if statement is nothing, there are other places in the source of the analogRead function that have much more impact on performance. (but this doesn't mean much, because if statements have very little impact.)

I didn't mean to try to use a non existent pin when I first found this problem, I made a typo. and I am sure that I am not the only one that makes typos.

if you dislike this so much, please just make your own code to analogRead. you can make it faster than the built in analogRead function if you want.

this change is not unnecessary, it is meant to prevent typos from making peoples Arduinos hang.

raising something as an issue does not mean that it will be accepted and "imposed" on everyone, and there is no reason for me to not post this as an issue.

now, is there any way to close a topic?

1 Like

Actually, this is an excellent piece of work in finding a bug and reporting it.

Have you tried to see what happens if you use an out of bounds pin number? Please report back after running your own test rather than trying to contradict my opinion with your opinion.

FYI, I have run a similar program on the Uno, and it does not hang up!

And in response to your comment about not using non-existent pins, I highly recommend that every one should code perfectly :grinning:

Hi @cncnotes

But is it actually a bug though?

In engineering there's often no right or wrong way, just decisions that can be based on acceptable cost/time/speed/accuracy/precision etc...

Personally, I would prefer the efficiency that comes with speed of execution over checking pin validity and bounds when calling analogRead() each time in the loop(), otherwise the microcontroller would be unnecessarily running the same checks over and over again. Programming a small microcontroller isn't the same as programming a PC.

Yes I have, and to be honest I don't have or hold an opinion on the matter. The fact is that compiler compiles the following code for the MKRZero without error:

void setup() {}

void loop() {
  int result = analogRead(52);  // Read from an invalid pin number
}

Even though 52 is an invalid pin number, the compiler didn't catch the logical error.

Since on the MKR Zero the g_APinDescription array (in board's variant.cpp file) only has 36 elements, this code will attempt to access an invalid index outside the bounds of the g_APinDescription array.

So are you saying that if you enter an invalid analog pin number into analogRead(), it functions correctly?

The solution in this case is simple, if you don't want the microcontroller to crash, don't enter invalid numbers :smiley:.

hi @MartinL

You are correct. But did you try to run that code?

You are right about this too. But have you tested to see if this causes a hang?

Have you actually looked at the source for analogRead? there are checks for validity already present in the function that let you use channel numbers, pin numbers, or built in macros to reference pins. if you are concerned about speed, you should not be using the built in analogRead function, you should be making your own.

Yes indeed. I did exactly that in setup, and then in loop I ran just a simple print statement counting digits. No problems at all with the Uno hanging up.
And I do agree with you about the compiler not being expected to do analysis of the logic of the program.

My point, of course is not that one shouldn't be careful about coding, but that this behaviour seems to be a bug in the Zero.

Hi @awesome_tornado

Yes, it hangs, but if the pin number provided to the analogRead() function is incorrect anyway, does it matter whether the board hangs or not?

If you use the correct analog pin number, the board won't hang, problem solved.

Yes, analogRead() in wiring_analog.c, pinPeripheral() in wiring_private.c and the g_APinDescription array in the MKRZero's variant.cpp.

Yes, but your proposed solution doesn't solve the array bounds issue. Neither does Arduino's validation check, but theirs is not ADC specific:

// Handle the case the pin isn't usable as PIO
if ( g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN )
{
  return -1 ;
}

Note that the ulPin index is still expected to be within bounds of the g_APinDescription array.

I know how to make my own thanks, I'm just concerning myself with analogRead() :smiley:.