绚紫飞鸥的个人空间 https://bbs.21ic.com/?1185562 [收藏] [复制] [RSS]

日志

STM32F103ZET6 TIMER5 四通道输入捕获PWM,用DMA采集输入捕获值

已有 663 次阅读2017-9-12 09:53 |个人分类:STM32|系统分类:单片机

自己闲来无事,打算做一个小车玩,四个电机如果想知道速度的话,就需要对其编码器的信号进行采集。
之前采用的是编码器接口模式,但是这样的话,虽然使用很方便,但是比较占用单片机的资源,毕竟四个
电机就需要用四个编码器,暂时也不需要知道电机转动方向,所以就取编码器中的A相信号,利用定时器
输入捕获功能来计算电机转速。TIMER5 四个通道来采集四个电机,也可以利用中断的形势来计算,也能
实现,但是这样很占用中断,所以采用DMA来采集,然后通过TIMER定时中断,来每隔一段时间比如10ms
来计算一次转速,大致实验了一下,采集到的数据大致符合电机的转速。具体计算函数没写,可以自己来算
一个。也可能有更好的方式,以后如果发现的话,再来分享

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度            
u16 tim5_cnt[8];  //[0] [1]  TIM5_CH1 [2] [3]  TIM5_CH2 
                            //[4] [5]  TIM5_CH3 [6] [7]  TIM5_CH4 
//通用定时器5 通道1 2 3 4 用做输入捕获,通过DMA传送CNT
void TIM5_Cap_Init(u16 arr,u16 psc)
{         
        GPIO_InitTypeDef GPIO_InitStructure;       
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_ICInitTypeDef  TIM_ICInitStructure;
        
        //定时器时钟和GPIO配置
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);        //使能TIM5时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟

       
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //PA0 清除之前设置  
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0123 输入  
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);                        //PA0123 下拉
       
        //初始化定时器5 TIM5         
        TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
        TIM_TimeBaseStructure.TIM_Prescaler =psc;         //预分频器   
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  
        //初始化TIM5输入捕获参数
        TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01         选择输入端 IC1映射到TI1上
            TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
         TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
            TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        //配置输入分频,不分频 
           TIM_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
            TIM_ICInit(TIM5, &TIM_ICInitStructure);
       
        TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //CC1S=01         选择输入端 IC2映射到TI2上
            TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
            TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
            TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        //配置输入分频,不分频 
           TIM_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
            TIM_ICInit(TIM5, &TIM_ICInitStructure);
       
        TIM_ICInitStructure.TIM_Channel = TIM_Channel_3; //CC1S=01         选择输入端 IC3映射到TI3上
           TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
            TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
            TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        //配置输入分频,不分频 
            TIM_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
            TIM_ICInit(TIM5, &TIM_ICInitStructure);
       
        TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=01         选择输入端 IC4映射到TI4上
            TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
            TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
            TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        //配置输入分频,不分频 
            TIM_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
       
            TIM_ICInit(TIM5, &TIM_ICInitStructure);
       
        //使能DMA请求
        TIM_DMACmd(TIM5, TIM_DMA_CC1, ENABLE); 
        TIM_DMACmd(TIM5, TIM_DMA_CC2, ENABLE); 
        TIM_DMACmd(TIM5, TIM_DMA_CC3, ENABLE); 
        TIM_DMACmd(TIM5, TIM_DMA_CC4, ENABLE); 
            
           TIM_Cmd(TIM5,ENABLE );        
}


void TIM5_DMA_CONFIG(void)
{
        DMA2_Config(DMA2_Channel5,(u32)(&TIM5->CCR1),(u32)tim5_cnt,2);
        DMA2_Config(DMA2_Channel4,(u32)(&TIM5->CCR2),(u32)&tim5_cnt[2],2);
        DMA2_Config(DMA2_Channel2,(u32)(&TIM5->CCR3),(u32)&tim5_cnt[4],2);
        DMA2_Config(DMA2_Channel1,(u32)(&TIM5->CCR4),(u32)&tim5_cnt[6],2);       
}
//DMA2的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量 
void DMA2_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
        DMA_InitTypeDef DMA_InitStructure;
         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);        //使能DMA传输
       
          DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
        DMA1_MEM_LEN=cndtr;
        DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;    //DMA外设基地址
        DMA_InitStructure.DMA_MemoryBaseAddr = cmar;                          //DMA内存基地址
        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_HalfWord;  //数据宽度为32位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为32位
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有中优先级 
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器

        DMA_Cmd(DMA_CHx, ENABLE);  //使能 所指示的通道         
//开启一次DMA传输 
void DMA_Channel_Enable(DMA_Channel_TypeDef*DMA_CHx)
        DMA_Cmd(DMA_CHx, DISABLE );  //关闭 所指示的通道      
         DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
         DMA_Cmd(DMA_CHx, ENABLE);  //使能所指示的通道 
}       


void TIM3_IRQHandler(void)   //TIM3中断
{
        if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
                {
                TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 

                        //在此可进行对数据的处理
                        DMA_Channel_Enable(DMA2_Channel5); 
                        DMA_Channel_Enable(DMA2_Channel4);
                        DMA_Channel_Enable(DMA2_Channel2);
                        DMA_Channel_Enable(DMA2_Channel1);

                }
}

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)