打印
[研电赛技术支持]

GD32F103/303串口+空闲中断连续接收数据

[复制链接]
1398|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
t61000|  楼主 | 2022-11-18 15:29 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
头文件#ifndef _UART_H_
#define _UART_H_

#include "stdint.h"
#include "gd32f30x.h"
#define CACHE_NUM 128



//数据接收处理函数
typedef void (*recv_hanled)(uint8_t *data, uint16_t len);

typedef struct
{

    uint32_t usart_periph;                        //外设名称
    uint32_t dma_periph;                                //dma 外设
    dma_channel_enum dma_channelx;//dma外设通道
    uint8_t dma_nvic_irq;                         //dma中断号
    uint8_t uart_nvic_irq;                        //串口中断号
    uint8_t dma_or_idle;                                //传输完成标记和收到一帧数据标记dm
    uint16_t data_num;                                        //已经接收到的数据量
    uint8_t uart_rx_buffers[CACHE_NUM];//接收数据缓冲区
    recv_hanled hanled_fun;                        //数据接收处理函数
} uart_dam_t;


//初始化串口涉及的时钟和gpio gpio USART/UART
void bsp_uart_gpio_rcu_init(uint32_t usart_periph);

/*基本初始化函数*/
void bsp_uart_usart_base_init(uint32_t usart_periph);

//串口相关自定义结构初始化
uart_dam_t * init_uart_dma_struct(uint32_t usart_periph);

//使能串口中断
void bsp_uart_enable_uart_interrupt(uart_dam_t *puart_dma);

//dma 外设初始化
int bsp_uart_dma_nvic_init(uart_dam_t *puart_dma);

//注册一个处理函数
int bsp_uart_register_handle(uart_dam_t *puart_dma, recv_hanled hanled_fun);

循环调用函数,处理串口信息
void bsp_uart_dma_procees(uint32_t tick);
#endif



使用特权

评论回复
沙发
t61000|  楼主 | 2022-11-18 15:32 | 只看该作者
源文件
#include "uart.h"
#include "stdio.h"
#include "gd32f30x.h"




//每个串口外设一个自定义结构

uart_dam_t uart0_dma;
uart_dam_t uart1_dma;
uart_dam_t uart2_dma;
uart_dam_t uart3_dma;

//接收到的数据个数
volatile uint16_t uart_rxcount[5];
//串口空闲或缓冲器慢标志
volatile uint8_t idle_or_full = 0;

/* 重定向printf函数 */
int fputc(int ch, FILE *f)
{
    int cnt = 1000;
    usart_data_transmit(USART0, (uint8_t)ch);

    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE) && cnt--);


    usart_data_transmit(USART1, (uint8_t)ch);
    cnt = 1000;

    while(RESET == usart_flag_get(USART1, USART_FLAG_TBE) && cnt--);

    return ch;
}
//初始化自定义结构体
uart_dam_t * init_uart_dma_struct(uint32_t usart_periph)
{
                //UART0 DMA0-CH4
                //UART1 DMA0-CH5
                //UART2-DMA0-CH2
                //UART3-DMA1-CH2


    uart_dam_t *puart_dma = NULL;


    if(usart_periph == USART0)
    {
        puart_dma = &uart0_dma;
        puart_dma->dma_periph = DMA0;
        puart_dma->dma_channelx = DMA_CH4;
        puart_dma->dma_nvic_irq = DMA0_Channel4_IRQn;
        puart_dma->uart_nvic_irq = USART0_IRQn;
    }
    else if(usart_periph == USART1)
    {
        puart_dma = &uart1_dma;
        puart_dma->dma_periph = DMA0;
        puart_dma->dma_channelx = DMA_CH5;
        puart_dma->dma_nvic_irq = DMA0_Channel5_IRQn;
        puart_dma->uart_nvic_irq = USART1_IRQn;
    }
    else if(usart_periph == USART2)
    {
        puart_dma = &uart2_dma;
        puart_dma->dma_periph = DMA0;
        puart_dma->dma_channelx = DMA_CH2;
        puart_dma->dma_nvic_irq = DMA0_Channel2_IRQn;
        puart_dma->uart_nvic_irq = USART2_IRQn;
    }
    else if(usart_periph == UART3)
    {
        puart_dma = &uart3_dma;
        puart_dma->dma_periph = DMA1;
        puart_dma->dma_channelx = DMA_CH2;
        puart_dma->dma_nvic_irq = DMA0_Channel2_IRQn;
        puart_dma->uart_nvic_irq = UART3_IRQn;
    }

    puart_dma->usart_periph = usart_periph;
    return puart_dma;
}


/*!
    \brief      初始化串口涉及的时钟和gpio gpio USART/UART
    \param[in]  usart_periph: USARTx(x=0,1,2)/UARTx(x=3,4)
    \param[out] none
    \retval     none
*/

void bsp_uart_gpio_rcu_init(uint32_t usart_periph)
{
    /*********************串口0********************/
    if(usart_periph == USART0)
    {
        /* 使能串口时钟 */
        rcu_periph_clock_enable(RCU_USART0);
        /* 使能gpio时钟 */
        rcu_periph_clock_enable(RCU_GPIOA);

        /* gpio IO 初始化发送引脚 */
        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
        /* 初始化接收引脚 */
        gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    }
    /*********************串口0********************/

    /*********************串口1********************/
    else if(usart_periph == USART1)
    {
        /* 使能串口时钟 */
        rcu_periph_clock_enable(RCU_USART1);

        /* 使能gpio时钟 */
        rcu_periph_clock_enable(RCU_GPIOA);

        /* gpio IO 初始化发送引脚 */
        gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
        /* 初始化接收引脚 */
        gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
    }
    /*********************串口1********************/

    /*********************串口2********************/
    else if(usart_periph == USART2)
    {
        /* 使能串口时钟 */
        rcu_periph_clock_enable(RCU_USART2);

        /* 使能gpio时钟 */
        rcu_periph_clock_enable(RCU_GPIOB);

        /* gpio IO 初始化发送引脚 */
        gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
        /* 初始化接收引脚 */
        gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    }
    /*********************串口2********************/

    /*********************串口3********************/
    else if(usart_periph == UART3)
    {
        /* 使能串口时钟 */
        rcu_periph_clock_enable(RCU_UART3);

        /* 使能gpio时钟 */
        rcu_periph_clock_enable(RCU_GPIOC);

        /* gpio IO 初始化发送引脚 */
        gpio_init(GPIOC, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
        /* 初始化接收引脚 */
        gpio_init(GPIOC, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    }
    /*********************串口3********************/

    /*********************串口4********************/
    else if(usart_periph == UART4)
    {
        /* 使能串口时钟 */
        rcu_periph_clock_enable(RCU_UART4);
    }

    /*********************串口4********************/

}

/*基本初始化函数*/
void bsp_uart_usart_base_init(uint32_t usart_periph)
{
    //外设的gpio初始化和时钟初始化
    bsp_uart_gpio_rcu_init(usart_periph);

    /* 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_hardware_flow_rts_config(usart_periph, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(usart_periph, USART_CTS_DISABLE);
    //串口接收使能
    usart_receive_config(usart_periph, USART_RECEIVE_ENABLE);
    //串口发送使能
    usart_transmit_config(usart_periph, USART_TRANSMIT_ENABLE);
    //使能串口
    usart_enable(usart_periph);
}


/*
        使能串口中断
*/
void bsp_uart_enable_uart_interrupt(uart_dam_t *puart_dma)
{
    /*中断管理器使能,并分配优先级*/

    nvic_irq_enable(puart_dma->uart_nvic_irq, 1, 1);

    /*清除中断标志*/
    usart_interrupt_flag_clear(puart_dma->usart_periph, USART_INT_FLAG_IDLE);
    /* 使能串口中断 */
    usart_interrupt_enable(puart_dma->usart_periph, USART_INT_IDLE);//空闲中断
}



/*
        功能:DMA 中断接收数据,uart5 不可以使用dma传输数据
        hope_len:希望接收的数据个数
        circulation:是否使用连续模式
*/
int  bsp_uart_dma_nvic_init(uart_dam_t *puart_dma)
{
    dma_parameter_struct dma_init_struct;

    if(puart_dma == NULL)
    {
        return 0;
    }

    /* enable DMA0 clock 使能DMA0 的时钟*/
    if(puart_dma->usart_periph == USART0 ||
            puart_dma->usart_periph == USART1 ||
            puart_dma->usart_periph == USART2)
    {
        rcu_periph_clock_enable(RCU_DMA0);
    }
    else if(puart_dma->usart_periph == UART3)
    {
        rcu_periph_clock_enable(RCU_DMA1);
    }

    //中断管理器开启通道中断
    nvic_irq_enable(puart_dma->dma_nvic_irq, 0, 1);
    // 复位代码指定通道
    dma_deinit(puart_dma->dma_periph, puart_dma->dma_channelx);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY; //外设到内存
    dma_init_struct.memory_addr = (uint32_t)puart_dma->uart_rx_buffers;        //接收缓冲区开始地址
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//内存地址自动增长
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;//数据长度8bit
    dma_init_struct.number = CACHE_NUM;//缓冲区大小
#define USART0_DATA_ADDRESS      ((uint32_t)&USART_DATA(USART0))
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;//外设寄存器地址
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//外设寄存器地址不自动增加
    dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;//外输数据宽度
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;//DMA优先级
    dma_init(puart_dma->dma_periph, puart_dma->dma_channelx, &dma_init_struct);
    dma_circulation_enable(puart_dma->dma_periph, puart_dma->dma_channelx);//连续传输

    //数据传输方式不是内存到内存
    dma_memory_to_memory_disable(puart_dma->dma_periph, puart_dma->dma_channelx);

    /* USART DMA0 串口0DMA 数据接收使能 */
    usart_dma_receive_config(puart_dma->usart_periph, USART_DENR_ENABLE);
    /* enable DMA0 串口0 DMA 接收完成中断使能 */
    dma_interrupt_enable(puart_dma->dma_periph, puart_dma->dma_channelx, DMA_INT_FTF);
    /* enable DMA0 启用指定的DMA通道*/
    dma_channel_enable(puart_dma->dma_periph, puart_dma->dma_channelx);
    return 1;
}

/*
        禁止中断和DMA
*/
void  bsp_uart0_init_idle_it_disable()
{
    //禁止中断
    nvic_irq_disable(USART0_IRQn);
    usart_interrupt_disable(USART0, USART_INT_IDLE);//空闲中断
    //禁止DMA
    dma_channel_disable(DMA0, DMA_CH4);

}

/*
        串口0中断处理函数
*/
void USART0_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart0_dma;

    if(RESET != usart_interrupt_flag_get(p_uart_dma->usart_periph, USART_INT_FLAG_IDLE))
    {
        usart_data_receive(p_uart_dma->usart_periph);


        p_uart_dma->data_num = CACHE_NUM - dma_transfer_number_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

        p_uart_dma->dma_or_idle = 1;
        //重新使能dma,重新计算剩余未传输数量
        dma_channel_disable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);
        DMA_CHCNT(p_uart_dma->dma_periph, p_uart_dma->dma_channelx) = CACHE_NUM;
        dma_channel_enable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

    }
}
/*
        串口3中断处理函数
*/
void USART1_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart1_dma;

    if(RESET != usart_interrupt_flag_get(p_uart_dma->usart_periph, USART_INT_FLAG_IDLE))
    {
        usart_data_receive(p_uart_dma->usart_periph);


        p_uart_dma->data_num = CACHE_NUM - dma_transfer_number_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

        p_uart_dma->dma_or_idle = 1;
        //重新使能dma,重新计算剩余未传输数量
        dma_channel_disable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);
        DMA_CHCNT(p_uart_dma->dma_periph, p_uart_dma->dma_channelx) = CACHE_NUM;
        dma_channel_enable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

    }
}
/*
        串口2中断处理函数
*/
void USART2_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart2_dma;

    if(RESET != usart_interrupt_flag_get(p_uart_dma->usart_periph, USART_INT_FLAG_IDLE))
    {
        usart_data_receive(p_uart_dma->usart_periph);


        p_uart_dma->data_num = CACHE_NUM - dma_transfer_number_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

        p_uart_dma->dma_or_idle = 1;
        //重新使能dma,重新计算剩余未传输数量
        dma_channel_disable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);
        DMA_CHCNT(p_uart_dma->dma_periph, p_uart_dma->dma_channelx) = CACHE_NUM;
        dma_channel_enable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

    }
}
/*
        串口3中断处理函数
*/
void UART3_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart3_dma;

    if(RESET != usart_interrupt_flag_get(p_uart_dma->usart_periph, USART_INT_FLAG_IDLE))
    {
        usart_data_receive(p_uart_dma->usart_periph);


        p_uart_dma->data_num = CACHE_NUM - dma_transfer_number_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

        p_uart_dma->dma_or_idle = 1;
        //重新使能dma,重新计算剩余未传输数量
        dma_channel_disable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);
        DMA_CHCNT(p_uart_dma->dma_periph, p_uart_dma->dma_channelx) = CACHE_NUM;
        dma_channel_enable(p_uart_dma->dma_periph, p_uart_dma->dma_channelx);

    }
}
/*串口0dma*/
void DMA0_Channel4_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart0_dma;

    //获取中断标记并判断否是置位
    if(dma_interrupt_flag_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_FTF))
    {
        //清除dma中断标记
        dma_interrupt_flag_clear(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_G);
        p_uart_dma->dma_or_idle = SET;
    }

}
/*串口1 dma*/
void DMA0_Channel5_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart1_dma;

    //获取中断标记并判断否是置位
    if(dma_interrupt_flag_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_FTF))
    {
        //清除dma中断标记
        dma_interrupt_flag_clear(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_G);
        p_uart_dma->dma_or_idle = SET;
    }

}
/*串口2 dma*/
void DMA0_Channel2_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart2_dma;

    //获取中断标记并判断否是置位
    if(dma_interrupt_flag_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_FTF))
    {
        //清除dma中断标记
        dma_interrupt_flag_clear(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_G);
        p_uart_dma->dma_or_idle = SET;
    }

}
/*串口3 dma*/
void DMA1_Channel2_IRQHandler(void)
{
    uart_dam_t *p_uart_dma = &uart3_dma;

    //获取中断标记并判断否是置位
    if(dma_interrupt_flag_get(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_FTF))
    {
        //清除dma中断标记
        dma_interrupt_flag_clear(p_uart_dma->dma_periph, p_uart_dma->dma_channelx, DMA_INT_FLAG_G);
        p_uart_dma->dma_or_idle = SET;
    }

}

/*注册一个处理函数*/
int bsp_uart_register_handle(uart_dam_t *puart_dma, recv_hanled hanled_fun)
{

    puart_dma->hanled_fun = hanled_fun;
}

//循环调用函数用于处理各个串口数据
void bsp_uart_dma_procees(uint32_t tick)
{
    if(uart0_dma.dma_or_idle)
    {
        uart_dam_t *puart_dma = &uart0_dma;
        puart_dma->dma_or_idle = 0;
        puart_dma->hanled_fun(puart_dma->uart_rx_buffers, puart_dma->data_num);
        puart_dma->data_num = 0;
    }
    else if(uart1_dma.dma_or_idle)
    {
        uart_dam_t *puart_dma = &uart1_dma;
        puart_dma->dma_or_idle = 0;
        puart_dma->hanled_fun(puart_dma->uart_rx_buffers, puart_dma->data_num);
        puart_dma->data_num = 0;
    }

    if(uart2_dma.dma_or_idle)
    {
        uart_dam_t *puart_dma = &uart2_dma;
        puart_dma->dma_or_idle = 0;
        puart_dma->hanled_fun(puart_dma->uart_rx_buffers, puart_dma->data_num);
        puart_dma->data_num = 0;
    }

    if(uart3_dma.dma_or_idle)
    {
        uart_dam_t *puart_dma = &uart3_dma;
        puart_dma->dma_or_idle = 0;
        puart_dma->hanled_fun(puart_dma->uart_rx_buffers, puart_dma->data_num);
        puart_dma->data_num = 0;
    }

}

使用特权

评论回复
板凳
t61000|  楼主 | 2022-11-18 15:34 | 只看该作者
测试
#include "gd32f30x.h"
#include "gd32f303_sys.h"
#include "systick.h"
#include "uart.h"
#include "stdio.h"
#include "adc.h"
#include "button.h"
#include "bsp_gpio.h"
#include "at24cxx.h"

void uart0_recv_hanled(uint8_t *data, uint16_t len)
{
    printf("read len = %d:", len);

    for(int i = 0; i < len; i++)
    {
        printf("%02x ", (int)data[i]);
    }

    printf("\r\n");
}
/*!
        主函数:
*/

int main(void)
{


    /* 配置系统时钟 */
    systick_config();
    //设置中断分组
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    /* gpio时钟使能*/

    // 配置MCU调试下载使用swd方式,同时将pb3 和PB4 作为普通gpio
    rcu_periph_clock_enable(RCU_AF);
    gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);
    //gpio
    bsp_gpio_init();
    //串口初始化
    bsp_uart_gpio_rcu_init(USART0);
    bsp_uart_usart_base_init(USART0);
    //下边是串口的数据接收使用dma方式需要调用和函数
    uart_dam_t *uart_dma = init_uart_dma_struct(USART0);
    bsp_uart_enable_uart_interrupt(uart_dma);
    bsp_uart_dma_nvic_init(uart_dma);
    bsp_uart_register_handle(uart_dma, uart0_recv_hanled);

    // bsp_at24c02_init();
    printf("hello gd32\r\n");
    //初始化ADC
//    adc_init();
//    init_btn();

    BEEP = 0;

    while(1)
    {
        if(tick % 500 == 0)
        {
            LED0 = !LED0;
            LED1 = !LED1;
            LED2 = !LED2;
        }

        //循环调用
        bsp_uart_dma_procees(tick);


    }

}

使用特权

评论回复
地板
t61000|  楼主 | 2022-11-18 15:42 | 只看该作者

使用特权

评论回复
5
t61000|  楼主 | 2022-11-18 15:42 | 只看该作者
# 测试图

使用特权

评论回复
6
chenqianqian| | 2022-11-19 10:57 | 只看该作者
这样发帖,不方便阅读啊。

使用特权

评论回复
7
daichaodai| | 2022-11-19 17:41 | 只看该作者
用中断发送和接受数据,效率更高。

使用特权

评论回复
8
tpgf| | 2022-12-7 13:51 | 只看该作者
每个数据包之间应该都有多长时间的空闲时间呢

使用特权

评论回复
9
磨砂| | 2022-12-7 14:17 | 只看该作者
如何在这种情况下测试丢包数据情况呢

使用特权

评论回复
10
晓伍| | 2022-12-7 14:38 | 只看该作者
确实轮询太浪费资源 使用中断接收就会好很多

使用特权

评论回复
11
八层楼| | 2022-12-7 14:53 | 只看该作者
目前定时的间隔是多少呀  感觉时间准吗

使用特权

评论回复
12
观海| | 2022-12-7 15:17 | 只看该作者
看代码 貌似楼主启用了dma是吗  如果代码非常简单的话  使用dma会不会反而复杂化呢

使用特权

评论回复
13
观海| | 2022-12-7 15:20 | 只看该作者
当出现串口堵塞的时候 一般如何处理呢

使用特权

评论回复
14
guanjiaer| | 2022-12-7 15:37 | 只看该作者
感觉过程处理的有点复杂了  其实流程还是很简单的

使用特权

评论回复
15
vivilyly| | 2022-12-11 17:56 | 只看该作者
可以使用DMA的方式进行数据的传输的吗?

使用特权

评论回复
16
louliana| | 2022-12-12 10:44 | 只看该作者
串口的空闲字符是用来激活空闲中断的吗

使用特权

评论回复
17
jkl21| | 2022-12-12 11:41 | 只看该作者
GD32F103有空闲中断的吗?

使用特权

评论回复
18
elsaflower| | 2022-12-12 12:12 | 只看该作者
串口通信数据连续性,该怎么处理?   

使用特权

评论回复
19
janewood| | 2022-12-12 12:52 | 只看该作者
串口接收比较好的处理方式了吗?              

使用特权

评论回复
20
houjiakai| | 2022-12-12 13:41 | 只看该作者
串口中断接收程序,怎样一次性接受多个字符?  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

124

帖子

0

粉丝