OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3430|回复: 4

STM32F103串口DMA+串口空闲中断接收数据前两次接收数据异常

[复制链接]

2

主题

30

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
204
金钱
204
注册时间
2019-9-20
在线时间
103 小时
发表于 2020-5-19 18:43:31 | 显示全部楼层 |阅读模式
5金钱
大家好!具体问题是这样的:
STM32F103C8T6的串口2的DMA收发,采用串口空闲中断,测试程序功能是将串口助手发送的数据发送回来:
1、第一次用串口助手发送数据,进入串口接收中断,获得的数据长度rs485_receive_len=0;
2、第二次发送,进中断,能收到数据,但是数据长度和数据都不对,比实际的大1。
3、从第三次发送开始开始,通讯就都正常了:



主要程序如下:

#define TX_BUF_SIZE 100
#define RX_BUF_SIZE 100


uint8_t Rs485_TX_Buf[TX_BUF_SIZE];
uint8_t Rs485_RX_Buf[RX_BUF_SIZE];
uint8_t rs485_receive_flag=0;  //串口接收数据标志位
uint8_t rs485_receive_len=0;  //串口接收数据长度
uint8_t dma_send_en=0;       //可以发送标志位



//u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度             
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHxMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
static void DMA1_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr,uint8_t dir)
{
        DMA_InitTypeDef DMA_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //使能DMA传输
        DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
        //DMA1_MEM_LEN=cndtr;
        DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
        DMA_InitStructure.DMA_MemoryBaseAddr = cmar;      //DMA内存基地址
       
        if(dir==0)
        { DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; } //数据传输方向,从内存读取发送到外设
        if(dir==1)
        { DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; } //数据传输方向,从外设读取发送到内存
       
        DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
       
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn; //使能
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级2级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6; //从优先级2级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
        NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
        DMA_ITConfig(DMA1_Channel7, DMA_IT_TC , ENABLE);                           
}

void RS485_DMA_Send(uint8_t *send_buff,uint8_t length)
{
        unsigned int counter = 0;
        GPIO_SetBits(RS485_RE_GPIO,RS485_RE_PIN);////485发送使能
        raw_delaynop(300);
        while(dma_send_en!=0)                                        //等待上次的DMA传输完成
        {
                counter ++;
                if(counter >= 0xffff)
                {
                        break;
                }
        }                                                 
        dma_send_en = 1;
        DMA_Cmd(DMA1_Channel7, DISABLE );  //关闭USART1 TX DMA1 所指示的通道
        DMA1_Channel7 -> CMAR = (u32)send_buff;
        DMA_SetCurrDataCounter(DMA1_Channel7,length);          //设置传输长度
        DMA_Cmd(DMA1_Channel7, ENABLE);  //使能USART1 TX DMA1 所指示的通道
}
                                 
//初始化IO 串口2
//pclk1CLK1时钟频率(Mhz)
//bound:波特率          
static void Rs485_usart_init(u32 bound)
{  
  USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE);//复位串口2
        RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止复位

        USART_InitStructure.USART_BaudRate = bound;//波特率设置
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据长度
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;///奇偶校验位
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式

  USART_Init(RS485_UART, &USART_InitStructure); ; //初始化串口

        NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口2中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级2级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5; //从优先级2级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
        NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

  USART_ClearITPendingBit(RS485_UART, USART_IT_TC);//清除中断TC位       
        USART_ClearITPendingBit(RS485_UART, USART_IT_TC);//清除中断TC位       
       
  USART_ITConfig(RS485_UART, USART_IT_IDLE, ENABLE); //开启IDLE中断
        USART_ITConfig(RS485_UART, USART_IT_RXNE, DISABLE);//关闭RXNE中断

  USART_Cmd(RS485_UART, ENABLE);                    //使能串口
        GPIO_ResetBits(RS485_RE_GPIO,RS485_RE_PIN);              //默认为接收模式
        raw_delaynop(9250);
}

static void RS485_DMA_Init()
{
        Rs485_usart_init(115200);
        USART_DMACmd(RS485_UART,USART_DMAReq_Tx,ENABLE); //使能串口2的DMA发送
        USART_DMACmd(RS485_UART,USART_DMAReq_Rx,ENABLE); //使能串口2的DMA接收
       
        //DMA1通道7,外设为串口2,存储器为SendBuff,长度SEND_BUF_SIZE.
        DMA1_Config(DMA1_Channel7,(uint32_t)&USART2->DR,(u32)Rs485_TX_Buf,TX_BUF_SIZE,0);
        //DMA1_Enable(DMA1_Channel7,100);//开始一次DMA传输!
        RS485_DMA_Send(Rs485_TX_Buf,100);
        DMA_GetCurrDataCounter(DMA1_Channel6);
        raw_delaynop(500);
        DMA_GetCurrDataCounter(DMA1_Channel6);
        raw_delaynop(500);
  DMA1_Config(DMA1_Channel6,(uint32_t)&USART2->DR,(u32)Rs485_RX_Buf,RX_BUF_SIZE,1); //数据传输方向不同       
}



void RS485_UART_IRQHandler(void)
{
        raw_enter_interrupt();
        uint8_t i;
        uint8_t res;            
        if(USART_GetITStatus(RS485_UART, USART_IT_IDLE) != RESET) //监测到总线空闲
        {         
                i = RS485_UART->SR;
                i = RS485_UART->DR;        //清除USART_IT_IDLE标志
                DMA_Cmd(DMA1_Channel6, DISABLE); //读取数据前,先关闭DMA
                i=100-DMA_GetCurrDataCounter(DMA1_Channel6);
                DMA_SetCurrDataCounter(DMA1_Channel6,RX_BUF_SIZE);
                DMA_Cmd(DMA1_Channel6, ENABLE ); //打开DMA
               
                rs485_receive_flag=1;
                rs485_receive_len=i;
        }
        raw_finish_int();
}


//RS485 DMA方式发送中断
void DMA1_Channel7_IRQHandler()
{
  /* Clear DMA Stream Transfer Complete interrupt pending bit */
        if ( DMA_GetFlagStatus(DMA1_FLAG_TC7)!=RESET )
        {
                  DMA_Cmd(DMA1_Channel7, DISABLE);
                        DMA_ClearITPendingBit(DMA1_FLAG_TC7);
                  dma_send_en=0;
                  raw_delaynop(9250);
                  GPIO_ResetBits(RS485_RE_GPIO,RS485_RE_PIN);
                  raw_delaynop(9250);
        }
}


void main()
{
        RS485_DMA_Init();
        while(1)
        {
                if(rs485_receive_flag==1&&rs485_receive_len!=0)
                {
                        rs485_receive_flag=0;
                        {
                                rs485_receive_flag=0;
                                memcpy(Rs485_TX_Buf,Rs485_RX_Buf,rs485_receive_len);
                                RS485_DMA_Send(Rs485_TX_Buf,rs485_receive_len);
                        }
                }
                raw_delayms(5);
  }
}





11111.png

最佳答案

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

61

帖子

0

精华

初级会员

Rank: 2

积分
62
金钱
62
注册时间
2018-12-27
在线时间
0 小时
发表于 2020-5-19 18:43:32 | 显示全部楼层
回复

使用道具 举报

2

主题

30

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
204
金钱
204
注册时间
2019-9-20
在线时间
103 小时
 楼主| 发表于 2020-5-19 18:43:32 | 显示全部楼层
正点原子 发表于 2020-5-20 01:12
参考下论坛其他网友的例程

谢谢原子哥!  问题解决了,初始化DMA后,没有使能Channel6. 进一次中断才能使能,所以丢了第一帧数据。DMA的初始化函数中加上DMA_Cmd(DMA1_Channel6, ENABLE);就可以了
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165524
金钱
165524
注册时间
2010-12-1
在线时间
2116 小时
发表于 2020-5-20 01:12:45 | 显示全部楼层
参考下论坛其他网友的例程
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

1

主题

51

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
326
金钱
326
注册时间
2018-10-21
在线时间
108 小时
发表于 2020-5-20 13:49:50 | 显示全部楼层
1111111111111111
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-8 04:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表