C语言 是否有更合理的方法从序列中阅读重复值?

9jyewag0  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(83)

我写了一个程序,在两种模式下从atmega 2560读取采样值,一种是单采样模式,为用户选择的每个通道打印一个采样,另一种是批量模式,为每个选择的通道填充一个采样缓冲区,当所有这些缓冲区都满了时,将其打印在txt上。
我使用一个头0x 55 aa来宣布数据即将到来,如果我收到它,我会用我需要的信息填充结构体:序列号、通道、样本数。然后,我使用for循环来填充我感兴趣的缓冲区(它在continous模式下只执行一个循环,因为struct中的样本数为1)

int n_read = fscanf(f, "%d", & key); //we read 4 bytes and see if it equals the key
    
    if (key == 0x55aa) {
      n_read = fscanf(f, "%hhu", & structure.seq); //if it does we fill the struct
      n_read = fscanf(f, "%hhu", & structure.channel);
      n_read = fscanf(f, "%hhu", & structure.num_samples);
      num_samples_buffer[structure.channel - 1] = structure.num_samples;
      
      for (int k = 0; k < structure.num_samples; k++) { //we fill the received channel's buffer with the data in bulk (if mode is single it's executed once)
        switch (structure.channel) {
        case 1:
          curr_buff = buffer1;
          break;
        case 2:
          curr_buff = buffer2;
          break;
        case 3:
          curr_buff = buffer3;
          break;
        case 4:
          curr_buff = buffer4;
          break;
        case 5:
          curr_buff = buffer5;
          break;
        case 6:
          curr_buff = buffer6;
          break;
        case 7:
          curr_buff = buffer7;
          break;
        case 8:
          curr_buff = buffer8;
          break;
        }
        n_read = fscanf(f, "%hhu", & curr_buff[k]);

如果模式是连续的,我调用函数write_single,它将for循环后读取的值放入大小为8的临时缓冲区,每个通道一个位置;我用它为用户选择的每个通道保存一个样本,当我们感兴趣的每个通道都有样本时,我在输出文件中打印该行。

void write_single(FILE * ftxt, uint8_t sample, uint8_t channel) { 

  temp_buffer[structure.channel - 1] = sample;
  counter++;
  if (counter == num_channels) {
    for (int k = 0; k < 8; k++) {
      if (channel_buffer[k] != 0) {
        printf("-%d-\n",k);
        res = fprintf(ftxt, "%.1f", ((((float)((unsigned char) temp_buffer[k])) / 255) * 5));
        if (res < 0) {
          printf("error");
        }
        res = fprintf(ftxt, " ");
        if (res < 0) {
          printf("error");
        }
      }
    }
    res = fprintf(ftxt, "\n");
    if (res < 0) {
      printf("error");
    }
    counter = 0;
  }
}

如果我们在批量模式下,并且我们收到了用户指定的所有通道的初始结构所指示的所有样本,我们调用函数write_bulk。它用于在用户选择的每个缓冲区上循环,并用于每个缓冲区的BUFFER_SIZE元素。

void write_bulk(void) { 
  for (int j = 0; j < BUFFER_SIZE; j++) {
    for (int k = 0; k < 8; k++) {
      if (channel_buffer[k] != 0) {
        if (j >= num_samples_buffer[k]) {
          break;
        }
        switch (k) {
        case 0:
          curr_buff_bulk = buffer1;
          break;
        case 1:
          curr_buff_bulk = buffer2;
          break;
        case 2:
          curr_buff_bulk = buffer3;
          break;
        case 3:
          curr_buff_bulk = buffer4;
          break;
        case 4:
          curr_buff_bulk = buffer5;
          break;
        case 5:
          curr_buff_bulk = buffer6;
          break;
        case 6:
          curr_buff_bulk = buffer7;
          break;
        case 7:
          curr_buff_bulk = buffer8;
          break;
        }
        res = fprintf(ftxt, "%.1f", ((((float)((unsigned char) curr_buff_bulk[j])) / 255) * 5));
        res = fprintf(ftxt, " ");
      }
    }
    res = fprintf(ftxt, "\n");
  }
}

发送的值是头的int(在代码中称为key),其他每个数据的uint8_t。
我将串行设置为阻塞模式:

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);
}

现在,它工作了一个短暂的时期,然后进入分段故障,我不能管理使用gdb(我是一个虚拟机,可能是原因?)因为它不打开串行如果我运行的代码与它,但我打印了一些值,并使用valgrind和问题看起来像有时它是非常容易出错,它需要的是一个值时,它应该读取通道,反之亦然,或者它读取通道时,它应该读取序列号,我失去了一些序列号..等我认为串行在阻塞模式将使它很容易,我读的关键,之后我读的价值观一个接一个,价值观应该是在秩序的权利?scanf和fprintf怎么样,可以使用它们吗?
编辑:我添加了缓冲区和结构的定义,即使它们将按照@克雷格埃斯蒂的建议进行修改

typedef struct bulk {
  uint8_t seq;
  uint8_t channel;
  uint8_t num_samples;
}
bulk;
bulk structure;
uint8_t channel_buffer[8] = {0}; //used to keep track of what channel the user is interested in
uint8_t num_samples_buffer[8] = {0}; //used to limit the accesses to buffer on the number of samples we actually received
uint8_t num_channels = 0;

uint8_t buffer1[BUFFER_SIZE];
uint8_t buffer2[BUFFER_SIZE];
uint8_t buffer3[BUFFER_SIZE];
uint8_t buffer4[BUFFER_SIZE];
uint8_t buffer5[BUFFER_SIZE];
uint8_t buffer6[BUFFER_SIZE];
uint8_t buffer7[BUFFER_SIZE];
uint8_t buffer8[BUFFER_SIZE];

uint8_t temp_buffer[8] = {0}; //buffer to write the rows in continous mode

我从atmega 2560通过串行端口发送数据使用:http://mysinski.wieik.pk.edu.pl/MPUandMCU/Using%20printf%20with%20an%20AVR.pdf
这样使用它:

printf("%d\n", key); //32 bit
printf("%hhu\n", structure.seq); //8 bit
printf("%hhu\n", structure.channel); //8 bit
printf("%hhu\n", structure.num_samples); //8 bit
printf("%hhu\n", ADCH); //this is the sampled value, 8 bit

然后在我的客户端中,我在while(1)循环中读取键和结构体值:

while (1) {
    int n_read = fscanf(f, "%d", & key); //we read 4 bytes and see if it equals the key
    if (n_read < 0) {
      perror("error");
    }
    if (key == 0x55aa) {

      n_read = fscanf(f, "%hhu", & structure.seq); //if it does we fill the struct
      if (n_read < 0) {
        perror("error");
      }
      n_read = fscanf(f, "%hhu", & structure.channel);
      if (n_read < 0) {
        perror("error");
      }
      n_read = fscanf(f, "%hhu", & structure.num_samples);
      if (n_read < 0) {
        perror("error");
      }

      .
      .
      .
      //other code
      .
      .
      .

      n_read = fscanf(f, "%hhu", & curr_buff[k]);
      if (n_read < 0) {
        printf("error");
      }

      .
      .
      .
      //other code
      .
      .
      .

      }
      key = 0; //we clear the key
    }
  }

下面是我在continous模式下得到的错误示例:

.
.
.
seq: 37
channel: 3
num_samp: 1
seq: 38
channel: 4
num_samp: 1
seq: 39
channel: 5
num_samp: 1
seq: 40
channel: 46 <-----------------error
num_samp: 5
kkbh8khc

kkbh8khc1#

服务器中的printf函数将每个uint8_t转换为char值,所以如果我读取255,我将发送3个字节而不是1个字节,这最终会使波特率饱和。我按照@克雷格Estey的建议,将printf替换为低级别的UDR 0 =data来发送二进制值,并将fscanf替换为read。

相关问题