0

I have to pass frequency and sampling channels to the atmega2560 via serial port, if I use echo from terminal to the serial it takes the arguments without problem, if I use dprintf the thing won't go on

I am using serial port USART0 in atmega2560

this is the code I run from pc

#include "serial_linux.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
  if (argc<6) {
    printf("usage: %s <filename> <baudrate> <textfile> <number of channels> <frequency>", argv[0]);
    return 0;
  }
  char* filename=argv[1];
  int baudrate=atoi(argv[2]);
  printf( "opening serial device [%s] ... ", filename);
  int fd=serial_open(filename);
  if (fd<=0) {
    printf ("Error\n");
    return 0;
  } else {
    printf ("Success\n");
  }
  printf( "setting baudrate [%d] ... ", baudrate);
  int attribs=serial_set_interface_attribs(fd, baudrate, 0);
  if (attribs) {
    printf("Error\n");
    return 0;
  }
  serial_set_blocking(fd, 1);
  //we open text file where we write conversions
  int fdoutput=open_file(argv[3]);
  //////////////////////////////////////////////WORK IN PROGRESS
  const int bsize=10;
  char buf[bsize];
  int n_read=read(fd, buf, bsize);
    for (int i=0; i<n_read; ++i) {
      printf("%c", buf[i]);
      dprintf(fdoutput,"%c", buf[i]);
    }

  dprintf(fd ,"%s\n", argv[4]);
  dprintf(fd ,"%s\n", argv[5]);
  printf("%s",argv[4]);
  printf("%s",argv[5]);
  
  //////////////////////////////////////////////
  while (1) {
    int n_read=read(fd, buf, bsize);
    for (int i=0; i<n_read; ++i) {
      printf("%c", buf[i]);
      dprintf(fdoutput,"%c", buf[i]);
    }
  }
}

this is what I run in atmega2560

#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "../avr_common/uart.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

uint8_t num_channels;//number of channels passed by client
uint8_t channels[8];
volatile uint8_t flag_config=0;
uint8_t i=0;
uint16_t ticks;

   
void setup_pwm(void) {
    //configure 8 bit timer in fast PWM with Timer0
    TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00); // enable Fast PWM mode 3, mark first
    TCCR0B = (1 << CS02) |(0 << CS01) | (1 << CS00); //set clock prescaler to 16MHz/128->9600 
    OCR0B = 100; //sets the desired pulse width (0-255)
    DDRG |= (1 << PG5); // Set pin 4 (PG5) as output
}


void setup_adc(void) {
  // enable adc, auto trigger, interrupt enable, start first rilevation, prescaler should be 2 or disabled? can't find out
  ADCSRA = (( 1<<ADEN ) | ( 1<<ADATE ) | ( 1<<ADIE ) | (1 << ADSC) );
  // Timer/Counter 1 Compare Match B 
  ADCSRB = (( 1<<ADTS2 ) | ( 1<<ADTS0 ));
  // ref=AVcc + adc chan   
  ADMUX = (1 << REFS0) | (1 << ADLAR); //set Voltage reference to Avcc (5v), left adjust converted value, if this is commented we use AREF, last tree are for selecting A7 as input as shown in the table

}

void timer1_init ( uint16_t ticks )
{
  
  TCCR1A  = 0;
  TCCR1B  = 0;
  TCNT1   = 0;
  TIMSK1  = 0;
  
  TCCR1B  = ( 1 << WGM12 ) ;  // Configure for CTC mode 4 OCR1A=TOP
  
  OCR1A   = ticks;            // Set CTC TOP value, if we set this to 32000 and prescaler to 1024 we have 16Mhz/1024=15600, 32000/15600=2 seconds interval between interrupts
  
  // start timer, give it a clock
  TCCR1B |= ( 1 << CS10 ); //(( 1 << CS10 ) | ( 1 << CS12 )) ;  // Fcpu/1024, 64us tick @ 16 MHz
  
}

void set_ADMUX(void){
    switch(channels[i]){
        case 0:
            ADMUX = (1 << REFS0) | (1 << ADLAR);
            break;
        case 1:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX0);
            break;
        case 2:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX1);
            break;
        case 3:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX0) | (1 << MUX1);
            break;
        case 4:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX2);
            break;
        case 5:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX2) | (1 << MUX0);
            break;
        case 6:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX2) | (1 << MUX1);
            break;
        case 7:
            ADMUX = (1 << REFS0) | (1 << ADLAR) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0);
            break;
      }
    }

// ADC complete interrupt service routine
ISR(ADC_vect) {
    // Wait for empty transmit buffer
    while ( !(UCSR0A & (_BV(UDRE0))) ){;}
    //Start transmission over usart0
    printf("%d", ADCH);
    while ( !(UCSR0A & (_BV(UDRE0))) ){;}
    UDR0 = ' ';
    i++;
    if(i>num_channels-1){i=0;
        //printf("\n");
        while ( !(UCSR0A & (_BV(UDRE0))) ){;}
        UDR0 = '\n';
        }
    
    set_ADMUX();
    TIFR1 = ( 1<<OCF1B ); // clear Compare Match B Flag
} 
/////////////////////////////////////////////////////////////////WORK IN PROGRESS
uint8_t ReceivedChar;
uint8_t buffer[255];
volatile int j=0;
// interrupt routine for receiving frequency and number of channels

ISR (USART0_RX_vect){   
    ReceivedChar=UDR0;
    if(ReceivedChar=='\n'){
        buffer[j]=ReceivedChar;
        j=0;
        //switch between cases in flag for configuration
        switch(flag_config){
            case 0:
                flag_config=1;
                break;
            case 2:
                flag_config=3;
                break;
                }
            }
    else{
        //write on buffer the char we receive
        buffer[j]=ReceivedChar;
        j++;
        }
}

//main function
int main(void){
  _delay_ms(1000);
  cli();
  //initialize printf
  printf_init();
  //initialize pwm
  setup_pwm();
  sei();
  printf("ready");
  while(1){
    if(flag_config==1){
            //set frequency
            ticks=16000000/atoi((const char *)buffer);
            //set flag for configuration to second stage
            flag_config=2;
    } 
    if(flag_config==3){
            cli();
            num_channels=atoi((const char *)buffer);
            //set the right amount of channels
            for(int l=0; l<num_channels;l++){
            channels[l]=l;
            }
            //initialize timer
            timer1_init (ticks);
            //initialize adc and start convertions
            setup_adc();
            //reset flag for configuration
            flag_config=0;
            sei();
    } 
  }  
}


If I run a terminal with the code on Linux side it will stop after in the WORK IN PROGRESS part and I have to open another terminal on which I run echo to send what should have been passed in that part, why do dprintf doesn't work?

EDIT: here's the functions opening and setting baudrate

#include "serial_linux.h"
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int serial_set_interface_attribs(int fd, int speed, int parity) {
  struct termios tty;
  memset (&tty, 0, sizeof tty);
  if (tcgetattr (fd, &tty) != 0) {
    printf ("error %d from tcgetattr", errno);
    return -1;
  }
  switch (speed){
  case 19200:
    speed=B19200;
    break;
  case 57600:
    speed=B57600;
    break;
  case 115200:
    speed=B115200;
    break;
  case 230400:
    speed=B230400;
    break;
  case 576000:
    speed=B576000;
    break;
  case 921600:
    speed=B921600;
    break;
  default:
    printf("cannot sed baudrate %d\n", speed);
    return -1;
  }
  cfsetospeed (&tty, speed);
  cfsetispeed (&tty, speed);
  cfmakeraw(&tty);
  // enable reading
  tty.c_cflag &= ~(PARENB | PARODD);               // shut off parity
  tty.c_cflag |= parity;
  tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;      // 8-bit chars

  if (tcsetattr (fd, TCSANOW, &tty) != 0) {
    printf ("error %d from tcsetattr", errno);
    return -1;
  }
  return 0;
}

void serial_set_blocking(int fd, int should_block) {
  struct termios tty;
  memset (&tty, 0, sizeof tty);
  if (tcgetattr (fd, &tty) != 0) {
      printf ("error %d from tggetattr", errno);
      return;
  }

  tty.c_cc[VMIN]  = should_block ? 1 : 0;
  tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

  if (tcsetattr (fd, TCSANOW, &tty) != 0)
    printf ("error %d setting term attributes", errno);
}

int serial_open(const char* name) {
  int fd = open (name, O_RDWR | O_NOCTTY | O_SYNC );
  if (fd < 0) {
    printf ("error %d opening serial, fd %d\n", errno, fd);
  }
  return fd;
}

int open_file(const char* filename){
    int res=open(filename, O_RDWR|O_CREAT, 0666);
    if(res==-1){printf("error while opening textfile");}
    return res;
    }

I now notice it sometimes work when I run it on terminal, but not always, when it stops it looks like it stops just before the printf that should print "setting baudrate [%d] ... " in the pc code, so it is probably not a problem of the dprintf that comes later

Also the atmega code works fine when dealing with teraterm or cutecom, so it shouldn't be causing the problem

EDIT2: I added an fflush after the baudrate printf, and can now tell you that I need to run the code two times each time I want it to work, the first time it prints some row of sampled channels and then stops, the second time it works fine.

beginner
  • 11
  • 4
  • 2
    Parsing, atoi etc in ISR and even printf (commented but present). It is not something which should be there. ISR has to be short. All parsing and app logic should be done in the main program (sometimes called main loop) – 0___________ Aug 14 '23 at 21:39
  • Busy loop wait in ISR isn't a good idea. UDRE can produce it's own interrupt. Why `cli()` in ISR? Interrupts are automatically disabled when ISR executed. Learn AVR programming from simple tasks. What are `serial_open()`, `serial_set_interface_attribs()` etc? They aren't glibc functions. – dimich Aug 14 '23 at 23:25
  • Since there's only one interrupt and your main loop isn't doing anything, it's probably fine to do a bunch of work in the ISR (though it's a weird design). Calling `sei()` in the ISR is not needed and potentially a big problem... I think it might allow other ISRs to happen while the first one is still executing, and it could make your stack overflow. But you call it right before returning from the ISR so maybe it works in practice. – David Grayson Aug 15 '23 at 00:55
  • 2
    So is `dprintf` never returning? That couldn't possibly be the AVR's fault, that is an issue with your PC code or the serial port device your computer uses (what serial port device are you using, by the way?) Your are calling a bunch of functions on the PC side but not showing the source code to them... maybe one of them enabled flow control by accident on your serial port, which is causing the write that `dprintf` does to take forever. – David Grayson Aug 15 '23 at 01:01
  • What bitrate are you using? From this, calculate how much time you have to process ISR of reception of one character. You cannot exceed this time, otherwise some parts of the message will be lost when sending the message at full speed. Of course, this does not happen when sending characters manually, because the time between each sent character is huge. It is done in such a way that the ISR only stores the received characters in a circular queue and the main program reads them from there. See how the Serial class in Arduino does it. – Peter Plesník Aug 15 '23 at 08:21
  • I rearranged the code so it is no longer inside the ISR, @DavidGrayson the functions not included should not be a problem but i'll edit the text including them. – beginner Aug 15 '23 at 17:21
  • @PeterPlesník do you think just using the ISR to set a flag and read UDR0 is a reasonable approach? like I did in the edit? – beginner Aug 15 '23 at 17:24
  • It's not much better approach than it was originally. A circular queue is not implemented. This means that the contents of the buffer can be overwritten before their analysis. Also, the **j** variable can overflow and cause data to be written outside the buffer if more data than the length of the buffer is sent. If you still have problems with the functionality, you need to set up and use a debug serial port (this MCU has a total of 4 serial ports). It is very problematic to find out the reason for the malfunction without debugging – Peter Plesník Aug 15 '23 at 21:54
  • Seems like there's mostly MCU folks giving advice on improving the ATMega code, and ignoring the salient facts. It doesn't help that you have described your situation poorly and neglect to post all of the relevant Linux code. "*this is the code I run from pc ...*" -- What little you have posted indicates you're using [one of these answers](https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c). However if you chose the higher-voted answer, then you've copied the less-reliable code. Posting all the `echo` commands would also be relevant. – sawdust Aug 15 '23 at 22:50
  • Thank you, I will re-elaborate my code with a circular queue and post all the code I'm using as soon as I can – beginner Aug 16 '23 at 07:38
  • meanwhile I added the code for setting the communication if you want to check it @DavidGrayson – beginner Aug 16 '23 at 15:41
  • You still haven't said what serial port device/adapter you are using, and that could be really important if it has buggy firmware. What is the name of the serial port you are connecting to? Can you run `stty -F ` followed by the name of your serial port in a shell and post the full output of that command so we can see if some weird options are enabled? – David Grayson Aug 16 '23 at 17:45
  • of course, that's what I get: speed 19200 baud; line = 0; eof = ^A; min = 1; time = 5; -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke – beginner Aug 16 '23 at 17:52
  • "*of course, that's what I get: ...*" -- No, that's incomplete. The shell command that you need to use is `stty -aF ...` to report ***all*** termios flags. Regardless, your termios configuration is incomplete, and leaves the **CSTOPB**, **CREAD**, **CLOCAL**, and **CRTSCTS** flags at whatever previous & unknown state. If you're going to copy someone else's code, then copy code that actually works; see https://stackoverflow.com/questions/12437593/how-to-read-a-binary-data-over-serial-terminal-in-c-program/12457195#12457195 And you still need to post all the `echo` commands. – sawdust Aug 17 '23 at 05:38

0 Answers0