qinyunti 发表于 2022-12-15 21:53

【ST NUCLEO-U575ZI-Q 测评 】基于环形缓冲区实现串口接收驱动

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

<p >上一篇我们进行了串口的收发测试,在串口接收中断时原样返回数据,测试了收发回环。这显然不能直接给应用层使用,实际项目中需要给应用层提供好用的串口收发接口。这里通过环形缓冲区的方式实现串口的接收,应用层只需要调用读接口从缓冲区不断查询接收数据即可。</p>

<p >&nbsp;</p>

<h1 ><b>过程</b></h1>

<h2 ><b>临界段处理</b></h2>

<p >由于缓冲区的基本数据流是串口接收中断中写缓冲区,读接口函数读缓冲区,存在多方使用缓冲区资源,所以对缓冲区资源访问时必须做临界段处理。这里简单的使用开关中断处理。</p>

<p >&nbsp;</p>

<div class="parsedown-markdown">
<p>__disable_irq();</p>

<p>__enable_irq();</p>
</div>

<p >&nbsp;</p>

<p >由于写缓冲区只在串口接收中断中进行,所以uart_rx_handler写缓冲区就不需要再做临界段处理了,只有uart_read读缓冲区时需要进行临界段处理。</p>

<p >当然更好的方式是,只关串口中断对应的优先级中断,而不是关所有中断。</p>

<h2 ><b>环形缓冲区结构</b></h2>

<p >通过以下结构体实现</p>

<p >&nbsp;</p>

<p >分别设计了读写指针,当前有效数据个数,缓冲区大小。</p>

<p >&nbsp;</p>

<p >写缓冲区时in_u32写指针递增,到末尾后绕到最开始,如果缓冲区满则丢失。</p>

<p >&nbsp;</p>

<p >读缓冲区时out_u32读指针递增,到末尾后绕到最开始。</p>

<p >&nbsp;</p>

<div class="parsedown-markdown">
<p>typedef struct</p>

<p >&nbsp;</p>

<p >{</p>

<p >&nbsp;</p>

<p >uint32_t datalen_u32;</p>

<p >&nbsp;</p>

<p >uint32_t maxlen_u32;</p>

<p >&nbsp;</p>

<p >uint32_t in_u32;</p>

<p >&nbsp;</p>

<p >uint32_t out_u32;</p>

<p >&nbsp;</p>

<p >uint8_t* buffer_pu8;</p>

<p >&nbsp;</p>

<p >}ring_buffer_t;</p>
</div>

<p >&nbsp;</p>

<p >&nbsp;</p>

<h2 ><b>代码</b></h2>

<p >添加文件uart.c,内容如下</p>

<p ></p>

<div class="parsedown-markdown">
<p>#include &lt;stdint.h&gt;</p>

<p >#include &quot;stm32u575xx.h&quot;</p>

<p >#include &quot;stm32u5xx_ll_gpio.h&quot;</p>

<p >#include &quot;stm32u5xx_ll_bus.h&quot;</p>

<p >#include &quot;stm32u5xx_ll_lpuart.h&quot;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >uint8_t uart_ring_buffer;</p>

<p >&nbsp;</p>

<p >typedef struct</p>

<p >{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t datalen_u32;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t maxlen_u32;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t in_u32;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t out_u32;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint8_t* buffer_pu8;</p>

<p >}ring_buffer_t;</p>

<p >&nbsp;</p>

<p >ring_buffer_t s_ring_buffer_t=</p>

<p >{</p>

<p >.datalen_u32 = 0,</p>

<p >.maxlen_u32 = sizeof(uart_ring_buffer),</p>

<p >.in_u32 = 0,</p>

<p >.out_u32 = 0,</p>

<p >.buffer_pu8 = uart_ring_buffer,</p>

<p >};</p>

<p >&nbsp;</p>

<p >void uart_rx_handler(const uint8_t *buffer, uint32_t length)</p>

<p >{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;for(uint32_t i=0;i&lt;length;i++)</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(s_ring_buffer_t.datalen_u32 &lt; s_ring_buffer_t.maxlen_u32)</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s_ring_buffer_t.buffer_pu8 = buffer;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s_ring_buffer_t.datalen_u32++;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s_ring_buffer_t.in_u32++;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s_ring_buffer_t.in_u32 %= s_ring_buffer_t.maxlen_u32;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* full */</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;}</p>

<p >}</p>

<p >&nbsp;</p>

<p >int uart_read(uint8_t *buff, uint32_t len)</p>

<p >{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t readlen = 0;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;uint32_t mask;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;if(s_ring_buffer_t.datalen_u32 == 0)</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;__disable_irq();</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;for(uint32_t i=0;i&lt;len;i++)</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;{</p>

<p >&nbsp;&nbsp;if(s_ring_buffer_t.datalen_u32 &gt; 0)</p>

<p >{</p>

<p >buff = s_ring_buffer_t.buffer_pu8;</p>

<p >s_ring_buffer_t.datalen_u32--;</p>

<p >s_ring_buffer_t.out_u32++;</p>

<p >s_ring_buffer_t.out_u32 %= s_ring_buffer_t.maxlen_u32;</p>

<p >readlen++;</p>

<p >}</p>

<p >else</p>

<p >{</p>

<p >break;</p>

<p >}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;__enable_irq();</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;return readlen;</p>

<p >}</p>

<p >&nbsp;</p>

<p >int uart_write(uint8_t *buff, uint32_t len)</p>

<p >{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;for(uint32_t i=0; i&lt;len; i++)</p>

<p >{</p>

<p >&nbsp;&nbsp;LL_LPUART_TransmitData8(LPUART1,buff);</p>

<p >&nbsp;&nbsp;while(LL_LPUART_IsActiveFlag_TXE_TXFNF(LPUART1)!=1);</p>

<p >}</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;return 0;</p>

<p >}</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >void LPUART1_IRQHandler(void)</p>

<p >{</p>

<p >uint8_t ch;</p>

<p >if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1) == SET)</p>

<p >{</p>

<p >ch = LL_LPUART_ReceiveData8(LPUART1);</p>

<p >uart_rx_handler(&amp;ch, 1);</p>

<p >}</p>

<p >if(LL_LPUART_IsActiveFlag_TXE_TXFNF(LPUART1))</p>

<p >{</p>

<p >&nbsp;</p>

<p >}</p>

<p >}</p>

<p >&nbsp;</p>

<p >void uart_init(void)</p>

<p >{</p>

<p >&nbsp;</p>

<p >LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG);</p>

<p >&nbsp;&nbsp;/* PG7 TX */</p>

<p >LL_GPIO_InitTypeDef GPIO_InitStruct;</p>

<p >//LL_GPIO_StructInit(&amp;GPIO_InitStruct);</p>

<p >GPIO_InitStruct.Pin = LL_GPIO_PIN_7;</p>

<p >GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;</p>

<p >GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;</p>

<p >GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;</p>

<p >GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;</p>

<p >GPIO_InitStruct.Alternate = LL_GPIO_AF_8;</p>

<p >LL_GPIO_Init(GPIOG, &amp;GPIO_InitStruct);</p>

<p >&nbsp;&nbsp;/* PG8 RX */</p>

<p >GPIO_InitStruct.Pin = LL_GPIO_PIN_8;</p>

<p >GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;</p>

<p >GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;</p>

<p >GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;</p>

<p >GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;</p>

<p >GPIO_InitStruct.Alternate = LL_GPIO_AF_8;</p>

<p >LL_GPIO_Init(GPIOG, &amp;GPIO_InitStruct);</p>

<p >&nbsp;</p>

<p >LL_APB3_GRP1_EnableClock(LL_APB3_GRP1_PERIPH_LPUART1);</p>

<p >LL_LPUART_InitTypeDef LPUART_InitStruct;</p>

<p >LPUART_InitStruct.BaudRate = 115200;</p>

<p >LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;</p>

<p >LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;</p>

<p >LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;</p>

<p >LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;</p>

<p >LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;</p>

<p >LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;</p>

<p >LL_LPUART_Init(LPUART1, &amp;LPUART_InitStruct);</p>

<p >&nbsp;</p>

<p >LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);</p>

<p >//LL_LPUART_EnableIT_TXE_TXFNF(LPUART1);</p>

<p >__NVIC_EnableIRQ(LPUART1_IRQn);</p>

<p >__NVIC_SetPriority(LPUART1_IRQn, 3);</p>

<p >&nbsp;</p>

<p >LL_LPUART_Enable(LPUART1);</p>

<p >}</p>

<p >&nbsp;</p>
</div>

<p >&nbsp;</p>

<p >uart.h中提供三个接口</p>

<p ></p>

<div class="parsedown-markdown">
<p>#ifndef UART_H</p>

<p >#define UART_H</p>

<p >&nbsp;</p>

<p >#include &lt;stdint.h&gt;</p>

<p >void uart_init(void);</p>

<p >int uart_read(uint8_t *buff, uint32_t len);</p>

<p >int uart_write(uint8_t *buff, uint32_t len);</p>

<p >&nbsp;</p>

<p >#endif</p>
</div>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<h2 ><b>测试</b></h2>

<p >Main.c</p>

<p ></p>

<div class="parsedown-markdown">
<p>#include &quot;stm32u575xx.h&quot;</p>

<p >#include &quot;stm32u5xx_ll_gpio.h&quot;</p>

<p >#include &quot;stm32u5xx_ll_bus.h&quot;</p>

<p >#include &quot;uart.h&quot;</p>

<p >&nbsp;</p>

<p >void SysTick_Handler(void)</p>

<p >{</p>

<p >static volatile uint32_t num = 0;</p>

<p >if(num++ &gt;= 1000)</p>

<p >{</p>

<p >LL_GPIO_TogglePin(GPIOB, 1u&lt;&lt;7);</p>

<p >LL_GPIO_TogglePin(GPIOG, 1u&lt;&lt;2);</p>

<p >LL_GPIO_TogglePin(GPIOC, 1u&lt;&lt;7);</p>

<p >num=0;</p>

<p >}</p>

<p >HAL_IncTick();</p>

<p >}</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >void delay(uint32_t t)</p>

<p >{</p>

<p >volatile uint32_t timeout = t;</p>

<p >while(t--);</p>

<p >}</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >int main(void)</p>

<p >{</p>

<p >HAL_Init();</p>

<p >LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR);</p>

<p >HAL_PWREx_EnableVddIO2();</p>

<p >HAL_PWREx_ConfigSupply(PWR_SMPS_SUPPLY);</p>

<p >#if 1</p>

<p >LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PWR);</p>

<p >HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);</p>

<p >&nbsp;</p>

<p >RCC_OscInitTypeDef pRCC_OscInitStruct;</p>

<p >pRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;</p>

<p >pRCC_OscInitStruct.HSIState = RCC_HSI_ON;</p>

<p >pRCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;</p>

<p >pRCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;</p>

<p >pRCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;</p>

<p >pRCC_OscInitStruct.PLL.PLLM = 1;</p>

<p >pRCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV1;</p>

<p >pRCC_OscInitStruct.PLL.PLLN = 20;</p>

<p >pRCC_OscInitStruct.PLL.PLLP = 1;</p>

<p >pRCC_OscInitStruct.PLL.PLLQ = 1;</p>

<p >pRCC_OscInitStruct.PLL.PLLR = 2;</p>

<p >pRCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_0;</p>

<p >pRCC_OscInitStruct.PLL.PLLFRACN = 0; &nbsp;/* */</p>

<p >HAL_RCC_OscConfig(&amp;pRCC_OscInitStruct);</p>

<p >&nbsp;</p>

<p >RCC_ClkInitTypeDef pRCC_ClkInitStruct;</p>

<p >pRCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;</p>

<p >pRCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;</p>

<p >pRCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;</p>

<p >pRCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;</p>

<p >pRCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;</p>

<p >pRCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV2;</p>

<p >HAL_RCC_ClockConfig(&amp;pRCC_ClkInitStruct, FLASH_LATENCY_4);</p>

<p >#endif</p>

<p >&nbsp;</p>

<p >LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);</p>

<p >LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG);</p>

<p >LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);</p>

<p >&nbsp;</p>

<p >LL_GPIO_InitTypeDef GPIO_InitStruct;</p>

<p >//LL_GPIO_StructInit(&amp;GPIO_InitStruct);</p>

<p >GPIO_InitStruct.Pin = LL_GPIO_PIN_7;</p>

<p >GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;</p>

<p >GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;</p>

<p >GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;</p>

<p >GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;</p>

<p >GPIO_InitStruct.Alternate = LL_GPIO_AF_0;</p>

<p >LL_GPIO_Init(GPIOB, &amp;GPIO_InitStruct);</p>

<p >&nbsp;</p>

<p >GPIO_InitStruct.Pin = LL_GPIO_PIN_2;</p>

<p >GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;</p>

<p >GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;</p>

<p >GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;</p>

<p >GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;</p>

<p >GPIO_InitStruct.Alternate = LL_GPIO_AF_0;</p>

<p >LL_GPIO_Init(GPIOG, &amp;GPIO_InitStruct);</p>

<p >&nbsp;</p>

<p >GPIO_InitStruct.Pin = LL_GPIO_PIN_7;</p>

<p >GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;</p>

<p >GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;</p>

<p >GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;</p>

<p >GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;</p>

<p >GPIO_InitStruct.Alternate = LL_GPIO_AF_0;</p>

<p >LL_GPIO_Init(GPIOC, &amp;GPIO_InitStruct);</p>

<p >&nbsp;</p>

<p >uart_init();</p>

<p >&nbsp;</p>

<p >uint8_t buffer;</p>

<p >while(1)</p>

<p >{</p>

<p >int len=0;</p>

<p >if((len = uart_read(buffer, sizeof(buffer))) &gt;0)</p>

<p >{</p>

<p >uart_write(buffer, len);</p>

<p >}</p>

<p >}</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >上位机持续发送,测试发送的内容是否和接收到的内容完全一样,测试稳定性。</p>

<p >&nbsp;</p>

<p >同时也可以测试发送效率。</p>

<p >&nbsp;可以看到收发数据完全一致,测试OK。</p>

<p >&nbsp;</p>

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

<p >以上实现了串口驱动,可以方便的供应用层使用。</p>

<p >这里发送函数直接使用的查询方式,当然也可以类似的使用环形缓冲区中断发送。</p>

<p >read缓冲区时还可以实现超时控制等等。</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>

okhxyyo 发表于 2023-1-12 09:43

<p>测评汇总:免费申请|ST NUCLEO-U575ZI-Q https://bbs.eeworld.com.cn/thread-1228653-1-1.html</p>
页: [1]
查看完整版本: 【ST NUCLEO-U575ZI-Q 测评 】基于环形缓冲区实现串口接收驱动