一、DMA的配置
这里的一些配置介绍在前两篇介绍串口 DMA发送的时候写过了,可以去看,配置是一样的,这里不多介绍;
static INT __dmaTxConfig (UART_SIOCHAN *pUartSioChan)
{
UINT32 uiCtlReVal;
UINT32 uiDmaM0Addr;
UINT32 uiDmaM1Addr;
UINT32 iDmaTxChan;
UINT32 uiBase;
UINT32 uiDmaX = pUartSioChan->SIOCHAN_pDmaBase;
UINT32 uiDmaTxPeri = pUartSioChan->SIOCHAN_iDmaTxPeri;
uiBase = pUartSioChan->SIOCHAN_pBase;
iDmaTxChan = pUartSioChan->SIOCHAN_iDmaTxChan;
rcu_periph_clock_enable(RCU_DMA1);
dma_single_data_parameter_struct DmaInitStruct;
dma_deinit(uiDmaX, iDmaTxChan);
DmaInitStruct.direction = DMA_MEMORY_TO_PERIPH;
DmaInitStruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; /* 内存自增 */
DmaInitStruct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; /* 存储和外设的传输宽度 */
DmaInitStruct.number = DMA_TXBUFF_SIZE;
DmaInitStruct.periph_addr = (UINT32)(uiBase + 0x4);
DmaInitStruct.periph_inc = DMA_MEMORY_INCREASE_DISABLE; /* 外设地址固定 */
DmaInitStruct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(uiDmaX, iDmaTxChan, DmaInitStruct);
dma_channel_subperipheral_select(uiDmaX, iDmaTxChan, uiDmaTxPeri); /* 外设选择 */
dma_flow_controller_config(uiDmaX, iDmaTxChan, DMA_FLOW_CONTROLLER_DMA);
uiDmaM0Addr = (UINT32)_G_ucTxbuffer1;
DMA_CHM0ADDR(uiDmaX, iDmaTxChan) = uiDmaM0Addr; /* 设置存储器0基地址 */
uiDmaM1Addr = (UINT32)_G_ucTxbuffer2;
DMA_CHM1ADDR(uiDmaX, iDmaTxChan) = uiDmaM1Addr; /* 设置存储器1基地址 */
uiCtlReVal = DMA_CHCTL(uiDmaX, iDmaTxChan);
uiCtlReVal &= ~(1 << 18); /* 不设置存储切换模式 */
uiCtlReVal &= ~(1 << 19); /* 首先设置存储器0作为存储区域 */
DMA_CHCTL(uiDmaX, iDmaTxChan) = uiCtlReVal;
dma_channel_disable(uiDmaX, iDmaTxChan); /* disable DMA channel */
return (ERROR_NONE);
}
二、串口发送数据
应用程序调用write函数去主动发送数据时,在驱动中首先会调用txStartUp()函数,此函数是开始通过串口发送数据,使用了DMA传输,首先这个数据就会被转移到DMA BUFF中;等开启DMA 传输,DMA会主动搬运数据到外设,而不经过CPU;
从TTY设备中获取数据放到DMA BUFF,代码如下:
DMA_TXBUFF_SIZE 表示DMA BUFF最多可以放置的字节数;
while(usart_flag_get(uiBase, USART_FLAG_TC) == RESET);
{
uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan); /* 双BUFF时判断 */
while (uiCount < DMA_TXBUFF_SIZE) {
if (GETTXCHAR(&cChar)) { /* 没有数据可以取出 */
break;
}
if (uiBuffUsed == DMA_MEMORY_0) { /* 确定使用的是哪一个BUFF */
_G_ucTxbuffer1[uiCount] = cChar;
uiBuffNext = DMA_MEMORY_1;
} else {
_G_ucTxbuffer2[uiCount] = cChar;
uiBuffNext = DMA_MEMORY_0;
}
++uiCount;
}
上面将数据放置到DMA BUFF中结束或者DMA BUFF满了,需要配置DMA 计数器的值和要配置用于DMA传输的BUFF(因为这里是双BUFF):
dma_interrupt_disable(uiDmaX, iDmaTxChan, DMA_CHXCTL_FTFIE); /* full transfer finish interrupt enable*/
dma_channel_disable(uiDmaX, iDmaTxChan); /* 通道失能 */
_G_TxNextDate = uiCount;
DMA_CHCNT(uiDmaX, iDmaTxChan) = _G_TxNextDate;//dma要发送的数据多少
dmaSwitchBuffer(uiDmaX, iDmaTxChan,_G_iTxNextBuff);
_G_iTxNextBuff = !(_G_iTxNextBuff);
为了提高效率,如果填充完第一个DMA BUFF之后,仍然有数据需要传输,可以继续往第二个DMA BUFF中填充数据;
uiBuffNext 是使用的哪一个DMA BUFF标志;
uiCount 记录了传输到DMA BUFF中的数据大小
if (uiCount == DMA_TXBUFF_SIZE) { /* 上一个BUFF被填满 */
uiCount = 0;
while (uiCount < DMA_TXBUFF_SIZE) {
_PrintFormat("func= %s, line = %d\r\n", __func__, __LINE__);
if (GETTXCHAR(&cChar)) {
break;
}
if (uiBuffNext == DMA_MEMORY_0) {
_PrintFormat("1func= %s, line = %d\r\n", __func__, __LINE__);
_G_ucTxbuffer1[uiCount] = cChar;
} else {
_G_ucTxbuffer2[uiCount] = cChar;
}
++uiCount;
}
_G_TxNextDate = uiCount;
}
下面就是开启DMA传输,完成中断使能,应该先使能完成中断,再开启传输;
dma_interrupt_enable(uiDmaX, iDmaTxChan, DMA_CHXCTL_FTFIE); /* full transfer finish interrupt enable*/
dma_channel_enable(uiDmaX, iDmaTxChan);
DMA传输完成:指的是第一个DMA BUFF传输完成,传输完成指的是DMA CNT的值减到0,每通过DMA 传输一个字节,那么CNT就会减一;
三、完成中断的服务函数
第一个BUFF传输完成进入传输完成中断服务函数,在这个函数里面需要开启第二个DAM BUFF的传输;
并完成修改下一个使用DMA BUFF的标志;
if((RESET != dma_interrupt_flag_get(uiDmaX, iDmaTxChan, DMA_INTF_FTFIF))) {
UINT32 cnt = REG32(uiDmaX + (0x14 + 0x18 *iDmaTxChan));
_PrintFormat("func= %s, line = %d,cnt = %d\r\n", __func__, __LINE__, cnt);
dma_interrupt_flag_clear(uiDmaX, iDmaTxChan, DMA_INTC_FTFIFC);
if (_G_TxNextDate == 0) {
dma_channel_disable(uiDmaX, iDmaTxChan);
}
uiCount = 0;
uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan); /* 传输完成自动切换 */
_PrintFormat("func= %s, line = %d, uiBuffUsed = %d\r\n", __func__, __LINE__, uiBuffUsed);
if (_G_TxNextDate != 0) {
dma_channel_disable(uiDmaX, iDmaTxChan); /* 通道失能传输数据 */
DMA_CHCNT(uiDmaX, iDmaTxChan) = _G_TxNextDate; /* 设置传输长度 */
dmaSwitchBuffer(uiDmaX, iDmaTxChan,_G_iTxNextBuff);
_G_iTxNextBuff = !(_G_iTxNextBuff);
_PrintFormat("func= %s, line = %d,_G_TxNextDate = %d\r\n", __func__, __LINE__, _G_TxNextDate);
dma_channel_enable(uiDmaX, iDmaTxChan); /* 中断开始传输数据 */
uiBuffUsed = dma_using_memory_get(uiDmaX, iDmaTxChan); /* 传输完成自动切换 */
}
if (_G_TxNextDate == DMA_TXBUFF_SIZE) {
if (uiBuffUsed == DMA_MEMORY_0) {
uiBuffSelect = DMA_MEMORY_1;
} else {
uiBuffSelect = DMA_MEMORY_0;
}
while (uiCount < DMA_TXBUFF_SIZE) {
if (GETTXCHAR(&cTx)) {
_PrintFormat("func= %s, line = %d\r\n", __func__, __LINE__);
break;
}
if (uiBuffSelect == DMA_MEMORY_0) {
_G_ucTxbuffer1[uiCount] = cTx;
} else {
_G_ucTxbuffer2[uiCount] = cTx;
}
++uiCount;
}
_G_TxNextDate = uiCount;
} else {
_G_TxNextDate = 0;
dma_interrupt_flag_clear(uiDmaX, iDmaTxChan, DMA_INTC_FTFIFC);
_PrintFormat("func= %s, line = %d, _G_TxNextDate = %d\r\n", __func__, __LINE__, _G_TxNextDate);
}
}