gd32f103c8t6开发板实现串口DMA空闲中断高速收发

采用GD32F103C8T6开发板实现了串口1和串口3的DMA空闲中断通信,在串口1上还添加了SP485,实现了485方向控制,

串口1的针脚为PA9和PA10

串口3的针脚为PB10和PB11,

由于在串口1添加了485模块,所以必须控制数据收发的方向控制,只有485模块的DE和RE为低电平时才能接收,高电平时才能发送,方向控制的针脚为PA8,实现代码如下:

/*
USART0 
PA9:TX  发送
PA10:RX 接收
DMA_CH4为接收
DMA_CH3为发送
*/

#include <string.h>
#include "gd32f10x.h"
#include "systick.h"
#include "usart0_rs485.h"

uint8_t usart0_rx_buf[256];
uint8_t usart0_tx_buf[256];
volatile static uint8_t usart0_tx_busy = 0; //发送状态
volatile uint8_t usart0_rx_flag = 0; //接收状态
volatile uint16_t usart0_rx_len = 0; //接收长度

//若串口0添加了485,则需要配置485方向控制使能
#define rs485_rx_en GPIO_BC(GPIOA)=GPIO_PIN_8;
#define rs485_tx_en GPIO_BOP(GPIOA)=GPIO_PIN_8;

void USART0_nvic_config(void)
{
    /* 设置优先级组 */
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    
    //USART0 DMA0发送中断
    nvic_irq_enable(DMA0_Channel3_IRQn, 1, 0);
    
    //USART0 DMA0接收中断
    nvic_irq_enable(DMA0_Channel4_IRQn, 1, 0);
    
    //USART0 串口中断
    nvic_irq_enable(USART0_IRQn, 0, 0);
}

void USART0_rcu_config()
{
    rcu_periph_clock_enable(RCU_DMA0);
        
    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART0);
    
    rcu_periph_clock_enable(RCU_AF);    
}

void USART0_dma_recv_config()
{
    dma_parameter_struct dma_init_struct;
    
    /* deinitialize DMA channel4 (USART0 rx) */
    dma_deinit(DMA0, DMA_CH4);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)usart0_rx_buf;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = sizeof(usart0_rx_buf);
    dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART0);;
    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_CH4, &dma_init_struct);

    //dma_interrupt_enable(d->dma_periph, d->dma_rx_ch, DMA_INT_FTF);
    
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH4);
    /* enable DMA channel4 */
    dma_channel_enable(DMA0, DMA_CH4);    
    
}

void USART0_dma_send_config()
{
    uint32_t dma = DMA0;
    dma_channel_enum ch = DMA_CH3;
    dma_parameter_struct ds;
    uint32_t cpLen = sizeof(usart0_tx_buf);
      
    
    /* 复位DMA参数结构体变量 */    
    dma_struct_para_init(&ds);
    dma_deinit(dma, ch);
    ds.direction = DMA_MEMORY_TO_PERIPHERAL;
    ds.memory_addr = (uint32_t)usart0_tx_buf;
    ds.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    ds.memory_width = DMA_MEMORY_WIDTH_8BIT;
    ds.number = cpLen;
    ds.periph_addr = (uint32_t)&USART_DATA(USART0);
    ds.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    ds.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    ds.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(dma, ch, &ds);

    dma_circulation_disable(dma, ch);
    dma_memory_to_memory_disable(dma, ch);
    dma_interrupt_enable(dma, ch, DMA_INT_FTF);
    //dma_channel_enable(dma, ch);
    usart_dma_transmit_config(dma, USART_TRANSMIT_DMA_ENABLE);
}

void USART0_dma_send(uint8_t *p, uint32_t number)
{
    uint32_t dma = DMA0;
    dma_channel_enum ch = DMA_CH3;    
    uint32_t cpLen = number;
    if(cpLen > sizeof(usart0_tx_buf)){
        cpLen = sizeof(usart0_tx_buf);
    }
    memcpy(usart0_tx_buf, p, cpLen); 

    //准备发送
    rs485_tx_en;
  
    //关闭DMA发送
    dma_channel_disable(dma, ch);
    
    //在发送地址改变的情况下需要重新配置发送地址
    //dma_memory_address_config(dma, ch, (uint32_t)usart0_tx_buf);
    
    //重新配置发送数据的长度
    dma_transfer_number_config(dma, ch, number);
    
    //启动DMA发送
    dma_channel_enable(dma, ch);
}

void USART0_dma_recv_reconfig(void)
{
    //uint32_t dma = DMA0;
    //dma_channel_enum ch = DMA_CH4;
    usart0_rx_flag = 0;
    usart0_rx_len = 0;
    memset(usart0_rx_buf,0,sizeof(usart0_rx_buf));
}

void USART0_Config(void)
{
    uint32_t usart_periph = USART0;
    
    //配置中断
    USART0_nvic_config();
    
    //配置时钟
    USART0_rcu_config();
    
    //使用PA8控制485的方向
    gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP,ENABLE);
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
    rs485_rx_en;

    /* connect port to USARTx_Tx PA9 */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);

    /* connect port to USARTx_Rx PA10*/
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);

    /* USART configure */
    usart_deinit(usart_periph);
    usart_baudrate_set(usart_periph, 115200U);
    usart_word_length_set(usart_periph, USART_WL_8BIT);
    usart_stop_bit_set(usart_periph, USART_STB_1BIT);
    usart_parity_config(usart_periph, USART_PM_NONE);
    usart_receive_config(usart_periph, USART_RECEIVE_ENABLE);
    usart_transmit_config(usart_periph, USART_TRANSMIT_ENABLE);
    usart_hardware_flow_rts_config(usart_periph, USART_RTS_DISABLE);	/* 无硬件流控制 */
    usart_hardware_flow_cts_config(usart_periph, USART_CTS_DISABLE);	/* 无硬件流控制 */
    usart_dma_receive_config(usart_periph, USART_RECEIVE_DMA_ENABLE);
    usart_dma_transmit_config(usart_periph, USART_TRANSMIT_DMA_ENABLE);
    usart_enable(usart_periph);
    
    //配置DMA
    USART0_dma_recv_config();
    
    //配置串口空闲中断和错误中断    
    usart_interrupt_enable(usart_periph, USART_INT_IDLE); 
    usart_interrupt_enable(usart_periph, USART_INT_ERR);
    usart_interrupt_enable(usart_periph, USART_INT_PERR);    
    //配置传输完成中断
    usart_interrupt_enable(usart_periph, USART_INT_TC);    
    
    USART0_dma_send_config();
}

void USART0_IRQHandler(void)
{
    uint32_t usart_periph = USART0;
    uint32_t dma = DMA0;
    uint32_t err_flag = 0;
    uint32_t flag = 0;
    dma_channel_enum ch = DMA_CH4;
    
    uint32_t stat = USART_STAT(USART0);
    /* 统一错误处理(寄存器级) 性能影响‌:寄存器级操作比库函数更高效,但需确保位映射正确*/
    if(stat & (USART_FLAG_ORERR | USART_FLAG_FERR | USART_FLAG_NERR | USART_FLAG_PERR)) {
        uint16_t data = USART_DATA(USART0);  // 关键清除操作
        err_flag |= (stat & USART_FLAG_ORERR) ? 0x01 : 0;
        err_flag |= (stat & USART_FLAG_NERR)  ? 0x02 : 0;
        err_flag |= (stat & USART_FLAG_FERR)  ? 0x04 : 0;
        err_flag |= (stat & USART_FLAG_PERR)  ? 0x10 : 0;
    }
    
    if(RESET != usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_IDLE)) {
        /* clear IDLE flag */
        usart_interrupt_flag_clear(usart_periph, USART_INT_FLAG_IDLE);
        usart_data_receive(usart_periph);

        /* number of data received */
        usart0_rx_len = sizeof(usart0_rx_buf) - (dma_transfer_number_get(dma, ch));
        usart0_rx_flag = 1;

        //关闭DMA接收
        dma_channel_disable(dma, ch);
        //重新配置接收数据的长度
        dma_transfer_number_config(dma, ch, sizeof(usart0_rx_buf));
        //重启DMA接收
        dma_channel_enable(dma, ch);
    }
    
    /*传输完成中断
    触发时机:满足以下条件同时成立时触发:
    发送数据寄存器(TDR)为空
    移位寄存器完成最后一bit数据的物理发送
    */
    flag = USART_INT_FLAG_TC;
    if(usart_interrupt_flag_get(usart_periph, flag)){
        usart_interrupt_flag_clear(usart_periph, flag);
        usart0_tx_busy = 0;
        //传输完成后485启动接收使能,低电压
        rs485_rx_en;
    }
    
    /* 错误恢复处理 */
    if(err_flag){
        usart_disable(usart_periph);
        usart_enable(usart_periph); // 复位USART
    }
}

//DMA0 ch4接收中断
void DMA0_Channel4_IRQHandler()
{
    uint32_t usart_periph = USART0;
    uint32_t dma = DMA0;
    dma_channel_enum ch = DMA_CH4;
    if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_FTF)) {
        /* clear IDLE flag */
        usart_data_receive(usart_periph);
        
        usart0_rx_len = sizeof(usart0_rx_buf) - (dma_transfer_number_get(usart_periph, ch));
        usart0_rx_flag = 1;        
        //清除接收完成中断
        dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_FTF);
    }
    if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_ERR)) {
        dma_channel_disable(dma, ch);
        dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_ERR);
        dma_channel_enable(dma, ch);
    }
}

//USART0 DMA0 ch3发送中断
void DMA0_Channel3_IRQHandler(void)
{
    uint32_t dma = DMA0;
    dma_channel_enum ch = DMA_CH3;
    //触发时机:当DMA传输计数器递减至0时自动触发,代表可以添加数据到缓冲区了
    if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_FTF)){
        dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_FTF);
    }
    if(dma_interrupt_flag_get(dma, ch, DMA_INT_FLAG_ERR)) {
        dma_channel_disable(dma, ch);
        dma_interrupt_flag_clear(dma, ch, DMA_INT_FLAG_ERR);
        dma_channel_enable(dma, ch);
    }
}
void wait_usart0_send_finish(void)
{
    while(usart0_tx_busy){};
}

void usart0_set_tx_busy(uint8_t busy)
{
    usart0_tx_busy = busy;
}
uint8_t usart0_get_tx_busy(void)
{
    return usart0_tx_busy;
}

源代码地址为:gd32f103c8t6-practice: 练习gd32f103c8t6开发板

购买的gd32f103c8t6开发板的链接为:

全新GD32F103C8T6开发板 含例程 含教学视频 GD32学习板核心板-淘宝网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值