I'd throw that code away and start over from scratch; it's overly verbose and needlessly repetitious, which may account for the kinds of errors you are seeing.
A few notes:
• The contents of num_array_1 look identical to those of num_array_2. Ordinarily there is no need to have two copies of the same constant array, so get rid of one or the other.
• It doesn't make sense to use four different lengthy sections of code that do about the same trivial things, incrementing or decrementing a counter, displaying its value, and then doing some kind of switch check. Instead, use one section of code that handles all the cases. See example 1 below.
• In three of those four different sections of code just referred to, in effect you say
if (button_state == LOW) { start = !start; } and in one of them, you say
while (button_state == LOW) { start = !start; }. That statement loops forever if encountered when button_state is LOW.
• int counter_1 and int counter_2 declared in setup() are not the same variables as the ones of the same name declared in loop(). If you want them to be the same, declare them at global scope before setup(), and don't declare them within either of setup() or loop().
• As declared in loop(), counter_1 and _2 are automatic local variables, uninitialized and technically of undefined value. See discussion in an answer to “local variable initialized to zero in C” on stackoverflow.
• It doesn't make sense to use two different display-a-digit functions that do about the same thing. Instead, use one function to handle both cases. See example 2 below.
• You can save 126 bytes each of RAM and Flash by storing the ones and zeroes of num_array_1 or _2 packed into bytes. See example 3 below.
Example 1: First, declare int dir, which will be 1 when counting up, and -1 when counting down. To change direction, say dir = -dir;. To step the count, say:
count += dir;
if (dir<0) dir = 0;
if (dir>99) dir = 99;
showDigit(count/10, 40); // Hi digit ports are 40...46
showDigit(count%10, 22); // Lo digit ports are 22...28
After stepping the counter and showing its digits, you can do your check some kind of switch thing, once in one place instead of spread out in four different places. Note, you could (and should) create symbolic constants that stand for 40 and 22, via an enum like the following, and then use the symbols instead of hardcoded numbers in the showDigit() calls.
enum { hiDigitAt=40, loDigitAt=22};
...
showDigit(count/10, hiDigitAt);
showDigit(count%10, loDigitAt);
Example 2: Your Num_Write_1() and _2 functions are identical except that one says int pin= 22; and the other int pin= 40;. To combine them, you can add a parameter specifying the starting pin number:
void showDigit(byte value, byte basePin) {
for (byte j=0; j < 7; ++j) {
digitalWrite(basePin+j, num_array_1[value][j]);
}
Example 3: Instead of using 14 bytes of RAM and 14 bytes of Flash for each entry in your digit-segments-pattern arrays, you can pack the seven ones and zeroes that represent one digit into a single byte. For example, { 0,1,1,0,0,0,0 }, // 1 converts to 0b0110000, // 1. Then use shifts to unpack the bits, as below.
byte digitPats[10] =
{ 0b1111110, 0b0110000, 0b1101101, 0b1111001, 0b0110011,
0b1011011, 0b1011111, 0b1110000, 0b1111111, 0b1110011
};
...
void showDigit(byte value, byte basePin) {
byte pattern = digitPats[value], bitj;
for (byte j=0; j < 7; ++j) {
bitj = (pattern & 64)>>6;
digitalWrite(basePin+j, bitj);
pattern *= 2; // Shift the pattern one bit left
}