你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

USART1配置了dma IDLE空闲中断,接收缓存区收不到数据

[复制链接]
BigPoint 提问时间:2024-7-30 15:22 / 未解决

目前状况是32连接ttl转usb到电脑串口调试助手,发送数据完全正常,接收数据也可以进串口的idle空闲中断,但是DMA_GetCurrDataCounter(DMA2_Stream5);获得的数据一直是最大值,也就是没有数据进来.

这是我串口的调试信息:

image.png

发送数据后这三行打印数据为中断内部的打印.显示是没有数据

uart.c

//uart.c
#include "uart.h"

//指定串口2接收数据计数 - 最开始接收数据个数为0 
u32 UART1_RxCounter = 0; 
//定义串口2接收缓冲区 
u8 UART1_RxBuff[UART1_RXBUFF_SIZE];
//定义串口2发送缓冲区 
u8 UART1_TxBuff[UART1_TXBUFF_SIZE];

u8 UART1_CMD[UART1_RXBUFF_SIZE];

void USART1_Init(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    /* 使能时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);//使能DMA2时钟

    /* GPIO相关配置 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//无上拉  无下拉
    GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed ;//高速

    GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;//配置发送引脚
    GPIO_Init (USART1_TX_GPIO_PORT,&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;//配置接收引脚
    GPIO_Init (USART1_RX_GPIO_PORT,&GPIO_InitStructure);
    /* 将对应的IO口连接到外设,开始启动复用功能 */
    GPIO_PinAFConfig(USART1_TX_GPIO_PORT,USART1_TX_SOURCE,USART1_TX_AF);
    GPIO_PinAFConfig(USART1_RX_GPIO_PORT,USART1_RX_SOURCE,USART1_RX_AF);

    /* USART1的相关配置 */
    USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;//波特率115200
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx ;//USART模式控制:同时使能接收和发送
    USART_InitStructure.USART_Parity = USART_Parity_No;//校验位选择:不使用校验
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位:1个停止位
    USART_InitStructure.USART_WordLength  = USART_WordLength_8b ;//字长(数据位+校验位):8 
    USART_Init(USART1,&USART_InitStructure);


    /* 配置USART为中断源 */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStructure);

    /* 配置发送 */
    DMA_DeInit(USART1_TX_DMA_STREAM);

    DMA_InitStructure.DMA_BufferSize = UART1_TXBUFF_SIZE;//随便配置,因为在接收到数据后我们会重新给这值赋值
    DMA_InitStructure.DMA_Channel = USART1_TX_DMA_CHANNEL;//串口发送通道
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//DMA搬运方向:存储器到外设

    DMA_InitStructure.DMA_Memory0BaseAddr = (u32)UART1_TxBuff;//存储器地址
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择:单次模式
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据宽度:字节
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//使能存储器地址自动递增功能
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA 传输模式选择:一次传输
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//外设地址
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择:单次模式
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度:字节
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//禁止外设地址自动递增功能
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//软件设置数据流的优先级:中等
    /* FIFO不用随便配置 */
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

    DMA_Init(USART1_TX_DMA_STREAM, &DMA_InitStructure);

//    DMA_ClearITPendingBit(USART1_TX_DMA_STREAM,DMA_IT_TCIF7); //清除标志位

    DMA_Cmd(USART1_TX_DMA_STREAM, DISABLE);//发送先失能

    DMA_ITConfig(USART1_TX_DMA_STREAM,DMA_IT_TC,ENABLE); //使能发送完成中断  

    NVIC_InitStructure.NVIC_IRQChannel                   = DMA2_Stream7_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* 配置接收 */
    DMA_InitStructure.DMA_Memory0BaseAddr = (u32)UART1_RxBuff;//存储器地址
    DMA_InitStructure.DMA_BufferSize = UART1_RXBUFF_SIZE;//接收数据的长度
    DMA_InitStructure.DMA_Channel = USART1_RX_DMA_CHANNEL;//串口发送通道
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//DMA搬运方向:外设到存储器
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA 传输模式选择:一次传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//软件设置数据流的优先级:高

    /* 其余配置与上面一样 */
    DMA_Init(USART1_RX_DMA_STREAM, &DMA_InitStructure);

    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//开启空闲中断

    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

    USART_Cmd(USART1,ENABLE);//使能串口

    DMA_Cmd(USART1_RX_DMA_STREAM, ENABLE);//接收使能
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int c, FILE *fp) {
    //首先判断发送数据寄存器是否还有数据,如果有数据目前采用轮询方式死等,知道数据发送完毕
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
    //发送字符
    USART_SendData(USART1, c);
    return c;
}

void UART1_Putc(u8 c) {
    //首先判断发送数据寄存器是否还有数据,如果有数据目前采用轮询方式死等,知道数据发送完毕
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

    //发送字符
    USART_SendData(USART1, c);
}

//定义串口1发送字符串函数
//pstr = "hello,world\n"
void UART1_Puts(u8 *pstr) {
    while(*pstr) {
        UART1_Putc(*pstr);
        pstr++;
    }
}

uart.h

//uart.h
#ifndef  __UART_H
#define  __UART_H

//包含头文件
#include "public.h"

typedef struct __FILE FILE;

#define  T_Pri  printf
//#define  T_Pri  

#define  UART1_TXBUFF_SIZE  128  //串口1发送缓冲区大小
#define  UART1_RXBUFF_SIZE  128  //串口1接收缓冲区大小

extern u32 UART1_RxCounter;//指定串口1接收数据计数 
//声明串口1接收缓冲区 
extern u8 UART1_RxBuff[UART1_RXBUFF_SIZE];
//声明串口1发送缓冲区 
extern u8 UART1_TxBuff[UART1_TXBUFF_SIZE];

extern u8 UART1_CMD[UART1_RXBUFF_SIZE];

#define USART1_BAUDRATE           115200

/* 串口Tx——PA9 */
#define USART1_TX_PIN             GPIO_Pin_9
#define USART1_TX_GPIO_PORT      GPIOA
#define USART1_TX_SOURCE             GPIO_PinSource9
#define USART1_TX_AF                 GPIO_AF_USART1


/* 串口Rx——PA10 */
#define USART1_RX_PIN            GPIO_Pin_10
#define USART1_RX_GPIO_PORT      GPIOA
#define USART1_RX_SOURCE             GPIO_PinSource10
#define USART1_RX_AF                 GPIO_AF_USART1

/* DMA */
#define USART1_TX_DMA_CHANNEL    DMA_Channel_4
#define USART1_TX_DMA_STREAM         DMA2_Stream7

#define USART1_RX_DMA_CHANNEL    DMA_Channel_4
#define USART1_RX_DMA_STREAM         DMA2_Stream5

extern void USART1_Init(void);
extern void UART1_Puts(u8 *pstr);

#endif

USART1_IRQHandler中断处理函数:

void USART1_IRQHandler(void)
{
    uint16_t temp;

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        DMA_Cmd(DMA2_Stream5, DISABLE);
        USART1->SR; //先读状态寄存器
        USART1->DR; //后读数据寄存器
        //首先要获取此次串口1利用DMA接收到的数据大小=DMA总的内存大小-剩余DMA缓冲区大小
        UART1_RxCounter = UART1_RXBUFF_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
        printf("UART1_RxBuff = [%s]\r\n", UART1_RxBuff);
        printf("UART1_RxCounter = [%d]\r\n", UART1_RxCounter);
        printf("DMA_GetCurrDataCounter(DMA2_Stream5) = [%d]\r\n", DMA_GetCurrDataCounter(DMA2_Stream5));
        DMA2_Stream5->NDTR = UART1_RXBUFF_SIZE;
        DMA_Cmd(DMA2_Stream5, ENABLE);
    }
}

vTaskForCmd命令任务:

void vTaskForCmd(void *arg) {
    const TickType_t xTicksToWait = pdMS_TO_TICKS(500);
    while(1){

        //采用DMA+IDLE,如果下位机将上位机发送来的命令接收完毕了,下位机才能去查找命令
        if(UART1_RxCounter != 0) {
            //下位机先给上位机发送一个请求输入数据的提示信息
            UART1_Puts("\n Receive command is: ");
            UART1_Puts(UART1_RxBuff);
            UART1_Puts("\n");
            //下位机接收到上位机发送来的控制命令操作对应的硬件
            pcmd = find_cmd((char *)UART1_RxBuff); //根据上位机发送来的命令名称找到对应的命令对象
            if(pcmd != 0)
                pcmd->callback(); //调用匹配成功的命令的处理函数
            else
                UART1_Puts("invalid command\n");
            UART1_RxCounter = 0;    //重新等待下一个命令
        }
        vTaskDelay(xTicksToWait);
    }
}
收藏 评论2 发布时间:2024-7-30 15:22

举报

2个回答
butterflyspring 回答时间:2024-7-30 17:37:49
可以示波器看看上位机发送数据时,是不是先发出一个 idle 帧。



有些串口外设是以 IDLE 帧开始的。
xmshao 回答时间:2024-7-31 15:18:07
看你是基于标准库组织的代码,要是基于HAL库就方便了。


现在没收到数据,重点检查DMA接收这块的配置。


如果每次读到的数据剩余都是最大值,说明DMA没有发生接收。或者每次刚好收满重装了,我看你配置的是DMA接收循环模式,


但即使这样,可以通过接受缓冲看到数据。


另外,建议你先把FIFO关闭,即DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版