硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1512|回复: 15
收起左侧

[UART] 串口收发间隔比较长的问题

[复制链接]

8

主题

57

回帖

81

积分

初级会员

积分
81
发表于 2024-12-17 09:56:51 | 显示全部楼层 |阅读模式
请教一个问题,我使用STM32H743芯片的串口5做了一个串口更新固件的功能(串口功能移植的是硬汉的V7-012_串口和PC机通信(驱动支持8串口FIFO)),使用的波特率是115200,主频是400MHz。我是通过上位机发送一包数据,单片机返回一帧数据这种方式更新固件到内部flash中。问题出现在更新过程,上位机基本上在收到单片机串口返回的数据帧几毫秒内就发送下一个数据包,但是单片机从上位机发送完一帧数据到下一次返回数据需要80ms,我感觉这个串口收到解析在返回速率比较慢,请问有没有办法加快串口速率么?
单片机收到数据解析返回.png 上位机接收并发送下一帧.png
回复

使用道具 举报

2

主题

25

回帖

31

积分

新手上路

积分
31
发表于 2024-12-17 10:42:16 | 显示全部楼层
我感觉是写flash耗时较长,你让单片机接受数据后不写flash直接应答,看看速度是否正常
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-17 12:09:23 | 显示全部楼层
xiaokang2009 发表于 2024-12-17 10:42
我感觉是写flash耗时较长,你让单片机接受数据后不写flash直接应答,看看速度是否正常

我测试了一下只将上位机发送的数据解析,然后打包返回,不再将数据写入到flash中,相对于写入flash的,时间80ms一次收发速率变成了70ms一次收到,flash写入操作占用时间差不多在10ms左右。如下图所示:
去掉写入flash操作.png


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
发表于 2024-12-17 14:58:49 | 显示全部楼层
Ainit 发表于 2024-12-17 12:09
我测试了一下只将上位机发送的数据解析,然后打包返回,不再将数据写入到flash中,相对于写入flash的,时 ...

继续下,接受完毕后不解析,立即反馈时间多少。从而锁定下这70ms在处理什么了
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-17 16:44:44 | 显示全部楼层
eric2013 发表于 2024-12-17 14:58
继续下,接受完毕后不解析,立即反馈时间多少。从而锁定下这70ms在处理什么了

硬汉哥你好,我根据你提供的方向测试了一下。我通过串口调试助手发送一帧7字节数据。并且打开自动应答模式,只要单片机返回了相同的值,串口调试助手就会继续下发。通过以上测试,逻辑分析仪上显示的波形基本每一帧发送完,微秒时间内,单片机就会返回,基本上不存在延迟。但是等待下一帧在发送就需要60ms左右的延时,这个时间基本上是固定的,我测试了一下串口助手,发送速率30ms一帧可以很稳定,另外我测试了一下更新固件的上位机程序,收发数据的频率也可以达到20~30ms一帧。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
发表于 2024-12-18 08:31:27 | 显示全部楼层
Ainit 发表于 2024-12-17 16:44
硬汉哥你好,我根据你提供的方向测试了一下。我通过串口调试助手发送一帧7字节数据。并且打开自动应答模 ...

“但是等待下一帧在发送就需要60ms左右的延时”

这个是谁发送,和前面描述的“每一帧发送完,微秒时间内” 这个帧是什么区别
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-18 09:07:01 | 显示全部楼层
eric2013 发表于 2024-12-18 08:31
“但是等待下一帧在发送就需要60ms左右的延时”

这个是谁发送,和前面描述的“每一帧发送完,微秒时间 ...

模拟的正常更新固件,发送一包数据,STM32会返回一包收到的回复指令,我通过串口助手设定好如果STM32有数据返回,我就自动再发一包数据。每一帧发完,立马有返回是指,STM32串口返回数据给串口助手,串口助手在微秒级等待后立马再发送一帧数据。等待下一帧在发送的意思是,在上述串口助手发送完一帧后,间隔60ms后,STM32的串口才返回数据包给串口助手。如下图所示:
说明.png
回复

使用道具 举报

5

主题

229

回帖

249

积分

高级会员

积分
249
发表于 2024-12-18 09:40:10 | 显示全部楼层
你的解析处理函数有问题吧, 什么串口解析需要70ms
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-18 09:51:11 | 显示全部楼层
旮旯旭 发表于 2024-12-18 09:40
你的解析处理函数有问题吧, 什么串口解析需要70ms

解析函数应该是没有问题,因为是直接移植的以前用的芯片上的解析函数。为避免问题,上贴串口解析,请大哥们给点意见。
[C] 纯文本查看 复制代码
struct smart_telemetry_package *
SMART_Telemetry_Recieve_Datas(uintptr_t telemetry_id) {
  int32_t cmd_id = -1;

  if (!telemetry_id) {
    return NULL;
  }
  struct smart_telemetry_dev *dev = (struct smart_telemetry_dev *)telemetry_id;

  if (!dev->has_remain) {
    uint16_t buf_len = NUM_OF_ARRAY(dev->receive_buf);
    memset(dev->receive_buf, 0, buf_len);

    uint16_t len = 0;
    if ((len = comGetChar(COM_TELEMETRY, dev->receive_buf)) > 0) {
      // parse data
      dev->last_read_len = len;
      cmd_id = SMART_Telemetry_Parse_Datas(dev, dev->receive_buf, 0, len);
    }
  } else {
    cmd_id = SMART_Telemetry_Parse_Datas(
        dev, dev->receive_buf, dev->check_sum_pos + 1, dev->last_read_len);
    if (cmd_id < 0) {
      dev->last_read_len = 0;
      dev->check_sum_pos = 0;
      dev->has_remain = false;
    }
  }

  if (cmd_id > 0) {
    return &dev->recieve_package;
  }
  return NULL;
}


[C] 纯文本查看 复制代码
int32_t SMART_Telemetry_Parse_Datas(struct smart_telemetry_dev *dev,
                                    uint8_t *buffer, uint8_t offset,
                                    uint16_t len) {

  bool check_ok = false;
  for (uint8_t i = offset; i < len; i++) {
    uint8_t data = buffer[i];
    if (dev->parse_state == PARSE_STATE_HEAD0) {
      if (data == TELEMETRY_PROTOCOL_HEAD0) {
        dev->parse_state = PARSE_STATE_HEAD1;
        dev->recieve_package.head0 = data;
      }
    } else if (dev->parse_state == PARSE_STATE_HEAD1) {
      if (data == TELEMETRY_PROTOCOL_HEAD1) {
        dev->parse_state = PARSE_STATE_HEAD2;
        dev->recieve_package.head1 = data;
      } else {
        dev->parse_state = PARSE_STATE_HEAD0;
      }
    } else if (dev->parse_state == PARSE_STATE_HEAD2) {
      if (data == TELEMETRY_PROTOCOL_HEAD2) {
        dev->parse_state = PARSE_STATE_DATA_LEN;
        dev->recieve_package.head2 = data;
      } else {
        dev->parse_state = PARSE_STATE_HEAD0;
      }
      dev->recieve_package.len_recv_idx = 0;
      dev->recieve_package.len = 0;
    } else if (dev->parse_state == PARSE_STATE_DATA_LEN) {
      dev->recieve_package.len |=
          (data << (dev->recieve_package.len_recv_idx * 8));
      if (dev->recieve_package.len_recv_idx < 1) {
        dev->recieve_package.len_recv_idx++;
      } else {
        if (dev->recieve_package.len >= NELEMENTS(dev->recieve_package.data)) {
          dev->parse_state = PARSE_STATE_HEAD0;
        } else {
          dev->parse_state = PARSE_STATE_CMD;
        }
      }
    } else if (dev->parse_state == PARSE_STATE_CMD) {
      dev->recieve_package.cmd = data;
      dev->recieve_package.data_recv_idx = 0;
      if (dev->recieve_package.len <= 0) {
        dev->parse_state = PARSE_STATE_CHECK;
      } else {
        dev->parse_state = PARSE_STATE_DATA;
      }
    } else if (dev->parse_state == PARSE_STATE_DATA) {
      if (dev->recieve_package.data_recv_idx <
          NELEMENTS(dev->recieve_package.data)) {
        dev->recieve_package.data[dev->recieve_package.data_recv_idx++] = data;
        if (dev->recieve_package.data_recv_idx >= (dev->recieve_package.len)) {
          dev->parse_state = PARSE_STATE_CHECK;
        }
      } else {
        dev->recieve_package.data_recv_idx = 0;
        dev->recieve_package.len_recv_idx = 0;
        dev->recieve_package.len = 0;
        dev->parse_state = PARSE_STATE_HEAD0;
      }
    } else if (dev->parse_state == PARSE_STATE_CHECK) {
      dev->recieve_package.check = data;
      dev->parse_state = PARSE_STATE_HEAD0;
      check_ok = (SMART_Telemetry_Checksum_Package((uintptr_t)dev) == data);
      dev->has_remain = (i != (len - 1));
      dev->check_sum_pos = i;
      break;
    }
  }
  if (check_ok) {
    return dev->recieve_package.cmd;
  }
  return -1;
}
回复

使用道具 举报

5

主题

229

回帖

249

积分

高级会员

积分
249
发表于 2024-12-18 13:42:41 | 显示全部楼层
Ainit 发表于 2024-12-18 09:51
解析函数应该是没有问题,因为是直接移植的以前用的芯片上的解析函数。为避免问题,上贴串口解析,请大哥 ...

我觉得你要查下你的这个时间消耗在哪里了,KEIL有个工具 Event Recorder去了解下,看看是那部分占用这么长的时间
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2024-12-19 13:28:10 | 显示全部楼层
目测问题应该是出现在断帧处理上。
回复

使用道具 举报

1

主题

7

回帖

10

积分

新手上路

积分
10
发表于 2024-12-19 15:47:18 | 显示全部楼层
解析不会花太多时间.
如果是收一帧写一下flash,那么耗时就在写flash上.
如果是最后一起写flash,可能就是任务的切换逻辑有问题.
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-23 13:54:16 | 显示全部楼层
zmsxhy 发表于 2024-12-19 15:47
解析不会花太多时间.
如果是收一帧写一下flash,那么耗时就在写flash上.
如果是最后一起写flash,可能就是 ...

是这样的操作逻辑,收到一帧数据,写入一帧数据。测试过有写入flash操作跟没有flash写入操作,时间基本在10ms内完成了,波形不是现在这种间隔超过60ms。
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-23 13:58:28 | 显示全部楼层
yixu 发表于 2024-12-19 13:28
目测问题应该是出现在断帧处理上。

这个解析的函数是以前在老版的f4芯片上用很久的,没有出现过串口数据需要间隔这么久,20ms~30ms的极限了。
回复

使用道具 举报

1

主题

7

回帖

10

积分

新手上路

积分
10
发表于 2024-12-24 17:19:37 | 显示全部楼层
打时间戳吧,看哪个函数用时比较多。
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2024-12-26 13:47:18 | 显示全部楼层
zmsxhy 发表于 2024-12-24 17:19
打时间戳吧,看哪个函数用时比较多。

你好,这个问题已经解决了。问题出在我移植的串口接收读取缓存的函数上。原来的串口接收函数是将缓存中的数据全部取出到另外的数组中,然后再统一去判断帧头帧尾,校验,字节数等。移植的这个函数是每次只从串口接收缓存中接收一个,从而导致接收的帧过长的话,接收一个字节进入一次解析函数中判断。后续修改了一下从串口接收缓存中取数据后这个问题就解决了。
如下是移植的,每次从串口缓存中取一个数据:
[C] 纯文本查看 复制代码
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte) {
  uint16_t usCount;

  /* usRxWrite 变量在中断函数中被改写,主程序读取该变量时,必须进行临界区保护 */
  DISABLE_INT();
  usCount = _pUart->usRxCount;
  ENABLE_INT();

  /* 如果读和写索引相同,则返回0 */
  // if (_pUart->usRxRead == usRxWrite)
  if (usCount == 0) /* 已经没有数据 */
  {
    return 0;
  } else {
    *_pByte = _pUart->pRxBuf[_pUart->usRxRead]; /* 从串口接收FIFO取1个数据 */

    /* 改写FIFO读索引 */
    DISABLE_INT();
    if (++_pUart->usRxRead >= _pUart->usRxBufSize) {
      _pUart->usRxRead = 0;
    }
    _pUart->usRxCount--;
    ENABLE_INT();
    return 1;
  }
}

修改后的,串口接收缓存中的数据全部取出
[C] 纯文本查看 复制代码
static uint16_t UartGetAllChars(UART_T *_pUart, uint8_t *buffer,
                                uint16_t bufferSize) {
  uint16_t usCount;
  uint16_t bytesRead = 0;

  DISABLE_INT();
  usCount = _pUart->usRxCount;
  ENABLE_INT();

  while (usCount > 0 && bytesRead < bufferSize) {
    buffer[bytesRead++] = _pUart->pRxBuf[_pUart->usRxRead];

    DISABLE_INT();
    if (++_pUart->usRxRead >= _pUart->usRxBufSize) {
      _pUart->usRxRead = 0;
    }
    _pUart->usRxCount--;
    usCount = _pUart->usRxCount; // 更新剩余字节数
    ENABLE_INT();
  }

  return bytesRead; // 返回读取到的数据长度
}


串口速率相比于以前,提高了很多,问题基本解决。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-14 03:39 , Processed in 0.055100 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表