完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
最近固定翼入魔,练模拟器的时候,有个很不爽的事(因为我是把电脑接到大电视上玩模拟器),遥控器上要拖一根教练机接口的线。其实这个线输出的是PPM六通道融合的信号(当然,如果遥控器是10通道的就是10通道融合的)。作为一个玩STM32的人,怎么能容忍这种拖着一根线玩模拟器呢,随便找个开发板,把接收机的6通道信号分别如下连接:通道1---> PA0;
通道2---> PA1; 通道3---> PA2; 通道4---> PA3; 通道5---> PA0; 通道6---> PA4; 凤凰模拟器---> PB1和GND; 别忘了从开发板上供一个+5V的电源给接收机。 OK,先理理程序怎么设计。 接收机如图 第一排引脚输出的其实是一组舵机控制信号,20ms周期,高电平脉宽1000us~2000us对应油门0%~100%(具体对应关系可能略微差别)。而模拟器从遥控器教练输出端口输出来的其实是6通道融合后的PPM信号,具体看下图 所以现在要做的就是,把遥控器的6路PWM信号的每周期高电平脉宽测量出来,并融合成一个PPM信号,要测量的同时要保证其实时性,我们所有通道做最大油门和最小油门的情况分析,如下图 左边全部最小油门,右边最大油门 上图可以看出, 要想在最后一个通道数据测量完成之后再对第六通道的PPM进行融合处理的最佳时间为第3个通道下降沿发生时进行PPM融合处理,这样保证在接收到的信号与融合的信号只差半个周期(也就是10ms左右),如果本周期测量,下周期融合输出,会滞后一个周期。 六个通道的输入信号不会同时发生,是依次发生的(不信可以自己用逻辑分析仪或者示波器看~) 有了这个思路,程序基本上就清楚了 利用外部中断(上升下降触发模式)进中断记录systick定时器10us的计数获得每通道的高电平时长(单位10us),因为信号精度0.1ms,10us的systick定时器可以做到非常精准还原。 利用另一个系统定时器计时还原输出信号。具体程序如下: 1:端口配置GPIO.H中定义跳变宏 [C] 纯文本查看 复制代码 #define digitalHi(p,i) {p->BSRR=i;} //设置高电平 #define digitalLo(p,i) {p->BRR =i;} //设置低电平#define PPM_ON digitalHi(GPIOB,GPIO_Pin_1)#define PPM_OFF digitalLo(GPIOB,GPIO_Pin_1) 2:GPIO.C中初始化函数 [C] 纯文本查看 复制代码 void GPIO_Config(void){ /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启GPIOB和GPIOF的外设时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /*选择要控制的GPIOB引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIOB0*/ GPIO_Init(GPIOB, &GPIO_InitStructure); } 3:设置滴答定时器systick.h中 [C] 纯文本查看 复制代码 #ifndef __SYSTICK_H#define __SYSTICK_H#include "stm32f10x.h"void SysTick_Init(void);#endif 4:systick.c中配置初始化 [C] 纯文本查看 复制代码 void SysTick_Init(void){ /* SystemFrequency / 1000 1ms中断一次 * SystemFrequency / 100000 10us中断一次 * SystemFrequency / 1000000 1us中断一次 */// if (SysTick_Config(SystemFrequency / 100000)) // ST3.0.0库版本 if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5.0库版本 { /* Capture error */ while (1); }//关闭滴答定时器,初始化完后再开启 SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;} 5:中断初始化配置.h中的申明就不写了,直接配置函数如下: [C] 纯文本查看 复制代码 static void NVIC_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; EXTI_DeInit(); /* Configure one bit for preemption priority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置中断源 */ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //PA0的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13; //优先级,别设置太高 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 13; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //PA1的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 14; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //PA2的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //PA3的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 16; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 16; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //PA4的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 17; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 17; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //PA5的中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 18; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 18; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } [C] 纯文本查看 复制代码 void EXTI_PA0_5_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; /* config the extiline clock and AFIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); /* config the NVIC */ NVIC_Configuration(); /* EXTI line gpio config*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入,因为是测高电平脉冲 GPIO_Init(GPIOA, &GPIO_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升下降中断 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); EXTI_InitStructure.EXTI_Line = EXTI_Line1; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2); EXTI_InitStructure.EXTI_Line = EXTI_Line2; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3); EXTI_InitStructure.EXTI_Line = EXTI_Line3; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4); EXTI_InitStructure.EXTI_Line = EXTI_Line4; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); /* EXTI line mode config */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5); EXTI_InitStructure.EXTI_Line = EXTI_Line5; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } 6:输出用的系统定时器设置 [C] 纯文本查看 复制代码 /// TIM2中断优先级配置void TIM2_NVIC_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}/* * TIM_Period / Auto Reload Register(ARR) = 1000 TIM_Prescaler--71 * 中断周期为 = 1/(72MHZ /72) * 10 = 10us * * TIMxCLK/CK_PSC --> TIMxCNT --> TIM_Period(ARR) --> 中断 且TIMxCNT重置为0重新计数 */void TIM2_Configuration(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* 设置TIM2CLK 为 72MHZ */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); //TIM_DeInit(TIM2); /* 自动重装载寄存器周期的值(计数值) */ TIM_TimeBaseStructure.TIM_Period=10; /* 累计 TIM_Period个频率后产生一个更新或者中断 */ /* 时钟预分频数为72 */ TIM_TimeBaseStructure.TIM_Prescaler= 71; /* 对外部时钟进行采样的时钟分频,这里没有用到 */ TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); TIM_Cmd(TIM2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , DISABLE); /*先关闭等待使用*/ } 7:具体的中断函数写到main.c中,因为代码量不大-^- [C] 纯文本查看 复制代码 uint8_t i; //输出开关volatile u32 time = 0; // 输出计时变量 __IO u32 timenow; //输入信号暂存测量值__IO u32 timeCh1; //通道测量值__IO u32 timeCh2;__IO u32 timeCh3;__IO u32 timeCh4;__IO u32 timeCh5;__IO u32 timeCh6;void EXTI0_IRQHandler(void) //CH1通道中断处理{ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } // 判断进入后引脚是高电平,证明上升沿进入,暂存值清0,开启滴答定时器 if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)){ timeCh1 = timenow; } // 中断进入后引脚低电平,证明下跳沿进入,一个高电平测量结束,存入当前通道的滴答累加值 EXTI_ClearITPendingBit(EXTI_Line0);}void EXTI1_IRQHandler(void) { if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)){ timeCh2 = timenow; } EXTI_ClearITPendingBit(EXTI_Line1);}void EXTI2_IRQHandler(void){ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)){ timeCh3 = timenow; i=1; } // 本处多了i = 1;表示3通道结束时开启PPM融合输出开关 EXTI_ClearITPendingBit(EXTI_Line2);}void EXTI3_IRQHandler(void){ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)){ timeCh4 = timenow; } EXTI_ClearITPendingBit(EXTI_Line3);}void EXTI4_IRQHandler(void){ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)){ timeCh5 = timenow; } EXTI_ClearITPendingBit(EXTI_Line4);}void EXTI9_5_IRQHandler(void){ if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){timenow = 0;SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)){ timeCh6 = timenow; } EXTI_ClearITPendingBit(EXTI_Line5);}oid SysTick_Handler(void){ timenow++; //滴答定时器开启后进入中断,脉宽测量计数,单位10us}void TIM2_IRQHandler(void) //输出定时器中断,用于输出时输出信号计时{ if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) { time++; TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); } }int main(void){ GPIO_Config();/* 端口初始化 */ //USART1_Config(); 如果要调试,开启串口 EXTI_PA0_5_Config(); //中断配置 SysTick_Init(); /* 配置SysTick 为10us中断一次 */ TIM2_NVIC_Configuration(); //输出计时定时器中断配置 TIM2_Configuration(); //输出定时器配置 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); //开启输出定时器 PPM_ON; //拉高输出(PPM信号默认高电平) while(1) { if(i == 1) //第3通道测量完成开启输出 { time = 0; //输出计时清0 PPM_OFF; //拉低输出 while(time<=40){} //等待0.4ms的信号头 PPM_ON; //开始第一通道信号 time = 0; //同时清0计时器 while(time < timeCh1-30){}; //1通道高电平时间 = PWM测量脉宽-0.3ms信号分界 PPM_OFF; //时间到拉低 time = 0; //同事清0计时器 while(time<=30){} //0.3ms低电平 PPM_ON; //开始第2通道信号 while(time < timeCh2-30){}; PPM_OFF; time = 0; while(time<=30){} PPM_ON; while(time < timeCh3-30){}; PPM_OFF; time = 0; while(time<=30){} PPM_ON; while(time < timeCh4-30){}; PPM_OFF; time = 0; while(time<=30){} PPM_ON; while(time < timeCh5 -20){}; //本处理论应该是-30,但是根据实际波形微调成-20, PPM_OFF; time = 0; while(time<=30){} PPM_ON; while(time < timeCh6-20){}; PPM_OFF; time = 0; while(time<=30){} PPM_ON; //信号结束,从新拉高输出,一个信号完成 // printf("--- %d --- %d --- %d --- %d --- %d --- %d --- time: %drn",timeCh1,timeCh2,timeCh3,timeCh4,timeCh5,timeCh6,time); //如果要调试,开启 i = 0; //一组输出完成,从新锁定 } } } 基本代码就完成了,实际最大最小值情况的输出波形如下,蓝色为PPM信号,黄色为第一通道的波形,可以看出,每个周期完结前PPM融合也完成了。以后玩模拟器再也不用拖一根线了。其实另外的用处就是,某宝上卖的PPM信号编码器,玩航模的基本都不陌生,有些飞控只有PPM信号输入,但是很多遥控器都没有PPM输出,之后多通道的PWM信号输出,所以就要一块PWM编码PPM的模块来实现。 |
|
相关推荐
5个回答
|
|
楼主,最近也在弄这个,你是怎么捕捉这6路pwm高电平脉冲宽度的
|
|
|
|
收藏了,好东西。。
|
|
|
|
圖文解說清楚先收藏待以後再消化
|
|
|
|
滴答定时器加中断,具体看代码就行了
|
|
|
|
我的定时器为啥进不去中断,time_now的值一直是0
|
|
|
|
只有小组成员才能发言,加入小组>>
请问下图大疆lightbridge2遥控器主板电源芯片型号是什么?
4474 浏览 1 评论
使用常见的二极管、三极管和mos做MCU和模组的电平转换电路,但是模组和MCU无法正常通信,为什么?
346浏览 2评论
为了提高USIM卡电路的可靠性和稳定性,在电路设计中须注意的点有哪些?
349浏览 2评论
373浏览 2评论
366浏览 2评论
416浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 08:14 , Processed in 0.875373 second(s), Total 54, Slave 47 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号