• STM32解析航模遥控器的PPM信号


    一、前言

    • 通常遥控器能输出i-BUS、s-BUS、PPM、PWM信号,其中i-BUS、s-BUS需要配套的电平反向器(硬件取反),PWM信号占用引脚较多,对比而言PPM信号使用起来更为方便。
    • 航模遥控器性能强大,深受DIY用户的热爱,考虑到目前针对PPM信号解析的文章较少,本文将对其进行探讨。

    二、文章实验器材展示

    • 本人采用【FS-i6X航模遥控器】来完成本实验。
    • 本实验中,遥控器的输出为6通道
      在这里插入图片描述

    三、使用逻辑分析仪读取的PPM信号

    • 本人将遥控器配置成了6通道的输出模式,以下截图为读取到的一帧数据。

    请添加图片描述

    • 从下面这张大的截图中,我们可以发现规律:在每一帧数据之间会有一段较长的上升沿,这段上升沿的持续时间会波动,持续时间通常在8000微秒左右;紧接着,后面会产生8次上升沿,其中前面的6次分别对应着6个通道的输出,关于最后2个上升沿本人也不太清楚(不影响后续工作)。

    请添加图片描述

    • 上述两张关于PPM信号报文的规律将成为编程的依据。

    四、STM32定时器的输入捕获功能

    • 要想实现对PPT信号的采集处理,最重要的就是读取每一次上升沿的持续时间,这里要用到的就是定时器的输入捕获功能。

    定时器配置为输入捕获示例程序:

    //定时器5通道2输入捕获配置
    
    TIM_ICInitTypeDef  TIM5_ICInitStructure;
    
    void TIM5_Cap_Init(u16 arr,u16 psc)
    {	 
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
    
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5时钟
     	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
    	
    	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;  //PA1 清除之前设置  
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 输入  
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	GPIO_ResetBits(GPIOA,GPIO_Pin_1);						 //PA1 下拉
    	
    	//初始化定时器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输入捕获参数
    	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2; //CC1S=01 	选择输入端 IC1映射到TI1上
        TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
        TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
        TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
        TIM5_ICInitStructure.TIM_ICFilter = 0x0a;//IC1F=0000 配置输入滤波器 不滤波
        TIM_ICInit(TIM5, &TIM5_ICInitStructure);
    	
    	//中断分组初始化
    	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
    	
    	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允许更新中断 ,允许CC2IE捕获中断	
    	
        TIM_Cmd(TIM5,ENABLE ); 	//使能定时器5
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    定时器中断服务函数

    u8  TIM5CH1_CAPTURE_STA=0;	//输入捕获状态		    				
    u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值
    u16 remote_control_value[9];
     
    //定时器5中断服务程序	 
    void TIM5_IRQHandler(void)
    { 
    
     	if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
    	{	  
    		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)		 
    		{	    
    			if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
    			{
    				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
    				{
    					TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
    					TIM5CH1_CAPTURE_VAL=0XFFFF;
    				}else TIM5CH1_CAPTURE_STA++;
    			}	 
    		}
    	if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件
    		{	
    			if(TIM5CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
    			{	  			
    				TIM5CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
    				TIM5CH1_CAPTURE_VAL=TIM_GetCapture2(TIM5);
    		   	TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC2P=0 设置为上升沿捕获
    			}else  								//还未开始,第一次捕获上升沿
    			{
    				TIM5CH1_CAPTURE_STA=0;			//清空
    				TIM5CH1_CAPTURE_VAL=0;
    	 			TIM_SetCounter(TIM5,0);
    				TIM5CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
    		   	TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC2P=1 设置为下降沿捕获
    			}		    
    		}			     	    					   
     	}
     
        TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    五、PPM信号的解析

    主函数的处理过程如下:

    int main(void)
     {		
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 //串口初始化为115200
     	LED_Init();			     //LED端口初始化
     
     	TIM3_PWM_Init(20000-1,72-1); 		//不分频。PWM频率=72000/(899+1)=80Khz
     	TIM5_Cap_Init(0XFFFF,72-1);	//以1Mhz的频率计数 
      while(1)
    	{
     		if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
    		{
    			temp=TIM5CH1_CAPTURE_STA&0X3F;
    			temp*=65536;//溢出时间总和
    			temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
    			printf("HIGH:%d us\r\n",temp);//打印总的高点平时间
    			if(temp > 3000)
    			{
    				flag_i = 0;		
    				flag_k = 0;
    			}	
    			if(flag_i == 0)
    			{
    				remote_control_value[flag_k] = temp;
    				flag_k = flag_k + 1;
    				if(flag_k == 9)
    				{
    					flag_i = 1;
    					actual_ch_value[1] = remote_control_value[5];
    					actual_ch_value[2] = remote_control_value[1];
    					actual_ch_value[3] = remote_control_value[6];
    					actual_ch_value[4] = remote_control_value[2];
    					actual_ch_value[5] = remote_control_value[7];
    					actual_ch_value[6] = remote_control_value[3];				
    				}						
    			}				
    			TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
    		}
    		
    	}
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    六、实验效果展示

    • 在线调试截图
      在这里插入图片描述

    • 实物展示
      在这里插入图片描述

    七、疑问
    请添加图片描述

  • 相关阅读:
    VMware WorkStation安装和在VMware上安装Linux
    Vue2-一篇文章带你读懂Vue的代码(保姆篇详解)
    向量数据库X云计算驱动大模型落地电商行业,Zilliz联合AWS探索并贡献成熟解决方案
    【FPGA开发】时钟管理单元介绍与应用
    LeetCode笔记:Weekly Contest 310
    K8S二进制部署之定义CA证书与ETCD
    Java对文件操作包括CSV文件读写、文件压缩、文件下载
    【蜂鸟E203内核解析】Chap.4 累加运算NICE协处理器的设计
    navicate安装教程
    Teamcenter RAC 开发之《Excel模版导出》
  • 原文地址:https://blog.csdn.net/qq_44343584/article/details/126573194