qinyunti 发表于 2023-10-30 15:24

【ST多款开发板返场测评】STM32F767 Nucleo-144 非阻塞串口打印实现

<div class='showpostmsg'><h1><b>前言</b></h1>

<p >前面我们实现的串口发送采用的是阻塞查询方式,在USB开发中如果使用该方式作为打印调试,则可能会由于阻塞影响时序。所以需要该为非阻塞方式,我们的思路是先将待发送数据写入到缓存中,然后载在主循环中进行发送。核心部分还是和串口接收FIFO实现一样,可以复用其代码。</p>

<p >&nbsp;</p>

<h1 ><b>代码</b></h1>

<p >增加代码uart_debug.c/h</p>

<p >数据结构</p>

<pre>
<code class="language-cpp">typedef struct

{

uint32_t datalen_u32;

uint32_t maxlen_u32;

uint32_t in_u32;

uint32_t out_u32;

uint8_t* buffer_pu8;

}ring_buffer_t;



static uint32_t uart_debug_pop(uint8_t *buff, uint32_t len);



static uint8_t uart_ring_buffer;



static ring_buffer_t s_ring_buffer_t=

{

.datalen_u32 = 0,

.maxlen_u32 = sizeof(uart_ring_buffer),

  .in_u32 = 0,

  .out_u32 = 0,

  .buffer_pu8 = uart_ring_buffer,

};</code></pre>

<p >&nbsp;</p>

<p >写数据到缓存区</p>

<pre>
<code class="language-cpp">uint32_t uart_debug_push(uint8_t *buffer, uint32_t length)

{

    uint32_t i;

    uint32_t sendlen = 0;

Alloc_Critical();

    Enter_Critical();

    for(i=0;i&lt;length; i++)

    {

        if(s_ring_buffer_t.datalen_u32 &lt; s_ring_buffer_t.maxlen_u32)

        {

            s_ring_buffer_t.buffer_pu8 = buffer;

            s_ring_buffer_t.datalen_u32++;

            s_ring_buffer_t.in_u32++;

            s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;

  sendlen++;

        }

        else

        {

            /* full */

            break;

        }

    }

    Exit_Critical();

return sendlen;

}</code></pre>

<p >&nbsp;</p>

<p >从缓存区读出数据</p>

<pre>
<code class="language-cpp">static uint32_t uart_debug_pop(uint8_t *buff, uint32_t len)

{

    uint32_t readlen = 0;

    //uint32_t mask;

    if(s_ring_buffer_t.datalen_u32 == 0)

    {

        return 0;

    }

Alloc_Critical();

    Enter_Critical();

    uint32_t i;

    for(i=0;i&lt;len;i++)

    {

if(s_ring_buffer_t.datalen_u32 &gt; 0)

      {

buff = s_ring_buffer_t.buffer_pu8;

s_ring_buffer_t.datalen_u32--;

s_ring_buffer_t.out_u32++;

s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;

readlen++;

}

else

{

break;

}

    }

    Exit_Critical();

    return readlen;

}</code></pre>

<p >&nbsp;</p>

<p >发送处理,主循环中调用该接口实现真正的发送,</p>

<p >制定了一次发送的最大长度len,这样做目的是避免一次发送过长,导致主循环在这里等待过长,配置len来调整。</p>

<pre>
<code class="language-cpp">int uart_debug_send(uint32_t len)

{

uint32_t i;

uint8_t buff;

uint32_t slen;



if(0 != (slen = uart_debug_pop(buff, sizeof(buff))))

{

uart_write(buff, slen);

}

return slen;

}</code></pre>

<p >&nbsp;</p>

<p >重定向printf的底层接口</p>

<pre>
<code class="language-cpp">int fputc(int ch, FILE *f)

{

uint8_t tmp = (uint8_t)ch;

  /* Your implementation of fputc(). */

uart_debug_push(&amp;tmp, 1);

  return ch;

}</code></pre>

<p >uart_debug.h</p>

<pre>
<code class="language-cpp">#ifndef UART_DEBUG_H
#define UART_DEBUG_H

#include &lt;stdint.h&gt;

uint32_t uart_debug_push(uint8_t *buffer, uint32_t length);
int uart_debug_send(uint32_t len);

#endif</code></pre>

<p >uart_debug.c</p>

<pre>
<code class="language-cpp">#include &lt;stdio.h&gt;
#include "uart.h"
#include "uart_debug.h"
#include "stm32f7xx.h"

#define Alloc_Critical()
#define Enter_Critical()
#define Exit_Critical()   
                       
typedef struct
{
        uint32_t datalen_u32;
        uint32_t maxlen_u32;
        uint32_t in_u32;
        uint32_t out_u32;
        uint8_t* buffer_pu8;
}ring_buffer_t;

static uint32_t uart_debug_pop(uint8_t *buff, uint32_t len);

static uint8_t uart_ring_buffer;

static ring_buffer_t s_ring_buffer_t=
{
        .datalen_u32 = 0,
        .maxlen_u32 = sizeof(uart_ring_buffer),
.in_u32 = 0,
.out_u32 = 0,
.buffer_pu8 = uart_ring_buffer,
};

static uint32_t uart_debug_pop(uint8_t *buff, uint32_t len)
{
    uint32_t readlen = 0;
    //uint32_t mask;
    if(s_ring_buffer_t.datalen_u32 == 0)
    {
      return 0;
    }
                Alloc_Critical();
    Enter_Critical();
    uint32_t i;
    for(i=0;i&lt;len;i++)
    {
                        if(s_ring_buffer_t.datalen_u32 &gt; 0)
      {
                                buff = s_ring_buffer_t.buffer_pu8;
                                s_ring_buffer_t.datalen_u32--;
                                s_ring_buffer_t.out_u32++;
                                s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;
                                readlen++;
                        }
                        else
                        {
                                break;
                        }
    }
    Exit_Critical();
    return readlen;
}

uint32_t uart_debug_push(uint8_t *buffer, uint32_t length)
{
    uint32_t i;
    uint32_t sendlen = 0;
                Alloc_Critical();
    Enter_Critical();
    for(i=0;i&lt;length; i++)
    {
      if(s_ring_buffer_t.datalen_u32 &lt; s_ring_buffer_t.maxlen_u32)
      {
            s_ring_buffer_t.buffer_pu8 = buffer;
            s_ring_buffer_t.datalen_u32++;
            s_ring_buffer_t.in_u32++;
            s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;
                                          sendlen++;
      }
      else
      {
            /* full */
            break;
      }
    }
    Exit_Critical();
                return sendlen;
}

int uart_debug_send(uint32_t len)
{
        uint32_t i;
        uint8_t buff;
        uint32_t slen;

        if(0 != (slen = uart_debug_pop(buff, sizeof(buff))))
        {
                uart_write(buff, slen);
        }
        return slen;
}

int fputc(int ch, FILE *f)
{
        uint8_t tmp = (uint8_t)ch;
/* Your implementation of fputc(). */
        uart_debug_push(&amp;tmp, 1);
return ch;
}

</code></pre>

<h1 ><b>测试</b></h1>

<p >Main.c中</p>

<pre>
<code class="language-cpp">#include "uart_debug.h"



  while (1)

  {

printf("Hello World\r\n");

uart_debug_send(16);

  }</code></pre>

<p >串口调试助手看到打印如下</p>

<p >&nbsp;</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<h1 ><b>总结</b></h1>

<p >使用FIFO,将数据先陷入缓存区,再统一下主循环中不断查询发送,这样实现了非阻塞的串口输出,方便后面调试打印使用。</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

freebsder 发表于 2023-10-30 15:51

<p>前后台轮询?其实可以试试上个rtos</p>

wangerxian 发表于 2023-10-31 13:16

<p>我觉得这种非阻塞的,适用于LOG存到Flash的。</p>
页: [1]
查看完整版本: 【ST多款开发板返场测评】STM32F767 Nucleo-144 非阻塞串口打印实现