打开主菜单

谷雨文档中心 β

更改

NBDK-L4:基础实验教程

添加7,104字节2019年3月1日 (五) 11:04
无编辑摘要
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
</syntaxhighlight>串口DMA初始化(用于在DMA模式下,接收数据)。串口DMA初始化,用于在串口DMA模式下去接收数据(真正读取串口RX缓冲区的函数)。通过UartDma_Init()函数和gyu_usart_dma_ex.c的回调函数关系,将获取到的串口RX数据,交给gyu_usart_dma_ex.c去处理。<syntaxhighlight lang="c++" line="1" start="167">
void HAL_UARTDMA_Init(void)
{
HAL_UART_Receive_DMA(&huart1,UartDma_Init(uart_dma_send,USART1),RECE_BUF_MAX_LEN);
}
</syntaxhighlight>串口TX打印数据的函数,调用gyu_usart_dma_ex.c中的UartDma_Write()完成数据打印,最终是通过下面的uart_dma_send()函数打印数据。<syntaxhighlight lang="c++" line="1" start="181">
void HAL_UART_Write(uint8_t*buf,uint16_t len)
{
UartDma_Write(buf,len);
}
</syntaxhighlight>串口DMA打印的函数。<syntaxhighlight lang="c++" line="1" start="195">
static void uart_dma_send(uint8_t* buf,uint16_t len)
{
HAL_UART_Transmit_DMA(&huart1,buf,len);
}
</syntaxhighlight>读取串口数据的函数,不是真正从串口RX获取数据的函数,而是从gyu_usart_dma_ex.c这个文件获取保存好的数据。<syntaxhighlight lang="c++" line="1" start="209">
uint16_t HAL_UART_Read(uint8_t*buf,uint16_t len)
{
return UartDma_Read(buf,len);
}
</syntaxhighlight>串口轮询函数,主要是判断串口RX是否有数据,是否接收超时,以及是否有需要往外打印的串口数据。<syntaxhighlight lang="c++" line="1" start="222">
uint8_t HAL_UART_Poll(void)
{
return UartDma_Poll();
}
</syntaxhighlight>用于获取串口RX缓冲器数据长度。<syntaxhighlight lang="c++" line="1" start="235">
uint16_t HAL_UART_RxBufLen(void)
{
return UartDma_Avail();
}
</syntaxhighlight>
==== gyu_usart_dma_ex.c ====
这个文件主要是串口数据的获取和处理,大家有兴趣可以自行阅读,需要一定的代码阅读能力,并且对于串口理解的比较清晰。
 
获取当前DMA接收缓存区正在操作的位置。<syntaxhighlight lang="c++" line="1" start="51">
static uint16_t findTail(void)
{
uint16_t idx = dmaCfg.rxHead;
do
{
if (!DMA_NEW_RX_BYTE(idx))
{
break;
}
if (++idx >= RECE_BUF_MAX_LEN)
{
idx = 0;
}
} while (idx != dmaCfg.rxHead);
 
return idx;
}
</syntaxhighlight>串口DMA初始化函数,除了串口dma结构体dmaCfg的初始化参数,主要就是dmaSendCb这个回调函数,以及串口句柄hDmaUart赋值。<syntaxhighlight lang="c++" line="1" start="79">
uint8_t* UartDma_Init(sendData_cb sendCb ,USART_TypeDef* hUart)
{
memset(dmaCfg.buf,0xff,RECE_BUF_MAX_LEN<<1);
dmaCfg.rxHead = 0;
dmaCfg.rxTail = 0;
dmaCfg.rxTick = 0;
dmaCfg.rxShdw = 0;
dmaCfg.txSel = 0;
dmaCfg.txIdx[0] = 0;
dmaCfg.txIdx[1] = 0;
dmaCfg.rxTick = 0; //delay 1ms
dmaCfg.txDMAPending = FALSE;
dmaCfg.txShdwValid = FALSE;
dmaSendCb = sendCb;
hDmaUart = hUart;
return (uint8_t*)dmaCfg.buf;
}
</syntaxhighlight>串口DMA获取数据,只是数据的赋值和空间释放,不是真正的从出纳卡RX缓冲区获取数据。<syntaxhighlight lang="c++" line="1" start="109">
uint16_t UartDma_Read(uint8_t *buf, uint16_t len)
{
uint16_t cnt;
 
for (cnt = 0; cnt < len; cnt++)
{
if (!DMA_NEW_RX_BYTE(dmaCfg.rxHead))
{
break;
}
*buf++ = DMA_GET_RX_BYTE(dmaCfg.rxHead);
//释放占用空间
DMA_CLR_RX_BYTE(dmaCfg.rxHead);
 
if (++(dmaCfg.rxHead) >= RECE_BUF_MAX_LEN)
{
dmaCfg.rxHead = 0;
}
}
 
return cnt;
}
</syntaxhighlight>串口DMA打印函数,通用不是真正的TX打印函数,只是将当前要打印的数据分配好,并且使能打印的标识。<syntaxhighlight lang="c++" line="1" start="142">
uint16_t UartDma_Write(uint8_t *buf, uint16_t len)
{
uint16_t cnt;
uint8_t txSel;
uint8_t txIdx;
 
// Enforce all or none.
if ((len + dmaCfg.txIdx[dmaCfg.txSel]) > SENT_BUF_MAX_LEN)
{
return 0;
}
 
txSel = dmaCfg.txSel;
txIdx = dmaCfg.txIdx[txSel];
 
for (cnt = 0; cnt < len; cnt++)
{
dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
}
if (txSel != dmaCfg.txSel)
{
txSel = dmaCfg.txSel;
txIdx = dmaCfg.txIdx[txSel];
 
for (cnt = 0; cnt < len; cnt++)
{
dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
}
}
 
dmaCfg.txIdx[txSel] = txIdx;
 
if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
{
// TX DMA is expected to be fired
dmaCfg.txDMAPending = TRUE;
}
 
return cnt;
}
</syntaxhighlight>获取当前串口RX缓存区的数据长度。<syntaxhighlight lang="c++" line="1" start="192">
extern uint16_t UartDma_Avail(void)
{
uint16_t cnt = 0;
if (DMA_NEW_RX_BYTE(dmaCfg.rxHead))
{
uint16_t idx;
for (idx = 0; idx < RECE_BUF_MAX_LEN; idx++)
{
if (DMA_NEW_RX_BYTE(idx))
{
cnt++;
}
}
}
return cnt;
}
</syntaxhighlight>串口轮询函数,分别处理串口RX和TX。
 
RX部分:判断是否接收超时,接收的数据是否超过我们设置的缓冲区大小,两种情况都会返回对应的事件标识。
 
TX部分:判断当前是否有数据需要打印,如果有,则打印。<syntaxhighlight lang="c++" line="1" start="220">
uint8_t UartDma_Poll(void)
{
uint16_t cnt = 0;
uint8_t evt = 0;
 
if(DMA_NEW_RX_BYTE(dmaCfg.rxHead))
{
uint16_t tail = findTail();
// If the DMA has transferred in more Rx bytes, reset the Rx idle timer.
if (dmaCfg.rxTail != tail)
{
dmaCfg.rxTail = tail;
 
if (dmaCfg.rxTick == 0)
{
dmaCfg.rxShdw = HAL_GetTick();
}
dmaCfg.rxTick = HAL_UART_DMA_IDLE;
}
else if (dmaCfg.rxTick)
{
uint32_t Tick = HAL_GetTick();
uint32_t delta = Tick >= dmaCfg.rxShdw ?
(Tick - dmaCfg.rxShdw ):
(Tick + (UINT32_MAX - dmaCfg.rxShdw));
if (dmaCfg.rxTick > delta)
{
dmaCfg.rxTick -= delta;
dmaCfg.rxShdw = Tick;
}
else
{
dmaCfg.rxTick = 0;
}
}
cnt = UartDma_Avail();
}
else
{
dmaCfg.rxTick = 0;
}
 
if (cnt >= HAL_UART_DMA_FULL)
{
evt = HAL_UART_RX_FULL;
}
else if (cnt && !dmaCfg.rxTick)
{
evt = HAL_UART_RX_TIMEOUT;
}
if (dmaCfg.txShdwValid)
{
uint32_t decr = HAL_GetTick() - dmaCfg.txShdw;;
if (decr > dmaCfg.txTick)
{
// No protection for txShdwValid is required
// because while the shadow was valid, DMA ISR cannot be triggered
// to cause concurrent access to this variable.
dmaCfg.txShdwValid = FALSE;
}
}
if (dmaCfg.txDMAPending && !dmaCfg.txShdwValid)
{
// Clear the DMA pending flag
dmaCfg.txDMAPending = FALSE;
//Send data
if(dmaSendCb)
{
dmaSendCb(dmaCfg.txBuf[dmaCfg.txSel],dmaCfg.txIdx[dmaCfg.txSel]);
}
dmaCfg.txSel ^= 1;
}
 
return evt;
}
</syntaxhighlight>串口TX打印完成的回调函数,主要功能就是判断串口是否还没有需要打印的数据,如果有,则继续打印。<syntaxhighlight lang="c++" line="1" start="310">
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == hDmaUart)
{
// Indicate that the other buffer is free now.
dmaCfg.txIdx[(dmaCfg.txSel ^ 1)] = 0;
// Set TX shadow
dmaCfg.txShdw = HAL_GetTick();
dmaCfg.txShdwValid = TRUE;
 
// If there is more Tx data ready to go, re-start the DMA immediately on it.
if (dmaCfg.txIdx[dmaCfg.txSel])
{
// UART TX DMA is expected to be fired
dmaCfg.txDMAPending = TRUE;
}
}
}
</syntaxhighlight>
== 实验13-TFT显示屏 ==
510
个编辑