OpenEdv-开源电子网

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

开启串口DMA空闲中断接收不定长度数据求助

[复制链接]

6

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
432
金钱
432
注册时间
2016-1-19
在线时间
38 小时
发表于 2016-1-29 10:10:46 | 显示全部楼层 |阅读模式
3金钱
/**********************************************************************************
* 文件名  :usart1.c
* 硬件连接:------------------------
*          | PB6  - USART1(Tx)      |
*          | PB7  - USART1(Rx)      |
*           ------------------------
* 库版本  :ST3.5.0
* 作者    :大雄
* 时间    :2016-01-19
* 备注    :开启串口DMA空闲中断接收不定长度数据,可返回该数据内容及长度
**********************************************************************************/
#include "usart1.h"

uint8_t USART1_RX_BUF[USART1_LENTH_BUF];//接收缓冲,最大64个字节
uint8_t USART1_TX_BUF[USART1_LENTH_BUF];//发送缓冲,最大64个字节
uint8_t FLAG_USART_RX = 0;              //接收到数据
uint8_t LENTH_USART_RX = 0;             //接收到数据的长度

void USART1_Configuration(uint32_t bound)//bound为波特率
{
    GPIO_InitTypeDef    GPIO_InitStructure;
    USART_InitTypeDef   USART_InitStructure;
    DMA_InitTypeDef     DMA_Initstructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);//端口重映射
    //USART1_TX   PB6
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    //USART1_RX          PB7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);  

    //USART 初始化设置
    USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    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(USART1, &USART_InitStructure);

    DMA_Initstructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
    DMA_Initstructure.DMA_MemoryBaseAddr     = (uint32_t)USART1_RX_BUF;
    DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_Initstructure.DMA_BufferSize = 64;
    DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;
    DMA_Initstructure.DMA_Priority = DMA_Priority_High;
    DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_Initstructure);

    DMA_Cmd(DMA1_Channel5,ENABLE);//启动DMA

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  //若总线空闲,产生中断   
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);    //开启串口DMA接收

    USART_Cmd(USART1, ENABLE);                      //使能串口
    USART_ClearFlag(USART1, USART_FLAG_TC);         //清发送完成标志/////////////////////////////////////////////////////////////A
}

//重定向c库函数printf到USART1,由printf调用
int fputc(int ch, FILE *f)
{        
        USART_SendData(USART1, (unsigned char) ch);//将Printf内容发往串口
        while( USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);//检查串口发送是否完成的标志位TC        
        return (ch);
}

//输出单个字节
void USART1_PutChar(uint8_t Data)  
{  
    USART_SendData(USART1, Data);  
    while( USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET );
}  

//输出数组
void USART1_PutStr(uint8_t *str, uint8_t Data_lenth)   
{   
    uint8_t i = 0;
    for (i = 0; i < Data_lenth; i++)   
    {      
        USART1_PutChar(*str);   
        str++;   
    }   
}

//串口1中断服务程序
void MY_USART1_IRQHandler(void)                        
{
    uint8_t i = 0;
    if(USART_GetITStatus(USART1, USART_IT_IDLE)==SET) { //空闲总线中断   
        i = USART1->SR;
        i = USART1->DR; //  ---待解/////////////////////////////////////////////////////////////B     

        DMA_Cmd(DMA1_Channel5, DISABLE);                //关闭DMA
        USART_ClearITPendingBit(USART1, USART_IT_IDLE); //清除中断标志
        USART_ITConfig(USART1, USART_IT_IDLE, DISABLE); //禁止中断,防止覆盖

        LENTH_USART_RX = 64 -  DMA_GetCurrDataCounter(DMA1_Channel5);   //得到真正接收数据个数  
        DMA1_Channel5->CNDTR = 64;      //重新设置接收数据个数   
        DMA_Cmd(DMA1_Channel5, ENABLE); //开启DMA
        FLAG_USART_RX = 1;              //接收数据标志位置1
        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  //再次开中断
    }   
}

main函数中调用
    while (1) {
        if(FLAG_USART_RX == 1) {        
            USART1_PutChar(LENTH_USART_RX);
            USART1_PutStr(USART1_RX_BUF, LENTH_USART_RX);
            FLAG_USART_RX = 0;LED_EYE2_XOR;
        }

    }

有2点需重视下:A处如果屏蔽了,则上电的时候串口输出会缺少第一个数据;B是我没能理解的,有看到的请帮助理解下,多谢了!


最佳答案

查看完整内容[请看2#楼]

第一个问题: note说得很清楚啊,读一次SR随后再读一次DR就可以清除标记了。i = USART1->SR; i = USART1->DR;就是读嘛。 第二个问题: 你注意看param这一说明里面有没有USART_IT_IDLE这个参数。
原来你也在这里
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

16

主题

409

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1292
金钱
1292
注册时间
2014-12-20
在线时间
534 小时
发表于 2016-1-29 10:10:47 | 显示全部楼层
大雄先生 发表于 2016-1-29 18:00
多谢!原文:IDLE (Idle line detected) pending bits are cleared by software sequence: a read operat ...

第一个问题:
note说得很清楚啊,读一次SR随后再读一次DR就可以清除标记了。i = USART1->SR;   i = USART1->DR;就是读嘛。
第二个问题:
你注意看param这一说明里面有没有USART_IT_IDLE这个参数。
回复

使用道具 举报

16

主题

409

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1292
金钱
1292
注册时间
2014-12-20
在线时间
534 小时
发表于 2016-1-29 13:15:56 | 显示全部楼层
本帖最后由 BMN李 于 2016-1-29 13:20 编辑

第一个问题:
如果是判断的TC位来判断发送是否完成,就是说要等到数据到移位寄存器中完成后,再发送第二帧数据,但是,由于SR寄存器的复位值是0x00c0,所以首先TC位就是置1的,所以当第一次数据写入DR寄存器中的时候头一个while没起作用,然后第二个数据来了就把第一个数据覆盖了,这样我们经常发现,串口的第一个数据丢失了!所以在串口配置完成后加入USART_ClearFlag(USART1, USART_FLAG_TC);         //清发送完成标志

第二个问题:
3.5库里面是有说明的。在USART_ClearITPendingBit函数的note说明中,如下:
问题2.png


回复

使用道具 举报

17

主题

53

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
296
金钱
296
注册时间
2015-4-29
在线时间
26 小时
发表于 2016-1-29 14:08:03 | 显示全部楼层
顶一个
回复

使用道具 举报

6

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
432
金钱
432
注册时间
2016-1-19
在线时间
38 小时
 楼主| 发表于 2016-1-29 18:00:27 | 显示全部楼层
BMN李 发表于 2016-1-29 13:15
第一个问题:
如果是判断的TC位来判断发送是否完成,就是说要等到数据到移位寄存器中完成后,再发送第二帧 ...

多谢!原文:IDLE (Idle line detected) pending bits are cleared by software sequence: a read operation to USART_SR register (USART_GetITStatus()) followed by a read operation to USART_DR register (USART_ReceiveData());

对应的操作:       i = USART1->SR;   i = USART1->DR;

我想了解这样两次读操作就可以清标记了吗?和这句有何区分呢?USART_ClearITPendingBit(USART1, USART_IT_IDLE);多谢!

原来你也在这里
回复

使用道具 举报

6

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
432
金钱
432
注册时间
2016-1-19
在线时间
38 小时
 楼主| 发表于 2016-1-29 18:02:18 | 显示全部楼层
BMN李 发表于 2016-1-29 13:15
第一个问题:
如果是判断的TC位来判断发送是否完成,就是说要等到数据到移位寄存器中完成后,再发送第二帧 ...

第一个问题不是我不理解,而是重点提出来,因为有一些朋友会犯这样的错。李生知识还是很稳固的。
原来你也在这里
回复

使用道具 举报

16

主题

173

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2441
金钱
2441
注册时间
2014-11-5
在线时间
348 小时
发表于 2016-1-29 21:33:26 | 显示全部楼层
问题二
QQ截图20160129213057.png
回复

使用道具 举报

6

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
432
金钱
432
注册时间
2016-1-19
在线时间
38 小时
 楼主| 发表于 2016-1-30 09:09:56 | 显示全部楼层

已明,多谢!
原来你也在这里
回复

使用道具 举报

6

主题

188

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
432
金钱
432
注册时间
2016-1-19
在线时间
38 小时
 楼主| 发表于 2016-1-30 09:14:36 | 显示全部楼层
BMN李 发表于 2016-1-29 18:33
第一个问题:
note说得很清楚啊,读一次SR随后再读一次DR就可以清除标记了。i = USART1->SR;   i = USAR ...

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)    * @param  USART_IT: specifies the interrupt pending bit to clear.
  *   This parameter can be one of the following values:
  *     @arg USART_IT_CTS:  CTS change interrupt (not available for UART4 and UART5)
  *     @arg USART_IT_LBD:  LIN Break detection interrupt
  *     @arg USART_IT_TC:   Transmission complete interrupt.
  *     @arg USART_IT_RXNE: Receive Data register not empty interrupt.
看来这样调用是无效的了USART_ClearITPendingBit(USART1, USART_IT_IDLE);
多谢了!



原来你也在这里
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-21 07:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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