Question

I am writing code for an Arduino Mega 2560 in C and I am trying to do bidirectional communication over the serial port. However, only sending data from the Arduino to the PC works - the other way doesn't. The RX LED on the arduino shortly lights up, but my code doesn't receive the data (the LED connected to "pin 13" doesn't light up). Does anyone know how to fix this?

Here's the code that's running on the arduino:

#define USART_BAUDRATE 500000
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

static void serial_init(void) {
  // load upper 8 bits of the baud rate into the high byte of the UBRR register
  UBRR0H = (BAUD_PRESCALE >> 8);
  // load lower 8 bits of the baud rate into the low byte of the UBRR register
  UBRR0L = BAUD_PRESCALE;

  // 8data,1stopbit
  UCSR0C = (0 << UMSEL00) | (1 << UCSZ00) | (1 << UCSZ01);
  // turn on the transmission and reception circuitry
  UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (0 << UCSZ02);
}

static void sendbyte(uint8_t b) {
  // do nothing until UDR is ready for more data to be written to it
  while ((UCSR0A & (1 << UDRE0)) == 0) {};
  // memory was cleared - write to it
  UDR0 = b;
}

static void digi_init() {
  // configure port B7 (arduino digital port 13) as output and set it low
  PORTB = (0<<PB7);
  DDRB = (1<<DDB7);
}

static void digi_set(int val) {
  PORTB = (val<<PB7);
}

int main(void) {
  serial_init();
  digi_init();

  while (1) {
    if ((UCSR0A & (1 << RXC0)) == 1) {
      // we got some data, light up the LED!
      digi_set(1);
    }

    _delay_ms(50);
    sendbyte('.');
  }
}

And here's the code that's running on the PC:

int main(int argc, char *argv[]) {
  char *serialdevicepath = "/dev/ttyACM0";

  fprintf(stderr, "Connecting to serial device ...\n");
  int serial_fd = open(serialdevicepath, O_RDWR | O_NOCTTY);
  struct termios config;
  if(tcgetattr(serial_fd, &config) < 0) exit(1);
  config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
  config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
  config.c_cflag &= ~(CSIZE | PARENB);
  config.c_cflag |= CS8;
  config.c_cc[VMIN]  = 1;
  config.c_cc[VTIME] = 0;
  if(cfsetispeed(&config, B500000) < 0 || cfsetospeed(&config, B500000) < 0) exit(1);
  if(tcsetattr(serial_fd, TCSAFLUSH, &config) < 0) exit(1);
  FILE *serial = fdopen(serial_fd, "r");

  setbuf(stdin, NULL);
  fcntl(0/*stdin*/, F_SETFL, O_NONBLOCK);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);
  setbuf(serial, NULL);

  while (1) {
    char c;
    while (read(0, &c, 1) == 1) {
      if (c != '+' && c != '-') continue;
      uint8_t val = (c == '+') ? 42 : 41;
      if (write(serial_fd, &val, 1) != 1) {
        assert(0);
      }
    }

    char b = fgetc(serial))&0xe0);
    fprintf(stderr, "read 0x%x\n", b);    
  }

  return 0;
}

(And yes, I am typing <+> on the PC so that it does send data. Also, I tried turning on the LED from the code directly and it worked.)

Was it helpful?

Solution

if ((UCSR0A & (1 << RXC0)) == 1) 

That test is wrong, it will never be 1. The & operator here will produce either 0 or (1 << RXC0). Favor it this way instead:

if ((UCSR0A & (1 << RXC0)) != 0) 

Or in C you'd typically write it this way since any non-zero value is logically true:

if (UCSR0A & (1 << RXC0)) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top