OpenEdv-开源电子网

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

求助,串口DMA发送只能发送第一次

[复制链接]

0

主题

4

帖子

0

精华

新手入门

积分
13
金钱
13
注册时间
2020-11-25
在线时间
10 小时
发表于 2021-4-17 22:40:33 | 显示全部楼层 |阅读模式
13金钱
本帖最后由 wan431 于 2021-4-17 23:50 编辑

小白刚入STM32,试写了一个DMA串口1发送的程序,串口1DMA只能发送一次。查网络资料排查了几天,未果,遂来提问,金币都在这了,很少,希望有大佬来救,工程文件也一并附在下方。
注:还必须先发给串口1数据,串口1才能利用DMA发送一次,之后就一直不能发送了。很奇怪,也没有写任何和接收有关的代码。环境:STM32F103,正点原子精英板
//这是usart.h的内容
void usart1_Init(void)
{
                /* 串口1配置 115200          */
    USART_InitTypeDef USART_InitStruct;                // 串口配置
    NVIC_InitTypeDef NVIC_InitStructure;        // 中断配置
    DMA_InitTypeDef DMA_InitStruct;                        // DMA 配置
    GPIO_InitTypeDef GPIO_InitStruct;
        
    USART_DeInit(USART1);   // 寄存器恢复默认值
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  // 使能串口时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   // 使能GPIOA时钟


    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;    // TX 推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);     // PA9

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;      // RX上拉输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStruct);     // PA10

    /* 串口参数配置 */
    USART_InitStruct.USART_BaudRate = BAUD_RATE;            // 波特率:115200
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    // 无流控
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;    // 收发
    USART_InitStruct.USART_Parity = USART_Parity_No;                // 无校验位
    USART_InitStruct.USART_StopBits = USART_StopBits_1;             // 1个停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;        // 8个数据位
    USART_Init(USART1, &USART_InitStruct);
    USART_Cmd(USART1, ENABLE);  // 使能串口

    /* 串口中断配置 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          // 子优先级
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;           // 串口1中断
    NVIC_Init(&NVIC_InitStructure);     // 嵌套向量中断控制器初始化

    USART_ITConfig(USART1, USART_IT_TC,   ENABLE);  // 使能串口发送中断,发送完成产生 USART_IT_TC 中断
    //USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  // 使能串口空闲中断,接收一帧数据产生 USART_IT_IDLE 空闲中断

    /* 串口DMA配置 */
    DMA_DeInit(DMA1_Channel4);  // DMA1 通道4,寄存器复位

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  // 使能 DMA1 时钟

    // TX DMA1_CH4
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                   // 本次是外设到内存,所以关闭内存到内存
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;// 内存数据宽度,按照字节存储
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;        // 内存递增,每次串口收到数据存在内存中,下次收到自动存储在内存的下一个位置
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                  // 正常模式
    DMA_InitStruct.DMA_PeripheralBaseAddr = USART1_BASE + 0x04; // 外设的基地址,串口的数据寄存器
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    // 外设的数据宽度,按照字节存储,与内存的数据宽度一致
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   // 接收只有一个数据寄存器 RDR,所以外设地址不递增
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;            // 优先级

    DMA_InitStruct.DMA_BufferSize = 0;                          // 发送缓冲区的大小,初始化为0不发送
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;             // 发送是方向是外设到内存,外设作为目的地
    DMA_InitStruct.DMA_MemoryBaseAddr =(uint32_t)uart1SendData; // 发送内存地址,从哪里发送
    DMA_Init(DMA1_Channel4, &DMA_InitStruct);

    USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);// 使能DMA串口发送和接受请求
    DMA_Cmd(DMA1_Channel4, DISABLE);    // 禁止发送
}

void usart1_send(u8 *arr,u8 len)
{
  if(len == 0)        // 判断长度是否有效
      return;
        
    while (DMA_GetCurrDataCounter(DMA1_Channel4))BEEP = 1;  // 检查DMA发送通道内是否还有数据
                BEEP = 0;
    if(arr)
      memcpy(uart1SendData, arr, len);

    // DMA发送数据-要先关 设置发送长度 开启DMA
    DMA_Cmd(DMA1_Channel4, DISABLE);
               
                //重新写入要传输的数据数量
    DMA_SetCurrDataCounter(DMA1_Channel4, len);   
                //重新设置外设地址
                DMA1_Channel4->CPAR = USART1_BASE + 0x04;
                //重新设置内存地址
                DMA1_Channel4->CMAR = (uint32_t)uart1SendData;
               
    DMA_Cmd(DMA1_Channel4, ENABLE);     // 启动DMA发送  
        
}
void USART1_IRQHandler(void)
{
                if(USART_GetFlagStatus(USART1,USART_IT_TC) != RESET)
                {
                        USART_ClearITPendingBit(USART1,USART_IT_TC);//清除标志位
                        DMA_Cmd(DMA1_Channel4,DISABLE); //关闭发送通道
                        uart1SendFlag = 1;
                }
}

//这下面是main.c的内容
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "a_bunch_init.h"
#include "usart.h"

u8 group[12] = {1,2,3,0};

int main()
{
        //usart2_Init();
        usart1_Init();
        
        LED_init();
        
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  // 设置中断优先级分组
        
        while(1)
        {
//                if(uart2RecvFlag == 1) //接收到数据
//                {
//                        uart2RecvFlag = 0;
//                        LED0 = 0;
//                        uart2DmaClear();//清空DMA接收通道计数
//                        usart1_send(group,12);
//                        memset(uart2RecvData,'\0',sizeof(uart2RecvData));  // 清空缓冲池        
//                }
                usart1_send(group,12);
                if(uart1SendFlag == 1) //发送完成
                {
                        uart1SendFlag = 0;
                        LED1 = 0;
                        LED0 = 1;
                        delay_ms(200);
                }
                else
                {
                        LED0 = 0;
                        LED1 = 1;
                        
                }

        }
        

}





Template_5.rar

274.59 KB, 下载次数: 3

存在错误的工程

最佳答案

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

你把发送中断发生查询函数改为 USART_GetITStatus ,然后你延时定时器没有初始化,你需要在延时使用前调用一次 delay_init()函数,我用你的工程改了这两个好了。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2021-4-17 22:40:34 | 显示全部楼层
y284858 发表于 2021-4-19 11:51
你开了发送中断标志清理有问题

你把发送中断发生查询函数改为 USART_GetITStatus ,然后你延时定时器没有初始化,你需要在延时使用前调用一次 delay_init()函数,我用你的工程改了这两个好了。
回复

使用道具 举报

6

主题

890

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1481
金钱
1481
注册时间
2020-8-19
在线时间
336 小时
发表于 2021-4-19 11:19:32 | 显示全部楼层
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2021-4-19 11:51:31 | 显示全部楼层
本帖最后由 y284858 于 2021-4-19 14:16 编辑

你开了发送中断标志清理有问题
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2021-4-19 14:21:38 | 显示全部楼层
无需开启发送中断,参考DMA收发实现:
https://acuity.blog.csdn.net/article/details/108367512
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2021-4-19 14:26:25 | 显示全部楼层
Acuity 发表于 2021-4-19 14:21
无需开启发送中断,参考DMA收发实现:
https://acuity.blog.csdn.net/article/details/108367512

他是用发送中断来判断DMA发送完成
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2021-4-19 15:16:37 | 显示全部楼层
y284858 发表于 2021-4-19 14:26
他是用发送中断来判断DMA发送完成

那是开启DMA发送完成中断“DMA_IT_TC”,非“USART_IT_TC”。
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2021-4-19 15:30:10 | 显示全部楼层
Acuity 发表于 2021-4-19 15:16
那是开启DMA发送完成中断“DMA_IT_TC”,非“USART_IT_TC”。

也可以使用USART_IT_TC,USART_IT_TC是在数据寄存器和移位寄存器同时为空的时候触发,所以也可以作为DMA发送完成判断。
回复

使用道具 举报

0

主题

4

帖子

0

精华

新手入门

积分
13
金钱
13
注册时间
2020-11-25
在线时间
10 小时
 楼主| 发表于 2021-4-19 16:16:30 | 显示全部楼层
y284858 发表于 2021-4-17 22:40
你把发送中断发生查询函数改为 USART_GetITStatus ,然后你延时定时器没有初始化,你需要在延时使用前调 ...

厉害,改了之后确实好了。折腾几天终于能用上DMA了,谢聚飞。
看来USART_GetITStatus和USART_GetFlagStatue的区别还是要好好查查的。谢飞谢飞。
回复

使用道具 举报

0

主题

4

帖子

0

精华

新手入门

积分
13
金钱
13
注册时间
2020-11-25
在线时间
10 小时
 楼主| 发表于 2021-4-19 16:20:55 | 显示全部楼层
本帖最后由 wan431 于 2021-4-19 16:23 编辑

厉害,这篇文章确实很有参考价值,小白我可要花段时间理解理解。非常感谢您的回答。
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2021-4-19 18:36:23 | 显示全部楼层
wan431 发表于 2021-4-19 16:16
厉害,改了之后确实好了。折腾几天终于能用上DMA了,谢聚飞。
看来USART_GetITStatus和USART_GetFlagStat ...

能帮到你就好
USART_GetITStatus是查询中断标志状态位的同时也检测是否打开了该状态的中断使能,
USART_GetFlagStatue是只查询对应的状态位他对应的宏应该使用USART_FLAG_TC,
但是因为有多个中断使用同一个中断服务函数的情况,所以判断是否发生了中断应该使用USART_GetITStatus,以防止中断标志位置位但是实际上没有打开中断的时候其他中断产生误进入该中断处理程序
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-27 23:19

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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