【STM32H7S78-DK】测评+TouchGFX UART+DMA不定长接收
<div class='showpostmsg'> 本帖最后由 lugl4313820 于 2024-10-3 21:48 编辑<p>【前言】</p>
<p>STM32H7系列的串口+DMA接收,与F1、F4不大相同,此篇使用STM32H7的串口+DMA中断回环接收,接收到数据后,通过TouchGFX显示接收到的数据。此次的重点是如何配置GPDMA以及如何处理接收到的数。</p>
<p>这次试验的基础环境在上篇工程的基础之上添加:</p>
<p><a href="https://bbs.eeworld.com.cn/thread-1295300-1-1.html">【STM32H7S78-DK】测评+ToucGFX之Lwip移植 - stm32/stm8 - 电子工程世界-论坛 (eeworld.com.cn)</a></p>
<p>【图形化配置】</p>
<p>1、前面工程是已经开启好了UART4的基础之上添加,打开GPDMA来开启,如下图所示。</p>
<p> 在配置中,主要是开启回环接收模式,Request Configration中选择UART4_RX,然后开启Destination Data Setting下的After Transfer为Enabled,其余的保持默认就行。</p>
<p>2、然后在UART4中开启两个中断,即GPDMA1,以及UART4 global interrrupt。</p>
<p> </p>
<p>配置好后,更新工程。</p>
<p>【代码编写】</p>
<p>1、接收数组的创建,创建三个数据用于存放接收缓存,以及1个数组用于给TouchGFX进行显示。</p>
<pre>
<code>uint8_t aRXBufferUser __attribute__((section("noncacheable_buffer")));
/**
* <a href="home.php?mod=space&uid=159083" target="_blank">@brief </a> Data buffers used to manage received data in interrupt routine
*/
uint8_t aRXBufferA __attribute__((section("noncacheable_buffer")));
uint8_t aRXBufferB __attribute__((section("noncacheable_buffer")));
uint8_t show_buff;</code></pre>
<p>因为STM32H7的GPDMA是不能直接访问ITCM、DTCM两个内存的,在用户手册表有上说明:</p>
<p> </p>
<p>所以需要用__attribute__((section("noncacheable_buffer")))来指定存放到指定的内存中,同时在分段加载文件中指定内存段:</p>
<p> </p>
<pre>
<code> RW_NONCACHEABLEBUFFER0x24072000-0x400 0x400{
*(.noncacheable_buffer)
}</code></pre>
<p>2、编写中断回调函数:</p>
<pre>
<code>/**
* @briefUser implementation of the Reception Event Callback
* (Rx event notification called after use of advanced reception service).
* @paramhuart UART handle
* @paramSizeNumber of data available in application reception buffer (indicates a position in
* reception buffer until which, data are available)
* @retval None
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
static uint8_t old_pos = 0;
uint8_t *ptemp;
uint8_t i;
/* Check if number of received data in recpetion buffer has changed */
if (Size != old_pos)
{
/* Check if position of index in reception buffer has simply be increased
of if end of buffer has been reached */
if (Size > old_pos)
{
/* Current position is higher than previous one */
uwNbReceivedChars = Size - old_pos;
/* Copy received data in "User" buffer for evacuation */
for (i = 0; i < uwNbReceivedChars; i++)
{
pBufferReadyForUser = aRXBufferUser;
show_buff = aRXBufferUser;
}
}
else
{
/* Current position is lower than previous one : end of buffer has been reached */
/* First copy data from current position till end of buffer */
uwNbReceivedChars = UART_RX_BUFFER_SIZE - old_pos;
/* Copy received data in "User" buffer for evacuation */
for (i = 0; i < uwNbReceivedChars; i++)
{
pBufferReadyForUser = aRXBufferUser;
show_buff = aRXBufferUser;
}
/* Check and continue with beginning of buffer */
if (Size > 0)
{
for (i = 0; i < Size; i++)
{
pBufferReadyForUser = aRXBufferUser;
show_buff = aRXBufferUser;
}
uwNbReceivedChars += Size;
}
}
/* Process received data that has been extracted from Rx User buffer */
UserDataTreatment(huart, pBufferReadyForUser, uwNbReceivedChars);
/* Swap buffers for next bytes to be processed */
ptemp = pBufferReadyForUser;
pBufferReadyForUser = pBufferReadyForReception;
pBufferReadyForReception = ptemp;
usart_dma_rcv_state = Size;
show_buff = 0;
}
/* Update old_pos as new reference of position in User Rx buffer that
indicates position to which data have been processed */
old_pos = Size;
}</code></pre>
<p>此段代码中,使用HAL库的扩展HAL_UARTEx_RxEventCallback,来接收数据,并完成处理。在回调函数中返回Size即此次中断接收到的数据长度,使用old_pos与Size来找到此次接收到的数据位置,并把数据转存到其他的几个buff数组,接收完成后,更新一个usart_dama_state,给TouchGFX来处理接收到的数据。</p>
<p>同时在系统初始化好接,执行一次开启DMA的函数:</p>
<pre>
<code>void StartReception(void)
{
/* Initializes Buffer swap mechanism (used in User callback) :
- 2 physical buffers aRXBufferA and aRXBufferB (RX_BUFFER_SIZE length)
*/
pBufferReadyForReception = aRXBufferA;
pBufferReadyForUser = aRXBufferB;
uwNbReceivedChars = 0;
/* Print user info on PC com port */
//PrintInfo(&huart4, aTextInfoStart, COUNTOF(aTextInfoStart));
/* Initializes Rx sequence using Reception To Idle event API.
As DMA channel associated to UART Rx is configured as Circular,
reception is endless.
If reception has to be stopped, call to HAL_UART_AbortReceive() could be used.
Use of HAL_UARTEx_ReceiveToIdle_DMA service, will generate calls to
user defined HAL_UARTEx_RxEventCallback callback for each occurrence of
following events :
- DMA RX Half Transfer event (HT)
- DMA RX Transfer Complete event (TC)
- IDLE event on UART Rx line (indicating a pause is UART reception flow)
*/
if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(&huart4, aRXBufferUser, UART_RX_BUFFER_SIZE))
{
Error_Handler();
}
}</code></pre>
<p>下载后工程后,使用串口组手发送不定长的数据,可以从串口接收到不定长的返回数据:</p>
<p> </p>
<p>说明不定长是可以正确接收的。</p>
<p>【TouchGFX代码实现】</p>
<p>在ScreenView.cpp中添加HandleTickEvent函数,每10毫秒检查一下次是否接收到串口数据,发果发现有,将数据打印到屏幕上:</p>
<pre>
<code>void Screen1View::handleTickEvent()
{
static int tick=0;
if(tick%10 == 0 && usart_dma_rcv_state > 0)
{
Unicode::fromUTF8(show_buff,textFileBuffer,40);
textFile.invalidate();
memset((void *)show_buff, 0U, sizeof(show_buff));
usart_dma_rcv_state = 0;
}
tick = (tick+1)%60;
}</code></pre>
<p>【实现现象】</p>
<p>从串口助手发送到开发板,可以在屏上实时查看到发送的数据:</p>
<p> </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> <p>串口空闲中断加DMA是不定长数据接收的最优解 </p>
页:
[1]