GD32F103使用串口DMA收发信息(无中断)
前言
对于信息无实时性响应,采用DMA模式对串口数据进行处理。这里没有用到收发中断。在收数据过程中,我们要保证读取数据的速率,否则,收到的数据还没读到,就有被后面的数据覆盖了。
一、GPIO+USART1+DMA基本设置
具体的usart1连接到哪个DMA,哪个ch,需要看user_manual手册---- DMA request mapping
void com_init(uint32_t com)
{
uint32_t com_id = 0U;
if(EVAL_COM0 == com){
com_id = 0U;
}else if(EVAL_COM1 == com){
com_id = 1U;
}
/* enable GPIO clock */
rcu_periph_clock_enable(COM_GPIO_CLK[com_id]);
/* enable USART clock */
rcu_periph_clock_enable(COM_CLK[com_id]);
/* connect port to USARTx_Tx */
gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, COM_TX_PIN[com_id]);
/* connect port to USARTx_Rx */
gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, COM_RX_PIN[com_id]);
/* USART configure */
usart_deinit(com);
usart_baudrate_set(com, 115200U);
usart_word_length_set(com, USART_WL_8BIT);
usart_stop_bit_set(com, USART_STB_1BIT);
usart_parity_config(com, USART_PM_NONE);
usart_hardware_flow_rts_config(com, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(com, USART_CTS_DISABLE);
usart_receive_config(com, USART_RECEIVE_ENABLE);
usart_transmit_config(com, USART_TRANSMIT_ENABLE);
usart_enable(com);
}
void usart_dma_config(void)
{
dma_parameter_struct dma_init_struct;
/* enable DMA0 */
rcu_periph_clock_enable(RCU_DMA0);
/* initialize USART */
com_init(EVAL_COM1);
/* deinitialize DMA channel6(USART1 tx) */
dma_deinit(DMA0, DMA_CH6);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)uart_data[1].DMA_TxBuf;//这个是DMA的数要存到哪个数组里,可根据自己需求替换
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = USART_TX_BUF_SIZE;//这个是数组的大小,可更改
dma_init_struct.periph_addr = USART1_DATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMA0, DMA_CH6, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH6);//将TX的DMA 循环发送disable,是因为我们需要自己控制如何发送数据。
/* enable DMA channel3 */
dma_channel_enable(DMA0, DMA_CH6);
/* USART DMA enable for transmission and reception */
usart_dma_transmit_config(USART1, USART_DENT_ENABLE);
usart_dma_receive_config(USART1, USART_DENR_ENABLE);
/* wait DMA Channel transfer complete */
// while(RESET == dma_flag_get(DMA0, DMA_CH6, DMA_FLAG_FTF));
/* deinitialize DMA channel5 (USART1 rx) */
dma_deinit(DMA0, DMA_CH5);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)uart_data[1].DMA_RxBuf;//这个是DMA的数要存到哪个数组里,可根据自己需求替换
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = USART_RX_BUF_SIZE;//自己换
dma_init_struct.periph_addr = USART1_DATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMA0, DMA_CH5, &dma_init_struct);
/* configure DMA mode */
dma_circulation_enable(DMA0, DMA_CH5);//将RX的循环接收开启,是因为我们不希望漏过所有的数据,当然这个前提是,你要及时处理DMA数组里面的数据
/* enable DMA channel4 */
dma_channel_enable(DMA0, DMA_CH5);
/* wait DMA channel transfer complete */
// while(RESET == dma_flag_get(DMA0, DMA_CH5, DMA_FLAG_FTF));
}
通过以上的设置,基本上,我们就实现了串口的DMA收发。但是,我们需要对这个发到的数据进行处理。由于我们关闭了循环发送,所以需要我们手动开启DMA发送。
二、处理收到的数据RX
代码如下(示例):
dma_transfer_number_get(DMA0, DMA_CH5)
这里我只说一下思路:1、将DMA数据存入环形缓存区,2、通过高于写入速度的速率去读环形缓存区的数据,使数据不会溢出。ps:在取数据之前,需用通过上面这个dma_transfer_number_get(DMA0, DMA_CH5)函数去查DMA写到哪个位置。
二、处理发送的数据TX
代码如下(示例):
void Usart_TxMan(INT8U Port)
{
uint16_t idx,TxLen=0;
INT8S tx_finish_flag = 0;
if((dma_flag_get(DMA0,DMA_CH6,DMA_FLAG_FTF)!=RESET))//ÉÏÒ»´ÎµÄÊý¾ÝÍêÈ«·¢ËÍÍê
{
tx_finish_flag = 1;
}
if(tx_finish_flag) //ÉÏÒ»´Î´«ÊäÍê³É
{
TxLen = (uart_data[Port].tx_write + USART_TX_BUF_SIZE - uart_data[Port].tx_read) % USART_TX_BUF_SIZE;
if(TxLen>0) // ÓÐÐèÒª·¢Ë͵ÄÊý¾Ý
{
for(idx = 0; idx < TxLen; idx++)
{
uart_data[Port].DMA_TxBuf[idx]=uart_data[Port].TxBuf[uart_data[Port].tx_read];
uart_data[Port].tx_read = (uart_data[Port].tx_read + 1) % USART_TX_BUF_SIZE;
}
if(Port==1)
{
dma_parameter_struct dma_init_struct;
dma_deinit(DMA0, DMA_CH6);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)uart_data[1].DMA_TxBuf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = TxLen;
dma_init_struct.periph_addr = USART1_DATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMA0, DMA_CH6, &dma_init_struct);
/* configure DMA mode */
dma_circulation_disable(DMA0, DMA_CH6);
/* enable DMA channel3 */
dma_channel_enable(DMA0, DMA_CH6);
/* USART DMA enable for transmission and reception */
usart_dma_transmit_config(USART1, USART_DENT_ENABLE);
dma_flag_clear(DMA0,DMA_CH6,DMA_FLAG_G);
}
else if(Port==0)
{
}
}
}
}
代码如上,思路是:在发送数据之前,看看dma_flag_get(DMA0,DMA_CH6,DMA_FLAG_FTF),也就是DMA写完成flag(不是中断,是标志)是否完成,若完成,代表上一次发送的数据发出去了,这时候才可以再写入,否则退出写入。
写入的办法就是重新设置一遍DMA,(在STM32中可以简单设置其中几个参数即可,我没有去测试GD中设置哪几个,所以全部重新设置了一遍)。这样就实现了tx。
总结
以上为DMA串口收发。