Поясните, что за магия происходит в этом  коде?
Ничего "магического" в программировании не бывает:
В данном случае, вы столкнулись с работой оптимизатора. Оптимизатор, видя что в переменной now особого смысла нет, не стал ее создавать, а банально передал в sprintf то, что получил от процедуры RX (а RX наверняка выдает int).
Если же вы раскомментируете TX, оптимизатор не сможет выкинуть переменную, выполнит преобразование в тип char и вы получите верный результат.
Решение: sprintf для перевода байта в шестнадцатиричную строку - выстрел из пушки по воробью:unsigned char toAlf(unsigned char c)
{
      if(c<10)
      {
          return(c+'0');
      }
      return(c-10+'A');
}
...
char[0]=toAlf(now>>4);
char[1]=toAlf(now&0x0F);
Сделайте так, и всё у вас будет работать:void read_mem(){
  int iter;
  unsigned char now;
  char char1[4];
  for (iter=0; iter<=7;iter++){
    now = RX();
    memory[iter] = now;
    sprintf (char1, "%02X", now);
    lcd_display(char1);
    ms_delay(300);
  }
А именно:
- замените char на unsigned char, чтобы избежать знакового расширения при преобразовании в int при передаче в sprintf;
- добавьте 0 в форматную строку, чтобы значения 0...9 отображались как 00 ... 09.
К тому же ваш char2 не закрыт нулём в конце, так что lcd_display вам может всякого навыводить.