Luckybird 发表于 2019-7-1 12:46:06

基于STM32F103的DMA数据发送

实现功能:STM32每间隔1秒,串口采用DMA方式发送一帧数据。
硬件:STM32F103、485通讯接口
我的构思:开启定时器定时1秒,计数器溢出触发DMA,而后DMA发送一帧数据,DMA发送完成产生发送完成中断,重新配置DMA再次。
问题:我写完之后采用串口调试助手调试,结果发现数据是1秒一个字节发送出去的,而非一秒一帧发送出去的,然后等数据全部发送完之后进入的DMA中断服务函数。看了许久不知道有什么毛病。求各路大神帮助,不胜感激。以下是相关外设配置程序。
maim.c:
int main(void)
{       
                uint16_t i;
        /*填充将要发送的数据*/       
for(i=0;i<SENDBUFF_SIZE;i++)
{
    SendBuff       = 'P';
                if(i == SENDBUFF_SIZE-1)       
                        SendBuff       = 'Q';
}
       

        LED_GPIO_Config();

/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART3_Config(); //串口配置
        /* 发送使能 */
        USART3_DMA_Config();//DMA配置
        RS485_TX_EN();//485发送使能
        BASIC_TIM_Init();//定时器配置
        TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);//定时器发送DMA请求

        while(1)
        {

        }
}

DMA与串口的配置:
void USART3_DMA_Config(void)
{
                DMA_InitTypeDef DMA_InitStructure;
       
                // 开启DMA时钟
                RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
                /* 复位初始化 DMA 数据流 */
                DMA_DeInit(DMA1_Channel2);
                // 设置DMA外设地址:串口数据寄存器地址*/
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
                // 内存地址(要传输的变量的指针)
                DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
                // 方向:从内存到外设       
                DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
                // 传输大小       
                DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
                // 外设地址不增          
                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模式,一次或者循环模式
                DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
                //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;       
                // 优先级:中       
                DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
                // 禁止内存到内存的传输
                DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
                // 配置DMA通道                  
                DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);       
               
                DMA_SetCurrDataCounter(DMA1_Channel2, SENDBUFF_SIZE);
                NVIC_DMA1_2_Configuration();

                //开启DMA通道的TC中断:传输完成中断
    DMA_ITConfig(DMA1_Channel2,DMA_IT_TC,ENABLE);
                // 使能DMA
                DMA_Cmd (DMA1_Channel2,ENABLE);
}

void DMA1_Ch2_IRQ(void)//DMA中断服务函数
{
           if(DMA_GetITStatus(DMA1_IT_TC2))
               {                       
                               DMA_ClearITPendingBit(DMA1_IT_GL2); //清除全部中断标志 //DMA_ClearFLAG(DMA1_FLAG_TC2); //清除全部中断标志(这种写法也可以)
//                               RS485_RX_EN();
                                // 使能串口接收中断
//                                USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
//                                // 使能串口空闲中断(用于检测一帧数据接收完毕)
//                                USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);       
                }
//                       //清TC标志
//   DMA_ClearFlag(DMA1_FLAG_TC4);
   //关闭DMA通道
//   DMA_Cmd(DMA1_Channel2, DISABLE);
        USART3_DMA_Config();
//        RS485_TX_EN();               
        }定时器的配置:
static void BASIC_TIM_Mode_Config(void)
{
    TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
               
                // 开启定时器时钟,即内部时钟CK_INT=72M
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
       
                // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Period = 9000-1;       

          // 时钟预分频数为71,则驱动计数器的时钟 为CLK/(71+1)=1M
    TIM_TimeBaseStructure.TIM_Prescaler= 8000-1;
       
                // 时钟分频因子 ,基本定时器没有,不用管
    //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
               
                // 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
    //TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
               
                // 重复计数器的值,基本定时器没有,不用管
                //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
       
          // 初始化定时器
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
               
                // 清除计数器中断标志位
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
          
//                // 开启计数器中断
//                TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
               
                //UDE:更新DMA请求使能 (Update DMA request enable) */
//        TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);//定时器发送DMA请求
                // 使能计数器
    TIM_Cmd(TIM2, ENABLE);       

}

void BASIC_TIM_Init(void)
{
        BASIC_TIM_NVIC_Config();
        BASIC_TIM_Mode_Config();
}






天臆弄人 发表于 2019-7-1 13:43:30

1秒发1次,直接发就是了,不需要DMA啊,而且你的DMA配置有问题

Luckybird 发表于 2019-7-1 13:48:12

天臆弄人 发表于 2019-7-1 13:43
1秒发1次,直接发就是了,不需要DMA啊,而且你的DMA配置有问题

DMA发送比较快   所以我想用DMA发送   

Luckybird 发表于 2019-7-1 13:49:23

天臆弄人 发表于 2019-7-1 13:43
1秒发1次,直接发就是了,不需要DMA啊,而且你的DMA配置有问题

配置有什么问题   这个程序它可以的DMA发送数据   但是没有实现一次一帧而是一次一个字节

天臆弄人 发表于 2019-7-1 13:51:56

Luckybird 发表于 2019-7-1 13:48
DMA发送比较快   所以我想用DMA发送

DMA发送是硬件发送不占用CPU时间,和你直接发送时间是一样的,

Luckybird 发表于 2019-7-1 14:00:40

天臆弄人 发表于 2019-7-1 13:51
DMA发送是硬件发送不占用CPU时间,和你直接发送时间是一样的,

是的   这也是我选择DMA的原因之一    不占用CPU    CPU还要干其他的事儿

wenyangzeng 发表于 2019-7-1 16:34:30

直接在DMA传输结束中断里发送数据就好了,

mvvm 发表于 2019-7-1 17:16:34

我猜你这种情况下的DMA传输完成中断应该也不是1s进去一次,问题原因是要想产生DMA传输完成中断,必须产生N个DMA请求,这个N就等于DMA传输的数据大小,你现在定时器频率是1Hz也就是1s才产生一次中断请求,传输一个数据。因此你要实现发送一帧数据到串口需要把TIM的定时器频率更改比如1s发送10个数据,就需将定时器频率设为100ms也就是10Hz

Luckybird 发表于 2019-7-2 09:04:55

mvvm 发表于 2019-7-1 17:16
我猜你这种情况下的DMA传输完成中断应该也不是1s进去一次,问题原因是要想产生DMA传输完成中断,必须产生N ...

是的我测试过    发现DMA传输完成中断是我定义的数据一字节一字节的全部发送完毕之后才进入的       主要是我觉得很奇怪   之前我用串口发送DMA请求的时候    DMA是一次发送一帧的   而用TIM却成了一次一字节   我想要是实现的也是一次发送全部的数据。。。。。。然后在DMA传输完成中断里面刷新DMA配置
页: [1]
查看完整版本: 基于STM32F103的DMA数据发送