Wrong output without printing message

Hello,

I am trying to make a custom tv remote using an ATmeta328p microcontroller. It contains 28 buttons which are wired in a matrix configuration, resulting in 5 outputs (columns) and 6 inputs (rows). Before ordering a PCB, I want to make sure electrical the circuit which I came up with is going to work, so I have made a similar circuit with only 4 buttons (2 rows and 2 columns) on a piece of perf board to which I can test with an Arduino Uno.

Now I have written the code for it, but the program gives an incorrect correct output when do not I serial print a message at some line which I have used for debugging. If I do not print at a specific line, two buttons on the same row will sometimes not be detected correctly when I press them simultaneously. I have tried to put a delay on the line instead of serial print, but this does not help.

I have put my code below. The message which I need to print for the program to work correctly is on line 35 (Serial.print("\ntest");)

#include <stdint.h>
#define NR_OF_COLS 2
#define NR_OF_ROWS 2

int main() {
  init();
  Serial.begin(9600);
  for (uint8_t i = 0; i < NR_OF_COLS; i++) {
    //make pins 8 - 8+i low (cols)
    PORTB &= ~(1 << i);
    //make pins 8 - 8+i outputs (cols)
    DDRB |= (1 << i);
  }
  for (uint8_t i = 0; i < NR_OF_ROWS; i++) {
    //make pins A0 - A(0+i) = digital inputs (rows)
    DDRC &= ~(1 << i);
  }
  //make pin 2 input (interrupt)
  DDRD &= ~0x4; 

  while(1) {
    //if interrupt pin became low
    if (!(PIND & 4)) {
      uint8_t btnArray[NR_OF_COLS] = {0};
      uint8_t rows = 0;
      
      //the row pins need some time to change after the interrupt has been triggered
      delay(1);
      
      for (uint8_t i = 0; i < NR_OF_ROWS; i++) {
        //read rows, store inverted bits (rows are active low)
        rows |= (PINC & (1 << i)) ^ (1 << i);
      }
      
      Serial.print("\ntest");
      
      for (uint8_t i = 0; i < NR_OF_COLS; i++) {
        //set col pin i
        PORTB |= 1 << i; 
        //the row pins need some time to change based on thew new value of the col pins
        delay(1);        
        
        uint8_t newRows = 0;
        for (uint8_t j = 0; j < NR_OF_ROWS; j++) {
          //read rows again
          newRows |= (PINC & (1 << j)) ^ (1 << j);
        }
        
        for(uint8_t j = 0; j < NR_OF_ROWS; j++) {
          //check if row pins have changed based on thew new values of the col pins
          if (rows & (1 << j) && !(newRows & (1 << j))) {
            //store row values in col i
            btnArray[i] |= (1 << j);
          }
        }

        //clear col pin i
        PORTB &= ~(1 << i); 
      }

      //print debug information
      Serial.print("\nButtons:\n");
      for (int i = 0; i < NR_OF_ROWS; i++) {
        for (int j = 0; j < NR_OF_COLS; j++) {
          Serial.print((btnArray[j] & (1 << i)) != 0);
          Serial.print(' ');
        }
        Serial.print('\n');
      }
      delay(1000);
    }
  }
}

When I press for example the two buttons on row 0 simultaneously with the printing message in between, I see this on the serial monitor:

test
Buttons:
0 0
1 1

And when I remove the message, I get this on the serial monitor:

Buttons:
0 0
0 0

I am not sure if this problem arises because of the hardware or the software, so I have also put the schematic of the circuit below:

test schema

Can someone see what I'm doing wrong?

I would expect the compiler to produce big fat warnings because of the double usage of the same variable name ("i").

Your comments and the schematics tell that the buttons are LOW active but your code checks with every column pin set to HIGH.

Don't do such things even if it saves a code line. Set the correct pin at the start of the loop and reset it at the end.

To correctly detect multiple buttons pressed at the same time, you usually need a diode in series with each button.

Thanks for your feedback. I don't know why the compiler did not give me a warning because of the double i, but I resolved it. Also I have changed PORTB ^= 3 << i; which saves me some lines of code.

When a button is pressed, the corresponding row becomes low because all column pins are set to low (acting as a GND). If this happens, the row state is stored, and then every column pin will be set to high one by one. If then a specific column pin makes a specific row pin become high again, I know the row/column position of the button which was pressed.

This doesn't work if you push two buttons in the same row. If you pull only one column low at a time you know at least which columns have pushed buttons.

Imma guess because that woukd mean it should issue a crap-ton warnings…

The whole idea of having a local declaration is just so you can have multiple variables of the exact same name and not need to worry which will get used.

It is up to the programmer in this case to spot the problem.

a7

Sure, but usually not nested.

The gcc is actually realizing that and shows a warning if warnings are enabled.

What? When would you consider a local declaration not nested? If it was a bit further away from one declaring a variable of the same name? How much further?

The gcc is actually realizing that and shows a warning if warnings are enabled.

I have full warnings and verbosity enabled in IDE 1.8.7. No warnings. Please share the warnings you are getting and the full circumstances that give rise to them.

I don't doubt you, I just wonder at what point such a warning would no longer be issued, unless it reads something like

Warning: you are exploiting language features exactly as they are intended to be, but maybe you've done something a bit bone-headed here.

5 minutes later:

OK, so gcc needs -Wshadow (-Wall and -Wextra are not enough), and writes

main.c:12:18: warning: declaration of ‘i’ shadows a previous local [-Wshadow]
12 | for (int i = 3; i >= 0; i--) {
| ^

So you are right. And "full warnings" in the IDE doesn't deign to include -Wshadow.

a7

When it's not nested. Example (doesn't make sense actually):

uint16_t my_super_routine(uint8_t test) {
  uint16_t routine_variable = 0;
  if (test > 100) {
    for (uint8_t i = 0; i < 32; i++) {
      Serial.print("i = ");
      Serial.println(i);
    }
  } else {
    for (uint8_t i = 32; i < 64; i++) {
      Serial.print("i = ");
      Serial.println(i);
    }
  }
}

@pylon: Interesting. These are still flagged by -Wshadow as possibly conflicting local variables. I would have been left thinking "shadowing" had to, um, throw some shade. THX.

And you forgot a return value in your example :wink:

I think the lengths one has to go to to get this specific warning is support for my (erroneous ) guess the routinely issuing it would produce quite a bit of warning output to sort through, and someone thought better of doing that even with -Wall and -Wextra.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.